Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

手写六种继承 #22

Open
devinxiang opened this issue Apr 24, 2021 · 0 comments
Open

手写六种继承 #22

devinxiang opened this issue Apr 24, 2021 · 0 comments
Labels

Comments

@devinxiang
Copy link
Owner

1. 原型链继承

思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。

/**
 * 1. 原型链继承
 * child.prototype = new Parent()
 * 把父元素的实例,放大子的 prototype 上面
 */
// 父级元素
function Parent() {
  this.name = "Devin";
  this.species = ["rich", "poor"];
}

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

// 子元素继承父元素的东西
function Child() {}
Child.prototype = new Parent();
let child1 = new Child();
console.log(child1.name, child1.species);

存在问题

  1. 原始构造函数里面的应用类型属性被所有实例共享,这个属性一变化都在变化;
  2. 在 Child 构造原型链继承的时候不能向 Parent 传参

2. 借用构造函数(经典继承)

思想:在子类型构造函数的内部调用超类型构造函数,因为函数只不过是在特定环境中执行代码的对象,因此通过使用 apply() 和 call() 方法也可以在(将来)新创建的对象上执行构造函数。

function Parent2() {
  this.names = ["kevin", "daisy"];
}

function Child() {
  Parent.call(this);
}

var child1 = new Child();

child1.names.push('yayu');

console.log(child1.names); // ["kevin", "daisy", "yayu"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy"]

在新创建的 SubType 实例的环境下调用了 SuperType 构造函数。这样就会在新 SubType 对象上执行 SuperType()函数中定义的所有对象初始化代码。这样,每个实例都会有自己的属性副本了。

优点:

  • 避免了引用类型的属性被所有实例共享;
  • 可以在 Child 中向 Parent 传参;

缺点:

  • 无法复用到 Parent 的 prototype 上面的方法;
  • 需要继承的方法都需要在构造函数中定义,每次创建实例都会创建一遍方法。导致函数无法复用,浪费内存。

3. 组合继承

思想:

  • 通过原型链实现对原型属性和方法的继承
  • 通过借用构造函数实现对实例属性的继承
function Parent3(name) {
  this.name = name;
  this.colors = ["red", "blue"];
}

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

function Child3(name, age) {
  Parent3.call(this, name);
  this.age = age;
}

Child3.prototype = new Parent3();
Child3.prototype.constructor = Child3;

var child1 = new Child("Devin", "18");

避免了原型链继承的缺点-实例共享,避免了每个实例的方法不能复用。

4. 原型式继承

思想:将传入的对象作为创建的对象的原型

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

var person = {
    name: 'kevin',
    friends: ['daisy', 'kelly']
}

var person1 = createObj(person);
var person2 = createObj(person);

person1.name = 'person1';
console.log(person2.name); // kevin

person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]

缺点:

  1. 引用类型的属性值始终会共享;

5. 寄生式继承

思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象

function createAnother(original){
	var clone = Object.create(original);
	clone.sayName = function (){ 
		console.log('hi')
	}

	return clone; 
}

缺点:

  1. 每次创建对象都会创建一次方法

6. 寄生组合式继承

思路:通过组合寄生的方式继承 prototype 的内容;通过构造函数方式继承实例属性;

// 生成一个新的对象,把继承的属性放在 prototype 上面
function object(o) {
	function F() {}
	F.prototype = o;
	return new F();
}
// 继承 Parent 的 prototype 属性
function proto(Child, Parent){
	let prototype = object(Parent.prototype); // 创建对象
	prototype.constructor = Child;            // 增强对象
	Child.prototype = prototype;              // 指定对象
}

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}

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

function Child(name, age) {
  // 构造函数继承
  Parent.call(this, name);
  this.age = age;
}

proto(Child, Parent);

总结

继承分为

  1. 继承实例属性,构造函数 this 上面的。实例属性需要单独生成一份;
  2. 继承公用方法,在被继承的 prototype 上面的;

参考

  1. JavaScript深入之继承的多种方式和优缺点
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant