9

原型用于声明一类对象的属性和方法。使用原型的一个优点是它节省了内存,因为类的所有实例都指向原型的属性和方法,这节省了内存并有效地允许类的所有实例将属性视为静态。

原型用于通过原型链进行继承。

我的问题很简单。当你能做到的时候,为什么还要使用原型:

function car() {
    this.engine = "v8";
}
function mustang() {
    // nm, no good way to inherit without using prototypes
}

那正确吗?所以原型的主要目的有三个:

  1. 节省内存
  2. 提供静态属性
  3. 是引用类型从超类继承的唯一方法
4

6 回答 6

2

节省内存

是的,当您创建数百个 Car 实例并且它们都有自己的函数(具有自己的闭包范围)时,您将消耗更多内存。

找不到它的参考,但有人建议 Chrome 优化使用原型的构造函数,而不是使用构造函数主体中的所有内容的构造函数。

提供静态属性

静态更像Date.now(),每个实例都有来自原型的成员,但可以在实例上调用。

是引用类型从超类继承的唯一方法

您可以在 Child 中继承 with Parent.apply(this,arguments);,但这会使扩展 Parent 函数更加复杂并且不childInstance instanceof Parent成立。该代码所做的是将要创建的子实例作为调用对象 ( this) 运行父代码。继承通常在两个地方进行。

  1. 在 Child 主体Parent.apply(this,arguments);中重新使用 Parent 初始化代码,并使 Parent 实例成员成为 Child 实例成员(例如:this.name)。
  2. 将 Child.prototype 设置为 Parent.prototype 的浅拷贝Child.prototype=Object.create(Parent.prototype);Child.prototype.constructor=Child;这将确保共享的 Parent 成员在 Child 实例上可用(如函数 getName)。

这些点在这里更详细地解释:https ://stackoverflow.com/a/16063711/1641941

于 2014-01-24T02:38:48.223 回答
1

关于你的三点:

  1. 原型不一定具有更高的性能,尤其是对于变长或包含许多成员的原型链。原型越小,链越短,浏览器的编译器就越能优化它。最终,需要针对单个应用程序、它们的个性化需求以及目标浏览器(性能差异很大)提出这个问题。
  2. 根据定义,静态成员需要对象。也就是说,静态成员属于对象本身而不是特定实例。对象是在 JavaScript 中创建静态属性的唯一方法。请注意,作为“特殊”对象的对象字面量本质上是静态的。
  3. 一个人可以实现他自己的对象类型,它允许继承(即jQuery.extend)之类的东西,但就引用类型而言,原型是创建继承的唯一方法。
于 2014-01-24T01:54:39.343 回答
0

Regarding your points:

  1. There is definitely a performance boost, especially regarding functions - it's much better to declare functions on the prototype.
  2. I think you meant to say "public" properties so that information is retrieved by writing some_instance.foo. "Static" properties/methods are different (see below).
  3. Correct. Inheritance can only really happen from the prototype.

Let me explain some things to see if this helps. Creating new "classes" in javascript is a fairly straightforward process.

var MyClass = new Function();

At this point, the engine is aware of your new class and knows "what to do" (in terms of performance) when it creates new instances of your "class".

var my_instance = new MyClass();

If you want to modify the prototype, you can do so and know that every instance is going to get updated because they all share the same prototype.

MyClass.prototype.name = "default name";
console.log( my_instance.name ); //=> default name

