继承

原型链继承

  • 实现方式如下
function Parent() {
    this.name = '微言';
}
Parent.prototype.getName = function () {
    console.log(this.name);
};
function Child() {}
// new Parent产生的一个parent 的实例,同时包含了,实例属性以及原型方法。
Child.prototype = new Parent();
console.log(Child.prototype.constructor); // [Function: Parent]
// <1>
Child.prototype.constructor = Child;
console.log(Child.prototype.constructor); // [Function: Child]

const child = new Child();
child.getName();
  • 为什么需要<1>?

    存在的问题,直接把 Child 的原型对象给覆盖了,
    此处指向了 parent 通过 修改 constructor 来实现 指向 Child

缺点

  1. 如果有属性是引用类型的一旦某个实例修改了属性,所有的实例都会改变
  2. 无法传递参数
function Parent() {
    this.name = '微言';
    this.obj = { name: 'obj' };
}

Parent.prototype.getName = function () {
    console.log(this.name);
};

function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child = new Child();
const child1 = new Child();

child1.obj.name = 'child1';

// 因为都是找的原型对象上的同一个东西,所以都会受到影响
console.log(child.obj); // { name: 'child1' }
console.log(child1.obj); // { name: 'child1' }
  1. 创建 Child 实例的时候不可以进行传参

构造函数继承

缺点

  1. 属性或者方法如果想被继承,只能在构造函数中定义.

如果方法在构造函数中定义了。每次创建实例都会创建一遍方法,多占用一块内存。

function Parent(name, obj) {
    this.name = name;
    this.obj = obj;
    this.eat = function () {};
}

function Child(id, name, obj) {
    Parent.call(this, name, obj);
    this.id = id;
}

const child = new Child(1, 'c1', { name: '微言', age: '18' });
const child1 = new Child(2, 'c2', { name: '微言' });

console.log(child.eat === child1.eat); // false 造成了内存泄露,内存浪费

组合继承

组合了构造函数继承和原型链继承。

原型链继承:方法存在 prototype,子类可以随时调用,引用类型的属性会被放在所有实例上共享,并且不可以传参。

构造函数继承: 使用 call 在子构造中重复一遍属性和方法的操作,可以传参了。

function Parent(name, obj, actions) {
    this.name = name;
    this.obj = obj;
    this.actions = actions;
}

Parent.prototype.eat = function () {
    console.log(`${this.name} - eat`);
};

// 采用解构的方式,不用每次都增删值的传入
function Child(id, ...args) {
    // 为了实例属性的不共享
    Parent.apply(this, args); // 1
    this.id = id;
}

Child.prototype = new Parent(); // 2
Child.prototype.constructor = Child;

const child = new Child(1, 'c1', { name: '微言', age: '18' }, [1]);
const child1 = new Child(2, 'c2', { name: '微言1' }, [2, 43]);

console.log(child.eat === child1.eat); // true

child.obj.name = 'asdfasdf';

child.actions.pop();

console.log(child.obj); // { name: 'asdfasdf', age: '18' }
console.log(child1.obj); // { name: '微言1' }

console.log(child.actions); // []
console.log(child1.actions); // [2, 43]

缺点

  1. parent 构造函数被调用了两次

寄生组合式继承

function Parent(name, obj, actions) {
    this.name = name;
    this.obj = obj;
    this.actions = actions;
}

Parent.prototype.eat = function () {
    console.log(`${this.name} - eat`);
};

// 采用解构的方式,不用每次都增删值的传入
function Child(id, ...args) {
    // 为了实例属性的不共享
    Parent.apply(this, args);
    this.id = id;
}

// Child.prototype = new Parent();

// 第一个方法. temp构造函数
// let TempFunction = function () {};
// // 通过曲线救国,让临时构造函数和parent构造函数都一样
// TempFunction.prototype = Parent.prototype;  // 这里只是继承了原型对象, parent里面整个构造函数的创建并不会执行, (其实并没有parent里面的执行)
// // 让临时构造函数的实例指向Child.prototype
// Child.prototype = new TempFunction();

// 第二个方法. Object.create
Child.prototype = Object.create(Parent.prototype);

// 为什么不能直接这样写? 修改 子类 会影响到父类
// Child.prototype = Parent.prototype;

Child.prototype.constructor = Child;

const child = new Child(1, 'c1', { name: '微言', age: '18' }, [1]);
const child1 = new Child(2, 'c2', { name: '微言1' }, [2, 43]);

console.log(child.eat === child1.eat); // true

child.obj.name = '新的名字';

child.actions.pop();

console.log(child.obj); // { name: '新的名字', age: '18' }
console.log(child1.obj); // { name: '微言1' }

console.log(child.actions); // []
console.log(child1.actions); // [2, 43]

总结

本文涉及到的知识点,原型,原型链,Object

微言
0 声望0 粉丝

下一篇 »
this 指针详解