let和const
2019-05-19 本文已影响0人
简单背包客
![](https://img.haomeiwen.com/i6163202/61d01b4c3760f411.png)
let 概念:
声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
//例如
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
1、不存在变量提升
var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
注:变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。
2、暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)
let tmp = 123;
//代码块中的tmp将不受外部的tmp影响,形成TDZ
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
//上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。
3、不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。
//不会报错(外面的a和{}里的a不在同一作用域里)
let a = 18;
if(true) {
let a = 20;
}
//会报错(在同一作用域{}内)
if(true) {
let a = 18;
let a = 20;
}
4、块级作用域
ES5只有全局作用域和函数作用域,没有块级作用域,这导致很多场景不可理,例如:
- 内层变量可能会覆盖外层变量
var name = 'lee';
function getName(){
console.log(name); //undefined
if(true){
var name = 'jack';
}
}
getName();
- 用来计数的循环变量泄露为全局变量。
var funs= [];
for(var i = 0; i < 10; i++){
funs.push(function(){
console.log(i);
});
}
funs[6](); //10
//为了解决这个问题,可以使用立即调用函数表达式(IIFE),
//以强制生成计数器变量的副本,就像这样:
var funs = [];
for(var i = 0; i < 10; i++){
funs.push((function(value){
return function(){
console.log(value);
}
})(i));
}
funs[6](); //6
ES6的块级作用域,块级声明用于声明在指定的作用域外无法访问的变量。块级作用域存在于:
- 函数内部
- 块中(字符{和}之间的区域)
//例1
function f1(){
let n = 5;
if(true){
let n = 10;
}
console.log(n); //5
}
//例2
var funs= [];
for(let i = 0; i < 10; i++){
funs.push(function(){
console.log(i);
});
}
funs[6](); //6
//上面的代码中,变量i是let声明的,当前的只在本轮循环有效。
//所以每一次循环的i其实都是一个新的变量,
//于是最后输出的是6。大家可能会问,如果每一轮循环的变量i都是重新声明的,
//那它怎么知道上一轮循环的值从而计算出本轮循环的值呢?
//这是因为javascript引擎内部会记住上一轮循环的值,
//初始化本轮的变量i时,就在上一轮的基础上进行计算。
//另外,for循环还有一个特别之处,就是设置循环变量的
//那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for(let i = 0; i < 3; i++){
let i = 'abc';
console.log(i);
}
//abc
//abc
//abc
//正确运行以上代码将输出3次abc。这表明函数内部的变量i和循环变量i不在同一作用域,
//而是各自单独的作用域。
5、不可改变的const命令
const 声明一个只读的常量。一旦声明,常量的值就不会改变。这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const PI = 3.1415;
console.log(PI); //3.1415
PI = 3.14; //TypeError: Assignment to constant variable.
注:const命令跟let命令一样不存在变量提升、具有块级作用域、存在暂时性死区、不允许重复声明。
6、const命令的本质
const 命令实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
const foo = {};
foo.name = 'lee';
foo.age = 18;
console.log(foo.name); //lee
console.log(foo.age); //18
foo = {}; //TypeError: Assignment to constant variable.
注:如果真的想对象冻结,可以使用Object.freeze({})
7、顶层对象中的let和const
从es6开始,全局变量(例如:import、class、let、const声明的)将逐步与顶层对象的属性隔离。
//顶层对象中的var
var foo = {name: 'lee', age: 18};
cosole.log(foo === window.foo); //true
console.log(window.foo); //{name: 'lee', age: 18}
//顶层对象中的let和const
let obj = {height: 182, weight: 140};
const mt = {msg:'hello’};
console.log(obj === window.obj); //false
console.log(window.obj); //undefined
console.log(mt === window.mt); //false
console.log(window.mt); //undefined
8、获取顶层对象的两种方法
//方法一
(typeof window !== 'undefined'
? window
: (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object' )
? global
: this)
//方法二
var getGlobal = function(){
if(typeof self !== 'undefined') {return self;}
if(typeof window !== 'undefined') {return window;}
if(typeof global !== 'undefined') {return global;}
throw new Error('unable to locate global object');
};
文章参考:
《ECMAScript 6 入门》