在JavaScript中使用'原型'与'这个'?
有什么区别
var A = function () {
this.x = function () {
//do something
};
};
和
var A = function () { };
A.prototype.x = function () {
//do something
};
这些例子的结果非常不同。
在查看差异之前,应该注意以下几点:
[[Prototype]]
属性在实例之间共享方法和值的方法。 myObj.method()
)的情况下,该方法内的这个引用该对象。 如果这不是通过调用或通过使用绑定来设置的,则默认为全局对象(浏览器中的窗口)或严格模式,但仍未定义。 所以这里是所讨论的片段:
var A = function () {
this.x = function () {
//do something
};
};
在这种情况下,变量A
被赋值为一个函数的引用。 当使用A()
调用该函数时,该函数不会由调用设置,因此它默认为全局对象,并且表达式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属性。
另一个例子如下。 它与第一个类似(可能是你想问的):
var A = new function () {
this.x = function () {
//do something
};
};
在这个例子中, new
操作符已被添加到函数表达式之前,以便该函数作为构造函数被调用。 当使用new
调用时,函数将其设置为引用一个新的Object,其私有[[Prototype]]
属性被设置为引用构造函数的公共原型。 所以在赋值语句中,将在这个新对象上创建x
属性。 当作为构造函数调用时,函数默认返回它的这个对象,所以不需要单独return this;
声明。
要检查A是否有x属性:
console.log(A.x) // function () {
// //do something
// };
这是新的不常见的用法,因为引用构造函数的唯一方法是通过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不是低级语言。 将原型或其他继承模式视为明确改变内存分配方式的方式可能并不是很有价值。
正如其他人所说的第一个版本,使用“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') };
这里的大多数人(特别是评分最高的答案)都试图在不解释WHY的情况下解释它们的不同之处。 我认为这是错误的,如果你首先了解基本原理,那么区别就会变得明显。 让我们先试着解释基本面...
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
的属性。
你也许会想,为什么JavaScript在你定义函数时为函数创建一个名为prototype
的属性? 因为它试图欺骗你,是的,它欺骗你 ,它像基于类的语言。
让我们继续我们的例子,并从A
创建一个“对象”:
var a1 = new A();
发生这种事情时,背景中发生了一些事情。 a1
是一个普通变量,它被分配了一个新的空对象。
在函数调用A()
之前使用operator new
的事实在后台做了ADDITIONAL。 new
关键字创建了一个新对象,该对象现在引用了a1
并且该对象为空。 以下是另外发生的事情:
我们说过,在每个函数定义中都会创建一个名为prototype
的新属性(您可以访问它,与__proto__
属性不同)? 那么,该属性现在正在使用。
所以我们现在处于一个新鲜出炉的空的a1
对象。 我们说过,JavaScript中的所有对象都有一个内部的__proto__
属性,它指向了一些东西( a1
也有它),不管它是空还是另一个对象。 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()`时,背景中会发生三件事:
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
有两个SEPARATE属性,分别命名为hey
和2个SEPARATE函数! 我们现在在做同样的事情的同一个两个不同的对象中有重复的函数,oops ...如果我们有1000个用new A
创建的对象,你可以想象这个内存的含义,在所有的函数声明占用更多的内存之后,比数字2那么我们该如何预防呢?
记住为什么__proto__
属性存在于每个对象上? 所以,如果你检索a1
(不存在)的yoMan
属性,它的__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
属性区分为两个不同的东西。