Javascript基础之-var,let和const深刻解析(二)

你想在在变量声明以前就使用变量?之后再也别这样作了。javascript

新的声明方式(let,const)较之以前的声明方式(var),还有一个区别,就是新的方式不容许在变量声明以前就使用该变量,可是var是能够得。请看下面的代码,下面这个代码是能够正常运行的:java

function func() {
  console.log(localVariable);   // undefined
  var localVariable = 5;

  console.log(localVariable);   // 5
}

func();

可是这种却不能够函数

function func() {
  console.log(localVariable); // ReferenceError: localVariable is not defined
  let localVariable = 10;

  console.log(localVariable); // 10
}

func();

等下,咱们上一章曾经介绍了一个叫“提高”的概念,它会吧全部的变量定义在做用域的最前面。这是否意味着若是我不在实际的定义以前使用变量,而后就不会有提高了呢?答案是否认的。提高依然会有,而且适用于全部类型的变量类型。可是const和let却不是这样的。翻译

首先,咱们看一下var关键字是怎么工做的。规范对其是这样进行的描述的。调试

var声明定义了在正在运行的执行上下文(running execution
context)做用域内的变量环境(VariableEnvironment中)的变量。var变量在当包含的词法环境(Lexical
Environment)初始化时被建立,在建立的时候被赋值为undefined。[...]
在执行VariableDeclaration时,由带有Initializer的VariableDeclaration定义的变量被赋其设定项的Initializer's
AssignmentExpression的值。

规范中有许多的细节,让咱们简单的来看一下:code

当你进入到一个做用域中,在内部被定义的全部的变量都会被建立。blog

全部存在的变量,均可以被访问,而且会把undefined赋值给该变量。ip

当代码(执行时)到达初始化时,会被分配给一个实际的值。作用域

咱们来看一下规范中对let和const的表述:get

let和const声明是定义在当前执行上下文做用域中的词法环境中的变量。当包含的词法环境被初始化的时候,变量被建立。可是在变量的词法绑定时被计算以前是不容许经过任何方式来访问的。当词法绑定计算时而不是在变量被建立的时候,由词法绑定定义的变量的初始值被被赋予赋值表达式的值(也就是“=”右边的表达式)。当词法绑定被计算的时候,若是let声明中没有初始化的值的时候(也就是“let
a;”这样的形式),会被赋值为undefined。

简单来讲:

若是你进入到了指定的做用域中,它里面定义的全部的变量都会被初始化,这一点和var很像。

这里有一个不一样点:像var同样,全部的变量都会存在,可是他们目前还不能被访问(里面没有值,甚至是undefined)。

若是let变量在相同的地方被定义和初始化,他们会被赋予合适的值,反之,变量就是undefined。const变量必须在定义的时候初始化。

咱们来看一些相关的例子。

临时死区

实际上,这种描述引出了咱们的另外一个定义。他很让人可怕,由于他叫:临时死区(TDZ)。这个属于明确了一个咱们没法访问咱们的变量的代码的区域。咱们来看一下下面的代码和相关联的注释,来简单的解释一下TDZ是什么。

function func() {
  // Start of TDZ for deadVariable
  // we can still do something here, just our deadVariable is not available yet
  const exampleVariable = 5;
  console.log(exampleVariable); // 5
  // End of TDZ for deadVariable
  let deadVariable = 10;

  console.log(deadVariable);  // 10
}

func();

有一件事情值得去提醒。就是对于名字的建议,这是一个临时死区,意思这个区域是由时间定义的,而不是位置。所以当运行代码的时候,你的声明在被JS解析器解析以前是不能被访问的。所以你把使用的变量的位置放在哪里并不重要,只要是在声明执行后访问该变量就能够。因此看下面的代码:

function func() {
  return deadOrAlive;
}

let deadOrAlive = 'alive!'
console.log(func());  // alive!

这是运行代码的步骤:

函数被声明

变量deadOrAlive被声明,而且初始化了一个值“alive”

如今咱们调用咱们的函数。

因为变量deadOrAlive已经被声明,是可访问的,所以会打印出正确的结果 “alive”。

可是下面的例子却会报错,思考一下缘由。

function func() {
  return deadOrAlive;
}

console.log(func());  // ReferenceError: deadOrAlive is not defined
let deadOrAlive = 'dead!'

因此TDZ是一个避免因先使用后声明而致使的一些诡异的bug而出现的一个很好机制(具体看“提高”相关内容)。咱们不须要去额外作什么事情,就是记住永远不要在变量声明以前使用这个变量。即便咱们这样作了,咱们也会获得一个很好的报错信息。只有一个条件-你必须使用let或者是const来替换掉var。

双定义

var和let,const的另外一个区别是 - 后者仅仅能够被定义一次。而对于var的话,若是被同时定义屡次,程序也依然会很好的运行。

var doubledVariable = 5;
var doubledVariable = 6;

console.log(doubledVariable); // 6

可是如今,当你用let和const来作一样的事情,就会获得一个语法错误:

let doubledVariable = 5;
let doubledVariable = 6;  // SyntaxError: Identifier 'doubledVariable' has already been declared

可是,在嵌套的块级做用域中,使用相同名字的变量依然会很好的工做的,这个我想你们已经清楚了,就不用过多解释了吧。

let doubledVariable = 5;

if (true) {
  let doubledVariable = 6;
  console.log(doubledVariable); // 6
}

console.log(doubledVariable); // 5

不能重复定义这个功能其实是颇有用的,能够组织不少bug的发生。好比说你曾经在一个函数内,在不一样地方用var定义了多个相同名称的变量,此时以前定义的变量可能会被覆盖,这样对于代码来讲无疑是一个隐患,也就是由于这样,这个特性其实是一个简单的,开箱即用的解决方案。

总结

总结一下,在ES6中有两种新方法来声明变量:经过let和const关键字,除此以外,二者都是块级做用域,而且在声明以前不能访问该变量。与以前的var相比是一个主要的升级。而且会消除你不少的困扰。我提出了几个例子,可能会帮助你节省了很多调试的时间,可是还有更多。若是你感兴趣的话,能够在网上简单的搜索一下。好久以前,我我的曾建议中止使用var关键字,因此如今个人代码里充满了let和const。我建议你也是这样,在之后当你想改变变量的值,就使用let和const。不要再使用var了。

本文翻译自:

https://blog.pragmatists.com/...

本文转载自:http://www.lht.ren/article/16/

相关文章
相关标签/搜索