前言
这个概念是在js小黄书《你不知道的js》中了解到的,面向对象中模仿“类”的滥用导致代码苦涩难懂,所以可以通过
关联委托
的方式让代码更清晰易懂。
es6的class
类出现后,真正规范了类的使用,而且简单易懂。所以本文的意义在于了解一种好的设计理念,也可让自身也具有创新能力,写出更优雅的代码。
面向对象编程
其根本在于模仿类,我们这里写一个
寄生组合继承
,也是es6的class
的设计思想异曲同工。
function Parent(name, age) {
this.name = name;
this.age = age;
}
Parent.prototype.myName = function() {
return this.name;
};
Parent.prototype.myAge = function() {
return this.age;
};
function Child(name, age, sex) {
Parent.call(this, name, age);
this.sex = sex;
}
// 将Child.prototype关联到Parent.prototype上
Child.prototype = Object.create(Parent.prototype);
// var F = function() {};
// F.prototype = Parent.prototype;
// Child.prototype = new F();
// 如果此时要使用到Child.constructor, 手动重置一下此属性,使其指到Bar上。
Child.prototype.contructor = Child;
Child.prototype.mySex = function() {
return this.sex;
};
var c = new Child('tony', '16', '男');
console.log(c.myName()); // tony
console.log(c.mySex()); // 男
这里有两个,我需要强调一下:
Object.create原理
function objectCreate (obj) {
var F = function() {};
F.prototype = obj;
return new F();
}
在上述寄生组合继承中,就是下面注释掉的代码。
-
contructor丢失问题
当你这么使用Child.prototype = ...
,直接等于就会让contructor
丢失。原因是Child.prototype.contructor === Child
,也就是说contructor是函数自带的。所以需要重新指向。
面向委托设计
这里呢,我们将prototype去掉,直接通过Object.create方法,构建对象之间的关系。
var Parent = {
initParent: function(name, age) {
this.name = name;
this.age = age;
},
myName: function() {
return this.name;
},
};
// 创建Child,并委托Parent的属性
var Child = Object.create(Parent);
Child.initChild = function(name, age, sex) {
this.initParent(name, age);
this.sex = sex;
};
Child.mySex = function() {
return this.sex;
};
var c = Object.create(Child);
c.initChild('tony', '16', '男');
console.log(c.myName()); // tony
console.log(c.mySex()); // 男
通过Object.create继承父类的属性和方法,但这里还是有几点不优雅的问题。
-
Parent
对象写法比较优雅,可是Child
的方法不能直接用对象方法,只能Child.initChild
的方法,不然会丢失父类方法。(原因就是Child={}
这种写法本身就是重新赋值了) - 还是有烦人的
Object.create
进行继承。
为了足够优雅和使用,es6推出class类的概念。
es6的class编程
class Parent {
constructor(name, age) { // 初始化属性
this.name = name;
this.age = age;
}
myName() {
return this.name;
}
}
class Child extends Parent {
constructor(name, age, sex) {
super(name, age); // 用super调用父类的构造方法! 类似Parent.call(this, name, age),但是this指的是子类的实例
this.sex = sex;
}
mySex() {
return this.sex;
}
}
var c = new Child('tony', '16', '男');
console.log(c.myName()); // tony
console.log(c.mySex()); // 男
父子类完全可以用同样的优雅方法了;constructor
里面设置初始化值,与constructor
同级的是方法,取代了prototype
;super
方法就是类似Parent.cal
l调用父类构造方法,但是this
指的是子类的实例(es5
中this
指的是父类的实例),不过es6
做的更好,想继承必须调用不然报错,避免我们忘记。
Class作为构造函数的语法糖,同时有
prototype
属性和__proto__
属性,因此同时存在两条继承链。(有点难理解,想清晰了解prototype
和__proto__
的区别,请看https://blog.csdn.net/lc237423551/article/details/80010100)
简单来说呢,你只能看到__proto__
,但是这个__proto__
分两种,一种是你继承的
,一种是你prototype创建的
。第一种表示类的继承
,指向父类
;第二种表示类的实例继承
,指向类的prototype
。
- 子类
__proto__
属性,表示类的继承
,总是指向父类。- 子类
prototype
属性,表示类的实例的继承
,类的实例的__proto__
属性指向类的prototype
属性。
特性和寄生组合继承特性一致,所以类的继承可以看做是寄生组合继承的语法糖
,但是实现方式完全不同,思想是一致的。