Now the engine knows that there is a "name" property which expects a string value... it will allot those resources to all new AND existing instances of your class... which is very handy. Beware that modifying the prototype of an existing "class" like this is an expensive process and should not be performed frequently (but don't be afraid to do it either).

I can't really speak for the performance pros and cons of declaring ad-hoc properties/methods on an instance:

my_instance.foo = function() { /* this is not in the prototype chain */ };

My guess is that this is pretty simple for the engine and is no big deal unless you are doing this for tens of thousands of objects at the same time.

The main benefit of using the prototype IMO is that you can write code to extend a method's functionality and know that all instances of your "class" will get updated accordingly:

var old_foo = MyClass.prototype.foo;
MyClass.prototype.foo = function() {
    /* new business logic here */

    // now call the original method.
    old_foo.apply(this, arguments);
};

Regarding "static" properties, you declare those on the "class" (constructor) itself:

// example static property
MyClass.num_instances = 0;

Now you can create init/destroy methods like this:

MyClass.prototype.init = function() {
    this.constructor.num_instances++;
};

MyClass.prototype.destroy = function() {
    this.constructor.num_instances--;
};

// and call the init method any time you create a new instance
my_instance.init();
console.log( MyClass.num_instances ); //=> 1

var instance_2 = new MyClass();
instance_2.init();
console.log( MyClass.num_instances ); //=> 2

instance_2.destroy();
console.log( MyClass.num_instances ); //=> 1

Hope that helps.

于 2014-01-28T10:39:30.067 回答
0

如果您关心遵循约定以便人们(以及您在路上)真正理解您的代码,那么您不应该放入this.engine="v8"构造函数。Prototype 旨在为每辆汽车定义属性,而构造函数旨在定义单个实例。那么,为什么要在构造函数中为每个实例 smack dab 放置正确的东西呢?这属于原型。即使做这两件事最终会完成同样的事情,也有一些事情要放在适当的位置上。您和其他人都可以理解您的代码。

于 2014-01-24T01:26:28.353 回答
0

(1) 我不认为仅保留内存是使用 .prototype 的正当理由,除非您在复制对象方面变得非常极端。

(2) 静态属性的想法也不是使用 .prototype 的真正理由(恕我直言),因为它的行为不像传统的静态属性。你(据我所知)总是需要一个对象实例才能访问“静态”属性,这使得它根本不是静态的。

function Car() {}
Car.prototype.Engine = "V8";
// I can't access Car.Engine... I'll always need an instance.
alert(new Car().Engine);
// or
var car1 = new Car();
alert(car1.Engine); //you always need an instance.
//unless you wanted to do
alert(Car.prototype.Engine); //this is more like a static property, but has an
//unintended consequence that every instance of Car also receives a .Engine
//behavior, so don't do this just to create a "static property."

需要注意的是,从传统的 OO 角度来看,这种“静态”思想不仅适用于属性,还适用于所有成员,包括方法(函数)。

最好将原型(同样,恕我直言)视为具有附加到实例对象的行为的注入单例对象。Car() 的所有实例都可以有自己的实例成员,但 Car() 的每个实例也将“自动”注入所有 Car.prototype 的成员/行为。这在技术上并不相同,但我发现以这种方式考虑原型很方便。

//define Car and Car.GoFast
function Car() {}
Car.prototype.GoFast = function () { alert('vroom!'); };

var car1 = new Car();
var car2 = new Car();

car1.GoFast();
car2.GoFast(); //both call to same GoFast implementation on Car.prototype

//change the GoFast implementation
Car.prototype.GoFast = function () { alert('vvvvvrrrrroooooooooommmmm!!!!!'); };

car1.GoFast();
car2.GoFast(); //both have been "updated" with the new implementation because
//both car1 and car2 are pointing to the same (singleton) Car.prototype object!

Car.prototype 的行为就像一个单例对象,其成员/行为已被注入到 Car 类型的实例对象中。

(3) 原型不应与继承混淆。您可以获得看似继承的行为,但事实并非如此。原型上的成员/行为保留在原型对象上。它们不会像真正的继承那样成为派生类的成员/行为。这就是为什么我描述它更像是原型被“注入”到您的实例中。

function Car() {}
Car.prototype.Engine = "V8";
var car1 = new Car();
var car2 = new Car();

alert(car1.Engine); //alerts "V8"
//There is no "Engine" variable defined within the instance scope of 'car1'.
//Javascript searches the scope of car1's Type.prototype object to find 'Engine'.
//Equivalent to: Object.getPrototypeOf(car1).Engine

//But if we create 'Engine' within the scope of car1
car1.Engine = "V6"; //Car.prototype was never accessed or updated
alert(car1.Engine); //we get "V6"
alert(car2.Engine); //we still get "V8"
alert(Object.getPrototypeOf(car1).Engine); //we still get "V8"!

所以直接回答这个问题:使用原型而不是在对象本身上声明属性有什么好处吗?

是的,当您想在给定类型的实例对象之间共享行为实现时。巧合的是,您将减少内存占用,但这并不是使用原型的唯一理由。“创建静态属性”(它们不是)或继承(不是)也不是。

于 2014-01-29T18:24:53.523 回答
0

原型设计远不止这些。您还可以在运行时使用方法和属性扩展类和已经存在的对象实例。

这应该以一种非常容易理解的方式解释它:http: //javascript.info/tutorial/inheritance

于 2014-01-22T13:24:52.203 回答