看了《你不知道的 JavaScript(上卷)》的第一部分——作用域和闭包,感受颇深,遂写一篇读书笔记加深印象。路过的大牛欢迎指点,对这方面不懂的同学请绕道看书,以免误人子弟... 看过这本书的可以一起交流交流。
理解 js 作用域首先要了解 js 的编译过程(或者说解析过程)。
都说 node 是基于 chrome 的 V8 引擎开发 的。那么 V8 是引擎,node 是编译器吗?这个理解是错误的!我之前就是这么错误理解的,听说 node 是用 C++ 实现的,之前我一直以为 V8 是负责把 javascript 语言转换成底层的 C++,然后 node 很高级 node 负责编译,做 js 的语法检察,ES6 的新特性全都是 node 的开发人员,一点点的开发支持起来的。然而现实是,V8 包办了所有 js 编译的过程,而 node 只是一个环境。如首页所说
,是运行环境!node 只是在 V8 的基础上,做了终端命令行的支持、文件处理的支持、http 服务的支持等等,相当于一个给 V8 提供了各种功能的壳子。
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
上面说的三点是包含关系,不是并行关系!引擎包含编译器,对 js 进行编译,然后根据作用域和语句执行不同的代码逻辑。
怎么理解呢,我的理解是 LHS 查询是查询变量的命名空间,然后进行赋值。RHS 查询是在作用域链中,一级级的往上查找该变量的引用。
所以:
LHS:
的赋值、
- var c=
传参给
- foo(2)
时的赋值、
- foo(a)
的赋值
- var b=
函数调用时查找 foo() 方法、
- foo(2)
中 a 查找自己的值、
- var b=a
中 a 和 b 两个参数查找自己的值。
- a+b
作用域的概念,应该两张图几句话就能解释吧。
我觉得,说一个变量属于哪个作用域,可以顾名思义用来解释,所以上图中的 b 变量,可以说属于 bar() 的函数作用域内,也可以说是 foo() 的函数作用域内,也可以说是全局作用域内。
一层嵌一层的作用域形成了
,变量 b 在作用域链中的 foo() 函数内得到了自己的定义。
- 作用域链
、
- call()
之类的是改变作用域吗?他们只是改变了 this 的指向并不算改变作用域,是可以在编译阶段进行静态分析,所以不会导致上面说的无法优化的情况。
- bind()
我们知道函数可以形成作用域,还有哪些方式形成作用域呢?
可以指定变量的作用域(选择一个对象),在它的块作用域内,变量就相当于这个对象的属性。
- 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;
- }
不被推荐,因为它会影响性能,且不易阅读(代码块内的代码特别多的情况,根本不知道这个是普通的变量还是某个对象的属性,还是某个对象的属性的属性的属性)。
- try {
- undefined(); // 执行一个非法操作来强制制造一个异常
- }
- catch (err) {
- console.log( err ); // 能够正常执行!
- }
- console.log( err ); // ReferenceError: err not found
做错误状态传参的 err 变量是当前块的局部变量。
但是如果在 catch(err){…} 内部 var 其它变量,并没有效果,见下面代码。
- try {
- var abc='测试try块中的变量'
- }
- catch (err) {
- var b=2; // 没有错误,不会被执行到的。
- }
- console.log( abc ); // 测试try块中的变量
- try {
- throw '55'; // 制造一个异常
- }
- catch (err) {
- var abc='测试catch块中的变量';
- }
- console.log(abc); // 测试catch块中的变量
这是只属于 err 参数用的伪块作用域。
ES6 新特性,大神器。在
中形成块作用域,且不会遇到
- {}
的问题出现。 为变量显式声明块作用域,有助于
- 提升
。
- 回收内存垃圾
- function process(data) {
- // 在这里做点有趣的事情
- }
- // 在这个块中定义的内容可以销毁了! (这里指的是下面let定义的`someReallyBigData`)
- {
- let someReallyBigData = {..
- };
- process(someReallyBigData);
- }
- var btn = document.getElementById("my_button");
- btn.addEventListener("click",
- function click(evt) {
- console.log("button clicked");
- },
- /*capturingPhase=*/
- false);
let 有一个很有意思的地方,就是在 for 循环中。
编译器在解析作用域时,会对作用域中 var 声明的变量、函数进行
。
- 提升
- a = 2;
- var a;
- console.log(a); // 2
相当于
- var a;
- a = 2;
- console.log(a); // 2
- console.log( a ); // undefined
- var a=2;
相当于
- var a;
- console.log( a ); // undefined
- a=2;
- foo(); // 1
- var foo;
- function foo() {
- console.log( 1 );
- }
- foo = function() {
- console.log( 2 );
- };
相当于
- function foo() {
- console.log( 1 );
- }
- foo(); // 1
- foo = function() {
- console.log( 2 );
- };
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。
- function foo() {
- var a=2;
- function baz() {
- console.log( a ); // 2
- }
- bar( baz );
- }
- function bar(fn) {
- fn(); // 妈妈快看呀,这就是闭包!
- }
来源: http://www.cnblogs.com/xjchenhao/p/6516054.html