JavaScript:.extend和.prototype有什么用途?

128

我对JavaScript还比较新,经常在使用第三方库时看到 .extend 和 .prototype。 我以为这与Prototype JavaScript库有关,但我开始觉得这不是这样。 它们有何用途?


如果你想了解更多关于这个原型的东西,我建议你去看看可汗学院 https://www.khanacademy.org/computing/computer-programming/programming/object-oriented/pt/object-inheritance - Dolev Dublon
7个回答

141

JavaScript的继承是基于原型的,因此您可以扩展对象的原型,例如Date、Math甚至您自己定义的对象。

Date.prototype.lol = function() {
 alert('hi');
};

( new Date ).lol() // alert message
在上面的片段中,我定义了一个方法,适用于所有日期对象(已经存在的和所有新创建的)。 extend通常是一个高级函数,它会复制你想要从基类扩展的新子类的原型。
因此,你可以像这样做:
extend( Fighter, Human )

Fighter的构造函数/对象将继承Human的原型,因此如果您在Human上定义了livedie等方法,则Fighter也将继承这些方法。

更新的澄清:

“高级函数”意味着.extend不是内置的,而是通常由诸如jQuery或Prototype之类的库提供。


76
"high level function" 的意思是 .extend 不是内置功能,但通常由类库(如 jQuery 或 Prototype)提供。 - visum
13
不建议在JS中扩展原生对象的原型。 - framp
1
@meder - 你应该在你的回答中添加visum注释。 :) - Manish Gupta
10
在现代JavaScript编程中,通常将全局变量和本地对象看作公共浴室的元素;你不能避免去那里,但你应该尽量减少与表面接触。这是因为“更改本地对象可能会破坏其他开发人员对这些对象的假设”,导致JavaScript错误,往往需要花费很多时间来追踪。这个回答的领先句似乎误传了这个有价值的JavaScript实践。 - Ninjaxor

25

.extend()被许多第三方库添加以便于从其他对象创建对象。请参见http://api.jquery.com/jQuery.extend/http://www.prototypejs.org/api/object/extend了解一些示例。

.prototype指的是对象的“模板”(如果您想这样称呼它的话),因此通过将方法添加到对象的原型中(在许多库中可以看到添加到String、Date、Math甚至Function中),这些方法将添加到该对象的每个新实例中。


19

比如说在 jQuery 或者 PrototypeJS 中的extend 方法可以将源对象的所有属性复制到目标对象。

至于 prototype 属性,它是函数对象的一个成员,也是语言核心的一部分。

任何函数都可以被用作构造函数,用来创建新的对象实例。所有的函数都有这个 prototype 属性。

当你使用 new 运算符调用一个函数对象时,会创建一个新的对象,并且它会从其构造函数的 prototype 继承。

例如:

function Foo () {
}
Foo.prototype.bar = true;

var foo = new Foo();

foo.bar; // true
foo instanceof Foo; // true
Foo.prototype.isPrototypeOf(foo); // true

18
Javascript继承似乎在各个地方都存在着一个开放的辩论。它可以被称为“Javascript语言的好奇案例”。
这个想法是有一个基类,然后你扩展基类以获得类似继承的功能(不完全相同,但仍然如此)。
整个想法是要理解原型实际上意味着什么。我直到看到John Resig的代码(接近于jQuery.extend的代码)才明白了它,他编写了一小段代码来实现它,并声称base2和prototype库是灵感来源。
以下是代码。
    /* Simple JavaScript Inheritance
     * By John Resig http://ejohn.org/
     * MIT Licensed.
     */  
     // Inspired by base2 and Prototype
    (function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  this.Class = function(){};

  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;

    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;

            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];

            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;

            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }

    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }

    // Populate our constructed prototype object
    Class.prototype = prototype;

    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
})();

有三个部分在执行任务。首先,您循环遍历属性并将它们添加到实例中。之后,您创建一个构造函数以便稍后添加到对象中。现在,关键的行是:

// Populate our constructed prototype object
Class.prototype = prototype;

// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;

首先,您需要将Class.prototype指向所需的原型。现在,整个对象的含义已经改变了,因此您需要强制布局回到其自己的布局。

以下是使用示例:

var Car = Class.Extend({
  setColor: function(clr){
    color = clr;
  }
});

var volvo = Car.Extend({
   getColor: function () {
      return color;
   }
});

John Resig的Javascript继承文章中,可以了解更多相关技术内容。


2
  • .extends() 创建一个继承自另一个类的子类。
    在幕后,Child.prototype.__proto__ 的值被设置为 Parent.prototype,因此方法被继承。
  • .prototype 从一个对象继承特性到另一个对象。
  • .__proto__ 是原型链的 getter/setter。

1
这不应该是 .extends() 而是 .extend() 吗? - SJHowe

2

第三方库中的一些扩展函数比其他函数更复杂。例如,Knockout.js包含了一个最简单的函数,它没有像jQuery那样进行一些检查:

function extend(target, source) {
    if (source) {
        for(var prop in source) {
            if(source.hasOwnProperty(prop)) {
                target[prop] = source[prop];
            }
        }
    }
    return target;
}

0

对我来说,这似乎是最清晰和最简单的示例,它只是附加属性或替换现有属性。

function replaceProperties(copyTo, copyFrom)  {
  for (var property in copyFrom) 
    copyTo[property] =  copyFrom[property]
  return copyTo
}

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