let和const

1. let命令


let命令声明的变量只在所在的代码块中有效。for循环中很适合使用let,如果使用var则会创建一个全局变量,且for循环中声明的函数如果涉及i,指向的是同一个i值。在循环外调用永远是同一个值。

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

如果使用let的话,每一次循环中的i都是一个由let重新声明的新变量。上面这个例子如果使用let,最后的输出会是6。之所以能有这样的效果,是因为JavaScript引擎内部会记住上一轮循环的值,从而在上一轮的基础上初始化本轮的值。

另外,在for循环中,设置循环变量的部分是一个父作用域,而循环体是一个单独的子作用域。这意味着可以在循环体内用let单独声明i,并且与循环变量没有影响。

此外,let还有三个重要的特性:

  • 不存在变量提升:var有着变量提升的特点,在声明之前使用变量,值为undefined。但let在声明前使用会直接报错。
  • 暂时性死区(TDZ):只要块级作用域中存在let命令,那么这个声明就“绑定”了这个区域。哪怕这个变量已经有同名的全局变量被声明。在声明语句出现之前,这个变量都是存在但不可以使用的,如果使用则会报错。因此typeof运算符也不再是绝对安全的操作了。
  • 不允许重复声明:不允许在相同作用域内重复声明一个变量,也不能在函数内部重新声明参数。

不存在变量提升和暂时性死区的特性,都是为了减少运行时错误,防止在变量声明前就提前使用这个变量,从而导致意料之外的行为。

2. 块级作用域


let实际上为ES6新增了块级作用域。原本使用广泛的匿名立即执行函数表达式不再需要了。

函数能不能在块级作用域中声明是一个很复杂的问题。ES5规定,函数不能在块级作用域中声明(如if语句中)。而ES6引入了块级作用域,允许在块级作用域中声明函数。行为类似于let,在作用域外不得引用。

但在ES6浏览器中,为了减轻兼容带来的问题,允许浏览器有自己的行为方式。ES6浏览器会将函数声明提升到全局作用域或函数作用域头部,以及所在块级作用域的头部,所以在ES6浏览器环境下,函数声明是会报错的。

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }

(function () {
	//var f = undefined;   实际上函数声明被提升到了块级作用域的头部
  if (false) {
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

由于环境导致的行为差异较大,不推荐在块级作用域中声明函数。如果一定要这么做,建议写成函数表达式的形式。

另外需要注意,ES6的块级作用域必须用大括号包裹起来,如果没有的话JavaScript引擎不认为存在块级作用域。

3. const命令


const用于声明一个只读的常量,声明之后不得更改。这也说明const一旦声明必须立刻初始化,不能留到以后赋值。const与let一样,只在作用域内有效,不存在变量提升,存在暂时性死区,不能重复声明。

const实际上保证的不是变量的值不能改动,而是变量指向的内存地址所保存的数据不能改动。对于简单数据类型,值就保存在那个内存地址中,所以等同于常量。但对符合类型的数据来说,内存地址中保存的是一个指针,const只能保证这个指针是固定的(指向不能更改),但不能保证它指向的数据结构不发生改变。

如果真的想要将对象冻结,只能使用 Object.freeza 方法。

彻底冻结对象,不仅仅要冻结对象本身,还要将对象的属性也全部冻结。具体实现如下:

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

ES5只有两者变量声明方法:var 和 function 。而ES6有六种,除了这里介绍的 let 和 const,还有import 和 class。

4. 顶层对象的属性,globalThis对象


ES5中,顶层对象属性与全局对象是等价的。这样有很多缺点,如无法在编译时就报出变量未声明的错误,只有运行才知道(因为全局变量的可能是顶层对象的属性创造的,而属性的创造是动态的)。

ES6为了改变这一点,使得 let 和 const 和 class 声明的全局变量不属于顶层对象的属性。

ES2020引入globalThis对象作为顶层对象,任何环境下他都存在,并且可以指向全局环境的this。

原文地址:https://www.cnblogs.com/hermionepeng/p/13330955.html