在es6之前,js中没有类的概念 我们所有类的实现都是通过构造函数模拟的
一. 构造函数
首先我们看下构造函数 js中一切皆是对象函数也不例外 函数也是一个对象,请看下面的例子
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "PERSON";
this.say = () => {
console.log("人都可以说");
};
}
Reflect.ownKeys(Person).forEach(key => {
console.log(key, Person[key]);
})
代码结果如下
image.png
我们会发现函数有个属性prototype 这个属性有个constructor指针 指向Person 这个prototype对象就是构造函数的原型对象
接下来 我们实例化一个对象 并在原型上写一个公共方法
Person.prototype.needStudy = () => {
console.log("每个人都需要学习");
};
const p1 = new Person("Ernestwang", "25");
const p2 = new Person("zhangsan", "18");
console.log(p1.needStudy(), p2.needStudy());
结果如下 我们发现p1 和 p2都有了needStudy这个方法
image.png
接下来我们继续打印p1 p2 这个实例对象 看看有啥惊喜
image.png
我们会发现每个实例有个_proto_属性,指向的内容恰好是构造函数的prototype属性, 我们回顾之前构造函数的原型对象的_proto_属性是不是指向Object.prototype。
Object.protype._proto_属性指向null 这样就形成一条原型链
console.log(p1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
所以我们得出结论 实例的_proto_属性指向构造函数的原型对象
上面我们用构造函数模拟出了一个Person类 搞清楚了原型和原型链,下面我们看下如何实现继承
首先我们能想到的是直接让子类的protype指向父类的实例 这样应该是能实现继承的 我们通过原型链实现了继承代码如下
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "PERSON";
this.interest=[1,2];
this.say = () => {
console.log("人都可以说");
};
}
Person.prototype.needStudy = () => {
console.log('每个人都需要学习');
}
function Student(score){
this.type = 'Student';
this.score = score;
}
Student.prototype= new Person("Ernestwang", "25");
const s1 = new Student(97);
s1.interest.push(3);
const s2 = new Student(80);
console.log(s1, s2);
打印结果如下
image.png
我们发现s1 s2确实继承了父类的方法和属性 但是这种方式有个问题 当我们原型对象的属性是引用类型的时候 我们改变某个实例的属性 另外实例的属性也会跟着改变 如我们这个interes属性 s1改了以后 s2也跟着改了 这样不是我们想要的
还有个弊病 我们不能用构造函数定义name,age
下面我们借用构造函数实现继承代码如下
// 构造函数继承
function Student(name, age, score) {
Person.call(this, name, age);
this.type = "Student";
this.score = score;
}
const s1 = new Student('Ernestwang',24,97);
s1.interest.push(3);
const s2 = new Student('zhangsan',22,80);
console.log(s1, s2);
代码打印结果如下
image.png
从结果看 我们上面的两个问题这种方式都没有 他解决了实例改变引用属性以后不影响其他实例 可以在子类构造函数中传入参数 但是他有个另外的问题 就是我们父类的原型对象上面的方法实例继承不了 既然这俩种都有弊端 那我们吧他们组合一下捏 原型构造函数组合继承
// 原型构造函数组合继承
function Student(name, age, score) {
Person.call(this, name, age);
this.type = "Student";
this.score = score;
}
Student.prototype = {
constructor: Student,
...Person.prototype,
};
const s1 = new Student('Ernestwang',24,97);
s1.interest.push(3);
const s2 = new Student('zhangsan',22,80);
console.log(s1, s2);
打印结果如下
image.png
好了 目的达到 这就是我理解到的继承