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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)