图解js中继承的几种方式
文章目录
- 前言
- 原型链继承
- 实现
- 优点
- 缺点
- 借用构造函数继承
- 实现
- 优点
- 缺点
- 实例继承
- 实现
- 优点
- 缺点
- 组合式继承
- 实现
- 缺点
- 优点
- 寄生组合继承
- 实现
- es6语法实现继承
- 原型工厂
- 对象工厂
- 总结
- 参考资料
前言
建议先弄懂了原型链机制,再来观看本片笔记,这样收获更丰富哟。
首先,执行以下两句代码后,最终形成了这样的原型链。记住这张图!
function Foo() {};
var f1 = new Foo();
首先定义一个父类,为后面做铺垫。
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology(){this.type = 'Biology';this.sex = "m"// 实例方法this.sleep = function(){console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function(){console.log("生物在叫:别骂了别骂了")console.log(this.superType);
}
原型链继承
实现
父类的实例作为子类的原型
// ----------------------------父类-----------------------------------
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology() {this.type = 'Biology';this.sex = "m"// 实例方法this.sleep = function () {console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function () {console.log("生物在叫:别骂了别骂了")console.log("当前类型:" + this.type);
}// ------------------------子类---------------------------------------
function Person(name) {this.name = name;this.type = 'Person';
}// 继承实现关键代码:改变Person的protype指针,指向一个Biology实例
Person.prototype = new Biology()Person.prototype.yygq = function () {console.log("不会吧不会吧就这?")
}let sxc = new Person("孙笑川")
sxc.yygq()
sxc.superYygq()
console.log(sxc.type)
sxc.sleep()
输出
不会吧不会吧就这?
生物在叫:别骂了别骂了
当前类型:Person
Person
使用了原型链继承后,指针改变如下
优点
简单易于实现,父类的新增的实例与属性子类都能访问
实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。
缺点
新实例不会继承父类实例的属性
可以在子类中增加实例属性,如果要新增加原型属性和方法需要在new 父类构造函数的后面
无法实现多继承
创建子类实例时,不能向父类构造函数中传参数
借用构造函数继承
实现
(伪造对象、经典继承)
复制父类的实例属性给子类
// -----------------父类----------------------
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology() {this.type = 'Biology';this.sex = "m"this.name = ""// 实例方法this.sleep = function () {console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function () {console.log("生物在叫:别骂了别骂了")console.log("当前类型:" + this.type);
}// ------------------------子类---------------------------------------
function Person(name) {// 继承实现关键代码:在子类中call一个父类Biology.call(this)this.name = name;this.type = 'Person';
}Person.prototype.yygq = function () {console.log(this.name + "说:不会吧不会吧就这?")
}let sxc = new Person("孙笑川")
sxc.yygq()
// sxc.superYygq() 会报错
console.log(sxc.type)
sxc.sleep()
输出
优点
子类构造函数可以向父类构造函数中传递参数
可以实现多继承(call或者apply多个父类)
缺点
方法都在构造函数中定义,无法复用
不能继承父类的原型属性/方法
只能继承父类的实例属性和方法
实例继承
实现
是一种原型式的继承
// -----------------父类----------------------
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology() {this.superType = 'Biology';this.sex = "m"this.name = "生物"// 实例方法this.sleep = function () {console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function () {console.log("生物在叫:别骂了别骂了")console.log("当前类型:" + this.superType);
}// ------------------------子类---------------------------------------
function Person(name) {// 关键代码let instance = new Biology();instance.name = name || '人物';instance.type = 'Person';return instance;
}Person.prototype.yygq = function () {console.log("不会吧不会吧就这?")
}let sxc = new Person("孙笑川")
//sxc.yygq()
sxc.superYygq()
console.log("能否访问到父类中的属性:" + sxc.superType)
console.log(sxc.type)
sxc.sleep()
console.log(sxc instanceof Person)
console.log(sxc instanceof Biology)
输出如下
生物在叫:别骂了别骂了
当前类型:Biology
能否访问到父类中的属性:Biology
Person
孙笑川正在睡觉
false
true
优点
不限制调用方式
简单,易实现
缺点
- 不能多次继承
- 实例只是父类的实例,不是子类的实例
- 不能通过改写子类的prototype来改变实例
组合式继承
实现
调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用
// -----------------父类----------------------
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology(name, sex) {this.type = 'Biology';this.sex = sex || "m"this.name = name || "某生物"// 实例方法this.sleep = function () {console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function () {console.log("生物在叫:别骂了别骂了")console.log("type:" + this.type);
}// ------------------------子类---------------------------------------
function Person(name,sex) {Biology.call(this, name, sex)this.type = "日本天皇"this.money = 100
}Person.prototype = new Biology();
// 组合继承需要修复构造函数指向的。
Person.prototype.constructor = Person;// 要注意顺序
Person.prototype.yygq = function () {console.log(this.name + "说:不会吧不会吧就这?")
}
Person.prototype.getMoney = function(){console.log("剩下的钱:" + this.money)
}// ---------------------------------------------------------------let sxc = new Person("孙笑川")
sxc.yygq()
sxc.getMoney()sxc.superYygq()
console.log(sxc.type)
sxc.sleep()
console.log(sxc instanceof Person)
console.log(sxc instanceof Biology)
输出如下
孙笑川说:不会吧不会吧就这?
剩下的钱:100
生物在叫:别骂了别骂了
type:日本天皇
日本天皇
孙笑川正在睡觉
true
true
可以认为,这就是原型链继承和借用构造函数继承的组合体
缺点
- 调用了两次父类,所以产生了两份实例
优点
函数可以复用
不存在引用属性问题
可以继承属性和方法,并且可以继承原型的属性和方法
寄生组合继承
实现
通过寄生的方式来修复组合式继承的不足,完美的实现继承
// -----------------父类----------------------
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology(name, sex) {this.type = 'Biology';this.sex = sex || "m"this.name = name || "某生物"// 实例方法this.sleep = function () {console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function () {console.log("生物在叫:别骂了别骂了")console.log("type:" + this.type);
}// ------------------------子类---------------------------------------
function Person(name, sex, money) {// 继承父类属性Biology.call(this, name, sex)this.type = "日本天皇"this.money = sex || 100
}// 继承父类方法
// 创建空类
let Super = function(){};
Super.prototype = Biology.prototype;// 父类的实例作为子类的原型
Person.prototype = new Super()// 修复构造函数指向问题
Person.prototype.constructor = Person;// 要注意顺序,先完成继承实现后,再进行该操作
Person.prototype.yygq = function () {console.log(this.name + "说:不会吧不会吧就这?")
}
Person.prototype.getMoney = function () {console.log("剩下的钱:" + this.money)
}// ---------------------------------------------------------------let sxc = new Person("孙笑川")
sxc.yygq()
sxc.getMoney()sxc.superYygq()
console.log(sxc.type)
sxc.sleep()
console.log(sxc instanceof Person)
console.log(sxc instanceof Biology)
输出
孙笑川说:不会吧不会吧就这?
剩下的钱:100
生物在叫:别骂了别骂了
type:日本天皇
日本天皇
孙笑川正在睡觉
true
true
原理浅析
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
执行完上述的继承代码后,所得结果如图所示。
这样就不会生成两份父类实例了,而是用一个空类来寄生在父类的原型链上。
写成一个函数,如下。这里要注意一下object.create和new的差别。
Object.create 是创建一个新对象,使用现有的对象来提供新创建对象的 proto。意思就是生成一个新对象,该新对象的 proto(原型) 指向现有对象。
new 生成的是构造函数的一个实例,实例继承了构造函数及其 prototype(原型属性)上的属性和方法。
// -----------------父类----------------------
// 定义一个Biology(生物)构造函数,作为 Person的父类
function Biology(name, sex) {this.type = 'Biology';this.sex = sex || "m"this.name = name || "某生物"// 实例方法this.sleep = function () {console.log(this.name + '正在睡觉')}
}// 原型方法
Biology.prototype.superYygq = function () {console.log("生物在叫:别骂了别骂了")console.log("type:" + this.type);
}// -----------------子类---------------------
function Person(name, sex) {Biology.call(this, name, sex) // 继承第一次this.type = "日本天皇"this.money = 100
}/* 普通组合继承 */
// Person.prototype = new Biology(); //继承第二次/* 组合寄生 */
function inherit(son, father) {let prototype = Object.create(father.prototype); //不发生第二次继承prototype.constructor = son;son.prototype = prototype;}
inherit(Person, Biology)// 子类原型方法
Person.prototype.yygq = function () {console.log("就这?")
};// 测试
let sxc = new Person("孙笑川")
sxc.yygq()sxc.superYygq()
console.log(sxc.type)
sxc.sleep()
console.log(sxc instanceof Person)
console.log(sxc instanceof Biology)
es6语法实现继承
代码量少,易懂
//class 相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class Biology{constructor(name='某生物',sex='m'){this.name = name;this.age = age;this.type="生物"}eat(){console.log(`${this.name} ${this.age} eat food`)}
}//继承父类
class Person extends Biology{ constructor(name='某人类',sex='m', money){ //继承父类属性super(name, sex); this.money = money || 100} eat(){ //继承父类方法super.eat() console.log("就这?")}
} // 测试
let sxc = new Person("孙笑川")
sxc.eat()console.log(sxc.type)
sxc.sleep()
console.log(sxc instanceof Person)
console.log(sxc instanceof Biology)
原型工厂
原型工厂是将继承的过程封装,使用继承业务简单化。
function extend(sub, sup) {sub.prototype = Object.create(sup.prototype);sub.prototype.constructor = sub;
}function Access() {}
function User() {}
function Admin() {}
function Member() {}extend(User, Access); //User继承Access
extend(Admin, User); //Admin继承User
extend(Member, Access); //Member继承AccessAccess.prototype.rules = function() {};
User.prototype.getName = function() {};console.log(new Admin()); // 继承关系: Admin>User>Access>Object
console.log(new Member()); //继承关系:Member>Access>Object
对象工厂
在原型继承基础上,将对象的生成使用函数完成,并在函数内部为对象添加属性或方法。
function User(name, age) {this.name = name;this.age = age;
}
User.prototype.show = function() {console.log(this.name, this.age);
};function Admin(name, age) {let instance = Object.create(User.prototype);User.call(instance, name, age);instance.role=function(){console.log('admin.role');}return instance;
}let hd = Admin("管理员", 19);
hd.show();function member(name, age) {let instance = Object.create(User.prototype);User.call(instance, name, age);return instance;
}
let lisi = member("李四", 28);
lisi.show();
总结
这块就是链来链去的,多画图就悟了。
参考资料
js继承的几种方式
Object.create 和 new 区别与原理
前端进阶之道
图解js中继承的几种方式相关推荐
- js中继承的几种用法总结(apply,call,prototype)
本篇文章主要介绍了js中继承的几种用法总结(apply,call,prototype) 需要的朋友可以过来参考下,希望对大家有所帮助 一,js中对象继承 js中有三种继承方式 1.js原型(proto ...
- Js中自定义对象四种方式
Js中自定义对象四种方式 1 类似JAVA有参构造方式: 1.定义对象: function 对象(属性[age]){追加属性:如(this.age = age)[this代表当前对象的地址值的引用]追 ...
- (转)js实现继承的5种方式
js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承有以下通用的几种方式 1.使用对象冒充实现继承(该种实 ...
- js中数组排序的五种方式
下面主要介绍了数组排序的五种方式--sort()方法.选择排序.冒泡排序.插入排序和快速排序, 刚兴趣的朋友,可以往下看哦. 1.js中的sort()方法 基本思想:根据提供的排序规则,对数组元素进行 ...
- JS中创建对象:三种方式(pink)
在 JavaScript 中,现阶段我们可以采用三种方式创建对象(object): (1)利用字面量创建对象 (2)利用new Object创建对象 (3)利用构造函数创建对象
- JS 实现继承的 5 种方式
文章目录 继承 原型链继承 原型链继承的优缺点 构造继承 构造继承的优缺点 复制继承 复制继承的优缺点 组合继承 组合继承的优缺点 寄生组合继承 参考 继承 继承作为面向对象语言的三大特性之一(继承. ...
- JavaScript(js)实现继承的几种方式
1.原型链继承 核心:将父类的实例做为子类的原型对象 //动物类function Animal(name,sex) {this.name = name || 'Animal';this.sex = s ...
- 面试--js实现继承的几种方式
基于原型的继承 function father() {this.faName = 'father';this.names=['11','22']}father.prototype.getfaName ...
- JS 总结之原型继承的几种方式
在之前的总结中,我们详细分析了原型<JS 总结之原型>,原型很大作用用于模拟继承,这一次,我们来聊原型继承的几种方式. function Person (age) {this.age = ...
- Django中Model继承的三种方式
Django中Model继承的三种方式 Django中Model的继承有三种: 1.抽象继承 2.多表继承 3.proxy model(代理model) 1.抽象继承 第一种抽象继承,创建一个通用父类 ...
最新文章
- Windows 2008 R2 X64 安装WebsitePanel(WSP虚拟主机管理面板)
- centos7安装git_【DevOps】centos7 下的 gitlab托管服务器的介绍与安装
- sqlserver循环like变量_numba从入门到精通(6)—numba与循环与并行
- java包装项目_项目包装组织
- java创建临时文件_用Java创建一个临时文件
- 转:如何用EXCEL表运用FV函数
- UI素材|标签页 Tab实用案例,可临摹学习
- Scala学习笔记(1)-环境搭建
- win11联网不能打开网页怎么办 windows11联网不能打开网页的解决方法
- 问答| 四轮驱动移动机器人(SSMR)简化模型的虚拟轮间距dLR具体是多少
- html5之Canvas坐标变换应用-时钟实例
- mven2 + androMDA 初探
- 音乐彩灯控制器C语言程序,基于单片机的LED彩灯控制器
- 眼袋、眼袋、眼袋!眼袋一直有~~~~ 肿么办啊
- 均衡负载集群(LBC)-2
- c语言 日志滚动 大小,Logrotate 日志滚动 解决日志占用空间过大
- 养QQ宠物不花Q币?完全可以!(转)
- 动力节点面试题mysql真的难_动力节点整理120道面试问题集锦
- java arcgis envi_ArcGIS三维入门(2-15)使用ENVI基于立体影像提取DEM
- Vim 编辑器的使用