4 回答
TA贡献1995条经验 获得超2个赞
拿这两个例子:
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 // undefinedA = 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
是一个普通的变量,它被赋予了一个新的空对象。
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()
时候,背景中发生了三件事:
创建并分配一个全新的空对象
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()
是AGAIN执行的,这意味着a2
将获取hey
包含函数的属性。a1
并a2
有两个名为SEPARATE的属性hey
,指向2个SEPARATE函数!我们现在在相同的两个不同的对象中有重复的函数做同样的事情,oops ...你可以想象如果我们有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
属性区分开来,作为两个不同的东西。
TA贡献2051条经验 获得超10个赞
在大多数情况下,它们本质上是相同的,但第二个版本节省了内存,因为只有一个函数实例而不是每个对象的单独函数。
使用第一种形式的原因是访问“私人成员”。例如:
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的函数,但不在对象之外。
添加回答
举报