JavaScript 变量声明/赋值
声明
6 种声明
至今为止,除标签声明之外,JavaScript 中一共只有六条声明用的语句。注意,所有真正被定义“声明”的语法结构都一定是“语句”,并且都用于声明一个或多个标识符。这里的标识符包括变量、常量等。
严格意义上讲,JavaScript 只有变量和常量两种标识符,六条声明语句中:
- let x … 声明变量 x。不可在赋值之前读。
- const x … 声明常量 x。不可写。
- var x … 声明变量 x。在赋值之前可读取到 undefined 值。
- function x … 声明变量 x。该变量指向一个函数。
- class x … 声明变量 x。该变量指向一个类(该类的作用域内部是处理严格模式的)。
- import … 导入标识符并作为常量(可以有多种声明标识符的模式和方法)。
除了这六个语句之外,还有两个语句有潜在的声明标识符的能力,不过它们并不是严格意义上的声明语句(声明只是它们的语法效果)。
这两个语句是指:
- for (var|let|const x …) … for 语句有多种语法来声明一个或多个标识符,用作循环变量。
- try … catch (x) …catch 子句可以声明一个或多个标识符,用作异常对象变量。
标识符
let x
x 就是一个标识符
function fn(){
let x=2
}
- 在 Parser 工作阶段,Parser 需要确定 fn 作用域情况,所以它会询问当前作用域有无 x 这个变量,没有的话就会把该变量添加到作用域中
- 在 fn 执行前的创建上下文阶段,会【声明/创建】变量 x
- 执行函数 fn,对其进行赋值
变量声明在引擎的处理(存疑)
变量声明在引擎的处理上被分成两个部分:一部分是静态的、基于标识符的词法分析和管理,我们称之为声明标识符,它总是在相应上下文的环境构建时作为名字创建的,我们称之为创建标识符,注意;另一部分是表达式执行过程,是对上述名字的赋值,这个过程也称为绑定。
var 的初始化
var y = "outer";
function f() {
console.log(y); // undefined
console.log(x); // throw a Exception
let x = 100;
var y = 100;
}
正是由于 var y 所声明的那个标识符在函数 f() 创建(它自己的闭包)时就已经存在,所以才阻止了 console.log(y) 访问全局环境中的 y。类似的,let x 所声明的那个 x 其实也已经存在 f() 函数的上下文环境中。访问它之所以会抛出异常(Exception),不是因为它不存在,而是因为这个标识符被拒绝访问了。
在 ECMAScript 6 之后出现的 let/const 变量在“声明(和创建)一个标识符”这件事上,与 var 并没有什么不同,只是 JavaScript 拒绝访问还没有绑定值的 let/const 标识符而已。
回到 ECMAScript 6 之前:JavaScript 是允许访问还没有绑定值的 var 所声明的标识符的。这种标识符后来统一约定称为“变量声明(varDelcs)”,而“let/const”则称为“词法声明(lexicalDecls)”。>
对于“变量声明(varDelcs)”,函数的上下文环境在创建一个“变量名”后,会为它初始化绑定一个 undefined 值,再执行代码。而”词法名字(lexicalNames)”在创建之后就没有这项待遇(直到执行代码后才初始化为 undefined),所以它们在缺省情况下就是“还没有绑定值”的标识符。
var,function,let 的创建、初始化和赋值
对 我用了两个月的时间才理解 let 的总结
注意下面没有提到变量的【声明】
var 声明的「创建、初始化和赋值」过程
function fn(){
var x = 1
var y = 2
}
fn()
在执行 fn 时,会有以下过程(不完全):
- 进入 fn,为 fn 创建一个环境。
- 找到 fn 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
- 将这些变量「初始化」为 undefined。
- 开始执行代码
- x = 1 将 x 变量「赋值」为 1
- y = 2 将 y 变量「赋值」为 2
function 声明的「创建、初始化和赋值」过程
fn2()
function fn2(){
console.log(2)
}
JS 引擎会有以下过程:
- 找到所有用 function 声明的变量,在环境中「创建」这些变量。
- 将这些变量「初始化」并「赋值」为 function(){ console.log(2) }。
- 开始执行代码 fn2()
也就是说 function 声明会在代码执行之前就「创建、初始化并赋值」。
let 声明的「创建、初始化和赋值」过程
{
let x = 1
x = 2
}
我们只看 {} 里面的过程:
- 找到所有用 let 声明的变量,在环境中「创建」这些变量
- 开始执行代码(注意现在还没有初始化)
- 执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
- 执行 x = 2,对 x 进行「赋值」
const 声明的「创建、初始化」过程
const 只有「创建」和「初始化」,没有「赋值」过程。
{
console.log(x)
const x = 1
}
我们只看 {} 里面的过程:
- 找到所有用 let 声明的变量,在环境中「创建」这些变量
- 开始执行代码,将 x 「初始化」为 1
总结
- let 的「创建」过程被提升了,但是初始化没有提升。
- var 的「创建」和「初始化」都被提升了。
- function 的「创建」「初始化」和「赋值」都被提升了。
赋值
如果是在一门其它的(例如编译型的)语言中,为变量 x 绑定一个初值 就可能实现为“ 在创建环境时将变量 x 指向一个特定的初始值。这通常是静态语言的处理方法,然而,如前面说过的,JavaScript 是门动态的语言,所以它的 绑定初值 的行为是通过动态的执行过程来实现的,也就是赋值操作。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: JavaScript 匿名函数
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论