let、块级作用域、const定义变量和var定义变量区别及注意事项

我心飞翔 分类:javascript

2.1 let与var定义变量的区别

  1. let声明变量不存在变量提升。即:变量在声明前使用会报错,使用var声明变量在使用前不会报错,打印为undefined;
console.log(a);    //undefined
var a;
 
console.log(b);
let b;
 

报错:

index.html:19 Uncaught ReferenceError: Cannot access 'b' before initialization
 
  1. let声明变量会造成暂时性死区。即:在使用let声明变量前使用变量,会报错,直到使用let声明后会正常打印
var a = 0 ;
function fun(){
    console.log(a);
    let a = 3;
}

fun();
 

报错:在a变量声明前不可使用;这个例子中虽然使用var声明了全局变量a,但是只要在块级作用域中使用let声明了a,那么a就绑定了这个区域,不受外部的影响,在执行console打印之前let未声明a,所以会报错。

index.html:21 Uncaught ReferenceError: Cannot access 'a' before initialization
 

在块级作用域内,在let声明变量之前,都属于变量的“死区”,在“死区”里,使用typeof判断变量类型会抛出错误,在变量一定要声明后再使用!

  1. let不允许在相同作用域内重复声明同一个变量。

2.2 块级作用域

没有块级作用域会导致很多问题

  1. 函数内部变量覆盖函数外部变量。

在fun函数中,原意是要打印函数外部变量a的值,但是因为函数内部定义了a变量,变量提升导致fun函数内的a变量覆盖了外部的a变量,此时console打印的为还未声明的内部a变量。

var a="hello";
function fun() {
    console.log(a);      // undefined
    if(true){
        var a="world"
    }
}
fun();
 
  1. for循环语句中循环变量泄露为全局变量,容易造成内存泄露
var a=[];
for(let i=0;i<6;i++){
    a[i]=i;
}
console.log(i)
 

es6中新增块级作用域。

  1. 外层作用域无法读取内层作用域内变量。
  2. 内层作用于可以定义外层作用域的同名变量。

立即执行匿名函数写法:

写法一:

(function (a,b){console.log(a+b)})(3,4)    //7
 

写法二:

(function(a,b){console.log(a+b)}(1,2))   //3
 

写法三:立即执行函数也可以有返回值

var mynum=(function(a,b){return a+b;}(1,1));
console.log(mynum);            //2
 

一定是表达式才可以被执行符号执行,只要被() 括起来的都是表达式,如下:

(function(){} () )
(function(){})()
var test = function() {}() 
 

立即执行函数常见用法:

function test(){
    var arr=[];

    for(var i=0;i<10;i++){
        (function(j){
            arr[j] = function(){
                document.write(j+'')
            }
        })(i)
    }
    return arr;
}

var myArr = test();

for(var j=0; j<4;j++){
    myArr[j]();
}
 

页面显示 0,1,2,3

在块级作用域中声明函数

function f(){console.log("outside")};

(function () {
    if(false){
        function f() { console.log("inside") }
    }
    f()
}())
 

不用环境运行后主要区别,以上例讲解:

  1. es5中,输出inside,因为if内声明的函数f提升到函数头部。
  2. es6中,输出outside,块级作用域内声明的函数类似于let,对作用域之外没有影响。真的在es6浏览器中会报错,因为es6附录中规定,浏览器可以不遵守上面的规定(仅对es6浏览器),使用自己的行为方式。如下:
  • 允许块级作用域中声明函数
  • 函数声明类似于var,提升到全局作用于或函数作用域头部
  • 函数声明还会提升到所在块级作用域头部

es6浏览器中实际运行以下代码:

function f(){console.log("outside")};

(function () {
    var f = undefined;
    if(false){
        function f() { console.log("inside") }
    }
    f()
}())
 

在块级作用域中声明函数,不同浏览器会出现不同的结果。所以应该避免在块级作用域内声明函数,如果确实需要,应该写成函数表达式形式。块级作用域中声明函数必须使用大括号 { }

function f(){console.log("outside")};

(function () {
    if(false){
        let a = function f() { console.log("inside") }
    }
    f()
}())

//outside
 

2.3 const声明变量

const声明一个只读的常量。使用const声明变量时必须立即初始化。

const a;
//VM213:1 Uncaught SyntaxError: Missing initializer in const declaration
 

const和let一样不存在变量提升、存在暂存性死区,只能在声明后使用。和let一样不可重复定义相同变量。

本质:并不是变量的值不能改动,而是变量指向的内存地址不得改动。

  • 对于简单类型数据(数值、字符串、布尔值),值就存在变量指向的内存地址中,因此等同于一个常量。
  • 对于复合型数据(数组、对象),变量指向的内存地址只是一个指针,const只能保证这个指针是固定的,它指向的数据结构是否可变,不能控制。在使用const定义对象时必须注意。

使用const定义对象,可以添加属性方法、但不可以更改指针地址,如下面obj = { }

const obj = {};
obj.name="hello";

obj={}
// Uncaught TypeError: Assignment to constant variable
 

冻结对象方法:Object.freeze

const obj = Object.freeze({})
 

2.4 顶层对象的属性

回复

我来回复
  • 暂无回复内容