在JavaScript中使用“原型”还是“ this”?


776

之间有什么区别

var A = function () {
    this.x = function () {
        //do something
    };
};

var A = function () { };
A.prototype.x = function () {
    //do something
};


关键字的概念在此处明确说明scotch.io/@alZami/understanding-this-in-javascript
AL-zami

1
阅读“此”线程将显示JS多么可怕,以及许多开发人员尚不清楚其原理。易于理解的语言到底有什么问题?我认为现在是时候让开发人员表达自己的声音,拒绝对业务或开发工作毫无价值或几乎没有价值的混乱技术了。
NoChance

在对象:a1.x !== a2.x; 关于原型:a1.x === a2.x
Juan Mendes

Answers:


467

这些示例具有截然不同的结果。

在查看差异之前,应注意以下几点:

  • 构造函数的原型提供了一种通过实例的私有[[Prototype]]属性在实例之间共享方法和值的方法。
  • 函数的函数是通过调用函数的方式或通过使用bind设置的(此处未讨论)。其中一个函数被调用的对象(例如上myObj.method()),那么方法引用内的对象。当这种不被电话或通过使用设置绑定,则默认为全局对象(窗口浏览器),或者在严格模式下,仍然不确定。
  • JavaScript是一种面向对象的语言,即大多数值都是对象,包括函数。(字符串,数字和布尔值不是对象。)

因此,以下是这些片段:

var A = function () {
    this.x = function () {
        //do something
    };
};

在这种情况下,将为变量A分配一个值,该值是对函数的引用。当使用函数调用A()该函数时,该函数的this不会由调用设置,因此它默认为全局对象,并且表达式this.x有效window.x。结果是将对右侧函数表达式的引用分配给window.x

如果是:

var A = function () { };
A.prototype.x = function () {
    //do something
};

发生了非常不同的事情。在第一行中,为变量A分配了对函数的引用。在JavaScript中,默认情况下,所有函数对象都具有prototype属性,因此没有单独的代码来创建A.prototype对象。

在第二行中,为A.prototype.x分配了对函数的引用。如果x属性不存在,则将创建一个x属性;如果不存在,则将分配一个新值。因此,与第一个示例(其中对象的x属性涉及表达式)的区别。

下面是另一个示例。它与第一个类似(也许您想问什么):

var A = new function () {
    this.x = function () {
        //do something
    };
};

在此示例中,new运算符已添加到函数表达式之前,因此该函数被称为构造函数。当使用调用时new,该函数的this设置为引用一个新的Object,该对象的private [[Prototype]]属性设置为引用构造函数的public 原型。因此,在赋值语句中,x将在此新对象上创建属性。当作为构造函数调用时,函数默认情况下返回其this对象,因此不需要单独的return this;语句。

要检查A是否具有x属性:

console.log(A.x) // function () {
                 //   //do something
                 // };

这是new的罕见用法,因为引用构造函数的唯一方法是通过A.constructor。这样做会更常见:

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

获得相似结果的另一种方法是使用立即调用的函数表达式:

var A = (function () {
    this.x = function () {
        //do something
    };
}());

在这种情况下,A在右侧分配了调用函数的返回值。在这里,因为是不是在通话设置,将引用全局对象,this.x是有效的window.x。由于该函数不返回任何内容,A因此其值为undefined

如果要在JSON中对Javascript对象进行序列化和反序列化,则这两种方法之间的这些差异也很明显。序列化对象时,对象的原型上定义的方法不会序列化,例如,在您只想序列化对象的数据部分而不是序列化方法时,这会很方便:

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

相关问题

旁注:两种方法之间可能不会节省大量内存,但是使用原型共享方法和属性可能会比每个拥有自己副本的实例使用更少的内存。

JavaScript不是一种低级语言。将原型或其他继承模式视为显式更改内存分配方式的一种方法可能不是很有价值。


