Home >Web Front-end >JS Tutorial >How javascript uses prototype chain to implement inheritance method summary

How javascript uses prototype chain to implement inheritance method summary

伊谢尔伦
伊谢尔伦Original
2017-07-25 16:08:001396browse

Javascript itself is not an object-oriented language, but an object-based language. For people who are used to other OO languages, they are a little uncomfortable at first because there is no concept of "class" here, or " There is no distinction between "class" and "instance", let alone "parent class" and "subclass". So, how are these objects in JavaScript so connected?

Fortunately, JavaScript has provided the implementation of "inheritance" from the beginning of its design. Before understanding "inheritance", let's first understand the concept of the prototype chain.

Prototype chain

The specific code is as follows:


  function SuperClass(){
    this.name = "women"
  }
  SuperClass.prototype.sayWhat = function(){
    return this.name + ":i`m a girl!";
  }
  function SubClass(){
    this.subname = "your sister";
  }
  SubClass.prototype = new SuperClass();
  SubClass.prototype.subSayWhat = function(){
    return this.subname + ":i`m a beautiful girl";
  }
  var sub = new SubClass();
  console.log(sub.sayWhat());//women:i`m a girl!

Use prototype chain to implement inheritance

It can be seen from the above code that SubClass inherits the properties and methods of SuperClass. This inheritance is realized by assigning the instance of SuperClass to the prototype object of SubClass, so that the prototype object of SubClass is replaced by SuperClass's prototype object. An instance is overwritten, has all its properties and methods, and also has a pointer to the SuperClass prototype object.

There are some things we need to pay attention to when using the prototype chain to implement inheritance:

Pay attention to the changes in the constructor after inheritance. The constructor of sub here points to SuperClass, because the prototype of SubClass points to the prototype of SuperClass. When understanding the prototype chain, don't ignore the default Object object at the end. This is why we can use built-in methods such as toString in all objects.

When implementing inheritance through the prototype chain, you cannot use literals to define prototype methods, because this will overwrite the prototype object:


  function SuperClass(){
    this.name = "women"
  }
  SuperClass.prototype.sayWhat = function(){
    return this.name + ":i`m a girl!";
  }
  function SubClass(){
    this.subname = "your sister";
  }
  SubClass.prototype = new SuperClass();
  SubClass.prototype = {//此处原型对象被覆盖,因为无法继承SuperClass属性和方法
    subSayWhat:function(){
      return this.subname + ":i`m a beautiful girl";
    }
  }
  var sub = new SubClass();
  console.log(sub.sayWhat());//TypeError: undefined is not a function

Instance sharing problem. When explaining prototypes and constructors earlier, we have introduced that prototypes containing reference type attributes will be shared by all instances. Similarly, the prototypes we inherit will also share the reference type attributes in the "parent class" prototype. When After we modify the reference type attribute of the "parent class" through prototypal inheritance, all other instances inherited from the prototype will be affected. This is not only a waste of resources, but also a phenomenon we do not want to see:


  function SuperClass(){
    this.name = "women";
    this.bra = ["a","b"];
  }
  function SubClass(){
    this.subname = "your sister";
  }
  SubClass.prototype = new SuperClass();
  var sub1 = new SubClass();
  sub1.name = "man";
  sub1.bra.push("c");
  console.log(sub1.name);//man
  console.log(sub1.bra);//["a","b","c"]
  var sub2 = new SubClass();
  console.log(sub1.name);//woman
  console.log(sub2.bra);//["a","b","c"]

Note: When an element is added to the array here, all instances inherited from SuperClass will be affected, but if the name attribute is modified, it will not affect other instances. This is because Arrays are reference types, and name is a basic type.
How to solve the problem of instance sharing? Let’s look down...

Classic inheritance (constructor stealing)

As we have introduced that prototypes are rarely used alone to define objects, in actual development we The prototype chain is rarely used alone. In order to solve the problem of sharing reference types, JavaScript developers have introduced the classic inheritance pattern (also called borrowed constructor inheritance). Its implementation is very simple: calling the super in the subtype constructor. Type constructor. We need to use the call() or apply() function provided by javascript. Let’s take a look at the example:


function SuperClass() {
  this.name = "women";
  this.bra = ["a", "b"];
}
function SubClass() {
  this.subname = "your sister";
  //将SuperClass的作用域赋予当前构造函数,实现继承
  SuperClass.call(this);
}

var sub1 = new SubClass();
sub1.bra.push("c");
console.log(sub1.bra);//["a","b","c"]
var sub2 = new SubClass();
console.log(sub2.bra);//["a","b"]

