父类
function Person(name) {
this.name = name;
this.say = function () {
console.log("1 + 1 = 2");
};
}
Person.prototype.listen = function () {
console.log("エウテルペ");
};
将父类的实例作为子类的原型
function Person(name) {
this.name = name;
this.say = function () {
console.log("1 + 1 = 2");
};
}
Person.prototype.listen = function () {
console.log("エウテルペ");
};
function Student() {}
Student.prototype = new Person(); //关键
const stu = new Student();
stu.grade = 3;
console.log(stu.grade); // 3
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
优点:
简单易实现
父类新增原型方法/原型属性,子类都能访问
实例是子类的实例也是父类的实例
stu instanceof Student; // true
stu instanceof Person; // true
缺点:
存在的问题:
prototype
里有个属性constructor
指向构造函数本身,但是, Student
的原型已经被父类的实例取代了,所以指向也不正确,所以需要修复构造函数指向(这里网上的教程只是对组合继承、寄生组合式继承进行了修复,不知道是不是因为这个不常用的关系)
function Student() {}
console.log(Student.prototype.constructor);
Student.prototype = new Person(); //关键
const stu = new Student();
stu.grade = 3;
console.log(stu.grade); // 3
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
console.log(Student.prototype.constructor);
解决问题
Student.prototype.constructor = Student;
在一个类中执行另一个类的构造函数,通过 call
函数设置 this
的指向,这样就可以得到另一个类的所有属性
function WebsiteMaster(site) {
this.site = site;
}
function Student(name, grade, site) {
Person.call(this, name);
WebsiteMaster.call(this, site);
this.grade = grade;
}
const stu = new Student("clz", 3, "https://clz.vercel.app");
console.log(stu.name, stu.grade, stu.site); // clz, 3, https://clz.vercel.app
stu.say(); // 1 + 1 = 2
stu.listen(); // Uncaught TypeError: stu.listen is not a function
优点:
缺点:
方法在构造函数中定义,无法复用
只能继承父类的实例属性,不能继承原型属性、方法
实例并不是父类的实例,而只是子类的实例
stu instanceof Student; // true
stu instanceof Person; // false
继承继着不再是人了(笑)
为父类实例添加属性、方法,作为子类实例。
道格拉斯·克罗克福德在一篇文章中介绍了一种实现继承的方法,这种方法并没有使用严格意义上的构造函数。它的想法是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。为了达到这个目的,他给出了如下函数。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
const person = new Person("clz");
const stu = object(person);
stu.grade = 3;
stu.study = function () {
console.log("FrontEnd");
};
console.log(stu.name, stu.grade); // clz, 3
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
stu.study(); // FrontEnd
优点:
缺点:
为父类实例添加属性、方法,作为子类实例。(原理和原型式继承一样)
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function Student(name, grade) {
const person = object(new Person(name));
person.grade = grade;
person.study = function () {
console.log("FrontEnd");
};
return person;
}
const stu = new Student("clz", 3);
console.log(stu.name, stu.grade); // clz, 3
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
stu.study(); // FrontEnd
优点:
缺点:
不支持多继承
实例是父类的实例,不是子类的实例(因为只是在父类的实例上添加属性、方法而已)
stu instanceof Student; // false
stu instanceof Person; // true
原型链继承+借用构造函数继承
function WebsiteMaster(site) {
this.site = site;
}
function Student(name, grade, site) {
Person.call(this, name); // 继承属性
WebsiteMaster.call(this, site);
this.grade = grade;
}
Student.prototype = new Person(); // 继承方法
Student.prototype.constructor = Student;
const stu = new Student("clz", 3, "https://clz.vercel.app");
console.log(stu.name, stu.grade, stu.site); // clz, 3, https://clz.vercel.app
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
console.log(stu.constructor);
优点:
缺点:
通过 Object.create()
来代替给子类原型赋值的过程,解决了两次调用父类构造函数的问题
function WebsiteMaster(site) {
this.site = site;
}
function Student(name, grade, site) {
Person.call(this, name); // 继承属性
WebsiteMaster.call(this, site);
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype); // 继承方法
Student.prototype.constructor = Student; // 修复构造函数指向
const stu = new Student("clz", 3, "https://clz.vercel.app");
console.log(stu.name, stu.grade, stu.site); // clz, 3, https://clz.vercel.app
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
有人可能会提出:为什么不可以直接把父类原型赋值给子类原型来实现呢?
这是因为直接赋值的话,那就是引用关系。下面就来看看
function WebsiteMaster(site) {
this.site = site;
}
function Student(name, grade, site) {
Person.call(this, name); // 继承属性
WebsiteMaster.call(this, site);
this.grade = grade;
}
Student.prototype = Person.prototype; // 继承方法
Student.prototype.constructor = Student; // 修复实例
const stu = new Student("clz", 3, "https://clz.vercel.app");
console.log(stu.name, stu.grade, stu.site); // clz, 3, https://clz.vercel.app
stu.say(); // 1 + 1 = 2
stu.listen(); // エウテルペ
Student.prototype.listen = function () {
console.log("EGOIST");
};
console.log(Person.prototype.listen);
可以看到,修改 Student
原型上的方法时, Person
的原型上的也会跟着变化。
**
Object.create()
**方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
。 所以,此时修改Student
原型上的方法时,Person
的原型上的不会跟着变化。
es6 之前没有 Object.create()
方法,可以自己实现(实际就是原型式继承的关键函数)
关键:
newObj.__proto__ === obj
function object(obj) {
function F() {} // 新的构造函数
F.prototype = obj; // 继承传入的参数obj
return new F(); // 返回新的函数对象
}
参考链接:
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有