前端之旅:学习笔记和资料

原型

  • 理解一个函数的原型属性(function’s prototype property )其实和实际的原型(prototype)没有关系对我们来说至关重要。
  • A prototype chain is a finite chain of objects which is used to implemented inheritance and shared properties.(原型链是一个用于实现继承和共享属性的有限对象链)
  • 还有一点我们需要注意的是,我们可以赋值任何类型的对象到原型上,但是不能赋值原子类型的值,比如如下代码是无效的:Cat.Prototype = 5;
  • 原型原型链是用于对象标识符查找(属性和方法),而作用域作用域链是用于普通标识符的查找(变量和函数)

作用域、作用域链、闭包

  • 当在函数中访问一个变量的时候,搜索顺序是先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索父函数的活动对象,依次查找,直到找到为止。如果整个作用域链上都无法找到,则返回undefined。
  • 如果函数存在Prototype原型对象,则在查找完自身的活动对象后再查找自身的原型对象,这就是Javascript中的变量查找机制。(经如下代码证明这一点是错误的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function a() {
    var i = 0;
    function b() {
    alert(i); //0
    alert(y); //error
    alert(x); //error
    };
    b.prototype.y='y=5';
    return b;
    }
    a.prototype.x='x=10';
    var c=a();
    c();
  • 在代码执行过程中,作用域链可以通过使用with语句和catch从句对象来增强(将它们的对象参数作为活动对象加入到作用域链顶端)。并且由于这些对象是简单的对象,它们可以拥有原型(和原型链)。这个事实导致作用域链查找变为两个维度

    1. 首先是查找作用域链上的活动对象
    2. 然后在每个作用域链上的活动对象上查找该对象的原型链(如果有原型)
  • 注意,由于_每个(标准的)函数都在创建的时候保存了[[Scope]],所以理论上来讲,ECMAScript中的所有函数_都是_闭包_。
    闭包是一个代码块(在ECMAScript是一个函数)和以静态方式/词法方式进行存储的所有父作用域的一个集合体。所以,通过这些存储的作用域,函数可以很容易的找到自由变量。

this

  • this始终指向此时函数/方法调用者,而不是在定义函数/方法时指向的对象。
  • 任何对象都可以作为上下文的this值。我想再次澄清对与ECMAScript中,与执行上下文相关的一些描述——特别是this的误解。通常,this 被错误地描述为变量对象(活动对象)的属性。 请牢记:

    this是执行上下文环境的一个属性,而不是某个变量对象/活动对象的属性
    这个特点很重要,因为和变量不同,this是没有一个类似搜寻变量的过程。当你在代码中使用了this,这个 this的值就直接从执行的上下文中获取了,而不会从作用域链中搜寻。this的值只取决于进入上下文时的情况。

  • 注意this这个值在一个继承机制中,是指向它的当前对象,而不是从原型链上找到它时所属于的对象。

继承

  • 有一种叫作冒充继承的方式代码如:

    1
    Cat.prototype = Animal.prototype;

    这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。
    为了解决这个问题,后来增加了一个在中间桥接的空白对象。
    比如下面这个extend函数,就是YUI库如何实现继承的方法。

    1
    2
    3
    4
    5
    6
    7
      function extend(Child, Parent) {
        var F = function(){};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
        Child.uber = Parent.prototype;
      }

其他

  • JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性,就需要使用外部的 hasOwnProperty 函数来获取正确的结果
    // 使用{}对象的 hasOwnProperty
    {}.hasOwnProperty.call()

  • 在使用第三方JS类库的时候,往往有时候他们定义的原型方法是不能满足我们的需要,但是又离不开这个类库,所以这时候我们就需要重写他们的原型中的一个或者多个属性或function,我们可以通过声明的同样的名称(标识符)的形式来达到覆盖重写的功能

  • 在浏览器中setTimeout、setInterval和匿名函数执行时的this是全局对象window

  • apply用于改变函数执行时的当前对象,当无参数时,当前对象为window,有参数时当前对象为该参数。

  • 激活其它上下文的某个上下文被称为 调用者(caller) 。被激活的上下文被称为被调用者(callee) 。被调用者同时也可能是调用者(比如一个在全局上下文中被调用的函数调用某些自身的内部方法)。

深入理解作用域

来自 Javascript作用域原理:

  • 在JS中“一切皆是对象, 函数也是”
  • 在对应作用域内,函数定义会提升,但函数表达式没有提升
  • JS权威指南中有一句很精辟的描述:“JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里”
  • ECMA262中所述作用域的实现:
    1. 任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现
    2. 在一个函数被定义的时候, 会将该函数的[[scope]]属性指向它此时的作用域链(scope chain)
    3. 在一个函数被调用的时候,会创建一个活动对象,然后在活动对象中加入thisarguments属性,对于每一个函数的形参和变量,都命名为该活动对象的命名属性, 然后将这个活动对象加入到此时的作用域链的最前端
  • 在调用函数执行之前, 会首先创建一个活动对象, 然后搜寻这个函数中的形参、局部变量定义和函数定义, 将它们作为这个活动对象的同名属性, 对于形参则直接赋值,对于局部变量定义,变量的值会在真正执行的时候才计算,此时只是简单的赋为undefined
  • JS作为一门解释执行的语言其实是有预编译的过程的, JS在执行每一段代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式)。
  • JS的预编译是以段为处理单元的… 而段是按script标记来分的,所以一个script就是一个处理单元。 