SuperClass.call(this); This sentence means: The initialization work of the SuperClass constructor is called in the SubClass instance (context) environment, so that each instance will have its own copy of the bra attribute, without affecting each other.
However, this implementation is still not perfect. Since the constructor is introduced, we are also faced with the problem of the constructor mentioned in the previous article: If there is a method definition in the constructor, then for Each instance has a separate Function reference. Our purpose is to share this method, and the methods we define in the supertype prototype cannot be called in subtype instances:


  function SuperClass() {
    this.name = "women";
    this.bra = ["a", "b"];
  }
  SuperClass.prototype.sayWhat = function(){
    console.log("hello");
  }
  function SubClass() {
    this.subname = "your sister";
    SuperClass.call(this);
  }  
  var sub1 = new SubClass();
  console.log(sub1.sayWhat());//TypeError: undefined is not a function

Combined inheritance

Combined inheritance is a way to combine the advantages of the prototype chain and the constructor to express their respective strengths and combine them to achieve inheritance. , simply put, it uses the prototype chain to inherit properties and methods, and uses the borrowed constructor to realize the inheritance of instance properties. This not only solves the problem of instance property sharing, but also allows super-type properties and methods to be inherited:


  function SuperClass() {
    this.name = "women";
    this.bra = ["a", "b"];
  }
  SuperClass.prototype.sayWhat = function(){
    console.log("hello");
  }
  function SubClass() {
    this.subname = "your sister";
    SuperClass.call(this);       //第二次调用SuperClass
  }
  SubClass.prototype = new SuperClass(); //第一次调用SuperClass
  var sub1 = new SubClass();
  console.log(sub1.sayWhat());//hello

The combined inheritance method is also the most commonly used method for us to implement inheritance in actual development. This can already meet your actual development needs, but people’s pursuit of perfection is endless. , then, someone will definitely be "nitpicky" about this pattern: your pattern calls the supertype constructor twice! Twice. . . Do you think it is a performance loss if it is enlarged a hundred times?
The most powerful rebuttal is to come up with a solution. Fortunately, the developers have found the best solution to this problem:

Parasitic combined inheritance

Before introducing this inheritance method, let us first understand the concept of parasitic constructor. The parasitic constructor is similar to the factory pattern mentioned earlier. Its idea is to define a public function, which is specially used to handle the creation of objects and the completion of creation. After returning this object, this function is very similar to a constructor, but the constructor has no return value:


function Gf(name,bra){
  var obj = new Object();
  obj.name = name;
  obj.bra = bra;
  obj.sayWhat = function(){
    console.log(this.name);
  }
  return obj;
}

var gf1 = new Gf("bingbing","c++");
console.log(gf1.sayWhat());//bingbing

寄生式继承的实现和寄生式构造函数类似,创建一个不依赖于具体类型的“工厂”函数,专门来处理对象的继承过程,然后返回继承后的对象实例,幸运的是这个不需要我们自己实现,道哥(道格拉斯)早已为我们提供了一种实现方式:


function object(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
var superClass = {
  name:"bingbing",
  bra:"c++"
}
var subClass = object(superClass);
console.log(subClass.name);//bingbing

在公共函数中提供了一个简单的构造函数,然后将传进来对象的实例赋予构造函数的原型对象,最后返回该构造函数的实例,很简单,但疗效很好,不是吗?这个方式被后人称为“原型式继承”,而寄生式继承正是在原型式基础上,通过增强对象的自定义属性实现的:


function buildObj(obj){
  var o = object(obj);
  o.sayWhat = function(){
    console.log("hello");
  }
  return o;
}
var superClass = {
  name:"bingbing",
  bra:"c++"
}
var gf = buildObj(superClass);
gf.sayWhat();//hello

寄生式继承方式同样面临着原型中函数复用的问题,于是,人们又开始拼起了积木,诞生了——寄生组合式继承,目的是解决在指定子类型原型时调用父类型构造函数的问题,同时,达到函数的最大化复用。基于以上基础实现方式如下:


//参数为两个构造函数
function inheritObj(sub,sup){
  //实现实例继承,获取超类型的一个副本
  var proto = object(sup.prototype);
  //重新指定proto实例的constructor属性
  proto.constructor = sub;
  //将创建的对象赋值给子类型的原型
  sub.prototype = proto;
}
function SuperClass() {
  this.name = "women";
  this.bra = ["a", "b"];
}
SuperClass.prototype.sayWhat = function() {
  console.log("hello");
}

function SubClass() {
  this.subname = "your sister";
  SuperClass.call(this);
}
inheritObj(SubClass,SuperClass);
var sub1 = new SubClass();
console.log(sub1.sayWhat()); //hello

这个实现方式避免了超类型的两次调用,而且也省掉了SubClass.prototype上不必要的属性,同时还保持了原型链。

The above is the detailed content of How javascript uses prototype chain to implement inheritance method summary. For more information, please follow other related articles on the PHP Chinese website!

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