// 解构复制
// let [foo,[[bar],baz]] = [1,[[2],3]];
// console.log(foo);//1
// console.log(bar);//2
// console.log(baz);//3
// let [,,third] = ["foo","bar","baz"];
// console.log(third)//baz;
// let [x,,y] = [1,2,3];
// console.log(x+"---"+y)//1---3
// let [head,...tail] = [1,2,3,4];
// console.log(Array.isArray(tail))//证明是数组
// console.log(head+"--"+tail)//1--2,3,4
// console.log(typeof tail)//object
// let [x, y, ...z] = ['a'];
// 解构不成功,变量的值就等于undefined
// console.log(x+"---"+y+"---"+z)//a---undefined---[]
// 如果等号的右边不是数组(或者严格地说,不是可遍历的结构,参见《Iterator》一章),那么将会报错。
// 报错
// let [foo] = 1;
// let [foo] = false;
// let [foo] = NaN;
// let [foo] = undefined;
// let [foo] = null;
// let [foo] = {};
// 只要某种数据结构具有Iterator(迭代)接口,都可以采用数组形式的解构赋值。
// Generator函数(生产者?)
// function* fibs() {
// var a = 0;
// var b = 1;
// while (true) {
// //0 1 1 2 3 5
// yield a;
// //1,1 1,2 2,3 3,5 5,8
// [a, b] = [b, a + b];
// }
// }
// var [first, second, third, fourth, fifth, sixth] = fibs();
// console.log(first)//0
// console.log(second)//1
// console.log(third)//1
// console.log(fourth)//2
// console.log(fifth)//3
// console.log(sixth)//5
// var a = [];
// for (var i = 0; i < 10; i++) {
// a[i] = function () {
// console.log(i);
// };
// }
// a[6](); // 10
// var a = [];
// for (let i = 0; i < 10; i++) {
// a[i] = function () {
// console.log(i);
// };
// }
// a[6](); // 6
// let不像var那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
// console.log(foo); // 输出undefined
// //console.log(bar); // 报错ReferenceError
// var foo = 2;
// //let bar = 2;// 报错ReferenceError
// 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
// var tmp = 123;
// 存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
// if (true) {
// tmp = 'abc'; // ReferenceError
// let tmp;
// }
// var tmp = 123;
// if (true) {
// let tmp;
// tmp = 'abc'; //不报错
// }
// 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
// }
// “暂时性死区”也意味着typeof不再是一个百分之百安全的操作。
// typeof x; // ReferenceError
// let x
// 作为比较,如果一个变量根本没有被声明,使用typeof反而不会报错。
//console.log(typeof y)//undefined
// 调用bar函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于”死区“。如果y的默认值是x,就不会报错,因为此时x已经声明了。
// function bar(x = y, y = 2) {
// return [x, y];
// }
// bar(); // 报错
// function bar(y = x, y = 2) {
// return [x, y];
// }
// bar(); // 报错
// function bar(x = 2, y = x) {
// return [x, y];
// }
// bar(); // [2, 2]
// 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
// 不允许重复声明
// 报错
// function () {
// let a = 10;
// var a = 1;
// }
// // 报错
// function () {
// let a = 10;
// let a = 1;
// }
// function func(arg) {
// let arg; // 报错
// }
// function func(arg) {
// {
// let arg; // 不报错
// }
// }
// 变量提升
// ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
// 第一种场景,内层变量可能会覆盖外层变量。
// var tmp = new Date();
// function f() {
// console.log(tmp);
// if (false) {
// var tmp = "hello world";
// }
// }
// f(); // undefined
// var tmp = new Date();
// function f() {
// console.log(tmp);
// if (true) {
// var tmp = "hello world";
// }
// }
// f(); // undefined
// 上面代码中,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
// 第二种场景,用来计数的循环变量泄露为全局变量
// var s = 'hello';
// for (var i = 0; i < s.length; i++) {
// console.log(s[i]);
// }
// console.log(i); // 5
// var s = 'hello';
// for (let i = 0; i < s.length; i++) {
// console.log(s[i]);
// }
// console.log(i); // i is not defined
// 这表示外层代码块不受内层代码块的影响
// function f1() {
// let n = 5;
// if (true) {
// let n = 10;
// }
// console.log(n); // 5
// }
// f1()//5
// 互不影响
// {{{{
// let insane = 'Hello World';
// {let insane = 'Hello';
// console.log(insane)//Hello
// }
// console.log(insane)//Hello World
// }}}};
// 块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。
// IIFE写法
// (function () {
// var tmp = ...;
// ...
// }());
// // 块级作用域写法
// {
// let tmp = ...;
// ...
// }
// ES5严格模式
// 'use strict';
// if (true) {
// function f() {}
// }
// 报错
// ES6严格模式
// 'use strict';
// if (true) {
// function f() {}
// }
// 不报错
// 报错
// function f() { console.log('I am outside!'); }
// (function () {
// if (false) {
// // 重复声明一次函数f
// function f() { console.log('I am inside!'); }
// }
// f();
// }());
// ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
// 'use strict';
// if (true) {
// function f() {}
// }
// // 报错
// 'use strict';
// if (true)
// function f() {}
// const声明一个只读的常量。一旦声明,常量的值就不能改变。
// const foo = {};
// foo.prop = 123;
// foo.prop
// 123
//foo = {}; // TypeError: "foo" is read-only
// 常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
// const a = [];
// a.push('Hello'); // 可执行
// a.length = 0; // 可执行
// a = ['Dave']; // 报错
// 如果真的想将对象冻结,应该使用Object.freeze方法
// const foo = Object.freeze({});
// // 常规模式时,下面一行不起作用;
// // 严格模式时,该行会报错
// foo.prop = 123;
// 常量foo指向一个冻结的对象,所以添加新属性不起作用,严格模式时还会报错。
// ES6为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩。
// var a = 1;
// // 如果在Node的REPL环境,可以写成global.a
// // 或者采用通用方法,写成this.a
// window.a // 1
// let b = 1;
// window.b // undefined