• 作用域、闭包等概念的理解


    总结一下我对JS中这些基本却略纠结的概念的理解。

    作用域

    我们知道,JS不支持块级作用域,只支持函数作用域。函数体内,既不是局部变量,也不是参数的变量称为自由变量。如果没搞清楚函数的作用域,有时某些自由变量的值会与你所想的很不一样。举个简单例子

     1 var a = 10;
     2 
     3 function getA() {
     4     alert(a);
     5 }
     6 
     7 (function() {
     8     var a = 20;
     9     getA();     //10         
    10 })();    
    11 
    12 (function(fn) {
    13     var a = 20;
    14     fn();       //10,作为参数传入也是一样
    15 })(getA);    

    先说说执行上下文,也就是每一步的执行环境。执行上下文里有变量对象作用域链this

    变量对象存储着定义在上下文中的函数声明和变量,相当于保管数据。

    作用域链指定了各级作用域的优先顺序,比如在当前上下文中的变量对象没有找到,就会去父上下文中去找,顺着作用域链一直向上找。

    this是执行上下文的一个属性,很多地方说this是函数或构造函数的属性的说法是错误的。在进入上下文的时候确定了this的值,指向调用该函数的对象,this的值不会为null,所以如果没有明确调用该函数的对象那么this指向window,在整个上下文持续期间this的值不变。this不是变量,所以给this赋值是无效的。

    函数的作用域scope=AO+[[scope]]

    当函数被激活调用时,函数上下文中的变量对象称为活动对象AO,它除了存储定义在函数里的变量和函数之外,还包括传递给函数的参数。活动对象在函数被调用时创建。[[scope]]是函数的一个内部属性,它是所有父级变量对象的层级链,注意是所有父级,包括父的父级。函数被创建时就获得了[[scope]]属性,[[scope]]是静态的,也就是说在定义函数时就有它了,而且保持不变,不管函数有没有被调用,直到函数被销毁为止,[[scope]]属性都保持不变。注意,活动对象是和执行上下文相关的,而[[scope]]是函数的一个内部属性,它们结合在一起构成了函数的作用域。

    闭包

    官方定义看起来比较高大上,闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。换句话说,闭包是代码块和创建该代码块的上下文中数据的结合(详见汤姆大叔深入理解javascript系列)。如果你对上面作用域理解了,那么你可能会问,那JS中的所有函数岂不都是闭包吗。是的,理论上说JS中的所有函数都是闭包,因为函数创建时就把父上下文的数据保存在[[scope]]里了,即使是在window下创建的函数,按照定义它也是闭包。有点像在玩文字游戏,主要是为了加深理解。

    从实践角度,我们通常所说的闭包,我的理解是函数保留了对父上下文中变量的引用,简单说就是引用了自由变量,那么即使被引用变量所在的上下文销毁了,这个变量没有被GC回收,仍存在于函数的作用域链中;或者函数由父上下文返回被外部的变量引用了,即使创建函数的上下文销毁了,函数仍然存在。这样的函数,叫做闭包。换言之,其作用在于使变量和函数不会正常地被GC回收而可以通过闭包的作用域访问到。

    需要注意的是,同一个父上下文中创建的闭包共用一个[[scope]]属性,这是理所当然的,也就是说某一个闭包对它的[[scope]]里的变量进行了修改,那么其它闭包的[[scope]]里相应的变量也会变化,道理大家都懂,但初学时在实际应用中还是容易犯错误。

     1 var data = [];
     2 
     3 for (var i = 0; i < 3; i++) {
     4     data[i] = function() {
     5         alert(i);
     6     };
     7 }
     8 
     9 data[0]();   //3,而不是0
    10 data[1]();   //3,而不是1
    11 data[2]();   //3,而不是2    

    原型链

    原型的知识网上书上太多了,这里说一下prototype和__proto__的区别。

    prototype是函数的一个属性,JS中每个函数都有这个属性,它指向一个对象,就是我们常说的原型。

    __proto__是对象的一个内部属性,所谓内部就是本意是不会被外部看到的,是JS内部用来查找原型链的,只不过某些浏览器(chrome等)将其暴露出来了。

    1 function Person() {}
    2 
    3 var p = new Person();
    4 
    5 alert(p.__proto__ === Person.prototype);   //true
  • 相关阅读:
    安装IIS
    SQL 通过某个字段名称找到数据库中对应的表
    javascript 操作 drop down list
    The project type is not supported by this installationVS2005
    Get 和 Post 简介
    .Net 控件调用 javascript事件
    JQuery检测浏览器版本
    开车要点
    linux shell工程师要求
    memory management
  • 原文地址:https://www.cnblogs.com/coiorz/p/4719221.html
Copyright © 2020-2023  润新知