1,for循环的特别之处,即是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (var i=0;i<3;i++){
var i='andy';
console.log('i='+i)//i=andy(打印一次然后退出循环)
}
我们看下let语句
for(let i=0;i<3;i++){ let i='andy'; console.log(i); }
结果是:
andy
andy
andy
输出了3次,这表明函数内部的变量i和外部的变量i是分离的;
2,es6中不存在变量提升
console.log(aa);// undefiend(变量提升,但是没有值,所以会打印出undefined)
var aa = 2;
console.log(bb);// 引用错误(let不会发生变量提升,说明在声明之前,bb是不存在的,此时会抛出错误);
let bb = 2;
3,暂时性封闭性死区
//只要在块级作用域中存在let命令,它所声明的变量就绑定了在这个区域,不再受外部的影响;
var aa = 'andy';
if(aa){
aa = 'jack';// 引用错误
let aa;
}
//因此衍生的一个问题是typeof 操作符的运用是否安全
typeof aa;//referenceError
let aa; //然而,如果一个变量没有let声明,反而不会出错;
typeof bb;//undefined
在没有let之前,typeof运算符是安全的;现在在es6中则会出现问题;
这一设计原则是为了让大家,变量一定要在声明之后使用
否则会报错;
4,隐含的容易出错的
function act(x=y,y=2){
console.log(x+y);
// 参数x默认等于参数y,而此时y没有声明,有些浏览器会打印出NAN,有些浏览器会报错
}
act(); //如果y的值默认是x,就不会报错,因为此时x已经声明过了
function acts(x=2,y=x){
console.log(x+y);
}
acts();//4
5,使用let声明变量时,只要变量在还没有声明完成前使用,就会报错;
var x=x;// 不会报错
let x=x;//使用let声明变量时,只要变量在还没有声明完成前使用,就会报错
6,不允许重复声明变量;
function aa(){ let a=2; var a=1;//报错 }
aa();
因此不能在函数内部重新声明参数;
function aa(name){ let name;//报错 }
function aa(name){ { let name;//正确 } }
7,块级作用域
var mydate = new Date(); function create(){ console.log(mydate); if(false){ var mydate = 'andy'; } } create();// undefined
变量提升导致内部的mydate覆盖外层的mydate变量;
var a = 'andy'; for(var i =0;i<a.length;i++){ console.log(a[i]); } console.log(i);//4---循环结束后,i变量并没有消失,而是泄漏给了全局
而es6规定了块级作用域
function a(){ let i = 1; if(true){ let i =2; console.log(i);//2 } console.log(i);//1---外层变量不受内部变量的影响 } a();
而es6也允许块级作用域的嵌套
{ { { let name = 'andy'; console.log(name);//andy } console.log(name);//为空或者报错(看浏览器) } }
块级作用域的出现,实际上说明了我们之前用到了立即执行函数的退下历史舞台;
(function(){ let name = 'andy'; })(); { let name = 'andy'; }
考虑到环境导致的行为差异太大,应该避免在块级作用域中声明函数;如果确实需要也应该写成函数表达式而不是函数声明语句;
{ //函数声明语句 let name = 'andy'; function f(){ return name; } } { //函数表达式---推荐 let names = 'andy'; let f = function(){ return names; } }
提示:es6允许在块级作用域中声明函数,前提是必须有大括号,否则会报错;
if(true){ function f(){ } }
if(true) function f(){//报错,如果用es6编译的话,就提示报错 }
8,const命令
const声明的常量值,不允许改变;
const a = 1; console.log(a); a = 3;//报错
也就是说,一但声明了常量值,就必须赋值;
const a;//必须赋值,否则会报错
当然了,const和let作用域一样,必须在块级才生效;同时,不可重新赋值;
9,es6声明变量的6种方法;
1,var 2,function 3,let 4,const 5,import 6,class
10,顶层对象的属性;
顶层对象在浏览器中指的是window,在node环境中指的是global;
顶层对象的属性和全局变量关联;
window.a= 1; console.log(a);//1
而es6规定,let,const,class声明的全局变量不属于顶层对象;也就是说从es6开始,全局变量逐步与顶层对象的属性脱离;
11,数组的结构赋值
//以前,为变量赋值,只能指定值; let a =1; let b=2; let c=3; console.log(a,b,c);//1,2,3 //而es6,可以这样写--可以从数组中提取值,按照对应位置,对变量赋值。 let [a,b,c] = [1,2,3]; console.log(a,b,c);//1,2,3 let [a,[b,c]] = ['andy',[2,3]]; console.log(a,b,c);//andy,2,3
这种就是模式匹配,只要左边和右边的值对应,就会产生一一对应;
let [,,name] = [1,2,'andy']; console.log(name);//andy let [school,...name] = ['香港城市大学','andy','lucy','jack']; console.log(school,name);//香港城市大学 [andy,lucy,jack] let [x, y, ...z] = ['a']; console.log(x,y,z);//a,undefiend,[]
当然,如果解构不成功,就会返回undefiend;
let [foo] = []; let [bar, foos] = [1]; console.log(foo,bar,foos);//undefined,1,undefined
另外,
let [age] = 20; console.log(age);//报错
而对于Set结构,也同样可以使用数组的结构赋值
let [x,y,z] = new Set(['a','b','c']); console.log(x,y,z);//a,b,c
重点:迭代器iterators
Iterator接口的意思就是说:符合Iterator接口的对象里需要有一个叫next的函数,这个函数需要返回一个对象,
并且这个对象符合IteratorResult接口规范;
IteratorResult接口里面定义了两个属性,一个是done,代表迭代是否已经完成。另一个属性value代表迭代过程中产生的值。
之所以可以采用数组的结构赋值,是因为只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值;
12,es6中的默认值
let [x,y = 'b'] = ['a']; console.log(x,y);//a,b let [bar = true] = []; console.log('bar='+bar);//true let [a,b='2'] = ['1',undefined]; console.log(a,b);//1,2 let [m,n]= [1,2]; console.log(m,n);//1,2 //es6内部使用严格相等运算符(===),判断一个位置是否有值; //所以一个数组成员不严格等于undefiend,默认值就不会生效;即严格等于undefiend,默认值就会生效 let [m1 = '1'] = [undefined]; console.log(m1);//1 let [m2 = '2'] = [null]; console.log(m2);//null 因为此处相当于将null赋值给了m2
上面中一个数组成员为null,默认值就不会生效;因为null不严格等于undefiend;
但是,如果默认值是一个表