ECMAScript let和const命令

作者: 墨马 | 来源:发表于2017-09-13 16:45 被阅读24次

let命令

ES6新增了 let 命令,用来声明变量。它的用法类似于 var ,但是所声明的变量,只在 let 命令所在的代码块内有效

var a = [];
for (var i = 0; i < 10; i++) {
     a[i] = function () {
    console.log(i);
};
a[6]();//10

i是由var声明的所以全局范围内有效,所以每次循环都会覆盖旧值,最后输出的是最后一轮i的值

var a = [];
for (let i = 0; i < 10; i++) {
    a[i] = function () {
    console.log(i);
};
a[6](); // 6

i由let声明所以只在本轮循环有效,每次都是一个新的变量,所以最后输出是6

变量提升

javascript的预编译是以代码块为范围<script></script>,即每遇到一个代码块都会进行 预编译>执行

<script>
 var a = "1";    //声明变量a

    function b(){    //声明方法b
        alert();
 }
 var c = function(){    //声明变量c
    alert();
 }
</script>

a、c为变量赋值,b为函数声明
当执行以上的代码时,首先会进入预编译阶段,对与变量赋值a、c会在内存中开辟一块内存空间并指向变量名,且赋值为undefined对于函数声明,则同样会进行开辟内存空间,但此时会直接将函数体进行处理,即用函数声明方式,则在预编译阶段便已完成了函数的创建工作
无论顺序如何先声明变量后声明函数

预编译阶段
<script>
    var a = undefined;
    var c = undefined;
    var b = function(){
      alert();
    }
</script>
执行阶段
<script>
    a = "1";
    c = function(){
      alert();
    }
</script>

而let命令不存在预编译

暂时性死区

var tmp = 123;
if (true) {
   tmp = 'abc'; // ReferenceError
   let tmp;
}

ES6规定存在let和const的区块中,这些命令声明的变量从一开始就成了封闭作用,在声明之前使用就会报错。称为暂时性死区TDZ

if (true) {
   // TDZ开始
   tmp = 'abc'; // ReferenceError
   console.log(tmp); // ReferenceError
   let tmp; // TDZ结束
   console.log(tmp); // undefined
   tmp = 123;
   console.log(tmp); // 123
}
typeof undeclared_variable // "undefined"

undeclared_variable 是一个不存在的变量名,结果返回“undefined”。

typeof x; // ReferenceError
let x;

变量 x 使用 let 命令声明,所以在声明之前,都属于 x 的“死区”,typeof 运行时就会抛出一个 ReferenceError

function bar(x = y, y = 2) {
   return [x, y];
}
bar(); // 报错

上面代码中,调用 bar 函数之所以报错(某些实现可能不报错),是因为参数 x 默认值等于另一个参数 y ,而此时 y 还没有声明,属于”死区“。如果 y 的默认值是 x ,就不会报错,因为此时 x 已经声明了。

function bar(x = 2, y = x) {
   return [x, y];
}
bar(); // [2, 2]

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

不允许重复声明

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

// 报错
function () {
   let a = 10;
   var a = 1;
}
// 报错
function () {
   let a = 10;
   let a = 1;
}

不能在函数内部重新声明参数

function func(arg){
    let arg; // 报错
}
function func(arg) {
   let arg; // 不报错
}

块级作用域

function f1() {
   let n = 5;
   if (true) {
      let n = 10;
   }
   console.log(n); // 5
}

这表示外层代码块不受内层代码块的影响。外层作用域无法读取内层作用域的变量。内层作用域可以定义外层作用域的同名变量。如果使用 var 定义变量 n ,最后输出的值就是10。

function f() { 
   console.log('I am outside!');
 }
(function () {
   if(false) {// 重复声明一次函数f
       function f() { console.log('I am inside!'); }
   }
   f();
}());

在ES5中运行,会得到“I am inside!”,但是在ES6中运行,会得到“I am outside!”。这是因为ES5存在函数提升,不管会不会进入 if 代码块,函数声明都会提升到当前作用域的顶部,得到执行;而ES6支持块级作用域,不管会不会进入if代码块,其内部声明的函数皆不会影响到作用域的外部。

{
   let a = 'secret';
   function f() {
      return a;
   }
}
f(); // 报错

块级作用域外部,无法调用块级作用域内部定义的函数

let f;
{
   let a = 'secret';
   f = function () {
   return a;
   };
}
f(); // "secret"

const命令

const 声明一个只读的常量。一旦声明,常量的值就不能改变

const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: "PI" is read-only

const一旦声明变量,就必须立即初始化,不能留到以后赋值

const foo;

SyntaxError: missing = in const declarati

const的作用域与let命令相同:只在声明所在的块级作用域内有效

if (true) {
   const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined

const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

if (true) {
   console.log(MAX); // ReferenceError
   const MAX = 5;
}

const声明的常量,也与 let 一样不可重复声明

var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;

对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变

const foo = {};
foo.prop = 123;
foo.prop
// 123
foo = {}; // TypeError: "foo" is read-only

如果真的想将对象冻结,应该使用 Object.freeze 方法。

const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

除了将对象本身冻结,对象的属性也应该冻结。

var constantize = (obj) => {
   Object.freeze(obj);
   Object.keys(obj).forEach( (key, value) => {
      if ( typeof obj[key] === 'object' ) {
         constantize( obj[key] );
      }
   });
};

ES5只有两种声明变量的方法: var 命令和 function 命令。ES6除了添加 let 和 const 命令,后面章节还会提到,另外两种声明变量的方法: import 命令和 class 命令。所以,ES6一共有6种声明变量的方法

全局对象的属性

未声明的全局变量,自动成为全局对象 window 的属性,这被认为是JavaScript语言最大的设计败笔之一。这样的设计带来了两个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道,其次程序员很容易不知不觉地就创建了全局变量(比如打字出错)。另一方面,从语义上讲,语言的顶层对象是一个有实体含义的对象,也是不合适的。
ES6为了改变这一点,let 命令、 const 命令、 class 命令声明的全局变量,不属于全局对象的属性

var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1
let b = 1;
window.b // undefined

相关文章

网友评论

    本文标题:ECMAScript let和const命令

    本文链接:https://www.haomeiwen.com/subject/sjzksxtx.html