内容来源于掘金:单程线_01(黄灿民),别忘记看文末福利~
JavaScript 的继承是通过原型链实现的,要实现继承需要理解原型和原型链的概念。
原型和原型链
1、仅函数上存在 prototype 属性(这是一个显式原型属性),其指向原型对象,原型对象上存在两个属性:constructor 和 [[prototype]] (无法访问,部分浏览器可通过 __proto__
访问,这是一个隐式的原型属性);
-
constructor :指向构造函数,即是函数自身
-
[[prototype]](
__proto__
):指向构造函数的原型对象,即 Object ,Object.__proto__ = null
2、当通过 new 调用这个函数时,得到的对象会存在 [[prototype]](__proto__
),指向的是函数的 prototype 属性;
3、所有的对象上都存在 [[prototype]](__proto__
)属性;
4、这种通过 __proto__
在各对象间建立链接的工具就是原型链,通过原型链可以访问到其他对象的属性和方法。
继承的实现方式
继承指一个对象直接使用另一对象的属性和方法。JavaScript 的继承是通过原型链实现的。
实现属性继承的核心代码是 SuperFn.call(this)
,实现方法继承的核心代码是 SubFn.prototype.__proto__ = SuperFn.prototype
,具体实现方式有以下6种:
1、ES6 class 类 extends 继承
通过 ES6 新增的语法糖实现继承。
class
SuperClass
{
constructor
(name) {
this
.name = name ||
'super'
;
this
.arr = [
1
,
2
];
}
sayName(){
console
.log(
this
.name)
}
}
class
SubClass
extends
SuperClass
{
constructor
(){
super
();
}
}
const
sub0 =
new
SubClass();
const
sub00 =
new
SubClass();
sub00.name =
'sub11'
;
sub00.arr.push(
3
);
sub0.sayName();
// 'super'
console
.log(sub0.name, sub0.arr,
sub0.arr === sub00.arr,
sub0
instanceof
SubClass,
sub0.constructor ===SubClass);
// 'super' [1,2] false true true
原型赋值 问题点: 原型对象的 constructor 属性值变为 Object; 原型对象中包含引用值,值操作会同时反应到所有继承对象上; 子类型在实例化时不能给父类型的构造函数传参。 构造函数内部执行 问题点: 只能继承父类的实例属性和方法,不能继承原型属性/方法; 字类构造函数无法实现复用,每个子类都有父类实例函数的副本。
构造函数内部执行 SuperFn.call(this),原型赋值 此种方式和 构造函数内部执行 本实验采用原生 JavaScript 搭建类似网易云的音乐播放器,应用到的技术包括 ES6 新增的语法糖如解构赋值、箭头函数、展开运算符模板字符串 、异步处理 Promise、ES6 模块化等。 通过上述技术最终完成页面切换、轮播图、音乐播放器等功能。 播放器首页(轮播图)效果 推荐列表详情页效果 播放器页面效果 如果你想学习《JavaScript 从零构建音乐播放器》,欢迎扫码入群领取 3 日标准会员,即享 8 折优惠学。 ▼扫码立即领取优惠▼ 戳戳“阅读原文”直达课程页面!2、基于原型继承
SubFn.prototype = new SuperFn()
;
function
SuperFn
(
name
)
{
this
.name = name ||
'super'
;
this
.arr = [
1
,
2
];
}
SuperFn.prototype.sayName =
function
(
)
{
console
.log(
this
.name)
}
/* 基于原型继承: SubFn1.prototype = new SuperFn();
问题点:
- 原型对象的 constructor 属性值变为 Object
- 原型对象中包含引用值,值操作会同时反应到所有继承对象上
- 子类型在实例化时不能给父类型的构造函数传参
*/
function
SubFn1
(
)
{}
// 继承属性和方法:SubFn1 的原型指向 SuperFn 的实例和方法
// 带来的影响:
// - SubFn1 的构造函数变为 Object
// - SuperFn 的实例对象中包含引用值,值操作会同时反应到所有继承对象上
SubFn1.prototype =
new
SuperFn();
const
sub1 =
new
SubFn1();
const
sub11 =
new
SubFn1();
sub11.name =
'sub11'
;
sub11.arr.push(
3
);
sub1.sayName();
// 'super'
console
.log(sub1.name, sub1.arr,
sub1.arr === sub11.arr,
sub1
instanceof
SubFn1,
sub1.constructor ===SubFn1);
// 'super' [1,2,3] true true false
3、借用构造函数
SuperFn.call(this)
,只能继承属性,不能继承原型链上的方法。
/* 借用构造函数:SuperFn.call(this)
问题点:
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 字类构造函数无法实现复用,每个子类都有父类实例函数的副本
*/
function
SubFn2
(
)
{
// 继承属性:子类构造函数中调用父类构造函数并绑定 this ,
// 每次创建实例的过程,函数都会重新执行,同一个属性每次都会新建
SuperFn.call(
this
, ...arguments)
}
const
sub2 =
new
SubFn2(
'sub2'
);
const
sub22 =
new
SubFn2();
sub22.name =
'sub22'
;
sub22.arr.push(
3
);
// sub2.sayName(); // 报错
console
.log(sub2.name, sub2.arr,
sub2.arr === sub22.arr,
sub2
instanceof
SubFn2,
sub2.constructor ===SubFn2);
// 'sub2' [1,2] false true true
4、组合继承
SubFn.prototype.__proto__ = SuperFn.prototype
。
/* 组合继承:SuperFn.call(this) SubFn3.prototype.__proto__ = SuperFn.prototype;
问题点:属性重复建立
*/
function
SubFn3
(
)
{
// 继承属性:子类构造函数中调用父类构造函数并绑定 this ,
//每次创建实例的过程,函数都会重新执行,同一个属性每次都会新建
SuperFn.call(
this
, ...arguments)
}
// 继承方法
SubFn3.prototype.__proto__ = SuperFn.prototype;
const
sub3 =
new
SubFn3(
'sub3'
);
const
sub33 =
new
SubFn3();
sub33.name =
'sub33'
;
sub33.arr.push(
3
);
sub3.sayName();
// 'sub3'
console
.log(sub3.name, sub3.arr,
sub3.arr === sub33.arr,
sub3
instanceof
SubFn3,
sub3.constructor ===SubFn3);
// 'sub3' [1,2] false true true
5、原型式继承
subObj = Object.create(superObj)
实现的功能相同。
/* 原型式继承:此种方式和 Object.create() 实现的功能相同
问题点:
- 原型对象中包含引用值,值操作会同时反应到所有继承对象上
- 无法传递参数
*/
// 此函数用来创建一个对象,对象的原型指向传递的参数(继承的对象)
let
Fn;
function
CreateObj
(
obj
)
{
// 函数内部定义构造函数
function
F
(
)
{}
Fn = F;
// 构造函数的原型对象指向传递的进来的对象
F.prototype = obj;
// 创建实例化对象并返回,此实例对象的 __proto__ = F.prototype = obj
return
new
F();
}
const
SuperObj = {
name
:
'super'
,
arr
: [
1
,
2
],
sayName() {
console
.log(
this
.name)
}
}
const
sub4 = CreateObj(SuperObj);
// 等价于 sub4 = Object.create(SuperObj) sub4.__proto__ = SuperObj
const
sub44 = CreateObj(SuperObj);
sub44.name =
'sub44'
;
sub44.arr.push(
3
);
sub4.sayName();
// 'super'
console
.log(sub4.name, sub4.arr,
sub4.arr === sub44.arr,
sub4
instanceof
Fn,
sub4.constructor ===
Object
);
// 'super' [1,2,3] true true true
6、寄生式组合继承
SuperFn.call(this)
,原型赋值 SubFn.prototype = Object.create(SuperFn.prototype)
。
/* 5.寄生式组合继承
*/
function
inheritPrototype
(
SuperFn, SubFn
)
{
//此函数实现的目的如下 SubFn.prototype.__proto__ = SuperFn.prototype
SubFn.prototype =
Object
.create(SuperFn.prototype);
SubFn.prototype.constructor = SubFn;
}
function
SubFn5
(
)
{
SuperFn.call(
this
, ...arguments);
}
inheritPrototype(SuperFn, SubFn5);
const
sub5 =
new
SubFn5();
const
sub55 =
new
SubFn5();
sub55.name =
'sub55'
;
sub55.arr.push(
3
);
sub5.sayName();
// 'super'
console
.log(sub5.name, sub5.arr,
sub5.arr === sub55.arr,
sub5
instanceof
SubFn5,
sub5.constructor ===SubFn5);
// 'super' [1,2] false true true
另外,作者黄灿民在蓝桥云课上线了
《JavaScript 从零构建音乐播放器》
,供大家实战做项目。
原文始发于微信公众号(蓝桥云课精选):如何搭建一个自己的专属音乐播放器?教程拿去!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论