• 极品javascript进阶博客教程《ECMA2623 in detail》读书笔记


     前言:

     在我看来javascript是一门非常松散灵活的语言,松散并不是贬义,而恰恰相反,是对js的一种赞美。javascript相对于c#等静态编译语言来说,就像一个骑自行车已经十分熟练的顽皮孩子,已经不再需要遵循上车必须从车右边扶车把,左脚踏踏板右脚蹬地几下的标准动作了,他可以做出若干不同的上车花式,但始终不会背离平衡这一原则。正式这样的灵活性甚至看上去有点怪异的行为令javascript与c#这些语言表现格格不入:它是面向对象,却没有类,能继承又搞出个prototype,“this”的神出鬼没难以捉摸,闭包,作用域,弱类型等概念更是让人摸不着头脑。

      但是请不要忘记,我们觉得js怪异只是因为我们早已习惯类C语言的思维,js还是一门计算机语言,就像那个爱玩花式的骑车小男孩没有背离“平衡”这一原则一样,js并没有背离计算机语言的一个原则(就跟其他计算机语言一样):控制计算机去做事 。

    从另一个角度来说,正是因为js的灵活与怪异,我们更应该像学其他静态语言一样深入学习它。在“学习另一种颇为不同的语言”的原则指导下, 深入学习js还是很有价值的。学习js能令人从一种与c# java非常不同的思维去认识计算机语言和编程,去认识一些万变不离其宗的核心东西和思想。

    在我学习javascript的过程中,很自然地对很多概念迷惑不解,网上虽然有很多“深入理解javascript XXX”的教程,但始终觉得不够深入,就像你问别人栈是什么,别人只告诉你“先进后出”一样不痛不痒。

    但幸运的是,前几天终于发现了俄国人Dmitry Soshnikov写的一个系列博客教程: 《ECMA-262-3 in detail》,在园子里也有精彩的“官方翻译”:http://www.cnblogs.com/justinw/archive/2010/04/16/1713086.html。这系列博客真的是非常的有用,从ECMA规范的角度来解析js更加之深入和彻底,而且涵盖了js进阶的所有话题。但虽然该系列博客写的非常好,例子恰当,但毕竟其描述的都是js中比较高级的内容,因此不免有很多点很难马上弄懂,需要反复阅读斟酌,因此本编博客便是我在学习的过程中的笔记,随时记录一些心得和其他需要铭记的点。

     

    第一章:Execution Contexts

     函数递归调用的时候也会产生execution context

    execution context是逻辑堆栈结构


    第二章:Variable Object

    即使是在永远不会被执行的else分支中声明的变量,或者function定义,也会被存放在vo中 

    function中的vo叫做Activation Object(AO) ,而global中的vo也就是global,VO只是泛称。

    scope chain=list  of parent's VOs  + function's own AO 

    另外,有一个现象可能跟一句话有关:An activation object is created on entering the context of a function and initialized by propertyarguments which value is the Arguments object: 

    这个现象就是:如果一个函数中声明一个变量,且该变量的名称与函数的形参的名称相同,那么,这个变量和这个形参其实是同一个东西,看例子:

    function foo(i){
         var i=5;
        alert(arguments[0]); 

     foo(10);//5,而不是10
     

    而反过来也成立:

    function foo(i){
        var i;
        arguments[0]=10 
        alert(i); 

    foo(5);//10,而不是5

    至于底层的实现是不是就是这样现在还在研究中,但好像以这样来解析也行得通。 

    第三章:this.  

    Identifier 是变量名,函数名(例如 function foo(){}中的"foo",函数参数名及在global中未识别的属性)

    引用类型只出现在两种情况:处理Identifier的时候,和属性访问器。属性访问器就是"."和"[]",例如"obj.foo"和"obj["foo"]"

    引用类型的base属性指向该对象的所有者

    第四章:Scope chain.

    “scope”其实就是[[scope]],通常说scope的时候是指函数对象的[[scope]]属性,而 scope chain是指函数对象中[[scope]]链表(当然不一定就是链表结构) 

    [[scope]]是在一个函数创建的时候创建的,而不是在一个函数被执行的时候(进入execution context)时候创建,并且并不会在函数执行其中改变,因此,才有闭包的机制:

    var x=10;
    function a(){
        alert(x);
    }
    function b(){
        var x=20;
        a();
    }
    b();//10,而不是20

    第六章:闭包

     现在要解析闭包有点难,因为要说清楚闭包就必须要搞清楚scope/scope chain和 context,反正和前几章都有关联,要详细解析闭包可能还要写一遍比较完整的笔记。现在有些零碎的理解,先记下来吧,反正会有用到,先看一个闭包的经典例子:

    function foo(){
       var x=10;
       return function(){
           alert(x);
       }
    }
    var bar=foo();
    bar();//10

    如果不是闭包的话,按逻辑来说,x是foo中的局部变量,当foo执行完返回的时候,x已经被释放掉。那么,从表象的层面来解析,闭包就是在函数返回后仍能访问该函数内部数据的一种机制,从原理来说,就是被调用的函数(这里是foo中的那个匿名函数)的scope包含了外部函数(foo)的数据(“数据”一词是故意用的,这样说比较笼统,因为我还不清楚这应该是什么,难道是VO?迟些再研究一下),而从作用域的层面去看,就是因为“js是基于静态作用域”的,所以《ecmascript in detail》中有一句话:Referencing to algorithm of functions creation, we see that all functions in ECMAScript are closures, since all of them at creation save scope chain of a parent context.(ecmascript中所有函数都是闭包)

     这里必须看看“动态作用域”和“静态作用域”。先看一个例子:

    //假定这里是global
    var x=5;
    function foo(){
       alert(x);
    }

    function bar(){
       var x=10;
       foo();
    }
    bar();//5,而不是10

    静态作用域,有些地方称词法作用域,是指:一个作用域在函数创建的时候,就已经确定好了,而不是在执行的时候。也可以用另外一句话说:函数作用域是在源代码中确定的。

    通过上面例子来看看这个“源代码中确定“是什么意思:上面例子中,调用bar的时候,进入bar的上下文,bar里面声明了一个x,并赋值x=10,然后调用foo,如果是动态作用域,也就是在代码执行时刻确定作用域的话,那么,foo里面被alert的x应该是bar里面的x,也就是10,但是在静态作用域的原理下”函数作用域是在源代码中确定的”,也就是说,调用foo的时候,解析引擎会回到foo定义的地方(源代码的前4句)去找,而在这里,很明显,x是第一句源代码定义的那个值为5的x,而且在foo被定义的源代码中,值为10的x都还没有出世~~~。这个就是我自己对“静态作用域”的理解,不过对于动态作用域我理解不多,有空看看维基百科上关于动态作用域

    在这里顺便提一下,C语言也是静态作用域:

    #include "stdafx.h"


    int x=5;
    void fun1(){
        printf("%d",x);
    }

    void fun2()
    {
        int x=10;
        fun1();
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        fun2();//5
        scanf("%d",&x);

     ecmascript只使用静态作用域(语法作用域)

    ECMAScript中,闭包指的是:

    从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
    从实践角度:以下函数才算是闭包:
    1.即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
    2.在代码中引用了自由变量

  • 相关阅读:
    asp.net的Context.Cache缓存过期策略
    sql语句执行时算术运算导致溢出。
    sqlserver进行join的方式选择
    Apollo配置中心
    sqlserver的left join优化
    iis设置上传文件大小限制
    Android中的颜色值
    Network authentication method and device for implementing the same
    MongoDB GridFS
    MongoDB 正则表达式
  • 原文地址:https://www.cnblogs.com/lwhkdash/p/2347075.html
Copyright © 2020-2023  润新知