理解一个简单的原型继承示例

时间:2014-03-14 00:16:59

标签: javascript

我是编程的新手,我正在尝试理解JS中的继承。我在阅读JavaScript Objects in Detail并且无法辨别此示例中空构造函数的重要性。考虑一下:

function Fruit () {

}

Fruit.prototype.color = "Yellow";
Fruit.prototype.sweetness = 7;
Fruit.prototype.fruitName = "Generic Fruit";
Fruit.prototype.nativeToLand = "USA";

Fruit.prototype.showName = function () {
    console.log("This is a " + this.fruitName);
}

Fruit.prototype.nativeTo = function () {
    console.log("Grown in:" + this.nativeToLand);
}

var mangoFruit = new Fruit ();

这比定义具有与参数相同属性的构造函数更好吗?

另外,如果浏览器支持不是问题,那么使用Object.create()比使用构造函数还是使用prototype进行继承更可取?

4 个答案:

答案 0 :(得分:3)

实际上,你的例子(在这种情况下)并不是更好。您已经定义了与所有水果共享的属性和方法(通过原型),但并非所有水果都是黄色或甜度为7.在这种情况下,这些将作为传递的参数有意义并在每个实例中定义(使用构造函数中的this.color

那么你什么时候选择一个呢?让我们回到结果 - 我们可以做一些与水果共享的行动(例如,eatpeel) - 这些可能是你原型的方法。这些方法定义一次,所有实例都引用相同的方法。

独特的事物(colorsweetness)作为实例属性(通过参数传入)有意义。

如上所述,原型方法的另一个优点是:它们只是一个参考。调用new Fruit()一千次将创建一千个Fruit实例,但它们都将引用该单一原型方法(例如,eat)。

答案 1 :(得分:1)

您的示例存在继承错误,因为所有对象都将共享相同的属性。您必须划分原型链以便在底部组织公共属性,而该对象的更具体的属性最终位于链的顶部。

考虑一下:

想象一下,你有一个保存文件夹的盒子。每个文件夹都包含有关汽车的信息。想象一下,您想要写有关三菱Lancer Evolution VII的信息。在您的文件夹中,您可以写下有关品牌,型号,版本等的所有信息。现在,您可以为Mitsubishi Lancer Evolution VI添加其他文件夹并编写所有内容,然后为三菱Eclipse,丰田Supra等编写。

很快你就会意识到盒子很快就会满满的,很难快速获得特定的信息,而且你多次写同样的东西。然后您意识到您可以拥有三菱品牌信息的文件夹,并且每个三菱汽车文件夹都有一个“品牌:参见三菱文件夹”的参考。然后你意识到你可以用Lancer Evolution模型做同样的事情。删除了大量重复信息,框中有更多空间,添加新文件更容易,搜索特定信息更快...

现在认为盒子是计算机内存,那么品牌,型号,版本等都是对象,你文件中的信息是对象属性,品牌是模型作为对象的原型。

所以,你的汽车对象将是:

var Brand = function Brand(brand){
    this.brand = brand ;
    this.Model = function Model(model){
        this.model = model ;
        this.Edition = function Edition(edition){
            this.edition = edition ;
        } ;
        this.Edition.prototype = this ;
    } ;
    this.Model.prototype = this ;
} ;

var Mitsubishi = new Brand('Mitsubishi') ;
var LancerEvolution = new Mitsubishi.Model('Lancer Evolution') ;
var LancerEvolutionVII = new LancerEvolution.Edition('VII') ;
var LancerEvolutionVI = new LancerEvolution.Edition('VI') ;

所以:

LancerEvolutionVII.brand == 'Mitsubishi' // true
LancerEvolutionVII.model == 'LancerEvolution' // true
LancerEvolutionVII.edition == 'VII' // true

很酷的是,对于每辆三菱汽车来说,只有一个对象品牌,并且都共享它。因此,如果三菱公司明天改名,你只需要这样做:

Mitsubishi.brand = 'NewCompanyName' ;

所有三菱汽车都将更新品牌。

我希望这能帮助你,不管你怀疑还有什么问题。

答案 2 :(得分:1)

  

使用Object.create()比使用a更好   构造函数或使用原型进行继承?

这没有多大意义,Object.create返回第二个参数(或空对象)中的对象,第一个参数是原型。您可以为旧版浏览器进行polyfil,但不能使用第二个参数。所以你没有使用Object.create而不是原型,你仍然在使用原型。

要定义对象,您需要定义一个函数,该函数将初始化特定于实例的成员(this.name,this.age)并定义共享成员(prototype.getName,prototype.getAge)。创建对象时,需要运行初始化程序以初始化实例成员。这可能是构造函数,但还有其他模式。

您显示的代码是设置默认值的一种方法(仅适用于基本类型),我更喜欢将一个对象传递给构造函数,并让构造函数代码弄清楚如何设置默认值或抛出缺失值或无效的参数(请参阅下面“传递(构造函数)参数”下的链接)。

有关原型和构造函数see this answer的详细说明。

答案 3 :(得分:0)

我认为它是原型继承与类继承之间的区别,所以当它来自具有创建类的特殊语法的编程语言时会令人困惑。您还可以在javascript中访问构造函数并修改构造函数。可以更改对象的构造函数属性。如果构造函数属性被覆盖,则原始值将丢失。

我强烈推荐John Resig出版的“Javascript Ninja的秘密”一书中的第6章。我已经阅读了几次,但仍然是新的东西。

原型语言通常具有很强的可塑性。您可以更改任何对象上的任何插槽。 - 七周语言中的七种语言,IO语言,也是一种原型语言

function Ninja() {

}

it("The ninja object was created by the Ninja function", function () {
    var ninja = new Ninja();
    expect(ninja.constructor == Ninja).toEqual(true);
});