JavaScript伪经典继承和原型继承示例混淆

3

我并不是很理解伪经典继承和原型继承的区别。看一下下面的代码片段。

原型继承:我能够理解这段代码(道格拉斯·克罗克福德)。我已经更改了类名,并添加了实现以便更好地理解,因为他的代码块在展示时不完整。

var baseObject = {
    a : "old",
    firstMethod: function () {alert("First method");},
    secondMethod: function () {alert("Second method");}
};
var derivedObject = Object(baseObject);
alert(derivedObject.a);

derivedObject.thirdMethod = function () {alert("Third method");};
var derivedChildObject = Object(derivedObject);

derivedChildObject.firstMethod();
derivedChildObject.secondMethod();
derivedChildObject.thirdMethod();

伪经典:

由于他的演示代码片段中的方法不完整,我没有得到清晰的理解,因此我去了互联网上的其他链接。

http://javascript.info/tutorial/pseudo-classical-pattern

function Animal(name) {
  this.name = name
}

Animal.prototype = {
  canWalk: true,
  sit: function() {
    this.canWalk = false
    alert(this.name + ' sits down.')
  }
}

var animal = new Animal('Pet') // (1)

alert(animal.canWalk) // true

animal.sit()             // (2)

alert(animal.canWalk) // false

伪类的方案:

默认属性和方法都在原型中。原型中的方法使用this,因为this的值仅取决于调用上下文,所以animal.sit()会将this设置为animal。

你能否帮我提供正确的例子?你可以参考Animal用例或baseObject用例,也可以自己想出一个例子。


第二个例子“伪经典”不包括继承。继承仍然是基于原型的... - devnull69
如果您能提供一个伪经典的简单示例,那就太好了。 - Ravindra babu
@devnull69 只是为了澄清:你的意思是在 JavaScript 中,所有的继承都是基于原型的吗? - user2808054
@user2808054 正确 - devnull69
2个回答

2

这是一个原型继承的示例

var Mammal = function() {
   this.isMammal = true;
};

var Cat = function() {
   this.meow = function() {
      alert('Cat says: MEOW!');
   };
};

Cat.prototype = new Mammal();
Cat.prototype.constructor = Cat;

var kitty = new Cat();
if(kitty.isMammal) {...}       // evaluates to true
kitty.meow();

原型继承是通过将原型重新分配到基类的实例来实现的。这将导致原型链始终以Object.prototype结束。

如果您正在寻找一个方法或成员,您从实例开始,然后沿着原型链向下查找,直到找到方法/成员(或者没有)。

以toString()和kitty为例:

kitty.toString();

1. kitty instance doesn't have toString() method
2. Cat.prototype doesn't have toString() method
3. Mammal.prototype doesn't have toString() method
4. Object.prototype has toString() method, which will be called in the context of kitty

在你的例子中以下代码行有什么重要意义?Cat.prototype.constructor = Cat; - Ravindra babu
因为通过重新分配原型,您也会将构造函数重新分配给“Mammal”。因此,如果您仍希望Cat()是用于猫的构造函数,则需要重新重新分配它。 - devnull69
非常感谢。我已经清楚了原型继承。您能否提供伪经典的示例? - Ravindra babu
最后一个问题。Douglas Crockford的ppt说:伪经典的三个机制:1)构造函数。2)new运算符。3)函数的原型成员。阅读这张幻灯片后,我认为原型成员函数是原型继承的一部分,而伪经典继承是原型继承的超集,包括前两个特性——构造函数和New特性。如果我们只有原型继承,是否意味着他引用的所有三个特性都是原型继承的一部分? - Ravindra babu
1
@sunrise76 是的,它可以。你可以使用原型继承而不需要 new,甚至没有构造函数,但即使你使用这些特性,结果仍然是基于原型的。 - devnull69
显示剩余2条评论

1
你正在定义对象原型级别上的 canwalk 属性,这就是为什么在实例化对象时警报 (animal.canWalk) 会得到 true 的原因。你在第二个警报中得到 false 的原因是同时你调用了 sit 函数,在其中将 canWalk 设置为 false。
这就是为什么严格遵循原型继承模式不是太好的做法:通常,实例希望拥有它们自己的所有属性副本。这就是为什么原型模式很少单独使用的原因。
最推荐的方法是使用 寄生组合继承
组合继承是 JavaScript 中最常用的继承模式,虽然它并不是没有效率问题。该模式最低效的部分是超类型构造函数总是被调用两次:一次用于创建子类型的原型,另一次在子类型构造函数内部调用。
实质上,子类型原型最终拥有超类型对象的所有实例属性,只有在子类型构造函数执行时才会被覆盖。
function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    SuperType.call(this, name); //second call to SuperType()

    this.age = age;
}

SubType.prototype = new SuperType(); //first call to SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};

为了解决超类型构造函数总是被调用两次的问题,我们可以通过使用以下技巧来进行调整:不必调用超类型构造函数来分配子类型的原型,我们只需要一个超类型原型的副本即可。
function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype); //create object
    prototype.constructor = subType; //augment object
    subType.prototype = prototype; //assign object
}

然后,我们可以替换为:

SubType.prototype = new SuperType(); //first call to SuperType()
SubType.prototype.constructor = SubType;

使用

inheritPrototype(SubType, SuperType);

伪古典继承:

使用这种模式,您可以在对象级别上定义属性,在对象原型级别上定义方法:

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype = {
    constructor: SuperType,
    sayName : function() {
        alert(this.name);
    }
}

如果您在实例化对象时没有将构造函数指向SuperType,则调用instanceof仍将返回ObjectSuperType的true,但是构造函数属性现在等于Object而不是SuperType
var superType = new SuperType();
alert(superType instanceof Object); //true
alert(superType instanceof SuperType); //true
alert(superType.constructor == Person); //false
alert(superType.constructor == SuperType); //true

一个继承的例子:

function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
};

function SubType() {
    this.subproperty = false;
}

//inherit from SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function () {
    return this.subproperty;
};

//override existing method
SubType.prototype.getSuperValue = function () {
    return false;
};

这里有两个非常有关于Javascript继承的强大参考:

http://addyosmani.com/resources/essentialjsdesignpatterns/book/ http://www.allitebooks.com/professional-javascript-for-web-developers-3rd-edition/


非常感谢。我对原型继承有很清楚的了解,特别是构造函数部分。您能否提供伪经典示例? - Ravindra babu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接