解决ES5中只有全局做用域和函数做用域,没有块级做用域而带来的不合理的场景。
用法和var 同样,只是let声明的变量只有在let命令所在的代码块有效前端
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1
能够看出var 声明的变量在代码块以外也是能够调用,而let声明的则调用报错。因此let 声明只在它声明的当前代码块中才能调用。面试
在使用 var 的时候会出现 “变量提高”的现象,即变量能够在声明以前使用,值为undefined。let 改变了这种现状,可是必须先声明在使用,若是在声明以前使用则会出现报错。以下:数组
// var 的状况 console.log(foo); // 输出undefined var foo = 2; // let 的状况 console.log(bar); // 报错ReferenceError let bar = 2;
只要块级做用域内部存在 let 或者 const 命令,它所声明的变量就“绑定”在这个区域,不会受外部影响。且暂时性死区的本质就是,只要一进入当前做用域,所要使用的变量就已经存在了,可是不可获取,只有等到声明变量的那一行代码出现,才能够获取和使用该变量。以下:浏览器
var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; }
ES6 明确规定,若是区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就造成了封闭做用域。凡是在声明以前就使用这些变量,就会报错。安全
总之,在代码块内,使用let命令声明变量以前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 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声明变量时,只要变量在尚未声明完成前使用,就会报错。上面这行就属于这个状况,在变量x的声明语句尚未执行完成前,就去取x的值,致使报错”x 未定义“。++函数
let 不容许在同一个做用域内声明同一个变量,以下:this
// 报错 function func() { let a = 10; var a = 1; } // 报错 function func() { let a = 10; let a = 1; }
或者以下:es5
function func(arg) { let arg; } func() // 报错 function func(arg) { { let arg; } } func() // 不报错
上面也提到过在es5中没有块级做用域的概念,只有函数做用域和全局做用域,那么就带来了一些问题,以下:spa
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined 外层声明被内层声明所覆盖,内层使用的是外层的声明,内层变量提高致使 undefinded
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5 常见的面试题,最后输出不是预料中的 1 2 3 4 5 而所有是 5
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 } 内外层的 n 互不干扰
内外层能够同名 {{{{ let insane = 'Hello World'; {let insane = 'Hello World'} }}}}; 或者 {{{{ {let insane = 'Hello World'} console.log(insane); // 报错 }}}};
块级做用域的出现可让如下当即执行函数的写法没必要要,以下:
// IIFE 写法 (function () { var tmp = ...; ... }()); // 块级做用域写法 { let tmp = ...; ... }
在ES5中,函数只能在顶层做用域和函数做用域中声明,不能在块级做用域中声明,可是浏览器为了兼容性,仍是能够在块级做用域中声明,理论上在ES6中 块级做用域中声明的函数,在外部调用会报错,考虑环境的问题,应当避免在块级做用域中声明函数,若是须要也应当写成函数表达式的方式,而不是函数声明语句,以下:
// 函数声明语句 { let a = 'secret'; function f() { return a; } } // 函数表达式 { let a = 'secret'; let f = function () { return a; }; }
const声明的是一个常量 以下:
const PI = 3.1415; PI // 3.1415 PI = 3; // TypeError: Assignment to constant variable.
声明以后若是在赋值,将会报错,同时由于声明的是常量,即const声明后即要赋值否则也会报错
const 和 let 相同,声明也只在当前的块级做用域生效。一样也不会声明提高,也存在暂时死区,只能在声明以后使用,且和 let 同样不得重复声明,不能从新赋值。
const 所不能改变的并非值,而是变量指向的那个内存地址所保存的值不能变更,对于简单类型(数值、字符串、布尔值),值就保存在变量所指向的内存地址中,所以等同于常量。而对于复合类型(数组、对象),变量指向的内存地址,保存的只是一个指向实际数据的指针,const 只能保证这个指针是固定的(即总指向一个固定的地址)。至于它指向的数据结构则是不能控制的 ,以下:
const foo = {}; // 为 foo 添加一个属性,能够成功 foo.prop = 123; foo.prop // 123 // 将 foo 指向另外一个对象,就会报错 foo = {}; // TypeError: "foo" is read-only 常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址, 即不能把foo指向另外一个地址,但对象自己是可变的,因此依然能够为其添加新属性。
或者
const a = []; a.push('Hello'); // 可执行 a.length = 0; // 可执行 a = ['Dave']; // 报错 常量a是一个数组,这个数组自己是可写的,可是若是将另外一个数组赋值给a,就会报错
const foo = Object.freeze({}); // 常规模式时,下面一行不起做用; // 严格模式时,该行会报错 foo.prop = 123; 对象也能够冻结 var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };
1. function 2. var 3. let 4. const 5. import 6. class
在浏览器环境指的是window对象,在 Node 指的是global对象,ES5 之中,顶层对象的属性与全局变量是等价的。
window.a = 1; a // 1 a = 2; window.a // 2
ES5 的顶层对象,自己也是一个问题,由于它在各类实现里面是不统一的。
同一段代码为了可以在各类环境,都能取到顶层对象,如今通常是使用this变量,可是有局限性。