JavaScript .prototype如何工作?
我不是那种dynamic的编程语言,但是我已经写了我的一些JavaScript代码。 我从来没有真正理解这个基于原型的编程,有没有人知道这是如何工作的?
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
我记得有一段时间我和人们讨论过很多的话题(我不确定自己在做什么),但据我所知,没有一个阶级的概念。 这只是一个对象,这些对象的实例是原始的克隆,对吧?
但是这个.prototype
属性在JavaScript中的确切目的是什么? 它与实例化对象有什么关系?
编辑
这些幻灯片确实帮助我们理解了这个话题。
每个JavaScript对象都有一个名为[[Prototype]]的内部属性。 如果通过obj.propName
或obj['propName']
查找属性并且对象没有这样的属性 – 可以通过obj.hasOwnProperty('propName')
来检查 – 运行时查找对象中的属性而是由[[Prototype]]引用。 如果原型对象也没有这样的属性,那么就会依次检查原型对象的原型链,直到find匹配或到达末尾。
一些JavaScript实现允许直接访问[[Prototype]]属性,例如通过名为__proto__
的非标准属性。 通常,在对象创build过程中只能设置一个对象的原型:如果通过new Func()
创build一个新对象,则该对象的[[Prototype]]属性将被设置为Func.prototype
引用的对象。
这允许在JavaScript中模拟类,尽pipeJavaScript的inheritance系统(正如我们所看到的)是原型的而不是基于类的:
只要将构造函数看作类,并将构造函数的属性(即由构造函数的prototype
属性引用的对象)作为共享成员(即每个实例相同的成员)来考虑。 在基于类的系统中,方法对于每个实例都以相同的方式实现,所以方法通常被添加到原型中,而对象的字段是特定于实例的,因此在构build期间将其添加到对象本身。
在使用Java,C#或C ++等经典inheritance的语言中,首先创build一个类(对象的蓝图),然后可以从该类创build新对象,或者扩展该类,定义一个新类原来的class级。
在JavaScript中,你首先创build一个对象(没有类的概念),那么你可以增加自己的对象或从中创build新的对象。 这并不困难,但有点外国,很难代谢习惯于古典的方式。
例:
//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();
我扮演一个JavaScript教师的angular色,原型概念一直是我教授的一个有争议的话题。 我花了一段时间想出一个很好的方法来澄清这个概念,现在我将在本文中试图解释JavaScript.prototype是如何工作的。
这是一个非常简单的基于原型的对象模型,在解释过程中将被视为一个样本,目前还没有评论:
function Person(name){ this.name = name; } Person.prototype.getName = function(){ console.log(this.name); } var person = new Person("George");
在通过原型概念之前,我们必须考虑一些关键点。
1- JavaScriptfunction如何实际工作:
为了迈出第一步,我们必须弄清楚,JavaScript函数是如何工作的,像函数使用this
关键字一样,或者只是作为一个常规的函数,它的参数,它做了什么,返回什么。
假设我们要创build一个Person
对象模型。 但是在这一步中,我将会试图做同样的事情,而不使用prototype
和new
关键字 。
所以在这一步functions
, objects
和this
关键字,都是我们拥有的。
第一个问题是如何使用this
关键字,而不使用new
关键字 。
所以要回答这个问题,假设我们有一个空的对象,并且有两个函数:
var person = {}; function Person(name){ this.name = name; } function getName(){ console.log(this.name); }
现在没有使用new
关键字 ,我们可以如何使用这些function。 所以JavaScript有三种不同的方式来做到这一点:
一个。 第一种方法就是将函数作为常规函数调用:
Person("George"); getName();//would print the "George" in the console
在这种情况下,这将是当前的上下文对象,通常是浏览器中的全局window
对象或Node.js
GLOBAL
。 这意味着我们可以在浏览器中使用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
,该对象的prototype
可以在名为__proto__
任何JavaScript对象中find,并且我试图在摘要部分对其进行解释。 所以我们可以通过这样做来得到相似的结果:
person.__proto__.Person = Person; person.__proto__.getName = getName;
但是这样我们实际上正在修改Object.prototype
,因为每当我们使用文字( { ... }
)创build一个JavaScript对象时,它都是基于Object.prototype
创build的,这意味着它被附加到新创build的对象作为名为__proto__
的属性,所以如果我们改变它,就像我们在前面的代码片段中所做的那样,所有的JavaScript对象都会被改变,这不是一个好习惯。 那么现在最好的做法是什么呢?
person.__proto__ = { Person: Person, getName: getName };
现在还有其他的东西是和平的,但似乎还不是一个好的做法。 所以我们还有一个解决scheme,但是要使用这个解决scheme,我们应该回到创buildperson
对象的代码行( var person = {};
),然后将其更改为:
var propertiesObject = { Person: Person, getName: getName }; var person = Object.create(propertiesObject);
它所做的是创build一个新的JavaScript Object
并将propertiesObject
附加到__proto__
属性。 所以要确保你能做到:
console.log(person.__proto__===propertiesObject); //true
但是棘手的一点是,您可以访问person
对象第一级__proto__
定义的所有属性(请参阅摘要部分以获取更多详细信息)。
正如你所看到的,使用这两种方法中的任何一种,都将完全指向person
。
C。 JavaScript有另一种方式来提供这个function,这是使用调用或应用来调用该函数。
apply()方法使用给定的这个值和作为一个数组(或类似数组的对象)提供的参数来调用一个函数。
和
call()方法使用给定的值和单独提供的参数来调用函数。
这是我最喜欢的方式,我们可以轻松地调用我们的function:
Person.call(person, "George");
要么
//apply is more useful when params count is not fixed Person.apply(person, ["George"]); getName.call(person); getName.apply(person);
这三种方法是找出.prototypefunction的重要初始步骤。
2- new
关键字如何工作?
这是理解.prototype
function的第二步。这是我用来模拟这个过程的:
function Person(name){ this.name = name; } my_person_prototype = { getName: function(){ console.log(this.name); } };
在这一部分中,我会尝试在使用new
关键字时,不使用new
关键字和prototype
,而是采取JavaScript所需的所有步骤。 所以当我们做new Person("George")
, Person
函数作为构造函数,这些就是JavaScript所做的一个一个的:
一个。 首先它会创build一个空对象,基本上是一个空的哈希,如:
var newObject = {};
湾 JavaScript采取的下一步是将所有原型对象附加到新创build的对象
我们在这里具有类似于原型对象的my_person_prototype
。
for(var key in my_person_prototype){ newObject[key] = my_person_prototype[key]; }
这不是JavaScript实际附加在原型中定义的属性的方式。 实际的方式与原型链概念有关。
一个。 &b。 而不是这两个步骤,你可以通过做同样的结果:
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"
现在我们可以在我们的my_person_prototype
调用getName
函数:
newObject.getName();
C。 那么它将该对象赋予构造函数,
我们可以用我们的例子来做到这一点:
Person.call(newObject, "George");
要么
Person.apply(newObject, ["George"]);
那么构造函数可以做任何想做的事情,因为这个构造函数的内部是刚创build的对象。
现在模拟其他步骤之前的最终结果:Object {name:“George”}
概要:
基本上,当你在一个函数上使用new关键字时,你正在调用这个函数,并且该函数作为构造函数,所以当你说:
new FunctionName()
JavaScript在内部创build一个对象,一个空的散列,然后将该对象赋予构造函数,然后构造函数可以做任何想要的事情,因为这个构造函数的内部是刚刚创build的对象,然后当然会给你这个对象如果你没有在你的函数中使用return语句,或者你没有return undefined;
一个return undefined;
在你的函数体的末尾。
所以当JavaScript去查找一个对象的属性时,它首先要做的就是在对象上查找它。 然后有一个秘密的属性[[prototype]]
,我们通常把它看作__proto__
而这个属性就是JavaScript接下来要看的东西。 当它通过__proto__
查看时,只要它又是另一个JavaScript对象,它有它自己的__proto__
属性,它会一直向上,直到它到达下一个__proto__
为空的点。 该点是JavaScript中唯一的对象__proto__
属性为null是Object.prototype
对象:
console.log(Object.prototype.__proto__===null);//true
这就是inheritance在JavaScript中的作用。
换句话说,当你在一个函数上有一个prototype属性,并且你调用了一个new时,在JavaScript完成查看新创build的对象的属性之后,它将查看函数的.prototype
,也可能是这个对象有自己的内部原型。 等等。
prototype
允许你做类。 如果你不使用prototype
那么它就成为一个静态的。
这是一个简短的例子。
var obj = new Object(); obj.test = function() { alert('Hello?'); };
在上面的例子中,你有静态函数调用testing。 这个函数只能通过obj.test来访问,你可以把obj想象成一个类。
在下面的代码中
function obj() { } obj.prototype.test = function() { alert('Hello?'); }; var obj2 = new obj(); obj2.test();
obj已经成为一个现在可以被实例化的类。 可以存在多个obj实例,它们都具有test
function。
以上是我的理解。 我把它变成一个社区wiki,所以如果我错了,人们可以纠正我。
读完这个线程之后,我觉得对JavaScript Prototype Chain感到困惑,于是我find了这些图表
http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance
通过原型链显示JavaScriptinheritance是一个清晰的图表
和
http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/
这个包含一个代码和几个漂亮的图的例子。
原型链最终回落到Object.prototype。
原型链可以在技术上延伸,只要你想要,每次通过设置子类的原型等于父类的对象。
希望对您了解JavaScript Prototype Chain也有帮助。
0)两种不同的东西可以被称为“原型”:
-
原型属性,如
obj.prototype
-
原型内部属性, 在ES5中表示为
[[Prototype]]
。它可以通过ES5
Object.getPrototypeOf()
。Firefox使得它可以通过
__proto__
属性作为扩展来访问。 ES6现在提到__proto__
一些可选要求。
1)存在这些概念来回答这个问题:
当我做
obj.property
,JS在哪里寻找.property
?
直觉上,经典的inheritance应该影响财产查询。
2)
-
__proto__
用于点.
属性查找在obj.property
。 -
.prototype
不直接用于查找,只是间接的,因为它确定__proto__
用new
创build对象。
查找顺序是:
- 使用
obj.p = ...
或Object.defineProperty(obj, ...)
添加obj
属性 -
obj.__proto__
属性 -
obj.__proto__.__proto__
属性等等 - 如果一些
__proto__
为null
,则返回undefined
。
这就是所谓的原型链 。
你可以避免.
用obj.hasOwnProperty('key')
和Object.getOwnPropertyNames(f)
3)设置obj.__proto__
有两种主要的方法:
-
new
:var F = function() {} var f = new F()
那么
new
设定:f.__proto__ === F.prototype
这是使用
.prototype
地方。 -
Object.create
:f = Object.create(proto)
集:
f.__proto__ === proto
4)代码:
var F = function() {} var f = new F()
对应于下图:
(Function) ( F ) (f) | ^ | | ^ | | | | | | | | | | | +-------------------------+ | | |constructor | | | | | | | +--------------+ | | | | | | | | | | | | | | |[[Prototype]] |[[Prototype]] |prototype |constructor |[[Prototype]] | | | | | | | | | | | | | | | | +----------+ | | | | | | | | | | | | +-----------------------+ | | | | | | v | vv | v (Function.prototype) (F.prototype) | | | | |[[Prototype]] |[[Prototype]] | | | | | +-------------------------------+ | | vv (Object.prototype) | | ^ | | | | | +---------------------------+ | | | | +--------------+ | | | | | | | |[[Prototype]] |constructor |prototype | | | | | | | | -------------+ | | | vv | (null) (Object)
该图显示了许多语言预定义的对象节点: null
, Object
, Object.prototype
, Function
和Function.prototype
。 我们的2行代码只创build了f
, F
和F.prototype
。
5) .constructor
通常来自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
,例如设置字段,非常类似于经典的OOP语言。
6)经典的inheritance语法可以通过操作原型链来实现。
ES6增加了class
并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 Cc = 1 Cc === 1 // Because `D.__proto__ === C`. Dc === 1 // Nothing makes this work. dc === undefined
没有所有预定义对象的简化图:
__proto__ (C)<---------------(D) (d) | | | | | | | | | |prototype |prototype |__proto__ | | | | | | | | | | | +---------+ | | | | | | | | | | vv |__proto__ (D.prototype) | | | | | | | | |__proto__ | | | | | | | | +--------------+ | | | | | | | vv | (C.prototype)--->(inc) | v Function.prototype
每个对象都有一个内部属性[[Prototype]],将其链接到另一个对象:
object [[Prototype]] -> anotherObject
有些环境将该属性公开为__proto__
:
anObject.__proto__ === anotherObject
创build对象时创build[[Prototype]]链接:
var object = Object.create(anotherObject) // imagine: // object.__proto__ = anotherObject // or (ES6 only): var object = { __proto__: anotherObject };
在传统的JavaScript中,链接的对象是一个函数的prototype
属性:
object [[Prototype]] -> aFunction.prototype
你用new
:创build[[Prototype]]链接
var object = new aFunction; // imagine: // object.__proto__ = aFunction.prototype;
所以这些陈述是相同的:
var object = Object.create(Object.prototype); var object = new Object; // object.__proto__ === Object.prototype
请注意,链接目标( Object.prototype
)仅出现在Object.create
调用中。
记得:
- 每个对象都有一个链接,[[Prototype]],有时暴露为
__proto__
。 - 每个函数都有一个
prototype
属性。 - 使用
new
创build的对象链接到其构造函数的prototype
属性。 - 如果一个函数从来没有被用作构造函数,那么它的
prototype
属性将不会被使用。 - 如果你不需要一个构造函数,那么跳过
new
并改用Object.create 。
Javascript没有通常意义上的inheritance,但它有原型链。
原型链
如果在对象中找不到对象的成员,则在原型链中查找它。 链由其他对象组成。 给定实例的原型可以通过__proto__
variables来访问。 每个对象都有一个,因为在javascript中的类和实例之间没有区别。
向原型添加一个函数/variables的好处是,它只能在内存中存储一次,而不是每个实例。
这对于inheritance也很有用,因为原型链可以由许多其他对象组成。
这篇文章很长。 但是我相信它会清除你关于JavaScriptinheritance的“原型”性质的大部分查询。 甚至更多。 请阅读完整的文章。
JavaScript主要有两种数据types
- 非对象
- 对象
非对象
以下是非对象数据types
- 串
- 数量(包括NaN和Infinity)
- 布尔值(true,false)
- 未定义
这些数据types在使用typeof运算符时会返回以下内容
typeof “string literal” (或者一个包含string文字的variables)=== 'string'
typeof 5 (或任何数字文字或包含数字文字或NaN或Infynity的variables)=== 'number'
typeof true (或false或包含true或false的variables)=== 'boolean'
typeof undefined (或未定义的variables或包含未定义的variables)=== 'undefined'
string , 数字和布尔型数据types可以表示为对象和非对象 。当它们被表示为对象时,它们的typeof总是==='object'。 一旦我们理解了对象数据types,我们就会回到这个。
对象
对象数据types可以进一步分为两种types
- 函数types的对象
- 非函数types的对象
函数types对象是用typeof运算符返回string“函数”的 对象 。 所有用户定义的函数以及可以使用new运算符创build新对象的所有内置JavaScript的对象都属于此类别。 例如。
- 目的
- 串
- 数
- 布尔
- 排列
- 键入的数组
- 正则expression式
- function
- 所有其他内置的对象可以通过使用new运算符来创build新的对象
- 函数 UserDefinedFunction (){/ *用户定义的代码* /}
因此, typeof(Object) === typeof(String) === typeof(Number) === typeof(Boolean) === typeof(Array) === typeof(RegExp) === typeof(Function) == = typeof(UserDefinedFunction) === 'function'
所有的Functiontypes对象实际上都是内build的JavaScript对象Function (包括Function对象,也就是recursion定义的)的实例。 就好像这些对象是按照以下方式定义的
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")
As mentioned, the Function type objects can further create new objects using the new operator . For eg an object of type Object , String , Number , Boolean , Array , RegExp Or UserDefinedFunction can be created by using
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()
The objects thus created are all Non Function type objects and return their typeof === 'object' . In all these cases the object "a" cannot further create objects using operator new. So the following is wrong
var b=new a() //error. a is not typeof==='function'
The built in object Math is typeof === 'object' . Hence a new object of type Math cannot be created by new operator.
var b=new Math() //error. Math is not typeof==='function'
Also notice that Object , Array and RegExp functions can create a new object without even using operator new . However the follwing ones don't.
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'
The user defined functions are special case.
var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.
Since the Function type objects can create new objects they are also called Constructors .
Every Constructor/Function (whether built in or user defined) when defined automatically has a property called "prototype" whose value by default is set as an object. This object itself has a property called "constructor" which by default references back the Constructor/Function .
For example when we define a function
function UserDefinedFunction() { }
following automatically happens
UserDefinedFunction.prototype={constructor:UserDefinedFunction}
This "prototype" property is only present in the Function type objects (and never in Non Function type objects ).
This is because when a new object is created (using new operator)it inherits all properties and methods from Constructor function's current prototype object ie an internal reference is created in the newly created object that references the object referenced by Constructor function's current prototype object.
This "internal reference" that is created in the object for referencing inherited properties is known as the object's prototype (which references the object referenced by Constructor's "prototype" property but is different from it). For any object (Function or Non Function) this can be retrieved using Object.getPrototypeOf() method. Using this method one can trace the prototype chain of an object.
Also, every object that is created ( Function type or Non Function type ) has a "constructor" property which is inherited from the object referenced by prototype property of the Constructor function. By default this "constructor" property references the Constructor function that created it (if the Constructor Function's default "prototype" is not changed).
For all Function type objects the constructor function is always function Function(){}
For Non Function type objects (eg Javascript Built in Math object) the constructor function is the function that created it. For Math object it is function Object(){} .
All the concept explained above can be a little daunting to understand without any supporting code. Please go through the following code line by line to understand the concept. Try to execute it to have a better understanding.
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"
The prototype chain of every object ultimately traces back to Object.prototype (which itself does not have any prototype object) . Following code can be used for tracing the prototype chain of an object
var o=Starting object; do { alert(o + "\n" + Object.getOwnPropertyNames(o)) }while(o=Object.getPrototypeOf(o))
The prototype chain for various objects work out as follows.
- Every Function object (including built in Function object)-> Function.prototype -> Object.prototype -> null
- Simple Objects (created By new Object() or {} including built in Math object)-> Object.prototype -> null
- Object created with new or Object.create -> One or More prototype chains -> Object.prototype -> null
For creating an object without any prototype use the following:
var o=Object.create(null) alert(Object.getPrototypeOf(o)) //Displays null
One might think that setting the prototype property of the Constructor to null shall create an object with a null prototype. However in such cases the newly created object's prototype is set to Object.prototype and its constructor is set to function Object. This is demonstrated by the following code
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
Following in the summary of this article
- There are two types of objects Function types and Non Function types
-
Only Function type objects can create a new object using the operator new . The objects thus created are Non Function type objects. The Non Function type objects cannot further create an object using operator new .
-
All Function type objects by default have a "prototype" property. This "prototype" property references an object that has a "constructor" property that by default references the Function type object itself.
-
All objects ( Function type and Non Function type ) have a "constructor" property that by default references the Function type object / Constructor that created it.
-
Every object that gets created internally references the object referenced by "prototype" property of the Constructor that created it. This object is known as the created object's prototype (which is different from Function type objects "prototype" property which it references) . This way the created object can directly access the methods and properties defined in object referenced by the Constructor's "prototype" property (at the time of object creation).
-
An object's prototype (and hence its inherited property names) can be retrieved using the Object.getPrototypeOf() method. In fact this method can be used for navigating the entire prototype chain of the object.
-
The prototype chain of every object ultimately traces back to Object.prototype (Unless the object is created using Object.create(null) in which case the object has no prototype).
-
typeof(new Array())==='object' is by design of language and not a mistake as pointed by Douglas Crockford
-
Setting the prototype property of the Constructor to null(or undefined,number,true,false,string) shall not create an object with a null prototype. In such cases the newly created object's prototype is set to Object.prototype and its constructor is set to function Object.
希望这可以帮助。
The concept of prototypal
inheritance is one of the most complicated for many developers. Let's try to understand the root of problem to understand prototypal inheritance
better. Let's start with a plain
function.
If we use a new
operator on the Tree function
, we call it as a constructor
function.
Every JavaScript
function has a prototype
. When you log the Tree.prototype
, you get…
If you look at the above console.log()
output, you could a see a constructor property on Tree.prototype
and a __proto__
property too. The __proto__
represents the prototype
that this function
is based off, and since this is just a plain JavaScript function
with no inheritance
set up yet, it refers to the Object prototype
which is something just built in to JavaScript…
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
This has things like .toString, .toValue, .hasOwnProperty
etc…
__proto__
which was brought my mozilla is deprecated and is replaced by Object.getPrototypeOf
method to get the object's prototype
.
Object.getPrototypeOf(Tree.prototype); // Object {}
Let's add a method to our Tree
prototype
.
We have modified the Root
and added a function
branch to it.
That means when you create an instance
of Tree
, you can call it's branch
method.
We can also add primitives
or objects
to our Prototype
.
Let's add a child-tree
to our Tree
.
Here the Child
inherits its prototype
from Tree, what we are doing here is using Object.create()
method to create a new object based off what you pass, here it is Tree.prototype
. In this case what we're doing is setting the prototype of Child to a new object that looks identical to the Tree
prototype. Next we are setting the Child's constructor to Child
, if we don't it would point to Tree()
.
Child
now has its own prototype
, its __proto__
points to Tree
and Tree's prototype
points to base Object
.
Child | \ \ Tree.prototype - branch | | \ \ Object.prototype -toString -valueOf -etc., etc.
Now you create an instance
of Child
and call branch
which is originally available in Tree
. We haven't actually defined our branch
on the Child prototype
. BUT, in the Root prototype
which Child inherits from.
In JS everything is not an object, everything can act like an object.
Javascript
has primitives like strings, number, booleans, undefined, null.
They are not object(ie reference types)
, but certainly can act like an object
. Let's look at an example here.
In the first line of this listing, a primitive
string value is assigned to name. The second line treats name like an object
and calls charAt(0)
using dot notation.
This is what happens behind the scenes: // what the JavaScript
engine does
The String object
exists only for one statement before it's destroyed (a process called autoboxing
). Let's again get back to our prototypal
inheritance
.
-
Javascript
supports inheritance viadelegation
based onprototypes
. - Each
Function
has aprototype
property, which refers to another object. -
properties/functions
are looked from theobject
itself or viaprototype
chain if it does not exist
A prototype
in JS is an object which yields
you to the parent of another object
. [ie.. delegation] Delegation
means that if you are unable to do something, you'll tell someone else to do it for you.
https://jsfiddle.net/say0tzpL/1/
If you look up the above fiddle, dog has access to toString
method, but its not available in it, but available via the prototype chain which delegates to Object.prototype
If you look at the below one, we are trying to access the call
method which is available in every function
.
https://jsfiddle.net/rknffckc/
If you look up the above fiddle, Profile
Function has access to call
method, but its not available in it, but available via the prototype chain which delegates to Function.prototype
Note: prototype
is a property of the function constructor, whereas __proto__
is a property of the objects constructed from the function constructor. Every function comes with a prototype
property whose value is an empty object
. When we create an instance of the function, we get an internal property [[Prototype]]
or __proto__
whose reference is the prototype of the Function constructor
.
The above diagram looks bit complicated, but brings out the whole picture on how prototype chaining
works. Let's walk through this slowly:
There are two instance b1
and b2
, whose constructor is Bar
and parent is Foo and has two methods from prototype chain identify
and speak
via Bar
and Foo
https://jsfiddle.net/kbp7jr7n/
If you look up the code above, we have Foo
constructor who has the method identify()
and Bar
constructor which has speak
method. We create two Bar
instance b1
and b2
whose parent type is Foo
. Now while calling speak
method of Bar
, we are able to identify the who is calling the speak via prototype
chain.
Bar
now has all the methods of Foo
which are defined in its prototype
. Let's dig further in understanding the Object.prototype
and Function.prototype
and how they are related. If you look up the constructor of Foo
, Bar
and Object
are Function constructor
.
The prototype
of Bar
is Foo
, prototype
of Foo
is Object
and if you look closely the prototype
of Foo
is related to Object.prototype
.
Before we close this down, let's just wrap with a small piece of code here to summarize everything above . We are using instanceof
operator here to check whether an object
has in its prototype
chain the prototype
property of a constructor
which below summarizes the entire big diagram.
I hope this add's some information, I know this kinda could be big to grasp… in simple words its it's just objects linked to objects!!!!
what is the exact purpose of this ".prototype" property?
The interface to standard classes become extensible. For example, you are using the Array
class and you also need to add a custom serializer for all your array objects. Would you spend time coding up a subclass, or use composition or … The prototype property solves this by letting the users control the exact set of members/methods available to a class.
Think of prototypes as an extra vtable-pointer. When some members are missing from the original class, the prototype is looked up at runtime.
The Definitive Guide to Object-Oriented JavaScript – a very concise and clear ~30min video explanation of the asked question (Prototypal Inheritance topic begins from 5:45 , although I'd rather listen to the whole video). The author of this video also made JavaScript object visualizer website http://www.objectplayground.com/ .
It may help to categorise prototype chains into two categories.
Consider the constructor:
function Person() {}
The value of Object.getPrototypeOf(Person)
is a function. In fact, it is Function.prototype
. Since Person
was created as a function, it shares the same prototype function object that all functions have. It is the same as Person.__proto__
, but that property should not be used. Anyway, with Object.getPrototypeOf(Person)
you effectively walk up the ladder of what is called the prototype chain.
The chain in upward direction looks like this:
Person
→ Function.prototype
→ Object.prototype
(end point)
Important is that this prototype chain has little to do with the objects that Person
can construct . Those constructed objects have their own prototype chain, and this chain can potentially have no close ancestor in common with the one mentioned above.
Take for example this object:
var p = new Person();
p has no direct prototype-chain relationship with Person . Their relationship is a different one. The object p has its own prototype chain. Using Object.getPrototypeOf
, you'll find the chain is as follows:
p
→ Person.prototype
→ Object.prototype
(end point)
There is no function object in this chain (although that could be).
So Person
seems related to two kinds of chains, which live their own lives. To "jump" from one chain to the other, you use:
-
.prototype
: jump from the constructor's chain to the created-object's chain. This property is thus only defined for function objects (asnew
can only be used on functions). -
.constructor
: jump from the created-object's chain to the constructor's chain.
Here is a visual presentation of the two prototype chains involved, represented as columns:
To summarise:
The
prototype
property gives no information of the subject's prototype chain, but of objects created by the subject.
It is no surprise that the name of the property prototype
can lead to confusion. It would maybe have been clearer if this property had been named prototypeOfConstructedInstances
or something along that line.
You can jump back and forth between the two prototype chains:
Person.prototype.constructor === Person
This symmetry can be broken by explicitly assigning a different object to the prototype
property (more about that later).
Create one Function, Get Two Objects
Person.prototype
is an object that was created at the same time the function Person
was created. It has Person
as constructor, even though that constructor did not actually execute yet. So two objects are created at the same time:
- The function
Person
itself - The object that will act as prototype when the function is called as a constructor
Both are objects, but they have different roles: the function object constructs , while the other object represents the prototype of any object that function will construct. The prototype object will become the parent of the constructed object in its prototype chain.
Since a function is also an object, it also has its own parent in its own prototype chain, but recall that these two chains are about different things.
Here are some equalities that could help grasp the issue — all of these print 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);
I found it helpful to explain the "prototype chain" as recursive convention when obj_n.prop_X
is being referenced:
if obj_n.prop_X
doesn't exist, check obj_n+1.prop_X
where obj_n+1 = obj_n.[[prototype]]
If the prop_X
is finally found in the k-th prototype object then
obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X
You can find a graph of the relation of Javascript objects by their properties here:
When a constructor creates an object, that object implicitly references the constructor's “prototype” property for the purpose of resolving property references. The constructor's “prototype” property can be referenced by the program expression constructor.prototype, and properties added to an object's prototype are shared, through inheritance, by all objects sharing the prototype.
Let me tell you my understanding of prototypes. I am not going to compare the inheritance here with other languages. I wish people would stop comparing languages, and just understand the language as itself. Understanding prototypes and prototypal inheritance is so simple, as I will show you below.
Prototype is like a model, based on which you create a product. The crucial point to understand is that when you create an object using another object as it's prototype, the link between the prototype and the product is ever-lasting. 例如:
var model = {x:2}; var product = Object.create(model); model.y = 5; product.y =>5
Every object contains an internal property called the [[prototype]], which can be accessed by the Object.getPrototypeOf()
function. Object.create(model)
creates a new object and sets it's [[prototype]] property to the object model . Hence when you do Object.getPrototypeOf(product)
, you will get the object model .
Properties in the product are handled in the following way:
- When a property is accessed to just read it's value, its looked up in the scope chain. The search for the variable starts from the product upwards to it's prototype. If such a variable is found in the search, the search is stopped right there, and the value is returned. If such a variable cannot be found in the scope chain, undefined is returned.
- When a property is written(altered), then the property is always written on the product object. If the product does not have such a property already, it is implicitly created and written.
Such a linking of objects using the prototype property is called prototypal inheritance. There, it is so simple, agree?
Another attempt to explain JavaScript prototype-based inheritance with better pictures
Consider the following keyValueStore
object :
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; } }; })();
I can create a new instance of this object by doing this :
kvs = keyValueStore.create();
Each instance of this object would have the following public properties :
-
data
-
get
-
set
-
delete
-
getLength
Now, suppose we create 100 instances of this keyValueStore
object. Even though get
, set
, delete
, getLength
will do the exact same thing for each of these 100 instances, every instance has its own copy of this function.
Now, imagine if you could have just a single get
, set
, delete
and getLength
copy, and each instance would reference that same function. This would be better for performance and require less memory.
That's where prototypes come in. A prototype is a "blueprint" of properties that is inherited but not copied by instances. So this means that it exists only once in memory for all instances of an object and is shared by all of those instances.
Now, consider the keyValueStore
object again. I could rewrite it like this :
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; } }; })();
This does EXACTLY the same as the previous version of the keyValueStore
object, except that all of its methods are now put in a prototype. What this means, is that all of the 100 instances now share these four methods instead of each having their own copy.
I always like analogies when it comes to understand this type of stuff. 'Prototypal inheritance' is pretty confusing in comparison to class bass inheritance in my opinion, even though prototypes are much simpler paradigm. In fact there really is no inheritance, so the name in and of itself misleading, it's more like prototypal delegation.
Imagine this ….
You're in high-school, and you're in class and have a test that's due today, but you don't have a pen to complete it. Ohshi-
You have a best friend, Finnius Mcdinglebutt who might possibly have a pen? You ask, and he looks on his desk unsuccessfully, but instead of saying "Man, I don't have a pen", he's a nice friend he checks with his other friend Derp Derpington if he has a pen. Derp does have a pen and passes it back to Finnius, who passes it over to you to complete your test. Derp has entrusted the pen to Finnius, who has delegated the pen to you for use.
This, is essentially how prototypes work in a nutshell.
:~]