For developers who are familiar with class-based object-oriented languages (Java or C + +), the syntax for JavaScript is rather bizarre, because JavaScript is a dynamic language and it does not 类的概念 (ES6 adds the class keyword, but only the syntax sugar, JavaScript is still based on prototypes).
Involves inheriting this piece, Javascript has only one structure, that is: object. In JavaScript, each object has an internal link to its prototype (prototype) object. The prototype object has its own prototype until the prototype of an object null (i.e. no more prototypes) is formed, forming the last ring of the chain. This first-level chain structure is called the prototype chain (prototype chain).
Although prototype inheritance is often seen as a weakness of JavaScript, in fact, the prototype inheritance model is more powerful than the classic inheritance model. For example, it is fairly easy to build a typical model at the top of the prototype model.
Inheritance inheritance properties based on the prototype chain
A JavaScript object is a dynamic property "package" (referring to its own property). A JavaScript object has a chain that points to a prototype object. When attempting to access the properties of an object, it searches not only on the object, but also on the prototype of the object, as well as the prototype of the object, which is searched upward until a matching property of the name is found or at the end of the prototype chain.
According to ECMAScript Standard, someobject. The [[Prototype]] symbol is a prototype for assigning someobject. This is equivalent to JavaScript's __proto__ attribute (now deprecated). Starting with ECMAScript 6, [[Prototype]] can be accessed using object.getprototypeof () and object.setprototypeof () accessors.
The following code demonstrates the behavior that occurs when a property of an object is accessed:
//Suppose there is an object o whose own attributes (own properties) have A and B://{a:1, b:2}//O's prototype O.[[prototype]] has properties B and C://{b:3, c:4}//Finally, O.[[prototype]]. [[[Prototype]] is null.//This is the end of the prototype chain, which is null,//by definition, NULL is not [[Prototype]].//in summary, the entire prototype chain is as follows://{a:1, b:2}---> {b:3, c:4}---> NullConsole.log (O.A);//1//is a is O's own property? Yes, the value of this property is 1Console.log (O.B);//2//B is O's own property? Yes, the value of this property is 2//O.[[prototype]] also has a ' B ' property on it, but it is not accessed. This is referred to as "attribute shadowing (property shadowing)".Console.log (O.C);//4//is c the self-attribute of O? No, then look at O. [[Prototype]] there is no.//c is O. [[Prototype]] 's own property? Yes, the value of this property is 4Console.log (O.D);//undefined//is d a self-attribute of O? No, then look at O. [[Prototype]] there is no.//D is O. [[Prototype]] 's own property? No, then look at O. [[Prototype]]. [[Prototype]] there is no.//O.[[prototype]]. [[[Prototype]] is null, stop the search,//no d attribute, return undefined
The way to create an object with its own properties is to set the properties of the object. The only exception to get and set the rules of behavior is when there is a getter or a setter that is set to an inherited property.
Inheritance method
JavaScript does not have "methods" defined by other class-based languages. In JavaScript, any function can be added to an object as a property of an object. The inheritance of a function is no different from other property inheritance, including the above "attribute shadowing" (which is equivalent to a method override in another language).
When an inherited function is called, this it points to the object that is currently inheriting, not the prototype object where the inherited function resides.
varo ={A:2, M:function(){ return This. A + 1; }};console.log (O.M ()); //3//when O.M is called, ' this ' points to O.varp =object.create (o);//p is an object, P.[[prototype]] is O.P.a= 12;//Create the Self attribute of p a.Console.log (p.m. ());// -//when you call p.m., ' This ' points to p.//and because p inherits the M function of O//the ' this.a ' at this point is p.a, that is, P's own property ' a 'Use different methods to create objects and build prototype chains use normal syntax to create objects
varo = {a:1};//o This object inherits all the attributes above the Object.prototype//so you can use O.hasownproperty (' a ') this way.//hasOwnProperty is the property of Object.prototype itself. //the Object.prototype prototype is null. //The prototype chain is as follows://o---> object.prototype---> NullvarA = ["Yo", "whadup", "?"];//arrays are inherited from Array.prototype//(IndexOf, foreach, and so on are inherited from it).//The prototype chain is as follows://a---> array.prototype---> Object.prototype---> Nullfunctionf () {return2;}//functions are inherited from Function.prototype//(call, bind, and so on are inherited from it)://f---> Function.prototype---> Object.prototype---> NullTo create an object using the constructor
In JavaScript, the constructor is actually a normal function. When you use the new operator to function, it can be called a construction method (constructor).
function Graph () { this. vertexes = []; this. Edges == { function(v) { this. Vertexes.push (v); }}; var New Graph (); // g is the generated object, and his own attributes are ' vertexes ' and ' edges '. // when G is instantiated, G.[[prototype]] points to graph.prototype.
Create an object using Object.create
A new approach has been introduced in ECMAScript 5: Object.create() . You can call this method to create a new object. The prototype of a new object is the create first parameter passed in when the method is called:
varA = {a:1}; //a---> object.prototype---> Nullvarb =Object.create (a);//b---> A---> object.prototype---> NullConsole.log (B.A);//1 (inherited)varc =Object.create (b);//c---> B---> A---> object.prototype---> NullvarD = object.create (NULL);//d---> nullConsole.log (D.hasownproperty);//undefined, because D did not inherit Object.prototype
Use
classKey words
ECMAScript6 introduces a new set of keywords to implement class. Developers who use class-based languages are familiar with these constructs, but they are not the same. JavaScript is still prototype-based. These new keywords include class , constructor , static , extends , and Super .
' Use strict '; class Polygon { constructor (height, width) { this. Height = height; this. width = width; }} Class Square extends Polygon { Constructor (sidelength) { super (sidelength, sidelength); } Get Area () { returnThis this. width; } Set Sidelength (newlength) { this. Height = newlength; this. width = newlength; }} var New Square (2);
Performance
Finding properties on the prototype chain is time consuming and has side effects on performance, which is important in demanding performance situations. In addition, attempting to access an attribute that does not exist traverses the entire prototype chain.
each enumerable property on the prototype chain is enumerated as it iterates through the properties of the object.
It is necessary to use the hasOwnProperty method, all inherited from, to detect whether the object's properties are defined on itself or on the prototype chain Object.proptotype 的对象都包含这个方法 .
hasOwnProperty is the only method in JavaScript that involves only the object's own properties and does not traverse the prototype chain.
Note: Just by judging whether a value is undefined is not enough to detect whether a property exists, an attribute may exist and its value is exactly undefined.
Bad practice: Extending prototypes of Native objects
A frequently used error practice is to extend Object.prototype or prototype other built-in objects.
This technique is known as monkey patching, which destroys the sealing of the prototype chain. Although some popular frameworks, such as prototype.js, are using the technology, there is no good reason to confuse the built-in type system with other nonstandard methods.
The only reason we are going to extend the built-in object prototypes is to introduce some new features of the new JavaScript engine, such as Array.forEach .
Example
BWill inherit fromA:
functionA (a) { This. VarA =A;}//in the definition of function A above, since A.prototype.vara is always obscured by This.vara,//So what is the purpose of adding VarA to the prototype (prototype)? A.prototype ={varA:NULL,//since it has no effect, why not remove the VarA from the prototype (prototype)? //perhaps as a consideration for optimizing the allocation of space in a hidden class? //https://developers.google.com/speed/articles/optimizing-javascript#Initializing Instance Variables //Will verify what happens if VarA is not specifically initialized in each instance. DoSomething:function(){ // ... }}functionB (A, B) {A.call ( This, a); This. Varb =b;} B.prototype=object.create (A.prototype, {varb: {value:NULL, Enumerable:true, Configurable:true, writable:true}, DoSomething: {value:function(){//OverrideA.prototype.dosomething.apply ( This, arguments);//Call Super // ...}, Enumerable:true, Configurable:true, writable:true }}); B.prototype.constructor=B;varb =NewB (); b.dosomething ();
最重要的部分是:
类型被定义在 .prototype 中
而你用 Object.create() 来继承
prototype 和 Object.getPrototypeOf
对于从 Java 或 C ++ 转过来的开发人员来说 JavaScript 会有点让人困惑,因为它全部都是动态的,都是运行时,而且不存在类(classes)。所有的都是实例(对象)。即使我们模拟出的 “类(classes)”,也只是一个函数对象。
你可能已经注意到,我们的函数 A 有一个特殊的属性叫做原型。这个特殊的属性与 JavaScript 的 new 运算符一起工作。对原型对象的引用会复制到新实例内部的 [[Prototype]] 属性。例如,当你这样: var a1 = new A(), JavaScript 就会设置:a1.[[Prototype]] = A.prototype(在内存中创建对象后,并在运行 this 绑定的函数 A()之前)。然后在你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象中(即是否是该对象的自身属性),如果不是,它会在 [[Prototype]] 中查找。也就是说,你在原型中定义的元素将被所有实例共享,甚至可以在稍后对原型进行修改,这种变更将影响到所有现存实例。
像上面的例子中,如果你执行 var a1 = new A(); var a2 = new A(); 那么 a1.doSomething 事实上会指向Object.getPrototypeOf(a1).doSomething,它就是你在 A.prototype.doSomething 中定义的内容。比如:Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething。
简而言之, prototype 是用于类型的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。
[[Prototype]] 看起来就像递归引用, 如a1.doSomething,Object.getPrototypeOf(a1).doSomething,Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething 等等等, 直到它找到 doSomething 这个属性或者 Object.getPrototypeOf 返回 null。
因此,当你执行:
var o = new Foo ();
JavaScript actually performs the following:
var New = foo.prototype; Foo.call (o);
(or something like the one above), and then when you do:
O.someprop;
它会检查是否存在 someProp 属性。如果没有,它会查找Object.getPrototypeOf(o).someProp 仍旧没有,它会继续查找Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp ,一直查找下去,直到它找到这个属性 或者 Object.getPrototypeOf() 返回 null 。
结论
在用原型继承编写复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。此外,除非为了兼容新 JavaScript 特性,否则,永远不要扩展原生的对象原型。
JavaScript inheritance and prototype chain