ES6系列——之let与const(二)

写在前面:
关于letconstES6规定的一种创建变量和常量的一种方式。如果只是单独的了解这两个关键字的作用,那么你看到这里就可以了,后面我们将会对letconst进行详细的说明。这是最为基础的模块,同时也会说到浏览器在这个时候是如何让工作的,在与ES5中的var进行对比。


先说 var

我们先从ES5最基础的var说起。

  1. var a;

当我们的浏览器在执行这一句代码的时候,浏览器将它推入栈中执行,此时浏览器创建了一个a标识,该标识默认具有以下四个属性:

  1. - Configurable : 表示能否通过`delete`删除属性从而重新定义属性,这个特性默认为`true`
  2. - Enumerable : 表示能否通过`for-in`循环返回属性,像前面那样直接在对象上定义属性,它们的这个特性默认值为`true`
  3. - Writable : 表示能否修改属性值,这个属性默认为`true`
  4. - Value : 包含这个属性的数据值,这个特性默认为`undefined。

要修改属性的默认特性必须使用到ES5的Object.defineProperty()这个方法,这个方法接收三个参数,属性所在的对象、属性的名称、描述符对象。其中描述符对象必须是configurableenumerablewritablevalue,设置其中一个或多个值。例如:

  1. var p = { };
  2. Object.defineProperty(p,"name",{
  3. writable:false,
  4. value:"hello world"
  5. });
  6. console.log(p.name);//"hello world"
  7. p.name = "nihao";
  8. console.log(p.name);//"hello world"

所以,我们在默认调用a标识的时候,得到的值是undefined;
当我们给a标识赋值之后,如下:

  1. var a;
  2. a = 2;

此时,浏览器将a标识指向了一个对象,该对象的值为2。这里需要理解的是,创建的变量名称其实是一个标识,赋值是为了让这个标识指向一个对象,读取的时候得到的是这个对象的值。

同时,我们在ES5中创建的全局变量,其实是挂载到window对象下面的一个属性,全局对象会在执行的时候将该对象推入执行栈中,直到window对象被推出的时候,全局对象才会被销毁,否则全局对象会一直存在于浏览器的当前栈中。

ES5也会对var声明的变量进行变量提升,如果申明该变量是在全局作用域下,那么该变量的声明会被提到最前面执行,同时,直到执行到变量赋值的语句之后,变量才会被赋值,如果是在函数级作用域下声明的变量,也会被提升到当前函数作用域的最前面进行声明,这就是“变量提升

扩展讲解,函数名也会被提升,提升原理与变量提升原理类似。

如果我们再声明一个变量b,让变量b=a,如下:

  1. var a;
  2. a = 2;
  3. var b = a;

此时浏览器又创建了一个b标识,b标识同时具有configurableenumerablewritablevalue这四个属性,而且会将a标识指向的对象复制一份,其值会附带对象一起复制,这时候b标识指向的是一个之前a标识指向的对象复制的对象,


let命令

ES6引入了新的作用域(块级作用域),如何理解这个块级作用域,简单的说就是{}大括号所包含的这个区域。letconst都是作用域块级作用域,如果实在全局作用域下使用let或者const声明的变量或者常量不会被挂载到window对象下面,但是可以被调用,在我们的执行栈中会存在。示例:

  1. var a = 1;
  2. let b = 2;
  3. window.a;//1
  4. window.b;//报错 :b is not defined

同时在外部也不能调用块级作用域中的声明变量,如下:

  1. if(true){
  2. let a = 0;
  3. const b = 1;
  4. }
  5. console.log(a);// a is not defined
  6. console.log(b); // b is not defined

同时,我们使用let或者const命令在某一个块级作用域中,该作用域存在 暂时性死区 ,意思是只要在一个块级作用域中声明了变量,该变量就会绑定到该块级作用域,不会受到外部的影响。如下:

  1. var tmp = 123;
  2. if(true){
  3. tmp = "abc";
  4. let tmp;
  5. }

ES6明确规定,如果在区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域,只要在声明之前就是用这些变量,就会报错。

另外,ES6在相同的作用域内不允许重复声明变量。不管是使用var+let还是let+let都会报错。


const命令

const命令用来声明常量,一旦声明,其值就不能被改变。

注意const声明的值不能改变值,这意味着,const一旦声明常量,就必须立即初始化,不能留到后面赋值。

所以下面这种写法会报错:

  1. const gh;

constlet一样,只在声明所在的块级作用域中有效,同时也不可以重复声明。

注意:const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须非常小心。如下:

  1. const foo = {}
  2. foo.pop = 123;
  3. console.log(foo.pop); //123

上面的代码我们看到创建了一个常量foo,且常量的value指向了一个对象,常量中,不可以改变的是,value指向这个对象的路径,而指向的对象是可以被修改的,所以后面我们可以给对象添加属性,同时也可以读取和写入属性值。

总结:常量存储的是一个地址,指向的一个对象,不可变的只是这个地址,即不能把foo指向另一个地址,但是对象本身是可变的,所以依然可以添加新属性。

另外一个例子:

  1. const a = [];
  2. a.push("a");//可执行
  3. a.length = 0; //可执行
  4. a = ["s"];//报错

评论: