js便签笔记(12)——浏览TOM大叔博客的学习笔记 part2 1. 前言 2. 作用域链 3. 二维链查找  4. 独立作用域只能通过函数来创建 5. 隐式全局变量的本质 6. 函数声明和函数表达式的不同 7.js使用静态作用域

js便签笔记(12)——浏览TOM大叔博客的学习笔记 part2
1. 前言
2. 作用域链
3. 二维链查找 
4. 独立作用域只能通过函数来创建
5. 隐式全局变量的本质
6. 函数声明和函数表达式的不同
7.js使用静态作用域

昨天写了《js便签笔记(11)——浏览TOM大叔博客的学习笔记 part1》,简单记录了几个问题。part1的重点还是在于最后那个循环创建函数的问题,也就是多个子函数公用一个闭包数据的问题。如果觉得有兴趣,可以再重新翻出来看看。

今天继续把剩下的问题写完。

2. 作用域链

学js的人,即使初级入门的也都知道“原型链”,但是“作用域链”,可能好多人没有听说过。大部分人都知道或者听说过“闭包”,但是可能有好多人不知道闭包其实和作用域链有莫大的联系。如果理解闭包不从作用域链开始理解,那么你就只能理解闭包的皮毛。

我也是从TOM大叔的这些博客中才了解到作用域链的,之前也看过了许多本书籍,都没有很清晰的展开作用域链这个概念。其实作用域链简单说来也好理解,如下代码:

        var x = 10;
        function fn() {
            var y = 20;
            return function () {
                var z = 30
                console.log(x + y + z);
            }
        }

上面代码中,如果想要打印 x+y+z 的值,就必须要遍历三个层次的上下文环境或者作用域,这其实和原型链的结构表现形式类似。但要细细将来,连同闭包图文并茂的说明白,需要很多内容。

此处不再深入进去,以后有机会再另起一篇详细介绍。

3. 二维链查找 

上文讲到通过作用域练向上查找变量,实际在查找变量的过程中,是使用“二维链查找”——“作用域链” + “原型链”。看如下代码:

       Object.prototype.x = 10;
        function fn() {
            var y = 20;
            return function () {
                var z = 30
                console.log(x + y + z);
            }
        }

这份代码跟上文中演示作用域链的代码差不多,但是它却通过 Object.prototype.x = 10; 这么一句话,表现出了原型链在其中的作用。
因此,在查找变量值时,是同时兼顾原型链和作用域链两个方向的,即“二维链查找”。 

4. 独立作用域只能通过函数来创建

这句话的下半句是——不能通过if/for等语句块来创建。后半句大家可能知道,但是它的本质确实前半句——独立作用域只能通过函数来创建(除了独立作用域之外,剩下的就是全局作用域)。既然独立作用域只能通过函数来创建,那么函数中任何地方的*变量就都是函数层级的,因此,以下代码希望不要再次出现:

js便签笔记(12)——浏览TOM大叔博客的学习笔记 part2
1. 前言
2. 作用域链
3. 二维链查找 
4. 独立作用域只能通过函数来创建
5. 隐式全局变量的本质
6. 函数声明和函数表达式的不同
7.js使用静态作用域

5. 隐式全局变量的本质

var a = 10;
b = 20;

以上两句代码,看似都是声明两个全局变量,但是按照TOM大叔说的,只有var才能声明一个变量,也就是 var a = 10; 是真正的声明变量。

而下一句 b = 20,其实是相当于设置window的一个属性值而已。

因此,第一句的本质是声明一个全局变量;第二句的本质是设置window的一个属性值。

当然,不推荐用第二句的形式。

6. 函数声明和函数表达式的不同

js定义函数的方法有多种,但看看以下这段代码:

fn();
var fn = function() {  //函数表达式
    alert(123); // 报错
}
//------
fn();
function fn() {  //函数声明
    alert(123); // 123
}

两种函数定义方式,却得出不一样的结果。

此处我当时没有详细看,因为这样使用的情况不是很多,所以就没有过深入的细看,只是做了个标记。如果有了解的朋友,不放解释一下。

7.js使用静态作用域

在part1中讲过,当一个函数作为参数被传入,后者作为一个值被返回的时候,连同它一块被传递的,是它的作用域。也就是咱们常说的闭包。且看如下代码:

var x = 10;            
function foo() {
    alert(x);
}
(function (funarg) {
    var x = 20;
    funarg();    // 10, 不是20
})(foo); 

foo是一个函数,把它作为参数传入进另一个函数中执行,连同一起传递的,是foo的作用域。而foo使用的是静态作用域,其中的变量x在传递的时候已经被静态赋值,不会受其他环境下x变量的影响。
这个道理也同样适用于函数作为返回值。如下:

function fn() {
    var x = 10;
    return function () {
        alert(x);
    }
}
var ret = fn();
var x = 20;
ret();   // 10,不是20

更多内容请关注我的微博