JS继承的三种方式

前端开发
2020年02月17日
0

当谈到继承时,JavaScript只有一种结构:对象。每个实例对象(object)都有一个私有属性__proto__指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为null。根据定义,null没有原型,并作为这个原型链中的最后一个环节。

几乎所有JavaScript中的对象都是位于原型链顶端的Object的实例。

尽管这种原型继承通常被认为是JavaScript的弱点之一,但是原型继承模型本身实际上比经典模型更强大。例如,在原型模型的基础上构建经典模型相当简单。

1、基于原型链的继承

通过重写原型prototype来实现继承,但是子原型中的属性修改会导致父原型也被更改(同一引用)。

js
Teacher.prototype.name = '张三'; function Teacher () {} Student.prototype = Teacher.prototype; function Student () {} var student = new Student(); console.log(student.name); // '张三' // 弊端:子原型中的属性修改会导致父原型也被更改 Student.prototype.name = '李四'; Student.prototype.age = 20; console.log(Teacher.prototype); // {name: "李四", age: 20, constructor: ƒ}

2、借用构造函数

使用call/apply借用的形式来实现继承,这种形式无法访问到父类原型上的属性。

js
Teacher.prototype.name = '张三'; function Teacher () { this.skill = 'JS/JQ'; } function Student () { // Teacher.call(this); Teacher.apply(this); // 借用构造函数 this.age = 20; } var student = new Student(); console.log(student.skill); // 'JS/JQ' console.log(student.age); // 20 // 弊端:无法访问到父类原型上的属性 console.log(student.name); // undefined

3、圣杯模式

为了解决前面两种方式的弊端,我们采用圣杯模式来实现继承。

js
Teacher.prototype.name = '张三'; function Teacher () { this.skill = 'JS/JQ'; } function Student () { this.age = 20; } // 创建一个缓冲层 function Buffer () {} // 使得Buffer的原型对象与Teacher的原型对象指向同一个引用地址 Buffer.prototype = Teacher.prototype; var buffer = new Buffer(); // 然后让Student的原型指向Buffer的实例化对象 Student.prototype = buffer; var student = new Student(); console.log(student); // Student {age: 20} // 尝试修改原型上的属性 Student.prototype.test = 'test'; console.log(Student.prototype); // Teacher {test: "test"} console.log(Teacher.prototype); // {name: "张三", constructor: ƒ} // 父类原型未受到影响 // 获取父类原型上的属性 console.log(student.name); // '张三' // 能够获取得父类原型上的属性

虽然,圣杯模式下无法修改父类原型上的属性,但是对于引用值来说,还是会被修改的。

js
function Teacher () {} Teacher.prototype.students = ['马云', '马化腾']; Teacher.prototype.age = 88; function Student () { this.name = '张三'; this.skill = 'HTML/CSS'; } var Buffer = function () {} Buffer.prototype = Teacher.prototype; Student.prototype = new Buffer(); var student = new Student(); console.log(student); // Student {name: "张三", skill: "HTML/CSS"} student.students.push('马冬梅'); console.log(student.students); // ["马云", "马化腾", "马冬梅"] console.log(Teacher.prototype.students); // ["马云", "马化腾", "马冬梅"]

封装圣杯模式

js
/** * 继承 - 圣杯模式 * @name inherit * @param { Function } Target - 目标构造函数 * @param { Function } Origin - 继承源,父类构造函数 */ var inherit = (function () { var Buffer = function () {} return function (Target, Origin) { Buffer.prototype = Origin.prototype; Target.prototype = new Buffer(); Target.prototype.constructor = Target; // 还原构造器 Target.prototype.superClass = Origin; // 标识继承源 } })();