【JS继承】JS继承之构造函数继承

136 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

自我介绍:大家好,我是吉帅振的网络日志;微信公众号:吉帅振的网络日志;前端开发工程师,工作4年,去过上海、北京,经历创业公司,进过大厂,现在郑州敲代码。

JS继承专栏

1【JS继承】什么是JS继承?

2【JS继承】常见的7种继承方式

3【JS继承】JS继承之原型链继承

4【JS继承】JS继承之构造函数继承

5【JS继承】JS继承之组合继承

6【JS继承】JS继承之原型式继承

7【JS继承】JS继承之寄生式继承

8【JS继承】JS继承之寄生组合式继承

9【JS继承】JS继承之ES6 Class继承

一、构造函数

js中定义一个function,当使用new关键字调用这个function的时候,这个function成为一个构造函数。如下所示:

function Person(name) {
  this.name = name;
}
// 使用new关键字调用Person函数,Person函数成为构造函数
var xiaoming = new Person();

二、new关键字

当使用new关键字调用构造函数生成一个对象实例时,js做出的关键处理如下:
1.创建一个新的对象,将构造函数内this指针指向新建对象。
2.将新建对象的__proto__属性设置成构造函数的prototype属性,确保新建对象是构造函数实例。
3.运行一遍构造函数,如果构造函数本身需要返回一个object或者array对象,则舍去新创建对象,返回构造函数需要返回的对象;否则返回新建对象

/**
 * @bug 这里假设new出来实例的都是object,不是array对象
 * @description 传入构造函数,以及构造函数参数,模拟构造函数返回一个新建实例
 * @param {function} constructor 需要传入的构造函数
 * @param {any} params 跟在constructor参数后面的所有参数,用来模拟传入constructor的参数
 * @returns 返回一个新的对象
 */
function mockNew(constructor) {
  // 新建一个对象
  var obj = {};
  // 修改对象的__proto__ 确保使用instanceof关键字时没有问题
  obj.__proto__ = constructor.prototype;

  // 将构造函数的this指针绑定到obj,然后调用
  var params = Array.prototype.splice.call(arguments, 1);
  var result = constructor.apply(obj, params);

  // 判断构造函数是否返回一个对象,是的话直接返回构造函数返回的对象
  if (typeof result === 'object' || typeof result === 'array') {
    return result;
  }

  return obj;
}

// 定义两个构造函数,分别使用mockNew调用以及使用new关键字调用
function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Person2(name, age) {
  this.name = name;
  this.age = age;
  var result = {};
  return result;
}

// 使用mock函数
console.log('============ xiaoming start ==============');
var xiaoming = mockNew(Person, 'xiaoming', 26);
console.log(xiaoming);
console.log(xiaoming instanceof Person);

console.log('============ xiaohong start ==============');
var xiaohong = mockNew(Person2, 'xiaohong', 26);
console.log(xiaohong);
console.log(xiaohong instanceof Person2);

// 使用new关键字
console.log('============ xiaoqiao start ==============');
var xiaoqiao = new Person('xiaoqiao', 26);
console.log(xiaoqiao);
console.log(xiaoqiao instanceof Person);

console.log('============ xiaobing start ==============');
var xiaobing = new Person2('xiaobing', 26);
console.log(xiaobing);
console.log(xiaobing instanceof Person2);

输出截图如下:

三、基本思想

构造函数继承的基本思想就是利用call或者apply把父类中通过this指定的属性和方法复制(借用)到子类创建的实例中。因为this对象是在运行时基于函数的执行环境绑定的。在全局中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。call 、apply方法可以用来代替另一个对象调用一个方法。call、apply 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
new对象的时候(注意,new操作符与直接调用是不同的,以函数的方式直接调用的时候,this指向window,new创建的时候,this指向创建的这个实例),创建了一个新的实例对象,并且执行SubType里面的代码,而SubType里面用call调用了SuperTyep,也就是说把this指向改成了指向新的实例,所以就会把SuperType里面的this相关属性和方法赋值到新的实例上,而不是赋值到SupType上面。所有实例中就拥有了父类定义的这些this的属性和方法。

function SuperType(){
 this.colors = ["red", "blue", "green"];
}
function SubType(){
  //继承了SuperType
 SuperType.call(this);
}
 
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"