1.块级作用域绑定

var声明及变量提升(Hoisting)机制

先看例子

function getValue (condition){
  if(condition){
    var value = 'blue';
    // 其他代码
    return value
  } else {
    return value
  }
}

在函数或全局作用域中,使用 var 来声明的变量,无论在代码的哪里进行声明,都会被当作在当前作用域顶部声明的变量,这就是我们常说的提升(Hoisting)机制。

例:

function getValue (condition){
  var value
  if(condition){
    value = 'blue';
    // 其他代码
    return value
  } else {
    // 此处访问变量value值为 undefined
    return value
  }
  // 此处访问变量value值为 undefined
}

变量value的声明被提升到了顶部,这就意味着else语句中也可以访问到该变量并且由于还未进行初始化,所以访问到的值为 undefined,变量提升需要一些时间来进行习惯和熟悉,避免因为误解而产生bug。

块级声明

ES6引入的块级作用域来强化对变量生命周期的控制,块级声明用于声明在指定块的作用域之外无法访问的变量。块级作用域存在于:

  • 函数内部
  • 代码块中(字符{和}之间区域)

let 声明

let 声明的语法和 var 相同。但使用 let 代替 var 就可以把变量的作用域限制在当前的代码块中。由于 let 声明不会被提升,因此通常将 let 声明语句放在封闭代码块的顶部,以便整个代码块都可以访问。
例:


function getValue (condition) {
    if(condition){
      let value = 'blue';
      // 其他代码
      return value;
    } else {
      // 变量value在此处不存在
      return null;
    }
    // 变量value在此处不存在
}

现在这个getValue函数的运行结果更像C语言。变量value改由关键字let进行声明后,不再被提升至函数顶部。执行流离开if块会立即被销毁,如果condition为false就永远不会声明并初始化value。

禁止重声明

例:

var count = 30;

let count = 40;

这段代码由于count被声明了两次,第一次用的是var 第二次用的是 let。如果作用域里已经存在某个标识符,此时在进行let声明,就会抛出错误信息

例:

var count = 30;

if (condition) {
  let count = 40;
}

因为此处的let是在if的代码块内声明了新变量count,因此不会抛出错误。内部代码块里的count会遮蔽全局作用域中的count,后者只能在if代码块之外才能访问到。

const 声明

ECMAScript 6 标准还提供了 const 关键字。 使用const关键字声明的是常量,其值一旦被设定后不可更改,因此,每个通过 const 声明的常量必须进行初始化

const maxItems = 30;

const name;

这里在声明 maxItems 时进行了初始化操作, 而声明 name 时没有赋值,因此执行后者会抛出语法错误。

const 与 let

const 与 let 都是块级声明,所以常量也只是在当前的代码块有效,一旦执行到代码块外会立即被销毁,常量同样也不会被提升到作用域顶部。

  if(condition){
      const maxItems = 5;
      // 其他代码
  }
// 此处无法访问 maxItems

在这段代码中,在 if 语句中声明常量 maxItems ,语句执一结束,maxItems 即刻被销毁,在代码块外访问不到这个常量。

与 let 相似,在同一个作用域用 const 声明已经存在的标识符也会导致语法错误,无论该标识符是使用的 var(在全局或函数作用域中),还是 let(在块级作用域中)声明的。

var message = 'hello!';
let age = 25;

// 这两条语句都会抛出错误
const message = 'goodbye';
const age = 30;

后两条 const 声明语句本身没问题,但由于前面用 var 和 let 声明了两个同名变量,结果代码就无法执行了。

尽管相似之处很多,但 const 声明与let声明有一处很大的不同,即无论在严格模式还是非严格模式下,都不可以为 const 定义的常量再进行赋值,否则会抛出错误。

const maxItems = 5;

// 抛出语法错误
maxItems = 30;

ECMAScript6 中的常量与其他语言中的很像,此处定义的maxItems不可以再被赋值。但是对象有所不同,对象中的值可以进行修改。

用 const 声明对象

const person = {
  name: 'Sherry'
};

// 可以修改对象属性的值

person.name = 'Nocte'

// 抛出语法错误
person = {
  name:'Nocte'
}

在这段代码里,绑定 person 的值是一个包含了一个属性的对象,改变person.name 的值,不会抛出任何错误,因为修改的是 person 包含的值。如果直接给 person 赋值,即需要改变 person 的绑定,就会抛出错误。切记,const 声明不允许修改绑定,但允许修改绑定的值。

临时死区(Temporal Dead Zone 后续简称为 TDZ)

与 var 不同,let 和 const 声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,即使是使用相对安全的 typeof 操作符也会触发饮用错误。

if(condition){
  console.log(typeof value); // 饮用错误
  let value = 'Sherry'
}

由于 console.log(typeof value) 语句会抛出错误,因此用 let 定义并初始化变量 value 的语句不会执行。此时的 value 还位于 JavaScript 社区所谓的"临时死区"(Temporal Dead Zone)或TDZ中。虽然ECMAScript标准并没有明确提到TDZ,但人们却常用它来描述 let 和 const 的不提升效果。TDZ导致的声明位置的微妙差异对于 let 和const 是一样的。

JavaScript 引擎在扫描代码发现变量声明时,要么将他们提升至作用域顶部(var声明),要么将生命放到TDZ中(let和const声明)。访问TDZ中的变量或常量会触发运行时的错误。只有执行过声明语句后,才会从TDZ中移除,然后可以正常访问。

由上一个例子可见,即便是不易出错的 typeof 操作符也无法阻挡引擎抛出错误。但在 let 声明的作用域外对该变量使用typeof则不会报错。
例:

console.log(typeof value); 

if(condition){
  let value = 'Sherry'
}

typeof 是声明在变量 value 的代码块外执行的,此时 value 并不存在TDZ中。这也就意味着不存在 value 这个绑定,typeof最终返回"undefined"。TDZ只块级绑定的特性之一


钟楠@好乐互娱

你可能感兴趣的:(1.块级作用域绑定)