Inheritance in JavaScript is rather weird. Interface inheritance cannot be implemented and can only rely on prototypal inheritance.
Prototype Chain
A prototype is an object. The instance created through the constructor will have a pointer pointing to the prototype to obtain the properties and methods of the prototype. In this way, the instance object has the attribute methods of the constructor and the attribute methods of the prototype. Then point the prototype of the constructor that needs to be inherited to this instance, and you can have all the attribute methods of this instance to achieve inheritance.
Look at the demo code below:
//声明超类,通过构造函数和原型添加有关属性和方法 function Super(){ this.property = true; } Super.prototype.getSuperValue = function() { return this.property; }; //声明子类的构造函数 function SubType() { this.subproperty = false; } //将子类的原型指向超类的实例,得到超类的一切 SubType.prototype = new Super(); SubType.prototype.constructor = SubType; SubType.prototype.getSubValue = function(){ return this.subproperty; }; //由子类创建对象,测试是否继承超类方法和属性 var instance = new SubType(); console.log(instance.getSuperValue());
The default prototype of all functions is an instance of Object, so the default prototype will contain an internal pointer pointing to Object.prototype.
Use instanceof and isPrototypeOf to determine the relationship between prototype and instance:
instance instanceof Object; Object.prototype.isPrototypeOf(instance);
When using the prototype chain, you need to define the method carefully. If a subclass needs to override a method or extension of the supertype, it must be placed after the statement that replaces the prototype, so that it can take effect. In addition, when implementing inheritance through the prototype chain, you cannot use object literals to create prototype methods, which will override the prototype chain:
...... SubType.prototype = new Super(); SubType.prototype = { .... };
This will replace the pointer pointing to the new object, thus rewriting the prototype chain.
The inheritance method of the prototype chain is flawed. There are two main problems:
1. From a prototype containing a reference type value, it will be shared by all instances.
As mentioned in the previous article, prototype properties containing reference type values will be shared by all instances. If one instance is modified, other instances will change accordingly, so the properties need to be defined in the constructor. When inheriting through the prototype chain, no matter whether the attributes in the super class are defined in the constructor or the prototype, they all become instance objects and are inherited by the subclass, thus affecting the instances of the subclass.
2. When creating an instance of a subtype, parameters cannot be passed to the constructor of the supertype.
Inheritance of the prototype chain directly points the subclass prototype to the instance of the superclass. At this time, parameters can be passed to the superclass. But when a subclass creates an instance, parameters can only be passed to the constructor of the subclass, but not to the constructor of the superclass.
Therefore, in actual applications, the prototype chain is rarely used alone.
Some related coding practices
Identify a prototype attribute
function hasPrototypeProperty(object, name) { return name in object && !object.hasOwnProperty(name); }
Use prototype objects in constructors
function Person(name) { this.name = name; } Person.prototype = { constructor: Person, sayName: function () { console.log(this.name); }, toString: function() { } }; var person1 = new Person('Nicholas'); var person2 = new Person('Greg); console.log(person1 instanceof Person); // true console.log(person1.constructor === Person); // true console.log(person1.constructor === Object); // false console.log(person2 instanceof Person); // true console.log(person2.constructor === Person); // true console.log(person2.constructor === Object); // false
Object inheritance
var person1 = { name: 'Nicholas', sayName: function () { console.log(this.name); } }; var person2 = Object.create(person1, { name: { configurable: true, enumerable: true, value: 'Greg', writable: true } }); person1.sayName(); // Nicholas person2.sayName(); // Greg console.log(person1.hasOwnProperty('sayName')); // true console.log(person1.isPropertyOf(person2)); // true console.log(person2.hasOwnProperty('sayName')); // false
Module Mode
var person = (function () { var age = 25; function getAge() { return age; } function growOlder() { age++; } return { name: 'Nicholas', getAge: getAge, growOlder: growOlder }; }());
Scope constructor
function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this.name); }; var person1 = Person('Nicholas'); console.log(person1 instanceof Person); // false console.log(typeof person1); // undefined console.log(name); // Nicholas