摘要:词法作用域定义在词法阶段的作用域由你在写代码时将变量和块作用域写在哪来决定的,因此当词法分析器处理代码时会保持作用域不变。欺骗词法作用域在词法分析器处理过后依然可以修改作用域。
你不知道的JS(上卷)笔记
你不知道的 JavaScript
JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 JavaScript 开发者,如果没有认真学习的话也无法真正理解它们.
上卷包括俩节:
作用域和闭包
this 和对象原型
作用域和闭包希望 Kyle 对 JavaScript 工作原理每一个细节的批判性思 考会渗透到你的思考过程和日常工作中。知其然,也要知其所以然。
词法作用域作用域共有俩种主要的工作模型: 词法作用域和动态作用域。
词法阶段词法化:大部分标准语言编译器的第一个工作阶段叫作词法化(也叫单词化)。
词法化的过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋 予单词语义。
词法作用域
定义在词法阶段的作用域
由你在写代码时将变量和块作用域写在哪来决定的,因此当词法分析器处理代码时会保持作用域不变。
欺骗词法作用域: 在词法分析器处理过后依然可以修改作用域。
事实上,让词法作用域根据词法关系保持书写时的自然关系不变是一个非常好的最佳实践。
“作用域气泡法” 划分作用域
查找作用域气泡的结构和互相之间的位置关系给引擎提供了足够的位置信息,引擎用这些信息来查找标识符的位置。
作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者向上进行。
作用域查找会在找到第一个匹配的标识符时停止,或者直至找到最后一个全局作用域处。
window.a的方式可以访问那些被同名变量遮蔽了的全局变量,但非全局变量如果被遮蔽,就无法访问到了
遮蔽效应: 在多层的嵌套作用域中可以定义同名的标识符。
欺骗词法俩种欺骗手段:eval和with;
社区认为使用这俩种机制并不是什么好主意,因为使用这俩种机制会导致性能下降
另外一个不推荐使用 eval(..) 和 with 的原因是会被严格模式所影响(限 制)。with 被完全禁止,而在保留核心功能的前提下,间接或非安全地使用 eval(..) 也被禁止了。
JavaScript 中的 eval(..) 函数可以接受一个字符串为参数,并将其中的内容视为好像在书 写时就存在于程序中这个位置的代码。换句话说,可以在你写的代码中用程序生成代码并 运行,就好像代码是写在那个位置的一样。
function foo(str, a) { eval( str ); // 欺骗! console.log( a, b ); } var b = 2; foo( "var b = 3;", 1 ); // 1, 3
eval通常被用来执行动态创建的代码
在这个例子中,为了展示的方便和简洁,我们传递进去的“代码”字符串是 固定不变的。而在实际情况中,可以非常容易地根据程序逻辑动态地将字符 拼接在一起之后再传递进去。eval(..) 通常被用来执行动态创建的代码,因 为像例子中这样动态地执行一段固定字符所组成的代码,并没有比直接将代 码写在那里更有好处。
在严格模式的程序中,eval(..) 在运行时有其自己的词法作用域,意味着其 中的声明无法修改所在的作用域。
类似:setTimeout的第一个参数为字符串时;new Function的最后一个字符串参数;等都不提倡,不要使用。
withwith 通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象 本身。
例如:
var obj = { a: 1, b: 2, c: 3 }; // 单调乏味的重复 "obj" obj.a = 2; obj.b = 3; obj.c = 4; // 简单的快捷方式 with (obj) { a = 3; b = 4; c = 5; } // 但实际上这不仅仅是为了方便地访问对象属性。考虑如下代码: function foo(obj) { with (obj) { a = 2; } } var o1 = { a: 3 }; var o2 = { b: 3 }; foo( o1 ); console.log( o1.a ); // 2 foo( o2 ); console.log( o2.a ); // undefined console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!
可以注意到一个奇怪的副作用,实际上 a = 2 赋值操作创建了一个全局的变量 a。这 是怎么回事?
with 可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域,因此这个对 象的属性也会被处理为定义在这个作用域中的词法标识符。
尽管 with 块可以将一个对象处理为词法作用域,但是这个块内部正常的 var 声明并不会被限制在这个块的作用域中,而是被添加到 with 所处的函数作 用域中。
with 这种将对象及其属性放进一个作用域并同时分配标识符的行为很让人费解。
性能JavaScript 引擎会在编译阶段进行数项的性能优化。其中有些优化依赖于能够根据代码的 词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到 标识符。
小结词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段 基本能够知道全部标识符在哪里以及是如何声明的,从而能够预测在执行过程中如何对它 们进行查找。
JavaScript 中有两个机制可以“欺骗”词法作用域:eval(..) 和 with。前者可以对一段包 含一个或多个声明的“代码”字符串进行演算,并借此来修改已经存在的词法作用域(在 运行时)。后者本质上是通过将一个对象的引用当作作用域来处理,将对象的属性当作作 用域中的标识符来处理,从而创建了一个新的词法作用域(同样是在运行时)。
这两个机制的副作用是引擎无法在编译时对作用域查找进行优化,因为引擎只能谨慎地认 为这样的优化是无效的。使用这其中任何一个机制都将导致代码运行变慢。不要使用它们。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/108971.html
摘要:如果提升改变了代码执行的顺序,会造成非常严重的破坏。声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。要注意避免重复声明,特别是当普通的声明和函数声明混合在一起的时候,否则会引起很多危险的问题 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 Ja...
摘要:词法作用域的查找规则是闭包的一部分。因此的确同闭包息息相关,即使本身并不会真的使用闭包。而上面的创建一个闭包,本质上这是将一个块转换成一个可以被关闭的作用域。结合块级作用域与闭包模块这个模式在中被称为模块。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 Jav...
摘要:如果是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。给函数表达式指定一个函数名可以有效的解决以上问题。始终给函数表达式命名是一个最佳实践。也有开发者干脆关闭了静态检查工具对重复变量名的检查。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 Ja...
摘要:的抽象语法树中可能会有一个叫作的顶级节点,接下来是一个叫作它的值是的子节点,以及一个叫作的子节点。值得注意的是,是非常重要的异常类型。严格模式下,未声明的和俩者行为相同,都会是。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 JavaScript 开发者,如果...
iKcamp官网:http://www.ikcamp.com 访问官网更快阅读全部免费分享课程:《iKcamp出品|全网最新|微信小程序|基于最新版1.0开发者工具之初中级培训教程分享》。包含:文章、视频、源代码 showImg(https://segmentfault.com/img/remote/1460000011522427?w=1626&h=1242); 第二章:小程序中级实战教程之预...
阅读 763·2019-08-30 15:55
阅读 1494·2019-08-30 15:52
阅读 2668·2019-08-30 15:44
阅读 2059·2019-08-30 11:14
阅读 2596·2019-08-29 13:59
阅读 1760·2019-08-29 13:45
阅读 987·2019-08-29 13:21
阅读 3345·2019-08-26 13:31