Home  >  Article  >  Web Front-end  >  Basics of JavaScript inheritance (prototype chain, borrowed constructor, mixed mode, prototypal inheritance, parasitic inheritance, parasitic combined inheritance)_javascript skills

Basics of JavaScript inheritance (prototype chain, borrowed constructor, mixed mode, prototypal inheritance, parasitic inheritance, parasitic combined inheritance)_javascript skills

WBOY
WBOYOriginal
2016-05-16 16:39:321355browse

I promised to explain JavaScript inheritance, but it has been delayed until now. Without further ado, let’s get straight to the point.

Since you want to understand inheritance, it proves that you already have a certain understanding of object-oriented JavaScript. If you still don’t understand anything, you can refer to "Basic explanation of object-oriented JS, factory mode, constructor mode, prototype mode, Mixed mode, dynamic prototype mode 》, let’s talk about the methods generally used to complete JavaScript inheritance.

 Prototype chain

The simplest way to implement inheritance in JavaScript is to use the prototype chain. Just point the prototype of the subtype to the instance of the parent type, that is, "subtype.prototype = new parent type ();". The implementation method is as follows:

// 为父类型创建构造函数
function SuperType() {
  this.name = ['wuyuchang', 'Jack', 'Tim'];
  this.property = true;
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
  return this.property;
}

// 为子类型创建构造函数
function SubType() {
  this.test = ['h1', 'h2', 'h3', 'h4'];
  this.subproperty = false;
}

// 实现继承的关键步骤,子类型的原型指向父类型的实例
SubType.prototype = new SuperType();

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
  return this.subproperty;
}


/* 以下为测试代码示例 */
var instance1 = new SubType();
instance1.name.push('wyc');
instance1.test.push('h5');
alert(instance1.getSuerperValue());    // true
alert(instance1.getSubValue());      // false
alert(instance1.name);          // wuyuchang,Jack,Tim,wyc
alert(instance1.test);          // h1,h2,h3,h4,h5


var instance2 = new SubType();
alert(instance2.name);          // wuyuchang,Jack,Tim,wyc
alert(instance2.test);          // h1,h2,h3,h4

You can see that the above code is a simple inheritance implemented through the prototype chain, but there are still some problems in the test code example. I believe that children who have read my blog post "Basic explanation of object-oriented JS, factory mode, constructor mode, prototype mode, hybrid mode, dynamic prototype mode" must know the existence of prototype chain codeThe first problem is that because the prototype of the subtype is an instance of the parent type, that is, the attributes of the parent type contained in the prototype of the subtype, the prototype attributes of the reference type value will be shared by all instances . The instance1.name.push('wyc'); of the above code can prove the existence of this problem. The second problem with the prototype chain is: When creating an instance of a subtype, parameters cannot be passed to the constructor of the supertype. Therefore, in actual development, we rarely use the prototype chain alone.

 Borrow constructor

In order to solve the two problems existing in the prototype chain, developers began to use a technique called borrowed constructors to solve the problems existing in the prototype chain. The implementation idea of ​​this technology is also quite simple. You only need to call the constructor of the parent type within the constructor of the subtype. Don’t forget, a function is just an object that executes code in a specific environment, so you can execute a constructor via the apply() or call() method. The code is as follows:

// 为父类型创建构造函数
function SuperType(name) {
  this.name = name;
  this.color = ['pink', 'yellow'];
  this.property = true;

  this.testFun = function() {
    alert('http://tools.jb51.net/');
  }
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
  return this.property;
}

// 为子类型创建构造函数
function SubType(name) {
  SuperType.call(this, name);
  this.test = ['h1', 'h2', 'h3', 'h4'];
  this.subproperty = false;
}

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
  return this.subproperty;
}


/* 以下为测试代码示例 */
var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
instance1.name.push('hello');
instance1.test.push('h5');
instance1.color.push('blue');
instance1.testFun();            // http://tools.jb51.net/
alert(instance1.name);            // wuyuchang,Jack,Nick,hello
// alert(instance1.getSuerperValue());    // error 报错
alert(instance1.test);            // h1,h2,h3,h4,h5    
alert(instance1.getSubValue());        // false    
alert(instance1.color);            // pink,yellow,blue

var instance2 = new SubType('wyc');
instance2.testFun();            // http://tools.jb51.net/
alert(instance2.name);            // wyc    
// alert(instance2.getSuerperValue());    // error 报错
alert(instance2.test);            // h1,h2,h3,h4
alert(instance2.getSubValue());        // false
alert(instance2.color);            // pink,yellow

You can see that in the above code, the constructor of the subtype SubType calls the supertype "SuperType.call(this, name);", thus realizing the inheritance of properties. It can also be used as the parent when creating an instance of the subtype. Type parameters are passed, but new problems arise again. You can see that I defined a method in the constructor of the parent type: testFun, and a method in the prototype of the parent type: getSuperValue. However, after instantiates the subtype, it is still unable to call the method getSuperValue defined in the prototype of the parent type. It can only call the constructor method of the parent type: testFun. This is the same as using only the constructor pattern when creating objects, making the function non-reusable. Considering these problems, the technique of borrowing constructors is rarely used alone.

