JS类的创建和继承总结(含ES6 class类)
1. 类的创建
1.1 ES5中类的创建 (即构造函数的创建)
// 注:ES5中类的创建其实就是构造函数的创建,和函数创建相同,只是函数名首字母一般推荐大写
// 1. 方法一:直接在构造函数中直接定义属性和方法
// 优点:可以传参
// 缺点:每 new 一个实例,就会复制一份构造函数里面的属性和方法,费内存
function Star(name, sex) {
this.name = name
this.sex = sex
this.role = '明星'
this.singSong = function () {
console.log('他唱的歌叫' + song)
}
}
// 2. 方法二:先创建一个构造函数(类),然后在原型上挂载属性和方法
// 优点:继承公共属性或方法的时候不费内存,因为它相当于引用,也就是指向的问题,并非“复制”
// 缺点:定义非公共属性时费内存,而且不高效,即不能够用传参来“定制”属性值
function Star() {}
Star.prototype.name = '刘德华'
Star.prototype.sex = '男'
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
console.log('他唱的歌叫' + song)
}
// 3. 总结及注意:
// 3.1 一般将私有属性(即属性值不同的)定义在构造函数(类)中,公共的方法定义在原型上
// 3.2 当构造函数中的属性或方法和原型上的相同时,构造函数内的优先级高
// 4. 举例:
// 4.1 在构造函数中定义“私有”属性
function Star(name, sex) {
this.name = name
this, (sex = sex)
}
// 4.1 在原型上定义公共属性或方法
// 写法1:
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
console.log('他唱的歌叫' + song)
}
// 写法2:
Star.prototype = {
role: '明星',
singSong: function (song) {
console.log('他唱的歌叫' + song)
}
}
1.2 ES6中类的创建和继承
// 创建类
class Star {
// 构造方法,固定写法
constructor(name, sex) {
this.name = name
this.sex = sex
this.role = '明星'
}
// 公共方法
getName(name) {
console.log('他的名字是' + name)
}
}
// 子类继承
class LiuDeHua extends Star {
constructor(name, sex, role) {
super(name, sex) // super 表示继承父类的属性
this.role = role // 表示子类自己添加的
}
sing(song) {
// 子类自己的方法
console.log('我唱了' + song)
}
}
// 下面测试
let ldh = new LiuDeHua('刘德华', '男', '四大天王')
console.log(ldh.name) // 刘德华
ldh.getName('liudehua') // 他的名字是liudehua
ldh.sing('忘情水') // 我唱了忘情水
2. ES5中类的继承
2.1 基于原型链继承
// 1. 定义好的类
function Star(name, sex) {
this.name = name
this.sex = sex
}
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
console.log('他唱的歌叫' + song)
}
// --------------------------------------------------------
// 2. 用原型链的方式实现继承
// 2.1 创建一个空的函数(子函数)
function LiuDeHua() {}
// 2.2 让子函数的原型指向构造函数,相当于在原型上 new 一个构造函数
// 让子函数的原型上拥有构造函数的全部属性和方法
LiuDeHua.prototype = new Star('刘德华', '男')
// 2.3 new 一个实例,然后调用子函数(子类)的属性和方法
let ldh = new LiuDeHua()
console.log(ldh.name) // 输出 刘德华
console.log(ldh.role) //输出 明星
ldh.singSong('忘情水') // 输出 忘情水
// 3. 总结
// 3.1 优点:
// (1)简单,子类实例既是子类的实例也是父类的实例
// (2)父类在原型上新增的方法和属性都能被子类获取到
// 3.2 缺点:
// (1)传参不方便,只能在继承的时候传递参数,无法在创建子类实例的时候传入参数
// (2)无法实现多继承(一个子类继承多个父类)
// (3)原型上的属性、方法被所有的实例共享
// 1. 定义好的类
function Star(name, sex) {
this.name = name
this.sex = sex
}
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
console.log('他唱的歌叫' + song)
}
// 2. 用原型链的方式实现继承
// 2.1 创建一个空的函数(子函数)
function LiuDeHua() {}
// 2.2 让子函数的原型指向构造函数,相当于在原型上 new 一个构造函数
// 让子函数的原型上拥有构造函数的全部属性和方法
LiuDeHua.prototype = new Star('刘德华', '男')
// 2.3 调用子函数(子类)的属性和方法
console.log(LiuDeHua.prototype.name) // 输出 刘德华
console.log(LiuDeHua.prototype.role) //输出 明星
LiuDeHua.prototype.singSong('忘情水') // 输出 忘情水
// 3. 总结
// 3.1 优点:
// (1)简单,子类实例既是子类的实例也是父类的实例
// (2)父类在原型上新增的方法和属性都能被子类获取到
// 3.2 缺点:
// (1)传参不方便,只能在继承的时候传递参数,无法在创建子类实例的时候传入参数
// (2)无法实现多继承(一个子类继承多个父类)
// (3)原型上的属性、方法被所有的实例共享
2.2 基于构造(.call .apply)继承
// 1. 定义好的 Star 类
function Star(name) {
this.name = name
}
Star.prototype.role = '明星'
// 2. 定义好的 Star 类
function Sing(song) {
this.sing = function () {
console.log('他唱的歌有' + song)
}
}
// ------------------------------------------
// 3. 用构造继承方式继承,也就是用 .call .apply,未用原型
function Stars(name, song) {
Star.call(this, name) // 继承 Star 构造函数的属性或方法
Sing.call(this, song) // 继承 Sing 构造函数的属性或方法
}
// 4. new 一个实例,并调用属性和方法
let LiuDeHua = new Stars('刘德华', '忘情水') // new 实例的时候传参
console.log(LiuDeHua.name) // 输出 刘德华
console.log(LiuDeHua.role) // undefined,也就是不能继承原型上的属性和方法
LiuDeHua.sing() // 输出 忘情水
// 4. 总结:
// 特点:可以实现多继承
// 缺点:只能继承父类实例的属性和方法,不能继承原型上的属性和方法
2.3 组合继承
同时使用上面构造继承和原型链继承。
优点: 1.传参方便,可以在创建实例时传参
2.可以实现多继承
缺点: 1.无法继承父类原型上的属性和方法
2.因为原型上没有方法,所以无法实现函数复用
2.4 寄生组合继承(推荐)
// 1. 定义好的类
function Star(name, sex) {
this.name = name
this.sex = sex
}
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
console.log('他唱的歌叫' + song)
}
// 2. 寄生组合继承方式继承
// 2.1 先用构造继承,继承实例上的属性和方法,不继承原型上的
function LiuDeHua(name, sex) {
// Star.call(this, arguments)// 使用 arguments,输出[Arguments] { '0': '刘德华', '1': '男' }形式
Star.call(this, name, sex)
}
// 2.2 再用寄生继承,只继承原型上的
(function () {
let temp = function () {}
temp.prototype = Star.prototype
LiuDeHua.prototype = new temp()
})()
// 3. new 实例,调用属性和方法测试
let ldh = new LiuDeHua('刘德华', '男')
console.log(ldh.name) // 刘德华
ldh.singSong('忘情水') // 他唱的歌叫忘情水
// 4. 总结:
// 通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的
// 构造的时候,就不会初始化两次实例方法/属性
// 实现麻烦
本文来自博客园,作者:RHCHIK,转载请注明原文链接:https://www.cnblogs.com/suihung/p/16219899.html