Every object in JavaScript has a built-in _proto_ attribute. This attribute is invisible to programming. It is actually a function of the other objects. An object or a reference to null.
#When an object references an attribute, the JavaScript engine will first search it from the object's own attribute table. If found, it will perform corresponding read and write operations. If it is not found in its own attribute table, search in the attribute table of the object referenced by the _proto_
attribute, and so on until this attribute or _proto_ is found. The
attribute points to null
.
The reference chain of _proto_
is called the prototype chain .
Note that there is a performance optimization problem here: the deeper you search in the prototype chain, the more time it takes.
JavaScript is an object-oriented language and can Prototypal inheritance.
The function in JavaScript has an attribute prototype
, this prototype
attribute is an object, one of its attributes constructor
References the function itself. That is:
func.prototype.constructor === func; // ==> true
What is the use of this attribute? We know that when a function is called using the new
operator, it will return a new object as a constructor. The _proto_
property of this object refers to the prototype
property of its constructor.
So this is not difficult to understand:
var obj = new Func(); obj.constructor == Func; // ==> true
There is also this:
obj instanceof Func; // ==> true
It is also by looking up the prototype chain Implemented by the constructor
attribute.
The _proto_
attributes of different instances generated by the constructor are references to the same prototype
object. So modifying the prototype
object will affect all instances.
The reason why subclasses need to be quoted is because the concept of "class" is not rigorous here. JavaScript is an object-oriented language, but unlike Java and other languages, it does not have class definitions before the ES6 standard is released.
But programmers who are familiar with languages such as Java also hope that when using JavaScript, they can generate instances through classes and reuse code through subclasses, similar to using Java. So before ES6, how to use a "class"-like method like the following code?
var parent = new Parent("Sam");var child = new Children("Samson"); parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"child instanceof Parent; // ==> true
We see that here we use the constructor as a class.
Let’s discuss several ways to implement it:
Combined with the concept of prototype chain, we can easily write such code:
function Parent(name){this.name = name; } Parent.prototype.say = function(){ console.log("Hello, " + this.name + "!"); }function Children(name){this.name = name; } Children.prototype = new Parent(); Children.prototype.say = function(){ console.log("Hello, " + this.name + "! hoo~~"); }
The disadvantage of this method is obvious: as a constructor of a subclass, it needs to rely on an object of the parent class. The properties name
in this object are completely useless.
// ...Children.prototype = Parent.prototype;// ...
So that useless parent class attributes will not be generated.
However, in this case, the prototypes of the subclass and the parent class refer to the same object, and modifying the prototype
of the subclass will also affect the prototype of the parent class.
At this time we discovered:
parent.say(); // ==> "Hello,Sam! hoo~~"
This first improvement is worse than no change at all.
function F(){ // empty } F.prototype = Parent.prototype; Children.prototype = new F();// ...parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"
In this way, modifying the prototype of the subclass is just Modified the properties of a instance
of F without changing Parent.prototype
, thus solving the above problem.
In the era of ES5, we can also do this directly:
Children.prototype = Object.create(Parent.prototype);
这里的思路是一样的,都是让子类的prototype
不直接引用父类prototype
。目前的现代浏览器几乎已经添加了对这个方法的支持。(但我们下面会仍以临时构造函数为基础)
但是细细思考,这个方案仍有需要优化的地方。例如:如何让父类的构造函数逻辑直接运用到子类中,而不是再重新写一遍一样的?这个例子中只有一个name
属性的初始化,那么假设有很多属性且逻辑一样的话,岂不是没有做到代码重用?
使用apply/call
,实现“方法重用”的思想。
function Children(name){ Parent.apply(this, arguments);// do other initial things}
现在完整的代码如下:
function Parent(name){this.name = name; } Parent.prototype.say = function(){ console.log("Hello, " + this.name + "!"); }function Children(name){ Parent.apply(this, arguments);// do other initial things}function F(){ // empty } F.prototype = Parent.prototype; Child.prototype = new F(); Children.prototype.say = function(){ console.log("Hello, " + this.name + "! hoo~~"); }
这就是所谓“圣杯”模式,听着很高大上吧?
以上就是ES3的时代,我们用来实现原型继承的一个近似最佳实践。
“圣杯”模式依然存在一个问题:虽然父类和子类实例的继承的prototype
对象不是同一个实例,但是这两个prototype
对象上面的属性引用了同样的对象。
假设我们有:
Parent.prototype.a = { x: 1};// ...
那么即使是“圣杯”模式下,依然会有这样的问题:
parent.x // ==> 1child.x // ==> 1child.x = 2; parent.x // ==>2
问题在于,JavaScript的拷贝不是 深拷贝(deepclone)
要解决这个问题,我们可以利用属性递归遍历,自己实现一个深拷贝的方法。这个方法在这里我就不写了。
ES6极大的支持了工程化,它的标准让浏览器内部实现类和类的继承:
class Parent { constructor(name) { //构造函数 this.name = name; } say() { console.log("Hello, " + this.name + "!"); } } class Children extends Parent { constructor(name) { //构造函数super(name); //调用父类构造函数// ... } say() { console.log("Hello, " + this.name + "! hoo~~"); } }
从此走上强类型的不归路。。。
上张顿悟图
什么?还不明白?!麻烦出门左拐。推荐阮老师JavaScript万物诞生记。
The above is the detailed content of Detailed explanation of JavaScript prototype and inheritance examples. For more information, please follow other related articles on the PHP Chinese website!