Combined inheritance (prototype chain borrowed constructor)

As the name suggests, combined inheritance is a pattern that combines the advantages of using the prototype chain and borrowing constructors. The implementation is also very simple. Since it is a combination, it certainly combines the advantages of both parties, that is, the prototype chain inheritance method, and the constructor inheritance attribute . The specific code implementation is as follows:

// 为父类型创建构造函数
function SuperType(name) {
  this.name = name;
  this.color = ['pink', 'yellow'];
  this.property = true;

  this.testFun = function() {
    alert('http://tools.jb51.net/');
  }
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
  return this.property;
}

// 为子类型创建构造函数
function SubType(name) {
  SuperType.call(this, name);
  this.test = ['h1', 'h2', 'h3', 'h4'];
  this.subproperty = false;
}

SubType.prototype = new SuperType();

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
  return this.subproperty;
}


/* 以下为测试代码示例 */
var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
instance1.name.push('hello');
instance1.test.push('h5');
instance1.color.push('blue');
instance1.testFun();            // http://tools.jb51.net/
alert(instance1.name);            // wuyuchang,Jack,Nick,hello
alert(instance1.getSuerperValue());      // true
alert(instance1.test);            // h1,h2,h3,h4,h5    
alert(instance1.getSubValue());        // false    
alert(instance1.color);            // pink,yellow,blue

var instance2 = new SubType('wyc');
instance2.testFun();            // http://tools.jb51.net/
alert(instance2.name);            // wyc    
alert(instance2.getSuerperValue());      // true
alert(instance2.test);            // h1,h2,h3,h4
alert(instance2.getSubValue());        // false
alert(instance2.color);            // pink,yellow

The above code inherits the properties of the parent type through SuperType.call(this, name); and inherits the methods of the parent type through SubType.prototype = new SuperType();. The above code conveniently solves the problems encountered by the prototype chain and borrowed constructors, and has become the most commonly used instance inheritance method in JavaScript. But the mixed mode is not without its shortcomings. You can see that in the above code, when inheriting the method, the properties of the parent type have actually been inherited. However, the reference type is shared at this time, so it is called twice in the constructor of the subtype. The constructor of the parent type inherits the properties of the parent type and overwrites the properties inherited in the prototype. It is obviously unnecessary to call the constructor twice, but is there any way to solve it? Let’s look at the following two patterns when solving this problem.

Prototypal inheritance

The implementation method of prototypal inheritance is different from that of ordinary inheritance. Prototypal inheritance does not use a constructor in the strict sense. Instead, it uses prototypes to create new objects based on existing objects without having to create them. Custom type. The specific code is as follows:

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

Code example:

/* 原型式继承 */
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

var person = {
  name : 'wuyuchang',
  friends : ['wyc', 'Nicholas', 'Tim']
}

var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Bob');

var anotherPerson2 = object(person);
anotherPerson2.name = 'Jack';
anotherPerson2.friends.push('Rose');

alert(person.friends);  // wyc,Nicholas,Tim,Bob,Rose

Parasitic inheritance

/* 寄生式继承 */
function createAnother(original) {
  var clone = object(original);
  clone.sayHi = function() {
    alert('hi');
  }
  return clone;
}

Usage example:

/* 原型式继承 */
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
   
/* 寄生式继承 */
function createAnother(original) {
  var clone = object(original);
  clone.sayHi = function() {
    alert('hi');
  }
  return clone;
}

var person = {
  name : 'wuyuchang',
  friends : ['wyc', 'Nicholas', 'Rose']
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();

寄生组合式继承

  前面说过了JavaScrip中组合模式实现继承的缺点,现在我们就来解决它的缺点,实现思路是,对于构造函数继承属性,而原型链的混成形式继承方法,即不用在继承方法的时候实例化父类型的构造函数。代码如下:

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

/* 寄生组合式继承 */
function inheritPrototype(subType, superType) {
  var prototype = object(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype;
}

而在使用时只需要将组合模式中的“SubType.prototype = new SuperType();”这行代码替换成inheritPrototype(subType, superType);即可。寄生组合式继承的高效率体现在它只调用了一次父类型构造函数,避免了创建不必要的或多余的属性。与此同时,原型链还能保持不变,因此,还能够正常使用instanceof和isPrototypeof()。这也是目前来说最理想的继承方式了,目前也在向这种模式转型。(YUI也使用了这种模式。)

此博文参考《JavaScript高级程序设计第3版》,代码为经过改写,更具体,并加了注释使大家更易懂。如对JS继承方面有独到见解的童鞋不别吝啬,回复您的见解供大家参考!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn