Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

原型继承机制的原理 #10

Open
24wangchen opened this issue Sep 24, 2014 · 2 comments
Open

原型继承机制的原理 #10

24wangchen opened this issue Sep 24, 2014 · 2 comments

Comments

@24wangchen
Copy link
Owner

原型继承的定义

当你读到原型继承时,经常会看到如下定义:

当访问对象属性时,javascript将遍历原型链向上查找,直到找到该属性。

大多数javascript使用 __ proto __ 代表原型链中下一个对象,我们将会介绍 __ proto __ 与prototype的区别

Note: __ proto __ 是不标准的,不应在代码中使用。本文中用于解释javascript继承原理。

下面的代码显示了javascript引擎如何查找属性

function getProperty(obj,prop){
    if( obj.hasOwnProperty(prop) ){
        return obj[prop];
    }else if(obj.__proto__ != null){
        return arguments.callee(obj.__proto__,prop);
    }else{
        return undefined;
    }
}

让我们看一个普通的例子:一个2D点:有两个点坐标x、y和一个print方法
使用原型继承方式之前,我们创建一个有三个属性x、y、print的对象。为了创建一个新点,我们创建一个对象,该对象 __ proto __ 设置为point

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像传统的面向对象语言如java、c++。在那些语言中,我们使用new实例化一个类,所以他为javascript写了一个new操作符。

  • C++有constructor用于初始化实例属性。因此,new 操作符必须用于一个函数
  • 我们需要将对象的方法置于某处。因为我们在研究一个原型的语言,将它放在函数的原型属性中
    new操作符接受一个F函数及其参数:new F(arguments),分为3个步骤:
  • 创建类的实例 它是一个空对象,其 __ proto __ 属性指向F.prototype
  • 初始化实例 函数F被调用,this指向实例
  • 返回实例
    解释代码如下:
function New(f) {
    var n = {'__proto__':f.prototype}; //step 1
    return function(){
        f.apply(n,arguments); //step 2
        return n; //step 3
    }
}

测试代码

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

Note:__ proto __ 与prototype并不是等价的。__ proto __ 实际上是某个实体对象的属性,而prototype则是属于构造器函数的属性

javascript中真正的原型继承

javascript规范只给出了new操作符供我们使用。然后Douglas Crockford使用new操作符实现了真正的原型继承。他写了Object.create函数

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

这个看上去很奇怪,但是真的很简单。它只是创建了一个新的对象,并将他的prototype属性指向了你所想要的。如果可以使用 __ proto __ ,也可以写成这样:

Object.create = function(parent) {
    return {'__proto__':parent};
}

以下代码为使用真正的原型继承实现的Point示例

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 __ )存在一些缺点:

  • 不标准 __ proto __ 不标准甚至被弃用。同样,原生的Object.create与Douglas Crockford的实现不完全相同
  • 没有优化 Object.create(原生或者自定义)并没有像new一样很好地优化,较new慢10倍

延伸阅读:

本文翻译自Vjeux

@lixuejiang
Copy link

好顶赞

@arjenhill
Copy link

分析的很透彻,点赞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants