JS继承性总结
-
原型链继承
核心:创建父类实例对象作为子类原型
优点: 可以访问父类原型上的方法或者属性,实现了方法复用
缺点: 创建子类实例时,不能传父级类的参数,子类实例共用了父级构造的属性值
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 学生类型
function Student(score) {
this.score = score;
}
// 老师类型
function Teacher(salary) {
this.salary = salary;
}
// 原型对象,可以将自己的属性和方法继承给将来的实例对象使用
Student.prototype = new Person("zs",18,"男");
Student.prototype.constructor = Student;
// 生成一个实例
var s1 = new Student(89);
var s2 = new Student(100);
console.dir(s1);
console.dir(s2);
console.log(s1.name);
console.log(s1.constructor);
-
构造函数继承 (借用call方法)
核心: 在子构造函数中调用父构造函数
优点: 解决了原型继承的缺点(使用构造函数来继承可以传父类的参数,可以解决子类共享父类构造函数中属性的问题)
缺点:子类无法访问父类原型中的方法和属性(原型链继承可以解决)
function Parent() { //构造函数
this.name = 'parent';
this.play = [1,2,3];
}
Parent.prototype.getName = function(){ //给构造函数增添原型属性
return this.name;
}
function Child() {
Parent.call(this); //将this指向Parent
this.type = 'child';
}
let child1 = new Child() //创建实例
let child2 = new Child()
child1.play.push(4);
console.log(child1); //[1,2,3,4]
console.log(child2); //[1,2,3] //不是共享的,互不影响
console.log(Child); //没有问题
console.log(child.getName()); //会报错,因为他没有继承到Parent自定义添加的原型属性getName
-
组合继承
核心: 使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承
优点:
a. 保留构造函数的优点:创建子类实例,可以向父级构造函数传参数
b. 保留原型链的优点:父类的方法定义在父类的原型对象上,可以实现方法的复用
c.补共享父类的引用属性。比如arr属性
缺点: 调用了两次父类构造函数,会存在多一份的父类实例属性
function Parent() {
this.name = 'parent';
this.play = [1,2,3]
}
Parent.prototype.getName = function() {
return this.play
}
function Child() {
//第二次借用Parent()
Parent.call(this);
this.type = 'child';
}
//第一次借用Parent()
Child.prototype = new Parent();
//手动挂上构造器,指向自己的构造函数
Child.prototype.constructor = Child;
var s1 = new Child();
var s2 = new Child();
s1.play.push(4);
console.log(s1,s2); //互不影响
console.log(s1.getName()); //[1,2,3,4]
console.log(s2.getName()); //[1,2,3]
4.寄生式继承
核心:创建一个仅仅用于封装继承性过程的函数,然后在内部以某种方式增强对象。最后返回对象。
优点: 原型式继承的扩展(其实就是在原型式继承得到对象的基础上,在内部再以某种方式来增加对象,然后返回)
缺点: 依旧没有类的概念
跟原型式继承一样,实现的是浅拷贝,共享一个内存空间。
var parent = {
name: "parent",
friends: [1,2,3],
getName:function() {
return this.name;
}
};
function cloneContext(original) {
var clone = Object.create(original);
clone.getFriends = function () {
return this.friends;
}
return clone;
}
var child = cloneContext(parent);
console.log(child);
console.log(child.getName()); //parent
console.log(child.getFriends()); //[1,2,3]
5.寄生组合式继承
优点: 寄生组合式继承就解决了上述所有问题,被认定是最理想的继承方式
function cloneContext(parent,child) {
//这里改用Object.create可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
function Parent() {
this.name = 'parent';
this.play = [1,2,3];
}
Parent.prototype.getName = function () {
return this.name;
}
function Child() {
Parent.call(this);
this.friends = 'child';
}
cloneContext(Parent,Child);
Child.prototype.getFriends = function() {
return this.friends;
}
var person1 = new Child();
console.log(person1);
console.log(person1.getFriends()); //child
console.log(person1.getName()); //parent
6.共享原型
缺点:原型之间改变相互影响
//父类
Father.prototype.name="Yezi"
funtion Father(){
this.age=20
}
//子类
Son.prototype=new Father()
function Son(){}
var son = new Son()
可以继承到name属性,son.name也是"Li",我们输出son.age也可以得到18。
但是Son的构造函数中并没有age属性,该属性是Son.prototype中的。
所以引入下面的圣杯模式:
7.圣杯模式
核心: 我们借用一个空的构造函数与Father共享原型,然后让Son的原型指向Temp的实例
分析:
1. Temp.prototype = Father.prototype,使Temp.prototype和Father.prototype指向同一个内存空间,并不会影响Son.prototype。
2. Son.prototype = new Temp()相当于Son.prototype__proto__ = Temp.prototype,也就是Son.prototype__proto__ = Father.prototype。从而实现了继承。
//父类
Father.prototype.name = "Li"
function Father() {
this.age = 18
}
//子类
function Son() {
Father.call(this)
}
//中间构造函数
function Temp() {}
Temp.prototype = Father.prototype
Son.prototype = new Temp()
var son = new Son()
优化:
但是每次继承都会创建一个新的Temp中间函数,造成了资源浪费.所以我们可以将它封装为一个方法
var inherit = (function () {
var Temp = function() {}
return function (Target, Origin) { //target继承自origin
Temp.prototype = Origin.prototype
Target.prototype = new Temp()
Target.prototype.constructor = Target //为了让Target原型的constructor指回Target
}
})()
二、call、bind、apply
- 我们可以通过使用bind()、call()、apply()来改变this的指向问题
- 他们本质上的区别是传入的参数数据类型不同
推荐阅读
-
JS、数组]平面数组的基本用法
-
Vue.js 组件开发:从基础到高级功能
-
App:templatesNuxt.js 应用程序中生成的事件钩子详解
-
node.js 下载和安装以及环境配置超级详细教程 [Windows 版本]。
-
打包用 Node.js 编写的程序的简单说明
-
Web Hid Api 浏览器读取 IC 卡号 Js 源代码,无需插件支持
-
LeetCode 问题练习和总结:二叉树的序列化和反序列化 - 297 - 输入:根 = [1,2] 输出: 根[1,2] 提示
-
详细介绍 Nuxt.js 应用程序中的应用程序:解析事件钩子
-
华为 OD 机测试 - RSA 加密算法(Python/JS/C/C++ 2024 E 卷 100 分) - V.Python 算法源代码
-
electron-vite_6js-cookie 故障