- let命令
- 块级作用域
- const命令
- 顶层对象的属性
- globalThis 对象
可抽取出的面试题
- es6中一共有多少声明变量的方法?例如:var和function
- 下面各输出什么?
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
3.关于块级作用域,for循环有何特殊之处?(提示:父作用域,子作用域)
4.说一下变量提升和暂时性死区?
从var举例说变量提升,暂时性死区用let举例
5.es6中关于顶层对象属性和全局变量的差异?(var和let举例 )
1. let命令
es6新增let命令,用来声明变量,用法类似于var,但是用let所声明的变量,只在let所在的代码块中有效,代码块外引用,为undefined,
使用场景,for循环中计数器特别适合
区别例子:
var a = [];
for (var i = 0;i<10;i++){
a[i] = function(){
console.log(i);
};
}
a[6]();//10
由于上面代码中,i是全局变量,在全局范围内有效,所以全局只有一个变量i,每一次循环,变量i的值都会发生改变,而循环内被赋值给a数组的函数内部的console.log(i)指向的是就是全局的i,所以是最后一轮输出的i的值,也就是10,
如果使用let声明,变量仅在代码块内有效,所以最后输出的是6。
另外,for循环有一个特别的作用域,就是设置循环变量的那一部分是一个父作用域,而循环内部是子作用域,
for(let i=0;i<3;i++){
let i = 'abc';
console.log(i);
}
上面代码运行了三次abc,这就说明for循环中,函数体重的变量i和循环变量i不在同一个作用域,有各自单独的作用域。
不存在变量提升
var变量会发生‘变量提升’现象,即变量可以在声明之前使用,值为undefined,这种现象多少有点奇怪,按照一般的逻辑,变量应该在声明后才能使用。
为了纠正这种现象,let命令改变了语法行为,它所声明的变量,一定要在声明后使用,否则报错,
暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域了,不再受外部影响。
tem = 123;
if(true){
tem = 'abc'; //ReferenceError
let tem;
}
上述代码,存在全局变量tem,但是块级作用域内let又声明了一个tem这个时候,其实是后者这个let声明的tem绑定到了这个if执行体中的跨级作用域,所以这时候报错就变成前面提到的不存在变量提升了,因为此时的tem是绑定的let声明的这个,es6明确规定:如果代码块中存在let和 const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域,凡是在声明前就使用这些变量,就会报错。这在语法上,称为“暂时性死区”
不允许重复声明
let不允许在相同作用域内,重复声明同一个变量
块级作用域
为什么需要块级作用域?
es5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种情景,内层变量可能会覆盖外层变量。
var tmp = new Date();
function f(){
console.log(tmp);
if(false){
var tmp = 'hello word';
}
}
f()//undefined
上面代码的原意是if代码块外的tmp使用外层声明的,if代码块内的使用内层声明的变量。但是,函数f执行后,输出结果是undefined,原因在于变量提升,导致console使用 的是if代码块内的,但是if代码块并没有执行,所以tmp是undefined。
第二种是for循环后,计数变量没有及时回收,一直存在于全局变量中,
块级作用域与函数声明
函数能不能在块级作用域之中声明?这是一个相当令人混淆的问题。
es5规定,函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明。但是浏览器没有遵守这个规定,为了兼容以前的代码,还是支持在块级作用域中声明函数,ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
另外,还有一个需要注意的地方。ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
3const命令
const是一个只读常量,一旦声明,常量的值不可改变。这意味着const一旦声明,就必须立刻初始化,不能留到以后赋值,
const的作用域与let相同,只在声明所在的块级作用域内有效。
const命令声明的常量也是不提升,存在暂时性死区,只能在声明后使用,在相同作用域内,也不可重复声明
es6声明变量的六种方法
es5只有两种声明变量的方法,var和function,但是在es6中,除了添加了上面我们提到的let和const外,后面章节还会提到,两外两种声明变量的方法:import命令和class命令。所以es6一共有6种声明变量的方法。
4.顶层对象的属性
顶层对象,在浏览器中指的是window对象,在Node中指的是global对象,es5之中,顶层对象的属性和全局变量是等价的。
window.a = 1;
a // 输出1
a = 2;
window.a //2
上面代码中,顶层对象的属性与全局变量的赋值,是同一件事。
顶层对象的属性与全局变量挂钩,被认为是JavaScript语言最大的设计败笔之一。这样的设计带来了几个很大的问题。
首先是没法在编译时就报出变量未声明的错误,只有在运行时才知道;
其次,程序员很容易不知不觉的就创建了全局变量,最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。
es6为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令,const命令,class命令声明的全局变量,不属于顶层对象的属性。也就是说,从es6开始,全局变量将逐步的与顶层对象属性脱钩。
5. globalThis对象
JavaScript语言存在一个顶层对象,它提供全局环境(即全局作用域),所有代码都是在这个环境中运行。但是,顶层对象在各种实现里面是不统一的
- 浏览器中,顶层对象是window,但Node和web worker没有window;
- 浏览器和web worker里面,self也指向顶层对象,但是Node没有self ;
- Node里面,顶层对象是global,但其他环境不支持。
同一段代码为了能够在各种环境中,都能取到顶层对象,现在一般是使用this变量,但是有局限性
- 全局环境中,this会返回顶层对象,但是,Node模块和es6模块中,this返回的是当前模块;
- 函数里面的this,如果函数不是作为对象的方法运行,而是单纯的作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined。
- 不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么eval、new Function这些方法都可能无法使用。
综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。
网友评论