Scope
-
Variable Lifetime
- Lexical Scoping(var): from when they are declared until when their function ends
- Block Scoping(const, let): until the next } is reached
- const和let的区别:
const thisIsAConst = 50;
thisIsAConst ++ //Error!!!
let thisIsALet = 50;
thisIsALet = 51; // 完全可行
//But! 不能用let声明同一个变量两次
//而用var则不会出现问题
let thisIsALet = 50; //Error!!!
!注意!const和let的区别在于前者用于声明常量,其所指向内存的位置(reference)不可改变,但后者let则用于声明变量。
此例中的obj虽然是用const定义的,但是之后却可以修改其属性,因为obj是对象,修改属性并没有改变它在内存里存储的位置(reference), 因此是可行的。
那么Lexical Scoping和Block Scoping的区别在哪呢?我们再来看下一个例子,来猜猜看console运行的结果是什么:
console.log(thisIsALet); //???结果是什么
let thisIsALet = 50;
thisIsALet = 51;
很多有些JS基础的人仔细看过会认为答案是undefined,但是实际结果却显示如下:
为什么?
因为let声明的拥有块级作用域(block scoping),它只作用于被声明处到下一个}之前,及时let声明的是全局变量,它也存在于一个看不见的块级作用域中,绝不可能作用于声明前的作用域。因此当JS引擎从第一行开始执行代码时,并不认识thisIsALet,所以引擎显示出错误。
但是如果用var来声明,那么我们就可以得到之前所猜想的答案:
console.log(thisIsAVar); //undefined
var thisIsAVar = 50;
之所以console的结果是undefined而不是50,是因为在声明thisIsAVar时变量的名字得到了提升(undefined),而赋值没有
-
Hoisting提升
- Function definitions are hoisted, but not all lexically-scoped intializations.
thisIsHoisted();
function thisIsHoisted() {
console.log('This is a function declared at the bottom of a file');
}
此处函数thisIsHoisted() 的声明得到了提升,所以即使是在声明之前安排执行此函数,也是可行的。
- 只有声明会被提升,而任何赋值或者其他执行逻辑都会被留在原处。同理,函数声明会被提升,但是函数表达式不会。(注意!只有用
var
声明的变量可以被提升,用const
或let
声明则引擎会提示错误)。
thisIsHoisted();
var thisIsHoisted = function() {
console.log('should this be hoisted?');
}
注意,此处的出现的结果是TypeError,因为它所被提升的值是undefined,并不是函数,所以用函数的方式去执行它,便会出现TypeError。再来看一下如果用
const
或者let
声明变量结果是什么:
thisIsHoisted();
const thisIsHoisted = function() {
console.log('should this be hoisted?');
}
正如前文所说到的,
const
或let
没有提升的功能,因此当想在其被真正声明之前调用变量,这些变量是不存在的,所以出现ReferenceError。
-
出现
ReferenceError
是因为作用域解析失败,而TypeError暗示着作用域解析成功了,但是试图对这个结果进行了一个非法/不可能的操作(比如将一个非函数的值作为函数运行,或者引用null或者undefined值得属性)。