49
@keparo:你错了。每个对象都有一个[内部]原型对象(可以是null),但这与prototype属性有很大的不同-属性位于函数上,并且使用构造时,所有实例的原型都设置为该属性new。不敢相信这真的有87个投票:-(
Bergi 2012年

8
"The language is functional"您确定这是功能性的意思吗?
phant0m 2012年

23
我赞同@Bergi关于原型的看法。函数具有原型属性。所有对象(包括函数)都具有另一个内部属性,可以在某些浏览器中使用Object.getPrototypeOf(myObject)或myObject .__ proto__进行访问。的属性指示在原型链中的对象的父(或对象从该对象继承)。prototype属性(仅在函数上)指示该对象将成为使用该函数使用new关键字创建新对象的所有对象的父对象。
Jim Cooper

11
本文颇有误导,并混淆了的设置方式。正在重写。
RobG 2014年

37
这个答案是很奇怪的,似乎完全错过了问题的重点。这个问题似乎是关于在构造函数和原型中定义类型属性的一个非常普遍的问题,但是答案的一半是关于如果您将其A用作函数会发生什么,另一半是关于晦涩和非常规的方法简单明了。
JLRishe 2014年

235

正如其他人所说的第一个版本一样,使用“ this”将导致类A的每个实例都有其自己的函数方法“ x”的独立副本。而使用“原型”将意味着类A的每个实例将使用方法“ x”的相同副本。

这是一些代码来显示这种细微的差别:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

正如其他人所提到的,出于多种原因选择一种或另一种方法。我的样本只是为了清楚地说明差异。


5
这就是我希望发生的情况,但是当我像上面那样更改Ax之后实例化一个新对象时,除非我像单身人士一样使用A,否则我仍然显示'A'。jsbin.com/omida4/2/edit
jellyfishtree

19
那是因为我的例子是错误的。仅仅错了两年。叹。但是这一点仍然有效。我用一个实际可行的示例更新了该示例。感谢您指出。
Benry

4
这是静态方法!:D

6
是的...'prototype'表示静态或类级别..它将被所有创建的实例共享... ...'this'是一个实例方法,每个实例将具有自己的副本
Aneer Dev

7
它不是静态的。在大多数OO语言中使用的静态方法意味着不依赖于this对象(该方法的所有者)。即该方法没有其所有者的对象。在这种情况下,存在一个this对象,如示例中的A类所示。
CJStuart

152

举两个例子:

var A = function() { this.hey = function() { alert('from A') } };

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

这里的大多数人(尤其是评分最高的答案)试图在不解释原因的情况下解释他们的不同之处。我认为这是错误的,如果您先了解基本知识,差异就会变得明显。让我们先解释一下基本原理...

a)函数是JavaScript中的对象。JavaScript中的每个对象都有一个内部属性(意思是,您不能像其他属性一样访问它,除了可能在Chrome之类的浏览器中),通常被称为__proto__(您可以实际上键入anyObject.__proto__Chrome以查看其引用的内容。 ,一个属性,仅此而已; JavaScript中的一个属性=对象内的一个变量,仅此而已,变量的作用是什么?它们指向事物。

那么这个__proto__属性指向什么呢?好吧,通常是另一个对象(稍后我们将解释原因)。强制该__proto__属性的JavaScript 不能指向另一个对象的唯一方法是使用var newObj = Object.create(null)。即使执行此操作,__proto__属性STILL仍作为对象的属性存在,只是它没有指向另一个对象,而是指向null

这是大多数人感到困惑的地方:

当您在JavaScript中创建一个新函数时(它也是一个对象,还记得吗?),在定义它的那一刻,JavaScript会自动在该函数上创建一个名为的新属性prototype。尝试一下:

var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined

A.prototype与该__proto__属性完全不同。在我们的示例中,“ A”现在具有两个称为“ prototype”和“”的属性__proto__。这对人们来说是一个很大的困惑。prototype__proto__属性绝不相关,它们是指向不同值的独立事物。

您可能会想:为什么JavaScript __proto__在每个对象上都创建了属性?好吧,一个字:委派。当您在对象上调用属性而该对象没有属性时,JavaScript会查找所引用的对象__proto__以查看它是否具有该属性。如果没有它,那么它将查看该对象的__proto__属性,依此类推...直到链结束。因此命名原型链。当然,如果__proto__没有指向对象而是指向null,那么运气很好,JavaScript会意识到这一点,并将undefined为您返回该属性。

您可能还想知道,为什么prototype在定义函数时JavaScript会创建一个为函数调用的属性?因为它试图欺骗您,所以可以欺骗您,它就像基于类的语言一样工作。

让我们继续我们的示例,并从中创建一个“对象” A

var a1 = new A();

当事情发生时,背景中发生了一些事情。a1是分配了一个新的空对象的普通变量。

new在函数调用之前使用运算符的事实A()在后台做了一些其他操作。该new关键字创建一个新的对象现在引用a1和对象为空。这是另外发生的事情:

我们说过,在每个函数定义上都创建了一个称为prototype(您可以访问它,与该__proto__属性不同)的新属性吗?好吧,该属性现在正在使用中。

因此,现在我们有了一个新鲜出炉的空a1对象。我们说过,JavaScript中的所有对象都有一个内部__proto__属性,该属性指向某个东西(a1也有它),无论它是null还是另一个对象。new操作员要做的是将其__proto__属性设置为指向函数的prototype属性。再读一遍。基本上是这样的:

a1.__proto__ = A.prototype;

我们说的A.prototype仅仅是空对象(除非在定义之前将其更改为其他对象a1)。因此,现在基本上a1.__proto__指向的是同一A.prototype对象所指向的东西,即那个空对象。它们都指向发生此行时创建的同一对象:

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

现在,在var a1 = new A()处理语句时发生了另一件事。基本上A()被执行,如果A是这样的:

var A = function() { this.hey = function() { alert('from A') } };

内部所有的东西function() { }都将执行。当您到达该this.hey..行时,this将更改为a1,您将获得以下信息:

a1.hey = function() { alert('from A') }

我不会介绍为什么this更改为,a1但这了解更多信息的好答案

综上所述,当您这样做时var a1 = new A(),后台会发生3件事:

  1. 将创建一个全新的空对象并将其分配给a1a1 = {}
  2. a1.__proto__属性分配为指向与指向相同的事物A.prototype(另一个空对象{})

  3. A()执行该函数时,将其this设置为在步骤1中创建的新的空对象(请阅读我上面提到的有关为什么this更改为的答案a1

现在,让我们尝试创建另一个对象:

var a2 = new A();

重复步骤1,2,3。你有注意到吗?关键字是重复。步骤1:a2将是一个新的空对象,步骤2:其__proto__属性将指向相同的对象,A.prototype最重要的是,步骤3:A()将再次执行函数,这意味着a2将获得hey包含函数的属性。a1a2具有两个SEPARATE属性hey,它们分别指向2个SEPARATE函数!现在,我们在同一对象的两个不同对象中有重复的函数,哎呀...如果我们用创建的1000个对象new A,在所有函数声明都比数字2占用更多的内存之后,您可以想象它的内存含义。我们如何防止这种情况?

还记得为什么该__proto__属性存在于每个对象上吗?因此,如果您在yoMan上检索该属性a1(不存在),__proto__则将查询该属性,如果它是一个对象(在大多数情况下是对象),它将检查它是否包含yoMan,如果不包含,它将查询该对象的__proto__等。如果是,它将获取该属性值并将其显示给您。

因此,有人决定使用以下事实+当您创建时a1,其__proto__属性指向同一(空)对象A.prototype指向并执行以下操作:

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

凉!现在,当您创建时a1,它再次经历了以上所有3个步骤,而在步骤3中,它什么也没做,因为function A()没有什么要执行的。如果我们这样做:

a1.hey

它将看到其中a1不包含hey__proto__对象,并且将检查其属性对象以查看是否包含它(是这种情况)。

使用这种方法,我们从第3步中删除了在每次创建新对象时都会复制功能的部分。相反的a1,并a2具有独立的hey财产,现在他们没有了它。我想您到现在就知道了。很好。如果您理解__proto__Function.prototype,类似这样的问题将非常明显。

注意:某些人倾向于不将内部Prototype属性称为__proto__,我在帖子中使用了此名称,以将其与Functional.prototype属性区别开来是两个不同的东西。


1
真正彻底而翔实的答案。我使用上面的对象结构(A.prototype.hey与对象this.hey)进行了一些内存测试,并为每个对象创建了1000个实例。与原型相比,对象属性方法的内存占用量大约大100kb。然后,我添加了另一个具有相同目的的函数“傻”,并且线性增加到200kb。不重要,但也不是花生。
jookyone

更有意思的是,原型方法比本地运行的对象属性方法要慢一些。总的来说,我不确定应该使用javascript来处理编号大于10k的对象的数据,因此否定了基于潜在的内存影响而更改方法的任何理由。那时,应将工作卸载到服务器上。
jookyone

关键是,__proto__并且.prototype是完全不同的事物。
Wayou

1
我不满意给你一个投票...做得好!
克里斯蒂安

58

在大多数情况下,它们基本上是相同的,但是第二个版本可以节省内存,因为该函数只有一个实例,而不是每个对象都有单独的函数。

使用第一种形式的原因是访问“私人成员”。例如:

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

由于javascript的范围规则,private_var可用于分配给this.x的函数,但不适用于该对象。


1
有关如何通过原型访问私有成员的示例,请参见这篇文章:stackoverflow.com/a/1441692/654708
GFoley83 2014年

@ GFoley83这个问题的答案并不能表明-原型方法只能访问给定对象的“公共”性质。只有特权方法(不在原型上)可以访问私有成员。
Alnitak

27

第一个示例仅更改该对象的接口。第二个示例更改该类所有对象的接口。


两者都将使该函数x可用于为其原型分配了A的新实例的所有对象:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
Spencer Williams

21

使用this而不是的最终问题prototype是,当重写方法时,基类的构造方法仍将引用重写的方法。考虑一下:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

与:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

如果您认为这不是问题,则取决于您是否可以不使用私有变量,以及是否有足够的经验在看到泄漏时知道泄漏。而且,必须将构造函数逻辑放在方法定义之后是不方便的。

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

与:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1

20

每个对象都链接到原型对象。当尝试访问不存在的属性时,JavaScript会在对象的原型对象中查找该属性,如果存在,则将其返回。

使用时prototype,函数构造函数的属性引用该函数创建的所有实例的原型对象new


在第一个示例中,您正在向x使用该A函数创建的每个实例添加一个属性。

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

在第二个示例中,您正在向原型对象添加一个属性,所有创建的实例都A指向该属性。

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

总之,在第一个示例中,该函数的副本已分配给每个实例。在第二个示例中,所有实例共享该函数的单个副本


1
投票赞成该问题最直接的答案。
尼克·皮内达

1
我喜欢你的直接方法!表示赞许,满意,胜利!
维杰·普拉塔普王子

16

有什么不同?=>很多。

我认为,该this版本用于启用封装,即数据隐藏。它有助于操纵私有变量。

让我们看下面的例子:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

现在,该prototype结构可以应用如下:

不同的成年人有不同的年龄,但是所有成年人都享有相同的权利。
因此,我们使用原型而非它添加它。

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

让我们现在来看一下实现。

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

希望这可以帮助。


3
+1比其他方法复杂得多,图形化答案更多。但是在提供这些(好的)示例之前,您应该详细说明。
yerforkferchips 2014年

1
我不确定“此版本用于启用封装,即数据隐藏”。如果在“ this.myProperty = ...”中使用“ this”定义函数内部的属性,则该属性不是“ private”,可以使用“ new”从类外部的对象访问。
NoChance

14

原型是该类的模板;这适用于它的所有未来实例。而这是对象的特定实例。


14

我知道这已经死了,但我想展示一个实际的速度差异示例。

直接作用于对象

原型功能

在这里,我们正在使用printChrome中的方法创建2,000,000个新对象。我们将每个对象存储在一个数组中。放置print原型大约需要1/2倍的时间。


13

让我给您一个我在JavaScript培训课程中学到的更全面的答案。

大多数答案已经提到了差异,即在原型设计中,该功能已与所有(未来)实例共享。而在类中声明该函数将为每个实例创建一个副本。

通常,没有对与错,这取决于您的需求,取决于品味或设计决定。但是,原型是用于以面向对象的方式进行开发的技术,我希望您会在此答案的结尾看到。

您在问题中显示了两种模式。我将尝试再解释两个,并尝试解释差异(如果相关)。随意编辑/扩展。在所有示例中,它都是关于具有一定位置并可以移动的汽车对象的。

对象装饰器模式

不确定这种模式是否在当今仍然有用,但是它存在。很高兴知道这一点。您只需将一个对象和一个属性传递给装饰器函数。装饰器返回带有属性和方法的对象。

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

功能类

JavaScript中的函数是专门的对象。除了被调用外,函数还可以像存储其他任何对象一样存储属性。

在这种情况下Car是一个函数认为对象),可以为你用来做被调用。它具有一个属性methods(具有move功能的对象)。当Car被调用时extend被调用的函数,其中做了一些魔法,并扩展了Car功能(认为对象)与内定义的方法methods

该示例尽管有所不同,但与问题中的第一个示例最接近。

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

原型类

前两种模式允许讨论使用技术定义共享方法或使用在构造函数主体中内联定义的方法。在这两种情况下,每个实例都有其自己的move功能。

原型模式不太适合进行相同的检查,因为通过原型委托进行功能共享是原型模式的真正目标。正如其他人指出的那样,它有望具有更好的内存占用量。

但是,有一点很有趣,您应该知道:每个prototype对象都有一个便捷属性constructor,它指向它附带的函数(认为对象)。

关于最后三行:

在这个例子中Car链接到prototype对象,它经由链路constructorCar本身,即Car.prototype.constructorCar本身。这使您能够确定哪个构造函数构造了某个对象。

amy.constructor的查找失败,因此被委托给Car.prototype,后者确实具有Constructor属性。所以amy.constructorCar

此外,amyinstanceof Car。该instanceof运营商的工作原理是看是否正确操作的原型对象(Car)可以在任何地方在左边的操作数的原型(发现amy)链。

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

一开始,有些开发人员可能会感到困惑。请参见以下示例:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

instanceof运营商的回报false,因为Dog的原型不能在任何地方找到fido的原型链“。fido是用对象文字创建的简单对象,即,它只是委托给Object.prototype

伪古典模式

这实际上只是原型模式的另一种简化形式,对于使用Java进行编程的人来说更熟悉,因为它使用了new构造函数。

实际上,它的功能与原型模式相同,只是原型模式之上的语法糖。

但是,主要区别在于JavaScript引擎中实现了一些优化,这些优化仅在使用伪经典模式时才适用。认为伪古典模式可能是原型模式的更快版本。两个示例中的对象关系相同。

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

最后,实现面向对象的程序设计应该不会太困难。有两个部分。

一节定义了原型(链)中的通用属性/方法。

在另一部分中,您将放置将对象彼此区分开的定义(loc示例中的变量)。

这就是我们可以在JavaScript中应用超类或子类之类的概念的原因。

随时添加或编辑。再完成一次,我可以使它成为社区Wiki。


并不是要敲一个非常透彻的文章,但我认为面向对象和原型继承本质上是不同的思想流派。
尼克·皮内达

是的,但是可以用不同的技术/思想“做OO”,不是吗?
2016年

真的不确定。许多人只是说原型哲学是不同的,许多人试图将其与OO进行比较,因为它是许多人习惯的思想流派。
尼克·皮内达

我的意思是,如果您想练习OO风格并且该语言提供了一组有助于实现OO风格的技术,那不一定是错误的。
2016年

11

我相信@Matthew Crumley是正确的。它们在功能上(如果不是在结构上)是等效的。如果使用Firebug查看使用创建的对象,则new可以看到它们是相同的。但是,我的偏好如下。我猜想这似乎更像是我在C#/ Java中所习惯的。即,定义类,定义字段,构造函数和方法。

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

编辑并不是要暗示变量的范围是私有的,我只是想说明如何在javascript中定义类。变量名称已更改以反映这一点。


2
_instance_var作为initializex methods do not refer to the 上_instance_var`财产A情况,但全球性的。使用this._instance_var,如果你打算使用_instance_var的财产A情况。
Lekensteyn 2011年

2
有趣的是,贝利也犯了这样的错误,两年后也被发现:p
Lekensteyn 2011年

10

正如在其他答案中所讨论的那样,这实际上是性能方面的考虑,因为原型中的函数与所有实例共享,而不是为每个实例创建函数。

我整理了一个jsperf来展示这一点。实例化类的时间有很大的不同,尽管实际上只有在创建多个实例时才有意义。

http://jsperf.com/functions-in-constructor-vs-prototype


8

考虑一下静态类型的语言,事物prototype是静态的,事物this是与实例相关的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.