javascript原型继承的学习研究
发布于 10 年前 作者 struCoder 5983 次浏览 最后一次编辑是 8 年前

###几句废话先
本人由.NET平台转向JS,但是对JS深层次的东西理解的不是很透彻,自己也花了些时间研究JS相关技术,今天这就当是自己学习研究的一个记录吧 :)
###js的原型链prototype chain 我一直很难理解js中德继承机制,他不像C#或者java中的那样去实现,而且在js中也没有instance这个概念,也就是说在js中没有子类父类的概念,他全靠prototype的模式实现继承机制。关于js的原型继承呢,你也会经常看到这样一句话:

当查找一个对象的属性时,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
}

说到这,就不得不说一下__proto__,在js中是靠他表示一个对象的原型链,但是使用他是不符合规范的,你将在下面的文章中看到,为什么不能使用他。我们先来举一个例子:

var say = {
    you:'',
    me:'',
    print:function(){console.log(this.you,this.me)}   
}
var p = {you:'jeff',me:'David','__proto__':say}
p.print();

在这里他的__proto__属性指向了say。(在这里我们可以吧这个属性当做一个指针,尽管不是:)) ###javascript中让人摸不着头脑的原型继承

  • 首先咱来谈谈 new 关键字:

造物者:Brendan Eich在设计javascript之初一定受到了c++和java的影响,c++和java还有c#之类的语言在调用new时一定会调用它们的构造函数constructor,但是呢,在javascript中new后面跟的不是,而是构造函数constructor 但是呢,这里有一个问题,使用构造函数生成的对象,无法共享其属性和方法,换句话说就是,你是你的,我是我的。 举一个例子吧:

function say(word){
    this.word = word;
    this.host = 'David zhang';
}
var sayer1 = new say('今儿北京雾霾太严重了');
var sayer2 = new say('下午6点回家');
//在这里这两个对象的host属性是独立的,
sayer2.host = 'jeff';
alert(sayer1.host);  //David Zhang;

从上面我们可以看出,每个实例对象有自己的属性和方法,实例与实例之间无法共享。这也就导致了资源的浪费 ####new的机制:

new 运算符接受一个函数 F 及其参数:new F(arguments…)。这一过程分为三步: + 创建类的实例。这步是把一个空的对象的 proto 属性设置为 F.prototype 。 + 初始化实例。函数 F 被传入参数并调用,关键字 this 被设定为该实例。 + 返回实例。

  • javascript中真正的原型继承

说到这咱又要说到咱的大宗师:道格拉斯了是他发现了一种可以利用 new 来实现真正的原型继承的方式:

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

上面那个say对象现在可以写成:

var say = {
    you:'',
    me:'',
    print:function(){console.log(this.you,this.me)}   
};
var s = Object.create(say);
s.you = 'noder';
s.me = 'David Zhang';
s.print();    //noder David Zhang

但是呢,Object.create()的性能比new差的很多! 现在说说为什么不要用__proto__:因为这样会对父类的所有子类开放整个原型链的操作权限(太可怕了!)

var P = new function(){}
P.prototype.name = 'David'
var a = new P()
a.name // 'David'
a.__proto__ === P.prototype //true
a.__proto__.name = 'Zhang'
P.prototype.name // 被修改为'Zhang'

###重点回顾

  • javascript中德原型链继承了啥:

    说到底就是继承了构造函数和原型链两个东西,构造函数继承就是意味着,把父类的属性方法给copy一遍,对其进行修改也不会影响到其他的实例。 而对于原型链的继承就表示子类和超类公用原型链上的东西,修改的话,只能从超类修改

###构造函数继承(觉得有些地方没有说清楚)

function a(){
    this.nameA = '我是基类a';
}

构造函数的继承在这我就先说两种方法: 1:构造函数绑定

function b(){
   a.apply(this,arguments);
   this.nameB = '我是子类b';
}
var p = new b();
b.nameA;  //我是基类a

2: 使用prototype 这里还是使用上面的a 和 b

b.prototype = new a();
b.prototype.constructor = b;
var p = new b();
b.nameA;//我是基类a

其实任何一个prototype对象都有一个constructor属性,指向它的构造函数 在这里b.prototype = new a();后b.prototype.constructor就指向a了 所以我们要加上b.prototype = b; (这是必须的,务必遵守!) ###几点要说:

14 回复

@hackerjs 嗯,保持学习 :)

支持一下。看来大家都喜欢原型继承,不过我也推荐使用模块模式来实现继承,不需要涉及原型以及相关概念,比较直观。

@name5566 嗯,您可以在回复板块进行补充啊。我也在不断地学习JS :)

@struCoder 模块模式可以通过代码来理解,代码比较多,移步:http://name5566.com/4690.html 继承相关部分。

@name5566 嗯,我看了楼主的文章,写得不错,但是在创建对象是不是有改进的地方呢? 并且在继承这一块给我一种繁琐的感觉(纯属个人愚见); 我们可以自己封装一个继承:

function classExtend(base,child){
    var F = function(){};
    F.prototype = base.prototype;
    child.prototype = new F();
    child.prototype.constructor = child;
}

或者进行深拷贝

function deepCopy(base, child) {
    var c = child|| {};
    for (var i in base) {
      if (typeof base[i] === 'object') {
        c[i] = (base[i].constructor === Array) ? [] : {};
        deepCopy(base[i], c[i]);
      } else {
         c[i] = base[i];
      }
    }
    return c;
  }

学习了很多遍,还是忘记了:( 咋办?

@kingapple 理解并且经常用,不会轻易忘记的 :)

@struCoder 经常用这货干啥?

var P = new function () {} P.prototype.name = ‘David’

is this correct? maybe not

@AdoHe
What do you mean? this two lines codes just an example that we should not use the__ proto also we can direct successor prototype like:

var F = function(){};
F.prototype = a.prototype;
b.prototype = new F();
b.prototype.constructor = b;

@struCoder well, write like this: var P = new function () {} P.prototype.name = ‘David’; maybe not correct, since the P.prototype maybe undefined…

@AdoHe sorry.That’s my wrong ! it should be:

var p = function(){};
p.prototype.name = 'David'

回到顶部