JS之作用域与提升

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则用于声明变量。

用const定义对象后修改对象的属性

此例中的obj虽然是用const定义的,但是之后却可以修改其属性,因为obj是对象,修改属性并没有改变它在内存里存储的位置(reference), 因此是可行的。

那么Lexical Scoping和Block Scoping的区别在哪呢?我们再来看下一个例子,来猜猜看console运行的结果是什么:

console.log(thisIsALet); //???结果是什么
let thisIsALet = 50;
thisIsALet = 51;

很多有些JS基础的人仔细看过会认为答案是undefined,但是实际结果却显示如下:


Error

为什么?
因为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声明的变量可以被提升,用constlet声明则引擎会提示错误)。
thisIsHoisted();

var thisIsHoisted = function() {
  console.log('should this be hoisted?');
}

TypeError

注意,此处的出现的结果是TypeError,因为它所被提升的值是undefined,并不是函数,所以用函数的方式去执行它,便会出现TypeError。再来看一下如果用const或者let声明变量结果是什么:

thisIsHoisted();

const thisIsHoisted = function() {
  console.log('should this be hoisted?');
}

ReferenceError

正如前文所说到的,constlet没有提升的功能,因此当想在其被真正声明之前调用变量,这些变量是不存在的,所以出现ReferenceError。

  • 出现ReferenceError是因为作用域解析失败,而TypeError暗示着作用域解析成功了,但是试图对这个结果进行了一个非法/不可能的操作(比如将一个非函数的值作为函数运行,或者引用null或者undefined值得属性)。

你可能感兴趣的:(JS之作用域与提升)