有什么区别
var A = function () { this.x = function () { //do something }; };
和
var A = function () { }; A.prototype.x = function () { //do something };
keparo.. 458
这些例子的结果截然不同.
在查看差异之前,应注意以下事项:
构造函数的原型提供了一种通过实例的私有[[Prototype]]
属性在实例之间共享方法和值的方法.
函数的这种由功能是如何调用或使用的设置绑定(这里不作讨论).其中一个函数被调用的对象(例如上myObj.method()
),那么该方法中引用的对象.当这种不被电话或通过使用设置绑定,则默认为全局对象(窗口浏览器),或者在严格模式下,仍然不确定.
JavaScript是一种面向对象的语言,即一切都是对象,包括函数.
所以这里是有问题的片段:
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
,函数的this被设置为引用一个新的Object,其private [[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
.
如果您将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是一种原型语言是什么意思?
JavaScript中函数的范围是什么?
"this"关键字如何运作?
旁注:两种方法之间可能没有任何显着的内存节省,但是使用原型共享方法和属性可能比使用自己的副本的每个实例使用更少的内存.
JavaScript不是一种低级语言.将原型设计或其他继承模式视为明确更改内存分配方式的方法可能不是很有价值.
这些例子的结果截然不同.
在查看差异之前,应注意以下事项:
构造函数的原型提供了一种通过实例的私有[[Prototype]]
属性在实例之间共享方法和值的方法.
函数的这种由功能是如何调用或使用的设置绑定(这里不作讨论).其中一个函数被调用的对象(例如上myObj.method()
),那么该方法中引用的对象.当这种不被电话或通过使用设置绑定,则默认为全局对象(窗口浏览器),或者在严格模式下,仍然不确定.
JavaScript是一种面向对象的语言,即一切都是对象,包括函数.
所以这里是有问题的片段:
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
,函数的this被设置为引用一个新的Object,其private [[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
.
如果您将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是一种原型语言是什么意思?
JavaScript中函数的范围是什么?
"this"关键字如何运作?
旁注:两种方法之间可能没有任何显着的内存节省,但是使用原型共享方法和属性可能比使用自己的副本的每个实例使用更少的内存.
JavaScript不是一种低级语言.将原型设计或其他继承模式视为明确更改内存分配方式的方法可能不是很有价值.
正如其他人所说的那样,使用"this"会导致A类的每个实例都有自己独立的函数方法"x".而使用"prototype"将意味着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
的属性.
您可能还想知道,为什么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
属性区分开来,作为两个不同的东西.
在大多数情况下,它们本质上是相同的,但第二个版本节省了内存,因为只有一个函数实例而不是每个对象的单独函数.
使用第一种形式的原因是访问"私人成员".例如:
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的函数,但不在对象之外.
第一个示例仅更改该对象的接口.第二个示例更改该类的所有对象的接口.
使用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
每个对象都链接到一个原型对象.当尝试访问不存在的属性时,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
总之,在第一个例子中,为每个实例分配了一个函数的副本.在第二个示例中,所有实例共享该函数的单个副本.
有什么不同?=>很多.
我认为,该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 ***
希望这可以帮助.
Prototype是该类的模板; 这适用于它的所有未来实例.而这是对象的特定实例.
让我给你一个我在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
对象,它经由链路constructor
到Car
本身,即Car.prototype.constructor
是Car
本身.这允许您找出构建特定对象的构造函数.
amy.constructor
查找失败,因此委托给它Car.prototype
,它具有构造函数属性.所以amy.constructor
是Car
.
而且,amy
是一个instanceof
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中应用超类或子类等概念.
随意添加或编辑.一旦更完整,我可以将其作为社区维基.
我知道这已经被解决了,但我想展示一个速度差异的实际例子.
在这里,我们使用print
Chrome中的方法创建了2,000,000个新对象.我们将每个对象存储在一个数组中.放置print
原型需要大约1/2的时间.
我相信@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中定义我的类.变量名称已更改以反映此情况.
正如其他答案所讨论的那样,它实际上是一个性能考虑因素,因为原型中的函数与所有实例化共享 - 而不是为每个实例化创建的函数.
我把一个jsperf放在一起来表明这一点.实例化类所花费的时间差别很大,尽管只有在制作许多实例时它才真正有用.
http://jsperf.com/functions-in-constructor-vs-prototype
考虑静态类型语言,事物prototype
是静态的,事物this
是与实例相关的.