来自 理解 JavaScript 作用域和作用域链

  • 函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JS引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
  • 在定义函数时,它的作用域链中会填入一个全局对象,该全局对象包含了所有全局变量,如下图所示(注意:图片只例举了全部变量中的一部分):

  • 执行此函数时会创建一个称为“运行时上下文(execution context)”的内部对象,运行时上下文定义了函数执行时的环境。每个运行时上下文都有自己的作用域链,用于标识符解析,当运行时上下文被创建时,它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。
    而形参和变量按照它们出现在函数中的顺序被复制到运行时上下文的作用域链中。它们共同组成了一个新的对象,叫“活动对象(activation object)”,该对象包含了函数的所有局部变量、命名参数、arguments以及this,然后此对象会被加入到作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。新的作用域链如下图所示:

  • 从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。

  • 函数每次执行时对应的运行期上下文都是独一无二的,所以多次调用同一个函数就会导致创建多个运行期上下文,当函数执行完毕,执行上下文会被销毁。每一个运行期上下文都和一个作用域链关联。一般情况下,在运行期上下文运行的过程中,其作用域链只会被with语句和catch语句影响。
    当代码运行到with语句时,运行期上下文的作用域链临时被改变了。一个新的可变对象被创建,它包含了参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着该函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。如下图所示:

    当try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变对象并置于作用域链的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中,此时的作用域链图和上图类似。
    因此在程序中应避免使用with语句,由于try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免,可通过优化代码来减少catch语句对性能的影响。一个很好的模式是将错误委托给一个函数处理。

参考资料

学习时,发现部分链接已经无效,并且自己补充了一些。

js基础:
http://weizhifeng.net/javascript-the-core.html
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
http://www.nowamagic.net/librarys/veda/detail/1643
http://www.nowamagic.net/librarys/veda/detail/1644
http://www.nowamagic.net/librarys/veda/detail/1645

面向对象:
http://www.ruanyifeng.com/blog/2010/05/object-ooriented_javascript_encapsulation.html
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html
http://www.nowamagic.net/librarys/veda/detail/1642
http://www.cnblogs.com/fool/archive/2010/10/16/1853126.html
http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html

this:
http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html
http://www.ibm.com/developerworks/cn/web/1207_wangqf_jsthis/index.html
http://www.cnblogs.com/justany/archive/2012/11/01/the_keyword_this_in_javascript.html
http://www.nowamagic.net/librarys/veda/detail/1647

原型:
http://blog.jobbole.com/9648/
http://www.nowamagic.net/librarys/veda/detail/1641
http://www.nowamagic.net/librarys/veda/detail/1648
http://www.cnblogs.com/wangfupeng1988/p/3977924.html

闭包:
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1530074.html
http://coolshell.cn/articles/6731.html
http://kb.cnblogs.com/page/105708/
http://www.nowamagic.net/librarys/veda/detail/1646

avatar

神无

舍悟离迷,六尘不改。