zoukankan      html  css  js  c++  java
  • 【翻译】javascript原型继承到底是如何实现的

    写文章之前先讲个笑话,活跃一下气氛——

    校长:“先生,我有一个好消息,一个坏消息,都是关于您儿子的。” 家长:“校长,先说坏消息吧!”

    校长:“坏消息是,您的儿子的动作十分女性化。” 家长:“那好消息呢?” 校长:“好消息是他现在是本校校花。”

     

    言归正传,先放原文地址:

    http://blog.vjeux.com/2011/javascript/how-prototypal-inheritance-really-works.html
    这是我第一次翻译别人的文章,不到之处,敬请谅解。
    全文:
    javascript——原型继承到底是如何实现的

    在网上,我们到处都看到javascript是基于原型的继承。尽管如此,javascript默认仅仅只是通过new操作符提供一种特殊形式的原型继承
    因此,许多言论和解释都是难以理解的。本文就是剖析原型继承的实质以及如何使用原型继承。

    原型继承定义
    当我们遇到原型继承时,我们通常会看到如下的一段解释:
    当需要读取一个对象的属性时,javascript会沿着原型链向上寻找直到发现吻合的属性为止。
    在多数的javascript实现中,__proto__用来表示原型链的下一个对象,
    在接下来的内容中,我们会发现__proto__与prototype的不同之处。
    Note:__proto__是非标准的 ,因此不应该出现在你的正式代码中,本文使用这个属性,只为明理,而非实用。

    下面的代码揭示了javascript引擎是如何检索一个属性的
    function getProperty(obj, prop) {
    if (obj.hasOwnProperty(prop))
    return obj[prop]

    else if (obj.__proto__ !== null)
    return getProperty(obj.__proto__, prop)

    else
    return undefined
    }

    我们举一个通俗的例子:二位坐标点。一个点有x、y两个坐标和一个print方法。
    遵循上面的原型继承定义,我们来生成一个具有3个属性(x、y、print)的对象,为了创建一个新的点,用__proto__来创建一个点对象。
    var Point = {
    x: 0,
    y: 0,
    print: function () { console.log(this.x, this.y); }
    };

    var p = {x: 10, y: 20, __proto__: Point};
    p.print(); // 10 20

    javascript怪异的原型继承
    让人迷惑的是,每一个引用上面的定义来教授javascript原型继承的人都没有给出类似上面的代码,
    取而代之的是类似下面的代码:

    function Point(x, y) {
    this.x = x;
    this.y = y;
    }
    Point.prototype = {
    print: function () { console.log(this.x, this.y); }
    };

    var p = new Point(10, 20);
    p.print(); // 10 20

    这与我们上面的代码完全不一样,Point现在是一个函数,我们使用prototype属性,使用new操作符,真是苦逼啊。

    new操作符是怎么工作的?
    Brendan Eich(JavaScript的发明人)想让javascript看起来与一些传统的面向对象语言(比如Java,C++)跟接近,在哪些传统语言中,通过new操作符来创建一个类的实例,因此,他也跟风实现了new操作符。
    C++有构造函数的概念,用来初始化一个实例属性。因此,new操作符必须用在一个函数上。
    对象的方法需要有地方存放,鉴于我们在使用原型式语言,我们就把他放在函数的prototype属性里面

    new操作符就像含有一个F函数,以及参数new F(arguments),几个步骤如下:
    1、创建一个类的实例,得到的是一个__proto__指向F。protoype的空对象。
    2、初始化前面得到的空对象,也就是实例。在这一步,F函数被调用,并且将参数传递进来,将this指向第一步的实例。
    3、返回这个实例

    可能说的不好理解,我们用代码方式来实现他的的步骤:
    function New (f) {
    /*1*/ var n = { '__proto__': f.prototype };
    return function () {
    /*2*/ f.apply(n, arguments);
    /*3*/ return n;
    };
    }

    下面是演示其工作原理的一个小例子:
    function Point(x, y) {
    this.x = x;
    this.y = y;
    }
    Point.prototype = {
    print: function () { console.log(this.x, this.y); }
    };

    var p1 = new Point(10, 20);
    p1.print(); // 10 20
    console.log(p1 instanceof Point); // true

    var p2 = New (Point)(10, 20);
    p2.print(); // 10 20
    console.log(p2 instanceof Point); // true

    javacsript中真正的原型继承

    javascript标准仅仅给出类new操作符。尽管如此,Douglas Crockford找到了一个方法利用new来实现真正的原型继承。他写出了如下的 Object.create函数:

    Object.create = function (parent) {
    function F() {}
    F.prototype = parent;
    return new F();
    };

    这样看起来很怪异,但是却是很简单。这个函数仅仅创建了新对象,并且他的原型可以指向任何你想要指向的位置,如果我们可以随意使用__proto__,我们也可以这么写:
    Object.create = function (parent) {
    return { '__proto__': parent };
    };

    下面用真正的原型继承来改写我们上面的坐标点例子:
    var Point = {
    x: 0,
    y: 0,
    print: function () { console.log(this.x, this.y); }
    };

    var p = Object.create(Point);
    p.x = 10;
    p.y = 20;
    p.print(); // 10 20

    结论:
    我们讨论了什么是原型继承,以及javascript是怎样通过一种特别的方式来实现。
    尽管如此,真正的原型继承(Object.create 和 __proto__)使用起来还是有一些缺点的:
    1、不是标准方法:__proto__是不标准甚至是已过时的,原生的Object.create方法与Douglas Crockford的实现并不是完全等价的。
    2、不够优化:Object.create (不管是原生的还是模仿的)都不如new操作符优化,速度差距可能达到10倍。


  • 相关阅读:
    Android和C#实时视频传输Demo
    cocos2d-x3.0 windows 环境配置
    WPF六个控制概述
    高度并行的指令级的超级处理器
    Oracle存储过程的简单示例
    SharePoint Search之(两)持续抓取Continues crawl
    第28周三
    第28周二
    第28周一
    第27周日
  • 原文地址:https://www.cnblogs.com/xesam/p/javascript_prototype.html
Copyright © 2011-2022 走看看