我不是那种动态编程语言,但是我写了很多JavaScript代码。我从来没有真正了解这个基于原型的编程,有没有人知道这是如何工作的?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
我记得很久以前我和人们进行了很多讨论(我不确定我在做什么)但是据我所知,没有一个类的概念。它只是一个对象,这些对象的实例是原始的克隆,对吗?
但JavaScript中这个“.prototype”属性的确切目的是什么?它与实例化对象有什么关系?
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
这些slides确实帮了很多忙。
答案 0 :(得分:1780)
在实现Java,C#或C ++等经典继承的语言中,首先要创建一个类 - 对象的蓝图 - 然后你可以从该类创建新对象,或者你可以扩展类,定义一个新的增强原始类的类。
在JavaScript中,您首先创建一个对象(没有类的概念),然后您可以扩充自己的对象或从中创建新对象。这并不困难,但对于习惯于传统方式的人来说,有点外来并难以代谢。
示例:
//Define a functional object to hold persons in JavaScript
var Person = function(name) {
this.name = name;
};
//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
return this.name;
};
//Create a new object of type Person
var john = new Person("John");
//Try the getter
alert(john.getName());
//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
alert('Hello, my name is ' + this.getName());
};
//Call the new method on john
john.sayMyName();
到目前为止,我一直在扩展基础对象,现在我创建了另一个对象,然后从Person继承。
//Create a new object of type Customer by defining its constructor. It's not
//related to Person for now.
var Customer = function(name) {
this.name = name;
};
//Now I link the objects and to do so, we link the prototype of Customer to
//a new instance of Person. The prototype is the base that will be used to
//construct all new instances and also, will modify dynamically all already
//constructed objects because in JavaScript objects retain a pointer to the
//prototype
Customer.prototype = new Person();
//Now I can call the methods of Person on the Customer, let's try, first
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
return this.amountDue;
};
//Let's try:
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
var Person = function (name) {
this.name = name;
};
Person.prototype.getName = function () {
return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
this.name = name;
};
Customer.prototype = new Person();
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
虽然我说不能在Person上调用setAmountDue(),getAmountDue()。
//The following statement generates an error.
john.setAmountDue(1000);
答案 1 :(得分:965)
每个JavaScript对象都有一个名为 [[Prototype]] 的内部属性。如果您通过obj.propName
或obj['propName']
查找属性,并且该对象没有这样的属性 - 可以通过obj.hasOwnProperty('propName')
进行检查 - 运行时会在引用的对象中查找属性[[原型]]而不是。如果prototype-object也没有这样的属性,则依次检查其原型,从而遍历原始对象的原型链,直到找到匹配或达到其结束。
某些JavaScript实现允许直接访问[[Prototype]]属性,例如通过名为__proto__
的非标准属性。通常,只能在对象创建期间设置对象的原型:如果通过new Func()
创建新对象,则对象的[[Prototype]]属性将设置为Func.prototype
引用的对象。
这允许在JavaScript中模拟类,尽管JavaScript的继承系统是 - 正如我们所见 - 原型,而不是基于类:
只需将构造函数作为类和原型的属性(即构造函数的prototype
属性引用的对象)作为共享成员,即每个实例的成员相同。在基于类的系统中,方法以相同的方式为每个实例实现,因此通常将方法添加到原型中,而对象的字段是特定于实例的,因此在构造期间将其添加到对象本身。
答案 2 :(得分:169)
我扮演JavaScript老师的角色,原型概念在我教授时一直是一个有争议的话题。我花了一段时间才想出一个澄清这个概念的好方法,现在在本文中,我将试图解释JavaScript .prototype是如何工作的。
这是一个非常简单的基于原型的对象模型,在解释过程中将被视为一个样本,尚无评论:
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
console.log(this.name);
}
var person = new Person("George");
在完成原型概念之前,我们必须考虑一些关键点。
要迈出第一步,我们必须弄清楚JavaScript函数的实际工作方式,使用 this
关键字作为类函数,或者仅作为常规函数及其参数,它做了什么以及它返回了什么。
我们想说我们要创建一个Person
对象模型。但是在这一步中,我将尝试在不使用prototype
和new
关键字的情况下做同样的事情。
因此,在此步骤 functions
, objects
和 this
关键字中,我们都是有
第一个问题是 this
关键字如何在不使用new
关键字的情况下有用。
所以回答一下,让我们说我们有一个空对象,以及两个函数,如:
var person = {};
function Person(name){ this.name = name; }
function getName(){
console.log(this.name);
}
现在不使用new
关键字我们如何使用这些功能。所以JavaScript有3种不同的方法:
Person("George");
getName();//would print the "George" in the console
在这种情况下,这将是当前的上下文对象,通常是浏览器中的全局window
对象或GLOBAL
中的Node.js
。这意味着我们可以在浏览器中使用window.name,在Node.js中使用GLOBAL.name,使用" George"作为它的价值。
- 最简单的方法是修改空的person
对象,例如:
person.Person = Person;
person.getName = getName;
这样我们可以称之为:
person.Person("George");
person.getName();// -->"George"
现在person
对象就像:
Object {Person: function, getName: function, name: "George"}
- 将属性附加到对象的另一种方法是使用该对象的prototype
,该对象可以在名称为__proto__
的任何JavaScript对象中找到,我试图在摘要部分解释一下。所以我们可以通过这样做得到类似的结果:
person.__proto__.Person = Person;
person.__proto__.getName = getName;
但是这样我们实际上正在修改Object.prototype
,因为每当我们使用文字({ ... }
)创建JavaScript对象时,它都会基于{创建{1}},这意味着它作为名为 Object.prototype
的属性附加到新创建的对象,因此如果我们更改它,就像我们在之前的代码片段中所做的那样,所有JavaScript对象会被改变,而不是一个好习惯。那么现在可能是更好的做法:
__proto__
现在其他物品都很平静,但它似乎仍然不是一个好习惯。所以我们还有一个解决方案,但是要使用这个解决方案,我们应该回到创建person.__proto__ = {
Person: Person,
getName: getName
};
对象的代码行(person
),然后改变它:
var person = {};
它的作用是创建新的JavaScript var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
并将Object
附加到propertiesObject
属性。所以要确保你能做到:
__proto__
但这里棘手的一点是你可以访问console.log(person.__proto__===propertiesObject); //true
对象第一层__proto__
中定义的所有属性(阅读摘要部分以获取更多细节)。
如您所见,使用这两种方式中的任何一种person
都会指向this
对象。
person
函数,该函数使用call或apply来调用函数。apply()方法调用一个给定此值的函数 作为数组(或类数组对象)提供的参数。
和
call()方法调用一个给定此值的函数 论据单独提供。
这是我最喜欢的方式,我们可以轻松调用我们的功能:
this
或
Person.call(person, "George");
这三种方法是确定.prototype功能的重要初始步骤。
//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);
getName.call(person);
getName.apply(person);
关键字的工作原理是什么?这是理解new
功能的第二步。这是我用来模拟过程的:
.prototype
在本部分中,当您使用function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
关键字时,我将尝试执行JavaScript所采取的所有步骤,而不使用new
关键字和prototype
。因此,当我们执行new
时,new Person("George")
函数充当构造函数,这些是JavaScript所做的,逐个:
Person
我们这里的var newObject = {};
类似于原型对象。
my_person_prototype
这不是JavaScript实际附加原型中定义的属性的方式。实际的方式与原型链概念有关。
for(var key in my_person_prototype){
newObject[key] = my_person_prototype[key];
}
现在我们可以调用var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"
中的getName
函数:
my_person_prototype
我们可以使用以下示例执行此操作:
newObject.getName();
或
Person.call(newObject, "George");
然后构造函数可以做任何想做的事情,因为该构造函数内的 this 是刚创建的对象。
现在是模拟其他步骤之前的最终结果: 对象{name:" George"}
基本上,当你在函数上使用 new 关键字时,你正在调用它并且该函数用作构造函数,所以当你说:
Person.apply(newObject, ["George"]);
JavaScript内部创建一个对象,一个空哈希,然后它将该对象提供给构造函数,然后构造函数可以做任何想做的事情,因为该构造函数内部的 this 就是对象如果您没有在函数中使用return语句,或者如果您在函数体的末尾添加了new FunctionName()
,那么它就会为您提供该对象。
因此,当JavaScript在一个对象上查找一个属性时,它所做的第一件事就是它在该对象上查找它。然后有一个秘密属性 return undefined;
,我们通常会将其视为 [[prototype]]
,该属性就是JavaScript下一步所看到的。当它查看 __proto__
时,只要它再次是另一个JavaScript对象,它就有自己的 __proto__
属性,它会上升直到它到达下一个 __proto__
为空的点。关键是JavaScript中唯一的 __proto__
属性为null的对象是__proto__
对象:
Object.prototype
以及继承如何在JavaScript中运行。
换句话说,当你在一个函数上有一个prototype属性并且你在它上面调用new时,在JavaScript完成查找新创建的属性对象之后,它将会查看函数{{1此对象也有可能有自己的内部原型。等等。
答案 3 :(得分:74)
prototype
允许你上课。如果你不使用prototype
那么它就变成了静态。
这是一个简短的例子。
var obj = new Object();
obj.test = function() { alert('Hello?'); };
在上面的例子中,你有静态函数调用测试。只能通过obj.test访问此函数,您可以将obj想象成一个类。
如以下代码所示
function obj()
{
}
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
obj已经成为一个现在可以实例化的类。可以存在多个obj实例,它们都具有test
函数。
以上是我的理解。我正在制作社区维基,所以如果我错了,人们可以纠正我。
答案 4 :(得分:67)
当Ciro San在深度冥想后降落火狐之后,他的思绪清晰而平和。
然而,他的手却焦躁不安,自己抓起一把刷子,记下下面的笔记。0)可以调用两种不同的东西"原型":
原型属性,如obj.prototype
原型内部属性,表示为[[Prototype]]
in ES5。
可以通过ES5 Object.getPrototypeOf()
检索它。
Firefox可以通过__proto__
属性作为扩展程序访问它。 __proto__
obj.property
的一些可选要求。
1)这些概念的存在是为了回答这个问题:
当我
.property
时,JS在哪里寻找__proto__
?
直观地说,经典继承应该影响属性查找。
<强> 2)强>
.
用于点obj.property
属性查找,如.prototype
中所示。 __proto__
不直接用于查找,只是间接用于new
在创建对象时确定obj
。查询顺序是:
obj.p = ...
已添加Object.defineProperty(obj, ...)
或obj.__proto__
obj.__proto__.__proto__
__proto__
的属性,依此类推null
为undefined
,请返回.
。这就是所谓的原型链。
您可以使用obj.hasOwnProperty('key')
和Object.getOwnPropertyNames(f)
obj.__proto__
查询
3)设置new
有两种主要方法:
var F = function() {}
var f = new F()
:
new
然后f.__proto__ === F.prototype
设置了:
.prototype
此是使用Object.create
的地方。
f = Object.create(proto)
:
f.__proto__ === proto
设定:
var F = function() {}
var f = new F()
4)代码:
(Function) ( F ) (f)
| ^ | | ^ |
| | | | | |
| | | | +-------------------------+ |
| |constructor | | | |
| | | +--------------+ | |
| | | | | |
| | | | | |
|[[Prototype]] |[[Prototype]] |prototype |constructor |[[Prototype]]
| | | | | |
| | | | | |
| | | | +----------+ |
| | | | | |
| | | | | +-----------------------+
| | | | | |
v | v v | v
(Function.prototype) (F.prototype)
| |
| |
|[[Prototype]] |[[Prototype]]
| |
| |
| +-------------------------------+
| |
v v
(Object.prototype)
| | ^
| | |
| | +---------------------------+
| | |
| +--------------+ |
| | |
| | |
|[[Prototype]] |constructor |prototype
| | |
| | |
| | -------------+
| | |
v v |
(null) (Object)
对应下图:
null
此图显示了许多语言预定义对象节点:Object
,Object.prototype
,Function
,Function.prototype
和f
。我们的2行代码仅创建了F
,F.prototype
和.constructor
。
5) F.prototype
通常来自.
到f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor
查询:
f.constructor
当我们编写.
时,JavaScript将f
查找为:
.constructor
没有f.__proto__ === F.prototype
.constructor === F
有f.constructor == F
,所以请将其结果F
直观正确,因为f
用于构建class
,例如设置字段,就像经典的OOP语言一样。
6)经典的继承语法可以通过操作原型链来实现。
ES6添加了extends
和class C {
constructor(i) {
this.i = i
}
inc() {
return this.i + 1
}
}
class D extends C {
constructor(i) {
super(i)
}
inc2() {
return this.i + 2
}
}
关键字,这些关键字只是以前可能的原型操作疯狂的语法糖。
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
__proto__
(C)<---------------(D) (d)
| | | |
| | | |
| |prototype |prototype |__proto__
| | | |
| | | |
| | | +---------+
| | | |
| | | |
| | v v
|__proto__ (D.prototype)
| | |
| | |
| | |__proto__
| | |
| | |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
没有所有预定义对象的简化图表:
{{1}}
答案 5 :(得分:65)
阅读完这篇帖子后,我觉得与JavaScript Prototype Chain感到困惑,然后我找到了这些图表
http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance
这是一个清晰的图表,用于显示原型链的JavaScript继承
和
http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/
这个包含一个带代码和几个漂亮图表的例子。
原型链最终会回归到Object.prototype。
原型链可以在技术上扩展,只要你想要,每次通过设置子类的原型等于父类的对象。
希望它对你理解JavaScript原型链也很有帮助。
答案 6 :(得分:37)
每个对象都有一个内部属性[[Prototype]],将它链接到另一个对象:
object [[Prototype]] -> anotherObject
在传统的javascript中,链接对象是函数的prototype
属性:
object [[Prototype]] -> aFunction.prototype
某些环境将[[Prototype]]公开为__proto__
:
anObject.__proto__ === anotherObject
创建对象时创建[[Prototype]]链接。
// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject
// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject
// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype
所以这些陈述是等价的:
var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;
new
语句未显示链接目标(Object.prototype
)本身;相反,构造函数(Object
)隐含了目标。
记住:
__proto__
。prototype
属性。new
创建的对象链接到其构造函数的prototype
属性。prototype
属性将不使用。new
。答案 7 :(得分:26)
这篇文章很长。但我相信它会清除你的大部分疑问 关于&#34;原型&#34; JavaScript继承的本质。甚至更多。请阅读完整的文章。
JavaScript基本上有两种数据类型
非对象
以下是 非对象 数据类型
当您使用 typeof 运算符
时,这些数据类型会返回typeof &#34;字符串文字&#34; (或包含字符串文字的变量)=== &#39; string&#39; < /强>
typeof 5 (或任何数字文字或包含数字文字的变量或 NaN或Infynity )= == &#39;号码&#39;
typeof true (或 false 或包含 true 或 false 的变量)=== &#39; boolean&#39;
typeof undefined (或未定义的变量或包含 undefined 的变量)=== &#39; undefined&#39 强>
字符串,数字和布尔数据类型可以表示为对象和非对象。当它们被表示为对象时,它们的typeof总是===&#39; object&#39;。一旦我们理解了对象数据类型,我们就会回到这里。
<强> 物件 强>
对象数据类型可以进一步分为两种类型
功能类型对象是使用 typeof 运算符返回字符串&#39;功能&#39; 的功能。 所有用户定义的函数和所有可以通过使用new运算符创建新对象的对象内置的JavaScript都属于此类别。例如。
所以, typeof(对象) === typeof(字符串) === typeof(数字) === typeof(布尔)< / strong> === typeof(数组) === typeof(RegExp) === typeof(功能) === typeof(UserDefinedFunction) === &#39;功能&#39;
所有 功能类型对象 实际上是内置JavaScript对象的实例功能(包括功能对象即它是递归定义的)。就好像这些对象已经按照以下方式定义
var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code for object Function])
var UserDefinedFunction= new Function ("user defined code")
如上所述, 功能类型对象 可以使用 new运算符进一步创建新对象。例如,对象类型为对象,字符串,数字,布尔,数组 , RegExp 或 UserDefinedFunction 可以使用
创建var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[] //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction()
这样创建的对象都是 非函数类型对象 并返回 typeof === &#39; object&# 39; 即可。在所有这些情况下,对象&#34; a&#34;无法进一步创造 使用operator new的对象。所以以下是错误的
var b=new a() //error. a is not typeof==='function'
内置对象数学是 typeof === &#39; object&#39; 。因此,新运算符无法创建Math类型的新对象。
var b=new Math() //error. Math is not typeof==='function'
另请注意,对象,数组和 RegExp 功能可以创建新对象,甚至无需使用 operator new 。然而,下面的人不会。
var a=String() // Create a new Non Object string. returns a typeof==='string'
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'
用户定义的函数是特例。
var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.
由于 功能类型对象 可以创建新对象,因此它们也称为 构造函数 。
自动定义时,每个构造函数/函数(无论是内置函数还是用户定义)都有一个名为&#34; prototype&#34; 的属性,其值默认设置为作为一个对象。该对象本身有一个名为&#34; constructor&#34; 的属性,默认情况下引用构造函数/函数。
例如,当我们定义一个函数
时function UserDefinedFunction()
{
}
自动发生
UserDefinedFunction.prototype={constructor:UserDefinedFunction}
&#34;原型&#34;属性仅出现在函数类型对象中 (绝不会在非函数类型对象中)。
这是因为在创建新对象时(使用new运算符),它继承了Constructor函数当前原型对象的所有属性和方法,即 内部引用 在新创建的对象中创建,该对象引用构造函数的当前原型对象引用的对象。
在对象中创建的用于引用继承属性的&#34;内部引用&#34; 称为对象的原型(引用构造函数&#34;原型&#34; 属性引用的对象,但与之不同)。对于任何对象(函数或非函数),可以使用 Object.getPrototypeOf()方法检索它。使用此方法可以跟踪对象的原型链。
此外,创建的每个对象(功能类型或非功能类型)都有&#34;构造函数&# 34; 属性,它继承自构造函数的prototype属性所引用的对象。默认情况下,&#34;构造函数&#34; 属性引用创建它的构造函数(如果构造函数&#39; 默认值&#34;原型&#34;没有改变)。
对于所有 函数类型对象 ,构造函数始终是 功能函数(){}
对于 非函数类型对象 (例如Javascript Built in Math对象),构造函数是创建它的函数。 对于数学对象,它是 function Object(){} 。
如果没有任何支持代码,上面解释的所有概念都可能有点令人生畏。请逐行浏览以下代码以了解概念。尝试执行它以便更好地理解。
function UserDefinedFunction()
{
}
/* creating the above function automatically does the following as mentioned earlier
UserDefinedFunction.prototype={constructor:UserDefinedFunction}
*/
var newObj_1=new UserDefinedFunction()
alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype) //Displays true
alert(newObj_1.constructor) //Displays function UserDefinedFunction
//Create a new property in UserDefinedFunction.prototype object
UserDefinedFunction.prototype.TestProperty="test"
alert(newObj_1.TestProperty) //Displays "test"
alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"
//Create a new Object
var objA = {
property1 : "Property1",
constructor:Array
}
//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA
alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype) //Displays false. The object referenced by UserDefinedFunction.prototype has changed
//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction
alert(newObj_1.TestProperty) //This shall still Display "test"
alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"
//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();
alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.
alert(newObj_2.constructor) //Displays function Array()
alert(newObj_2.property1) //Displays "Property1"
alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"
//Create a new property in objA
objA.property2="property2"
alert(objA.property2) //Displays "Property2"
alert(UserDefinedFunction.prototype.property2) //Displays "Property2"
alert(newObj_2.property2) // Displays Property2
alert(Object.getPrototypeOf(newObj_2).property2) //Displays "Property2"
每个对象的原型链最终追溯到Object.prototype(它本身没有任何原型对象)。 以下代码可用于跟踪对象的原型链
var o=Starting object;
do {
alert(o + "\n" + Object.getOwnPropertyNames(o))
}while(o=Object.getPrototypeOf(o))
各种对象的原型链如下所示。
要创建没有任何原型的对象,请使用以下命令:
var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null
有人可能会认为将Constructor的prototype属性设置为null将创建一个带有null原型的对象。但是在这种情况下,新创建的对象的原型设置为Object.prototype,其构造函数设置为Object。以下代码
证明了这一点function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)
var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype) //Displays true
alert(o.constructor) //Displays Function Object
以下是本文的摘要
只有功能类型对象可以使用运算符new 创建新对象。这样创建的对象是非函数类型对象。 非功能类型对象无法使用 operator new 进一步创建对象。
默认情况下,所有功能类型对象都有&#34;原型&#34; 属性。此&#34;原型&#34; 属性引用一个对象,该对象具有&#34;构造函数&#34; 属性,默认情况下引用函数类型对象本身。
所有对象(函数类型和非函数类型)都有一个&#34;构造函数&#34;默认情况下引用创建它的函数类型对象 / 构造函数的属性。
在内部创建的每个对象都引用引用的对象 创建它的构造函数的&#34; prototype&#34; 属性。此对象称为创建的 对象的原型 (与函数类型对象&#34;原型&#34;它引用的属性不同)。这样,创建的对象可以直接访问由构造函数&#34;原型&#34;引用的对象中定义的方法和属性。属性(在创建对象时)。
可以使用 Object.getPrototypeOf()方法检索对象的原型(以及其继承的属性名称)。实际上这种方法 可用于导航对象的整个原型链。
每个对象的原型链最终追溯到Object.prototype(除非使用Object.create(null)创建对象,在这种情况下对象没有原型)。
typeof(new Array())===&#39; object&#39; 是按语言设计的,而不是Douglas Crockford指出的错误
将Constructor的prototype属性设置为null(或undefined,number,true,false,string)不得创建具有null原型的对象。在这种情况下,新创建的对象的原型设置为Object.prototype,其构造函数设置为Object。
希望这有帮助。
答案 8 :(得分:25)
Javascript没有通常意义上的继承,但它有原型链。
如果在对象中找不到对象的成员,它将在原型链中查找它。该链由其他对象组成。可以使用__proto__
变量访问给定实例的原型。每个对象都有一个,因为javascript中的类和实例之间没有区别。
向原型添加函数/变量的优点是它必须只在内存中一次,而不是每个实例。
它对继承也很有用,因为原型链可以包含许多其他对象。
答案 9 :(得分:22)
prototypal
继承的概念是许多开发人员最复杂的概念之一。让我们尝试理解问题的根源,以便更好地理解prototypal inheritance
。让我们从plain
函数开始。
如果我们在new
上使用Tree function
运算符,我们将其称为constructor
函数。
每个JavaScript
函数都有一个prototype
。当您记录Tree.prototype
时,您会得到......
如果查看上面的console.log()
输出,您可以在Tree.prototype
和__proto__
属性上看到构造函数属性。 __proto__
表示此prototype
所依据的function
,因为这只是一个JavaScript function
但尚未设置inheritance
,所以它指的是Object prototype
这是JavaScript内置的内容......
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
这有像.toString, .toValue, .hasOwnProperty
等......
__proto__
带来了我的mozilla已被弃用,并被Object.getPrototypeOf
方法取代以获取object's prototype
。
Object.getPrototypeOf(Tree.prototype); // Object {}
让我们为Tree
prototype
添加一种方法。
我们修改了Root
并为其添加了function
分支。
这意味着当您创建instance
Tree
时,您可以将其称为branch
方法。
我们还可以将primitives
或objects
添加到Prototype
。
我们为child-tree
添加Tree
。
这里Child
从Tree继承了它的prototype
,我们在这里做的是使用Object.create()
方法根据你传递的内容创建一个新对象,这里是{{1 }}。在这种情况下,我们正在做的是将Child的原型设置为一个看起来与Tree.prototype
原型相同的新对象。接下来我们设置Tree
,如果我们不指向Child's constructor to Child
。
Tree()
现在有自己的Child
,prototype
指向__proto__
,Tree
指向基地Tree's prototype
。
Object
现在,您创建Child
|
\
\
Tree.prototype
- branch
|
|
\
\
Object.prototype
-toString
-valueOf
-etc., etc.
instance
并致电Child
,该branch
最初位于Tree
。我们尚未在branch
上实际定义Child prototype
。但是,在Child继承的Root prototype
中。
在JS中,一切都不是对象,一切都可以像对象一样。
Javascript
包含strings, number, booleans, undefined, null.
之类的原语。它们不是object(i.e reference types)
,但肯定可以像object
那样行事。我们来看一个例子。
在此商家信息的第一行中,将primitive
字符串值分配给name。第二行将名称视为object
,并使用点表示法调用charAt(0)
。
这是幕后发生的事情:
// JavaScript
引擎做什么
String object
仅在一个语句被销毁之前存在(一个名为autoboxing
的过程)。让我们再次回到prototypal
inheritance
。
Javascript
支持基于delegation
的继承
prototypes
。Function
都有一个prototype
属性,指的是另一个属性
对象。properties/functions
从object
本身或通过。{
prototype
链(如果不存在) JS中的prototype
是一个对象,yields
您是另一个object
的父级。 [即......代表团] Delegation
表示如果您无法做某事,您会告诉其他人为您做这件事。
https://jsfiddle.net/say0tzpL/1/
如果您查看上面的小提琴,狗可以访问toString
方法,但它不可用,但可以通过委托给Object.prototype
的原型链获得
如果您查看以下内容,我们会尝试访问每个call
中提供的function
方法。
https://jsfiddle.net/rknffckc/
如果你查看上面的小提琴,Profile
函数可以访问call
方法,但它不可用,但可以通过委托给Function.prototype
的原型链获得
注意: prototype
是函数构造函数的属性,而__proto__
是从函数构造函数构造的对象的属性。每个函数都带有prototype
属性,其值为空object
。当我们创建函数的实例时,我们得到一个内部属性[[Prototype]]
或__proto__
,其引用是函数constructor
的原型。
上图看起来有点复杂,但全面展示了prototype chaining
的工作原理。让我们慢慢走过这个:
有两个实例b1
和b2
,其构造函数为Bar
,父级为Foo,并且有两个来自原型链identify
和speak
的方法Bar
和Foo
https://jsfiddle.net/kbp7jr7n/
如果您查看上面的代码,我们的Foo
构造函数的方法identify()
和Bar
构造函数具有speak
方法。我们创建了两个Bar
实例b1
和b2
,其父类型为Foo
。现在,在调用speak
的{{1}}方法时,我们可以通过Bar
链识别谁正在调用发言。
prototype
现在拥有Bar
中定义的Foo
的所有方法。让我们进一步深入了解prototype
和Object.prototype
以及它们之间的关系。如果您查找Function.prototype
的构造函数,Foo
和Bar
为Object
。
Function constructor
的{{1}}为prototype
,Bar
的{{1}}为Foo
,如果您密切关注prototype
Foo
与Object
相关。
在我们关闭之前,让我们在这里用一小段代码包装总结上面的所有内容。我们在此处使用prototype
运算符来检查Foo
Object.prototype
链中instanceof
的{{1}}属性是否object
,以下是对整个大图的总结。
我希望这会添加一些信息,我知道这可能很难掌握......简单来说就是它只是与对象关联的对象!!!!
答案 10 :(得分:20)
这个“.prototype”属性的确切目的是什么?
标准类的接口变得可扩展。例如,您正在使用Array
类,还需要为所有数组对象添加自定义序列化程序。您是否会花时间编写子类,或使用合成或...原型属性通过让用户控制类可用的确切成员/方法集来解决这个问题。
将原型视为额外的vtable指针。当原始类中缺少某些成员时,将在运行时查找原型。
答案 11 :(得分:19)
将原型链分类为两类可能会有所帮助。
考虑构造函数:
function Person() {}
Object.getPrototypeOf(Person)
的值是一个函数。实际上,它是Function.prototype
。由于Person
是作为函数创建的,因此它共享所有函数具有的相同原型函数对象。它与Person.__proto__
相同,但不应使用该属性。无论如何,Object.getPrototypeOf(Person)
你有效地走上了原型链的阶梯。
向上的链条看起来像这样:
Person
→Function.prototype
→Object.prototype
(终点)
重要的是,这个原型链与Person
可以构建的对象几乎没有关系。那些构造的对象有自己的原型链,这个链可能没有与上面提到的共同的祖先。
以此对象为例:
var p = new Person();
p 与 Person 没有直接的原型链关系。他们的关系是不同的。对象 p 有自己的原型链。使用Object.getPrototypeOf
,您会发现链如下:
p
→Person.prototype
→Object.prototype
(终点)
此链中没有任何功能对象(尽管可能是这样)。
所以Person
似乎与两种生活相关的链条有关。要从一个链“跳”到另一个链,请使用:
.prototype
:从构造函数链跳转到created-object的链。因此,仅为函数对象定义此属性(因为new
只能在函数上使用)。
.constructor
:从创建对象的链跳转到构造函数链。
以下是所涉及的两个原型链的直观呈现,表示为列:
总结:
prototype
属性不提供主题的原型链的信息,但不提供主题创建的对象的信息。
属性prototype
的名称可能导致混淆,这并不奇怪。如果此属性已被命名为prototypeOfConstructedInstances
或该行的某些内容,则可能更清楚。
你可以在两个原型链之间来回跳转:
Person.prototype.constructor === Person
可以通过向prototype
属性明确指定不同的对象来解决这种对称性(稍后会详细介绍)。
Person.prototype
是在创建函数Person
的同时创建的对象。它有Person
作为构造函数,即使该构造函数实际上还没有执行。因此,同时创建了两个对象:
Person
本身两者都是对象,但它们具有不同的角色:函数对象构造,而另一个对象表示函数将构造的任何对象的原型。原型对象将成为其原型链中构造对象的父对象。
由于函数也是一个对象,它在自己的原型链中也有自己的父对象,但回想一下这两个链是不同的东西。
以下是一些可以帮助解决问题的平等 - 所有这些都打印true
:
function Person() {};
// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);
// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);
虽然在创建构造函数时会创建原型对象,但您可以忽略该对象,并指定另一个对象,该对象应该用作该构造函数创建的任何后续实例的原型。
例如:
function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();
现在 t 的原型链比 p 的原型链长一步:
t
→p
→Person.prototype
→Object.prototype
(终点)
另一个原型链不再是:Thief
和Person
是在原型链中共享同一个父级的兄弟姐妹:
Person
}
Thief
}→Function.prototype
→Object.prototype
(终点)
然后可以将之前呈现的图形扩展到此(原始Thief.prototype
被省略):
蓝线代表原型链,其他彩色线代表其他关系:
答案 12 :(得分:16)
The Definitive Guide to Object-Oriented JavaScript - 一个非常简洁明了〜30分钟的问题视频解释(原型继承主题从5:45开始,尽管我宁愿听完整个视频)。此视频的作者还制作了JavaScript对象可视化工具网站http://www.objectplayground.com/。
答案 13 :(得分:14)
我发现在引用obj_n.prop_X
时将“原型链”解释为递归约定是有帮助的:
如果obj_n.prop_X
不存在,请检查obj_n+1.prop_X
其中obj_n+1 = obj_n.[[prototype]]
如果最终在第k个原型对象中找到prop_X
,那么
obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X
您可以在此处找到Javascript对象的属性关系图:
答案 14 :(得分:13)
当构造函数创建对象时,该对象隐式引用构造函数的“prototype”属性以解析属性引用。构造函数的“prototype”属性可以由程序表达式constructor.prototype引用,添加到对象原型的属性通过继承共享原型的所有对象共享。
答案 15 :(得分:10)
让我告诉你我对原型的理解。我不打算将这里的继承与其他语言进行比较。我希望人们不要再比较语言,只要把语言理解为自己。了解原型和原型继承非常简单,我将在下面向您展示。
Prototype就像一个模型,在此基础上您可以创建产品。要理解的关键点是,当您使用另一个对象创建一个对象作为原型时,原型和产品之间的链接将持续存在。例如:
var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5
每个对象都包含一个名为[[prototype]]的内部属性,可以由Object.getPrototypeOf()
函数访问。 Object.create(model)
创建一个新对象并将其[[prototype]]属性设置为对象 model 。因此,当您执行Object.getPrototypeOf(product)
时,您将获得对象模型。
产品中的属性按以下方式处理:
使用prototype属性进行对象的这种链接称为原型继承。在那里,它很简单,同意吗?
答案 16 :(得分:10)
这里有两个不同但相关的实体需要解释:
.prototype
属性。[[Prototype]]
[1] 属性。 这是两件不同的事情。
[[Prototype]]
属性:这是一个存在于所有 [2] 对象上的属性。
这里存储的是另一个对象,作为一个对象本身,它有一个自己的[[Prototype]]
指向另一个对象。另一个对象有自己的[[Prototype]]
。这个故事一直持续到您提供的原型对象提供可在所有对象上访问的方法(如.toString
)。
[[Prototype]]
属性是构成[[Prototype]]
链的一部分。这个[[Prototype]]
对象链是在对对象执行[[Get]]
或[[Set]]
操作时检查的内容:
var obj = {}
obj.a // [[Get]] consults prototype chain
obj.b = 20 // [[Set]] consults prototype chain
.prototype
属性:这是一个只能在函数中找到的属性。使用一个非常简单的函数:
function Bar(){};
.prototype
属性包含一个对象,当您执行b.[[Prototype]]
时,该对象将分配给var b = new Bar
。您可以轻松地检查这一点:
// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true
最重要的.prototype
之一就是of the Object
function。该原型包含所有[[Prototype]]
链包含的原型对象。在其上,定义了新对象的所有可用方法:
// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))
现在,由于.prototype
是一个对象,因此它具有[[Prototype]]
属性。当您未对Function.prototype
进行任何分配时,.prototype
[[Prototype]]
指向原型对象(Object.prototype
)。每次创建新功能时都会自动执行此操作。
这样,只要您为new Bar;
设置原型链,就可以在Bar.prototype
上定义所有内容,并在Object.prototype
上定义所有内容:
var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)
当您做对Function.prototype
进行分配时,您所做的就是扩展原型链以包含另一个对象。它就像插入单链表中一样。
这基本上改变了[[Prototype]]
链,允许由分配给Function.prototype
的对象定义的属性被函数创建的任何对象看到。
[1:这不会让任何人感到困惑;在许多实现中通过the __proto__
property提供。
[2]:除null
以外的所有。
答案 17 :(得分:9)
考虑以下keyValueStore
对象:
var keyValueStore = (function() {
var count = 0;
var kvs = function() {
count++;
this.data = {};
this.get = function(key) { return this.data[key]; };
this.set = function(key, value) { this.data[key] = value; };
this.delete = function(key) { delete this.data[key]; };
this.getLength = function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
return { // Singleton public properties
'create' : function() { return new kvs(); },
'count' : function() { return count; }
};
})();
我可以通过这样做来创建这个对象的新实例:
kvs = keyValueStore.create();
此对象的每个实例都具有以下公共属性:
data
get
set
delete
getLength
现在,假设我们创建了此keyValueStore
个对象的100个实例。即使get
,set
,delete
,getLength
将对这100个实例中的每个实例执行完全相同的操作,但每个实例都有自己的此函数副本。
现在,想象一下,如果您只有一个get
,set
,delete
和getLength
副本,并且每个实例都会引用相同的功能。这样可以提高性能并减少内存。
原型进入的地方。原型是一个&#34;蓝图&#34;属性的继承但不被实例复制。所以这意味着它只在内存中存在一个对象的所有实例,并由所有这些实例共享。
现在,再次考虑keyValueStore
对象。我可以像这样重写它:
var keyValueStore = (function() {
var count = 0;
var kvs = function() {
count++;
this.data = {};
};
kvs.prototype = {
'get' : function(key) { return this.data[key]; },
'set' : function(key, value) { this.data[key] = value; },
'delete' : function(key) { delete this.data[key]; },
'getLength' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
return {
'create' : function() { return new kvs(); },
'count' : function() { return count; }
};
})();
这与keyValueStore
对象的先前版本完全相同,只是它的所有方法现在都放在原型中。这意味着,现在所有100个实例都共享这四种方法,而不是每种方法都有自己的副本。
答案 18 :(得分:8)
用更好的照片解释JavaScript prototype-based inheritance的另一种尝试
答案 19 :(得分:7)
在理解这类东西时,我总是喜欢类比。 &#39;原型继承&#39;在我看来,与类低音继承相比,它是相当令人困惑的,尽管原型是更简单的范例。事实上,对于原型,确实没有继承,因此名称本身就具有误导性,它更像是一种“授权”。
想象一下......
你在高中时,你在课堂上进行了一个今天到期的测验,但是你没有笔来填写你的答案。卫生署!
你坐在你的朋友Finnius旁边,他可能有一支钢笔。你问,他环顾他的办公桌不成功,但没有说&#34;我没有笔#34,他是一个很好的朋友,他与他的另一个朋友Derp检查,如果他有一个钢笔。 Derp确实有一支备用笔并将其传递回Finnius,后者将其传递给您以完成您的测验。 Derp已将笔委托给Finnius,后者已将笔委托给您使用。
重要的是,Derp不会给你笔,因为你与他没有直接的关系。
这是原型如何工作的简化示例,其中搜索数据树以查找您正在寻找的事物。
答案 20 :(得分:4)
new
关键字将函数用作构造函数时,对象将获取原型。可以在新创建的对象的__proto__
属性上找到对此原型的引用。__proto__
属性是指构造函数的prototype
属性。
function Person (name) {
this.name = name;
}
let me = new Person('willem');
console.log(Person.prototype) // Person has a prototype property
console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.
JavaScript具有一种在对象上查找属性的机制,称为“原型继承” ,基本上是这样的:
例如:
function Person(name) {
this.name = name;
}
let mySelf = new Person('Willem');
console.log(mySelf.__proto__ === Person.prototype);
console.log(mySelf.__proto__.__proto__ === Object.prototype);
更新:
__proto__
属性已被弃用,尽管它在大多数现代浏览器中都已实现,但获取原型对象引用的更好方法是:
Object.getPrototypeOf()
答案 21 :(得分:3)
答案 22 :(得分:2)
只是你已经有一个byte
的对象,但在使用构造函数语法时你仍然没有对象。
答案 23 :(得分:2)
原型通过克隆现有的对象来创建新对象。因此,当我们考虑原型时,真的可以考虑克隆或制作副本而不是制作副本。
答案 24 :(得分:0)
重要的是要理解,对象的原型(可通过Object.getPrototypeOf(obj)
或不推荐使用的obj.__proto__
属性获得)与构造函数上的prototype属性之间存在区别。前者是每个实例的属性,后者是构造函数的属性。也就是说,Object.getPrototypeOf(new Foobar())
与Foobar.prototype
指的是同一对象。
参考:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
答案 25 :(得分:0)
如果您想从基础上理解原型和基于原型的继承的概念,请查看官方MDN文档,他们对此进行了很好的解释。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
在继承方面,JavaScript只有一种构造: 对象。每个对象都有一个私有属性,其中包含指向 另一个对象称为其原型。该原型对象有一个 自己的原型,依此类推,直到到达null为止 作为其原型。根据定义,null没有原型,并充当 该原型链的最终链接。
此外,这是另一个很好的资源,它使用简单的示例进行了解释-https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes