Javascript什么时候使用原型

opl*_*opl 89 javascript performance prototype function-prototypes

我想了解何时在js中使用原型方法是合适的.它们应该一直使用吗?或者是否存在不使用它们和/或导致性能损失的情况?

在js中搜索命名空间的常用方法时,似乎大多数人使用非基于原型的实现:只需使用对象或函数对象来封装命名空间.

来自基于类的语言,很难不尝试绘制并行,并认为原型就像"类",我提到的命名空间实现就像静态方法.

Dan*_*ker 130

原型是一种优化.

使用它们的一个很好的例子是jQuery库.每次通过使用获得jQuery对象时$('.someClass'),该对象都有许多"方法".库可以通过返回一个对象来实现:

return {
   show: function() { ... },
   hide: function() { ... },
   css: function() { ... },
   animate: function() { ... },
   // etc...
};
Run Code Online (Sandbox Code Playgroud)

但这意味着内存中的每个jQuery对象都会有几十个包含相同方法的命名槽,一遍又一遍.

相反,这些方法是在原型上定义的,并且所有jQuery对象都"继承"该原型,以便以非常小的运行时成本获得所有这些方法.

jQuery如何正确使用它的一个至关重要的部分是程序员隐藏它.它纯粹是一种优化,而不是在使用库时需要担心的事情.

JavaScript的问题在于,裸构造函数要求调用者记住为它们添加前缀,new否则它们通常不起作用.这没有充分的理由.jQuery通过隐藏普通函数后面的无意义来实现它$,所以你不必关心对象是如何实现的.

因此,您可以方便地使用指定的原型创建对象,ECMAScript 5包含一个标准函数Object.create.它的大大简化版本看起来像这样:

Object.create = function(prototype) {
    var Type = function () {};
    Type.prototype = prototype;
    return new Type();
};
Run Code Online (Sandbox Code Playgroud)

它只是处理编写构造函数然后调用它的痛苦new.

你何时会避开原型?

有用的比较是流行的OO语言,如Java和C#.这些支持两种继承:

  • 接口继承,在那里你implement一个interface这样的类提供了自己独特的实施接口的每一个成员.
  • 实现继承,您可以extendclass其中提供某些方法的默认实现.

在JavaScript中,原型继承是一种实现继承.因此,在(在C#或Java中)您将从基类派生以获得默认行为的情况下,您可以对通过覆盖进行少量修改,然后在JavaScript中,原型继承是有意义的.

但是,如果您处于使用C#或Java接口的情况,那么您在JavaScript中不需要任何特定的语言功能.不需要显式声明代表接口的东西,也不需要将对象标记为"实现"该接口:

var duck = {
    quack: function() { ... }
};

duck.quack(); // we're satisfied it's a duck!
Run Code Online (Sandbox Code Playgroud)

换句话说,如果对象的每个"类型"都有自己的"方法"定义,那么继承原型就没有价值.之后,它取决于您为每种类型分配的实例数.但在许多模块化设计中,只有一个给定类型的实例.

事实上,很多人都认为实现继承是邪恶的.也就是说,如果某个类型有一些常见的操作,那么如果它们没有被放入基类/超类中,则可能会更清楚,而是在某些模块中作为普通函数公开,您将对象传递给它你希望他们操作.

  • +1与jQuery的比较是第一个清晰简洁的解释,说明何时以及为什么使用我已阅读过的原型.非常感谢你. (11认同)
  • 很好的解释。那么你是否同意,既然你认为原型是一种优化,那么它们总是可以用来改进你的代码?我想知道是否在某些情况下使用原型没有意义,或者实际上会导致性能下降。 (3认同)
  • **1.**我的意思是如果有一种类型的鸭子的大量实例,那么修改示例是有意义的,以便`quack`函数在原型中,许多鸭子实例有联系.**2.**对象文字语法`{...}`创建一个实例(不需要使用`new`).**3.**调用任何函数JS导致在内存中创建至少一个对象 - 它被称为`arguments`对象并存储调用中传递的参数:https://developer.mozilla.org/en/JavaScript /参考/ functions_and_function_scope /参数 (3认同)

Kea*_*her 44

如果要声明对象的"非静态"方法,则应使用原型.

var myObject = function () {

};

myObject.prototype.getA = function (){
  alert("A");
};

myObject.getB = function (){
  alert("B");
};

myObject.getB();  // This works fine

myObject.getA();  // Error!

var myPrototypeCopy = new myObject();
myPrototypeCopy.getA();  // This works, too.
Run Code Online (Sandbox Code Playgroud)


hel*_*tan 16

使用内置prototype对象的一个原因是,您将多次复制一个将共享通用功能的对象.通过将方法附加到原型,您可以节省每个new实例创建的重复方法.但是当你附加一个方法时prototype,所有实例都可以访问这些方法.

假设你有一个基Car()类/对象.

function Car() {
    // do some car stuff
}
Run Code Online (Sandbox Code Playgroud)

然后你创建多个Car()实例.

var volvo = new Car(),
    saab = new Car();
Run Code Online (Sandbox Code Playgroud)

现在,您知道每辆汽车都需要开车,开启等等.Car()您可以将方法直接附加到类(每个创建的实例占用内存),而不是将方法附加到原型上(仅创建方法)一次),因此可以访问新的volvosaab.

// just mapping for less typing
Car.fn = Car.prototype;

Car.fn.drive = function () {
    console.log("they see me rollin'");
};
Car.fn.honk = function () {
    console.log("HONK!!!");
}

volvo.honk();
// => HONK!!!
saab.drive();
// => they see me rollin'
Run Code Online (Sandbox Code Playgroud)

  • 实际上这是不正确的.volvo.honk()不会工作,因为你完全取代了原型对象,没有扩展它.如果你要做这样的事情,它会像你期望的那样工作:Car.prototype.honk = function(){console.log('HONK');} volvo.honk(); // 'HONK' (2认同)

Poi*_*nty 12

当您要创建特定类型对象的大量副本时,将函数放在原型对象上,并且它们都需要共享共同的行为.通过这样做,你可以通过只使用每个函数的一个副本来节省一些内存,但这只是最简单的好处.

更改原型对象上的方法或添加方法会立即更改相应类型的所有实例的性质.

现在,您完成所有这些工作的原因主要取决于您自己的应用程序设计,以及您需要在客户端代码中执行的操作.(完全不同的故事是服务器内部的代码;更容易想象在那里做更大规模的"OO"代码.)