在最初的JavaScript中没有面向对象的概念,在其他语言的开发者转向JavaScript后,由于习惯了面向对象的开发,在JavaScript中模拟了面向对象的几个核心概念。
封装
function Person(name) {
this.name = name
}
Person.prototype.eat = function() {
console.log(this.name + "正在吃饭")
}
const p1 = new Person("张三")
p1.name // 张三
p1.eat() // 张三正在吃饭
每个函数都有prototype属性,被函数new出来的对象会有一个__proto__属性,链接到函数prototype
p1.__proto__ === Person.prototype //true
继承
原型链继承
做法: 把当前构造函数的prototype 链接到由父构造函数创造的对象的__proto__上
function Student(name, sno) {
this.sno = sno
}
// 为学生构造函数的原型上添加父构造函数的原型上的方法
Student.prototype = new Person()
Student.prototype.study = function() {
console.log(this.sno + "正在学习")
}
缺点
- 通过直接打印对象是看不到这个属性的
- 这个属性会被多个对象共享,如果这个对象是一个引用类型,那么就会造成问题
- 不能给Person传递参数,因为这个对象是一次性创建的
借用构造函数继承
做法: 在构造函数中调用父构造函数,传入父构造函数需要的参数,即可在当前实例中添加属性。
function Student(name, sno) {
// 复用父构造器函数,让父构造器函数在当前学生实例中加入name属性
Person.call(this, name) // 第一次调用父构造函数
this.sno = sno
}
Student.prototype = new Person() // 第二次调用父构造函数
缺点
- 会调用两次父构造函数
- 实例中有两份父构造函数的属性:一次是用call调用父构造函数直接添加到实例中;一次是赋值到子构造函数的prototype中(Student.prototype = new Person())
最终解决方案:寄生组合式继承
// 寄生组合式继承
function Person(name) {
this.name = name;
}
Person.prototype.eat = function () {
console.log(this.name + "正在吃饭");
};
function Student(name, sno) {
Person.call(this, name);
this.sno = sno;
}
// 把Student的显式原型指向Person显式原型的隐式原型
Student.prototype = Object.create(Person.prototype);
Student.prototype.study = function () {
console.log(this.sno + "正在学习");
};
/**
* 改变Student实例的类型
* Student的实例的类型是Student.prototype.constructor
* 由于Student.prototype是创建出来的对象 没有constructor属
* 性,会找到Person.prototype.constructor
* 需要手动添加construcor属性
*/
Student.prototype.constructor = Student;
const stu = new Student("小明", 112);
// console.log(stu.name);
// stu.eat();
// stu.study();
console.log(stu);
// 让子构造函数的prototype的__proto__指向父构造函数的prototype
// 完成后的效果
// 子构造函数创建出的实例 const stu = new Student("小明", 122)
// stu的__proto__指向Student.prototype
// Student.prototype的__proto__指向Person.prototype
// 调用stu的eat方法,找到Student.prototype =>
// Student.prototype.__proto__ => Person.prototype =>
// Person.prototype.eat
通用继承函数
function inherit(Child, Parent) {
// 原型寄生
Child.prototype = Object.create(Parent.prototype);
// 定义constructor
Object.defineProperty(Child.prototype, "constructor", {
configurable: true,
enumerable: false,
writable: true,
value: Child,
});
}
Object.create 的polyfill
function create(proto) { var Fn = function() {} Fn.prototype = proto return new Fn }