【问题标题】:Prototyping in JavascriptJavascript 中的原型设计
【发布时间】:2012-06-16 19:17:56
【问题描述】:

在原型语言中,对象基本上可以相互克隆。

所以,假设我们有一个构造函数:

Bla = function()
{
    this.a = 1;
}

我可以像这样创建该对象的新实例:x = new Bla();。现在,x.a 返回 1。

如果我写Bla.prototype.b = 2,那么x.b 将返回2。但是,为什么呢?如果 x “克隆”了 Bla,为什么我不能只说 Bla.b = 2,而不引用 Bla.prototype,并且仍然获得相同的功能?这是否与 @987654328 有关@关键字?

【问题讨论】:

    标签: javascript oop prototype prototypal-inheritance


    【解决方案1】:

    ECMAScript (JavaScript) 支持“基于原型的继承”。这意味着 JS 中的“类”和“实例”之间没有区别。相对于其他语言的 OOP,在 JS 中“类”和“实例”基本上是一回事:

    当您定义 Bla 时,它会立即实例化(准备好使用),但也可以用作“原型”来克隆 object Bla 的初始(!)定义,用于具有相同属性的另一个实例和方法。

    在 ECMAScript 中,一切都是对象,甚至是对象的初始定义。在其他 OOP 语言中,定义部分有一个“类”。

    prototype 对象适用于您想要扩展“Bla”原型(阅读:类“Bla”)初始定义和将新属性/函数添加到Bla 的所有当前和未来实例

    如果您现在感到困惑,我认为这个代码示例可能有助于发现差异:

    // when defining "Bla", the initial definition is copied into the "prototype"
    var Bla = function()
    {
        this.a = 1;
    }
    // we can either modify the "prototype" of "Bla"
    Bla.prototype.b = 2;
    // or we can modify the instance of "Bla"
    Bla.c = 3;
    
    // now lets experiment with this..
    
    var x = new Bla();  // read: "clone the prototype of 'Bla' into variable 'x'"  
    console.log(x.b);     // logs "2"  -- "b" was added to the prototype, available to all instances
    
    console.log(x.c);     // undefined   -- "c" only exists in the instance "Bla"
    console.log(Bla.c);   // logs "3"  -- "Bla" is an object, just like our new instance 'x'
    
    // also note this:
    Bla.a = 1337;
    var y = new Bla();
    console.log(y.a);     // logs "1"  -- because the "prototype" was cloned, 
                    // opposed to the current state of object "Bla"
    console.log(Bla.a);   // logs "1337" 
    

    正如您在上一个示例中看到的,“原型”的概念对于避免克隆对象的当前“状态”是必要的。

    如果不以这种方式实现,您可能会得到奇怪的效果,因为如果在克隆之前使用/修改了原始对象“Bla”,那么当前状态也会被复制。这就是语言设计者选择prototype 构造的原因。

    永远记住:“Bla”不是静态定义,就像“类”在其他 OOP 语言中一样。


    ECMAScript specification 说的是prototype

    原型是用于在 ECMAScript 中实现结构、状态和行为继承的对象。当构造函数创建对象时,该对象隐式引用构造函数的关联原型,以解析属性引用。构造函数的关联原型可以由程序表达式constructor.prototype引用,并且添加到对象原型的属性通过继承由共享原型的所有对象共享。

    一些 JS 框架,例如同名的“prototype”,大量使用此功能来扩展 Javascript 内置对象的功能。

    例如,它使用方法forEach 扩展了原生array 对象,以将这个缺失的功能添加到JS:

    Array.prototype.forEach = function each(iterator, context) {
      for (var i = 0, length = this.length >>> 0; i < length; i++) {
        if (i in this) iterator.call(context, this[i], i, this);
      }
    }
    

    【讨论】:

    • "jQuery 大量使用这个特性来扩展 Javascripts 内置对象的功能"。这是一个正确的说法吗?与 Prototype 的方法相比,Resig (jQuery) 早期支持的一件事是不修改内置函数。到今天为止,我认为大多数人至少大部分都同意这是客观上好的建议(例如:sliceSmooshGate
    • @user120242 你是对的,我已经相应地更新了答案。
    【解决方案2】:

    这与类和实例之间的区别有关。 x 是 Bla 类的一个实例。如果设置 x.b = 2,则为该实例添加/设置属性 b。 如果在 b 的原型上设置 b,则为该类(以及每个现有实例)创建一个属性。 同时这也是在构造函数中设置它的区别,就像在此处使用 a 所做的那样:当在实例上更改 a 时,它......改变得很好。如果 b 改变了,原型 b 仍然存在,它的旧值,但是一个新的属性 b 为那个实例创建了新的值。 (这对于重置为默认值很有用)

    编辑意识到文本有时比代码示例清晰得多,因此添加了一个完整的示例,希望能说明差异

    ​function bla(){
        this.a= 1;
    }
    
    var x = new bla(),
        y= new bla();
    
    
    bla.prototype.b= 2;
    x.c = 3;
    
    //x already exists, but b is still set
    //by setting the prototype all existing instances are changed
    console.log(x.b); //2
    console.log(y.b); //2
    
    //because c has been explicitly set, it only exists on instance x
    console.log(x.c); //3
    console.log(y.c); //undefined
    
    
    x.b = 6; //at this point
    //as mentioned, setting a property on an instance, only affects
    //that instance
    console.log(x.b); //6
    console.log(y.b); //2
    
    //but, because b is also a prototype property, it can be 'reset'
    //by deleting the instance property
    delete x.b; //removes the instance b
    delete y.b; //on y this does nothing, because only the prototype property exists
    console.log(x.b); //2 (reset to prototype)
    console.log(y.b); //2
    
    //the final difference, because a is a class bla property, and
    //not of its prototype, deleting property 'a', will actually cause a remove
    delete x.a;
    console.log(x.a); //undefined
    

    【讨论】:

      猜你喜欢
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 2018-09-10
      • 1970-01-01
      相关资源
      最近更新 更多