网站改版建设的目的,企业邮箱怎么看,橱柜网站建设公司,wordpress表单邮件变量提升#xff1a;
变量提升#xff08; hoisting #xff09;。
我可恨的 var 关键字#xff1a;
你读完下面内容就会明白标题的含义#xff0c;先来一段超级简单的代码#xff1a;
script typetext/javascriptvar str Hello JavaScript hoi…变量提升
变量提升 hoisting 。
我可恨的 var 关键字
你读完下面内容就会明白标题的含义先来一段超级简单的代码
script typetext/javascriptvar str Hello JavaScript hoisting;console.log(str); // Hello JavaScript hoisting/script这段代码很意外地简单我们的到了想要的结果在控制台打印出了Hello JavaScript hoisting 。
现在我将这一段代码改一改将 调用 放在前面 声明 放在后面。
很多语言比如说 C 或者 C 都是不允许的但是 javaScript 允许。
你们试着猜猜得到的结果
script typetext/javascriptconsole.log(str); // undefinedvar str Hello JavaScript hoisting; console.log(str); // Hello JavaScript hoisting/script你会觉得很奇怪在我们调用之前为什么我们的 str undefined 而不是报错未定义
我将 var str Hello JavaScript hoisting 删除后试试思考这段代码的结果
script typetext/javascriptconsole.log(str); // Uncaught ReferenceError: str is not defined/script现在得到了我们想要的报错未定义。
事实上在我们浏览器会先解析一遍我们的脚本完成一个初始化的步骤它遇到 var 变量时就会先初始化变量为 undefined 。
这就是变量提升hoisting 它是指浏览器在遇到 JS 执行环境的 初始化引起的变量提前定义。
在上面的代码里我们没有涉及到函数因为我想让代码更加精简更加浅显显然我们应该测试一下函数。
script typetext/javascriptconsole.log(add); // ƒ add(x, y) { return x y; }function add(x, y) { return x y; }/script在这里我们并没有调用函数但是这个函数已经被初始化好了其实初始化的内容比我们看到的要多。
如何避免变量提升
使用 let 和 const 关键字尽量使用 const 关键字尽量避免使用 var 关键字
script typetext/javascript// console.log(testvalue1); // 报错testvalue1 is not defined// let testvalue1 test;/*---------我是你的分割线-------*/console.log(testvalue2); // 报错testvalue1 is not definedconst testvalue2 test;/script但如果为了兼容也就没办法喽哈哈哈致命一击
执行上下文
执行上下文又称为执行环境execution context听起来很厉害对不对其实没那么难。 作用域链
其实我们知道JS 用的是 词法作用域 的。
关于 其他作用域 不了解的童鞋请移步到我的《谈谈 JavaScript 的作用域》或者百度一下。参考 前端进阶面试题详细解答 每一个 javaScript 函数都表示为一个对象更确切地说是 Function 对象的一个实例。 Function 对象同其他对象一样拥有可编程访问的属性。和一系列不能通过代码访问的 属性而这些属性是提供给 JavaScript 引擎存取的内部属性。其中一个属性是 [[Scope]] 由 ECMA-262标准第三版定义。 内部属性 [[Scope]] 包含了一个函数被创建的作用域中对象的集合。 这个集合被称为函数的 作用域链它能决定哪些数据能被访问到。 来源于《 高性能JavaScript 》 我好奇的是怎样才能看到这个不能通过代码访问的属性经过老夫的研究得出能看到这个东西的方法
打开谷歌浏览器的 console 并输入一下代码
function add(x, y) {return x y;
}console.log( add.prototype ); // 从原型链上的构造函数可以看到add 函数的隐藏属性。可能还有其他办法但我只摸索到了这一种。
你需要这样 然后这样 好了你已经看到了[[Scope]] 属性下是一个数组里面保存了作用域链此时只有一个 global。
思考以下代码并回顾 词法作用域结合 [[Scope]] 属性思考你就能理解 词法作用域 的原理
var testValue outer;function foo() {console.log(testValue); // outerconsole.log(foo.prototype) // 编号1
}function bar() {var testValue inner;console.log(bar.prototype) // 编号2foo();
}bar();以下是执行结果
编号 1 的 [[Scope]] 属性Scopes[1] : 编号 2 的 [[Scope]] 属性Scopes[1] 因为初始化时[[Scope]] 已经被确定了两个函数无论是谁如果自身的作用域没找到的话就会在全局作用域里寻找变量。
再思考另外一段代码
var testValue outer;function bar() {var testValue inner;foo();console.log(bar.prototype) // 编号 1function foo() {console.log(testValue); // innerconsole.log(foo.prototype); // 编号 2 }
}bar();编号 1 的 [[Scope]] 属性Scopes[1] : 编号 2 的 [[Scope]] 属性Scopes[2] : 这就解释了为什么结果是testValue inner 。
当 需要调用 testValue 变量时
先找本身作用域没有JS 引擎会顺着 作用域链 向下寻找 [0] [1] [2] […]。
在这里找到 bar 函数作用域另外有趣的是Closure 就是闭包的意思 。 证明全局作用域链是在 全局执行上下文初始化时 就已经确定的
我们来做一个有趣的实验跟刚才按照我描述的方法你可以找到 [[Scope]] 属性。
那这个属性是在什么时候被确定的呢
很显然我们需要从函数声明前函数执行时和函数执行完毕以后三个方面进行测试
console.log(add.prototype); // 编号1 声明前function add(x, y) {console.log(add.prototype); // 编号2 运行时return x y;
}add(1, 2);
console.log(add.prototype); // 编号3 执行后编号1 声明前 编号2 运行时 编号3 执行后 你可按照我的方法做很多次实验试着嵌套几个函数在调用它们之前观察作用域链。
作用域链是在 JS 引擎 完成 初始化执行上下文环境已经确定了这跟我们 变量提升 小节讲述得一样。
它保证着 JS 内部能正常查询 我们需要的变量。
我的一点疑惑
注意在这里我无法证明一个问题。
全局执行上下文初始化完毕之后它是把所有的函数作用域链确定。还是初始化一个执行上下文将本作用域的函数作用域链确定。
这是我的疑惑我无法证明这个问题但是我更倾向于 2 的观点如果知道如何证明请联系我。至少《高性能JavaScript》中是这样描述的。
知道作用域链有什么好处
试想我们知道作用域链有什么用呢
我们知道如果作用域链越深 [0] [1] [2] […] [n]我们调用的是 全局变量它永远在最后一个(这里是第 n 个)这样的查找到我们需要的变量会引发多大的性能问题JS 引擎查找变量时会耗费多少时间
所以这个故事告诉我们尽量将 全局变量局部化 避免作用域链的层层嵌套所带来的性能问题。
理解 执行上下文
将这段代码放置于全局作用域之下。这一段代码改编自《高性能JavaScript》。
function add(x, y) {return x y;
}var result add(1, 2);这段代码也很简洁但在 JavaScript 引擎内部发生的事情可并不简单。
正如上一节变量提升 所论述JS 引擎会初始化我们声明 函数 和 变量 。
那么在 add(1, 2) 执行前我们的 add 函数 [[Scope]] 内是怎样的呢
这里有三个时期初始化 执行上下文、运行 执行上下文、结束 执行上下文。
很显然执行到 var result add(1, 2) 句时是程序正在准备初始化执行上下文 。 如上图所示在函数未调用之前已经有 add 函数的[[Scope]]属性所保存的 作用域链 里面已经有这些东西了。
当执行此函数时会建立一个称为 执行上下文 (execution context) 的内部对象。
一个 执行上下文 定义了一个函数执行时的环境每次调用函数就会创建一个 执行上下文 ;
一旦初始化 执行上下文 成功就会创建一个 活动对象 里面会产生 this arguments 以及我们声明的变量这个例子里面是 x、y。
运行执行上下文 阶段 结束 执行上下文 阶段 好了但是这里没有涉及到调用其他函数。
其实还有我们的 JavaScript 引擎是如何管理多个函数之间的 执行上下文