有什么区别
var A = function () {
this.x = function () {
//do something
};
};
和
var A = function () { };
A.prototype.x = function () {
//do something
};
有什么区别
var A = function () {
this.x = function () {
//do something
};
};
和
var A = function () { };
A.prototype.x = function () {
//do something
};
这些例子有非常不同的结果。
在查看差异之前,应注意以下几点:
[[Prototype]]
属性在实例之间共享方法和值的方法。myObj.method()
),则方法中的this引用该对象。如果这不是通过调用或使用bind 设置的,它默认为全局对象(浏览器中的窗口)或在严格模式下,保持未定义。所以这里是有问题的片段:
var A = function () {
this.x = function () {
//do something
};
};
在这种情况下,变量A
被分配一个值,该值是对函数的引用。当使用 using 调用A()
该函数时,该函数的this不是由调用设置的,因此它默认为全局对象并且表达式this.x
是 Effective window.x
。结果是对右侧函数表达式的引用分配给window.x
。
如果是:
var A = function () { };
A.prototype.x = function () {
//do something
};
发生了非常不同的事情。在第一行,变量A
被分配了一个对函数的引用。在 JavaScript 中,默认情况下所有函数对象都有一个原型属性,因此没有单独的代码来创建A.prototype对象。
在第二行中,A.prototype.x被分配了一个对函数的引用。如果它不存在,这将创建一个x属性,或者如果它存在则分配一个新值。因此,与第一个示例的不同之处在于,表达式中涉及对象的x属性。
另一个例子如下。它类似于第一个(也许你想问的是什么):
var A = new function () {
this.x = function () {
//do something
};
};
在本例中,new
运算符已添加到函数表达式之前,以便将函数作为构造函数调用。当调用 with 时new
,函数的this被设置为引用一个新的 Object ,其私有[[Prototype]]
属性被设置为引用构造函数的公共原型。所以在赋值语句中,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没有在调用中设置,它将引用全局对象并且this.x
是有效的window.x
。由于该函数不返回任何内容,A
因此值为undefined
。
如果您将 Javascript 对象序列化为/从 JSON 序列化和反序列化,这两种方法之间的这些差异也会表现出来。当您序列化对象时,在对象原型上定义的方法不会被序列化,例如,当您只想序列化对象的数据部分而不是方法时,这会很方便:
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 不是低级语言。将原型设计或其他继承模式视为显式更改内存分配方式的一种方式可能不是很有value。
正如其他人所说的第一个版本,使用“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
正如其他人所提到的,选择一种方法或另一种方法有多种原因。我的示例只是为了清楚地展示差异。
以这两个例子为例:
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__
属性指向什么呢?好吧,通常是另一个对象(我们稍后会解释原因)。强制 JavaScript 的__proto__
属性不指向另一个对象的唯一方法是使用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
的属性。
您可能还想知道,为什么 JavaScriptprototype
在定义函数时会创建一个为函数调用的属性?因为它试图欺骗你,是的,欺骗你它像基于类的语言一样工作。
让我们继续我们的例子并从 中创建一个“对象” 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 件事:
a1
。a1 = {}
a1.__proto__
属性被分配为指向与指向相同的东西A.prototype
(另一个空对象 {} )
该函数A()
正在执行,并this
设置为在步骤 1 中创建的新的空对象(阅读我上面引用的答案,了解为什么this
更改为a1
)
现在,让我们尝试创建另一个对象:
var a2 = new A();
步骤 1、2、3 将重复。你注意到什么了吗?关键词是重复。第1步:a2
将是一个新的空对象,第2步:它的__proto__
属性将指向相同的东西A.prototype
,最重要的是,第3步:函数A()
再次执行,这意味着a2
将获得hey
包含一个函数的属性。a1
并且a2
有两个单独的属性命名hey
,它们指向 2 个单独的函数!我们现在在相同的两个不同的对象中有重复的函数做同样的事情,oop...如果我们有 1000 个用 来创建的对象new A
,你可以想象这对内存的影响,毕竟函数声明比数字 2 占用更多的内存。所以我们如何防止这种情况?
还记得为什么__proto__
属性存在于每个对象上吗?所以,如果你获取yoMan
的财产a1
(不存在),它的__proto__
属性将进行磋商,而如果它是一个对象(而且是大多数情况下它是),它会检查它是否包含yoMan
,如果没有,它将咨询该对象__proto__
等。如果是,它将获取该属性值并将其显示给您。
所以有人决定使用这个事实 + 当你 create 时a1
,它的__proto__
属性指向同一个(空)对象A.prototype
指向的事实,并这样做:
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
凉爽的!现在,当您 create 时a1
,它再次执行上述所有 3 个步骤,在第 3 步中,它什么也不做,因为function A()
没有任何东西可以执行。如果我们这样做:
a1.hey
它会看到a1
不包含hey
,并且会检查它的__proto__
属性对象以查看它是否包含它,就是这种情况。
通过这种方法,我们消除了步骤 3 中在每个新对象创建时复制函数的部分。相反的a1
,并a2
具有独立的hey
财产,现在他们没有了它。我想,你现在明白了。这是一件好事...如果你理解__proto__
和Function.prototype
,像这样的问题将是非常明显的。
注意:有些人倾向于不将内部 Prototype 属性称为__proto__
,我在帖子中使用这个名称来清楚地将它与Functional.prototype
属性区分为两个不同的东西。
在大多数情况下,它们本质上是相同的,但第二个版本节省了内存,因为只有一个函数实例,而不是每个对象都有一个单独的函数。
使用第一种形式的一个原因是访问“私人成员”。例如:
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 的函数,但不能用于对象之外。
第一个示例仅更改该对象的接口。第二个示例更改了该类的所有对象的接口。