针对方案一的缺点,可以借用构造函数来进行优化 。
- 在子类中通过call调用父类,这样在实例化子类时,每个实例就可以创建自己单独属性了;
// 定义Person父类公共的属性function Person(name, age) {this.name = namethis.age = age}// 定义Person父类的公共方法Person.prototype.say = function() {console.log('I am ' + this.name)}// 定义Student子类特有的属性function Student(name, age, sno) {// 通过call调用Person父类,创建自己的name和age属性Person.call(this, name, age)this.sno = sno}// 实现继承的核心:将父类的实例化对象赋值给子类的原型Student.prototype = new Person()// 定义Student子类特有的方法Student.prototype.studying = function() {console.log(this.name + ' studying')}// 实例化Studentconst stu1 = new Student('curry', 30, 101111)const stu2 = new Student('kobe', 24, 101112)console.log(stu1) // Person { name: 'curry', age: 30, sno: 101111 }console.log(stu2) // Person { name: 'kobe', age: 24, sno: 101112 }
内存表现:文章插图
缺点:
- 在实现继承的过程中,Person构造函数被调用了两次,一次在new Person(),一次在Person.call();
- 在Person的实例化对象上,也就是stu1和stu2的原型上,多出来了没有使用的属性name和age;
通过上面两种方案,我们想实现继承的目的是重复利用另外一个对象的属性和方法,如果想解决方案二中的缺点,那么就要减少Person的调用次数,避免去执行new Person(),而解决的办法就是可以新增一个对象,让该对象的原型指向Person的原型即可 。(1)对象的原型式继承
将对象的原型指向构造函数的原型的过程就叫做对象的原型式继承,主要可以通过以下三种方式实现:
- 封装一个函数,将传入的对象赋值给构造函数的原型,最后将构造函数的实例化对象返回;
- function createObj(o) { // 定义一个Fn构造函数 function Fn() {} // 将传入的对象赋值给Fn的原型 Fn.prototype = o // 返回Fn的实例化对象 return new Fn() } const protoObj = { name: 'curry', age: 30 } const obj = createObj(protoObj) // 得到的obj对象的原型已经指向了protoObj console.log(obj.name) // curry console.log(obj.age) // 30 console.log(obj.__proto__ === protoObj) // true
- 改变上面方法中的函数体实现,使用Object.setPrototypeOf()方法来实现,该方法设置一个指定的对象的原型到另一个对象或null;
- function createObj(o) { // 定义一个空对象 const newObj = {} // 将传入的对象赋值给该空对象的原型 Object.setPrototypeOf(newObj, o) // 返回该空对象 return newObj }
- 直接使用Object.create()方法,该方法可以创建一个新对象,使用现有的对象来提供新创建的对象的__proto__;
- const protoObj = { name: 'curry', age: 30 } const obj = Object.create(protoObj) console.log(obj.name) // curry console.log(obj.age) // 30 console.log(obj.__proto__ === protoObj) // true
寄生式继承就是将对象的原型式继承和工厂模式进行结合,即封装一个函数来实现继承的过程 。而这样结合起来实现的继承,又可以称之为寄生组合式继承 。下面就看看具体的实现过程吧 。
- 创建一个原型指向Person父类的对象,将其赋值给Student子类的原型;
- 在上面的实现方案中,Student子类的实例对象的类型都是Person,可以通过重新定义constructor来优化;
// 定义Person父类公共的属性function Person(name, age) {this.name = namethis.age = age}// 定义Person父类的公共方法Person.prototype.say = function() {console.log('I am ' + this.name)}// 定义Student子类特有的属性function Student(name, age, sno) {// 通过call调用Person父类,创建自己的name和age属性Person.call(this, name, age)this.sno = sno}// 调用Object.create方法生成一个原型指向Person原型的对象,并将这个对象赋值给Student的原型Student.prototype = Object.create(Person.prototype)// 定义Student原型上constructor的值为StudentObject.defineProperty(Student.prototype, 'constructor', {configurable: true,enumerable: false,writable: true,value: Student})// 定义Student子类特有的方法Student.prototype.studying = function() {console.log(this.name + ' studying')}// 实例化Studentconst stu1 = new Student('curry', 30, 101111)const stu2 = new Student('kobe', 24, 101112)console.log(stu1) // Student { name: 'curry', age: 30, sno: 101111 }console.log(stu2) // Student { name: 'kobe', age: 24, sno: 101112 }
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- JavaScript 变量的秘密,你知道吗
- Python反射介绍
- Java 面向对象进阶内容
- 阳台适宜面向什么方位?
- Python对象及内存管理机制
- JavaScript的ID生成器-Nano ID
- 使用MapStruct,让Bean对象之间转换更简单
- 电脑病毒“销声匿迹”?其实它只是“换对象”了
- 送男友什么礼物比较好
- JavaScript 内置对象之——Date,看完你会更清晰