JavaScript是面向对象的弱类型语言,继承是其重要的特性之一,这里总结下常用的四种继承方法。
先定义一个父级构造函数,并在其原型上添加一个speak方法
//定义父级构造函数 function Person(name, age) { this.name = name; this.age = age; this.intro = function() { console.log(this.name + ' is ' + this.age + ' years old'); } } //父级原型添加新方法 Person.prototype.speak = function(language) { console.log(this.name + ' can speak ' + language); }
以下四种继承方式均在此父级构造函数基础上实现。
1、传统形式,通过原型链继承
将父级构造函数的实例作为子级构造函数的原型
//定义子级构造函数 function Man() { } //将父级实例作为子级原型 Man.prototype = new Person('Jack', 18); //子级原型添加name属性会覆盖原型name属性 Man.prototype.name = 'Toms'; //创建子级实例对象 var man = new Man(); //调用父级构造函数内的方法 man.intro(); // Toms is 18 years old //调用父级原型自定义的speak方法 man.speak('Chinese'); // Toms can speak Chinese
缺点:继承父级所有属性和方法,没有选择性
2、通过父级构造函数,即子级构造函数内调用父级构造函数
其实就是借用别人的方法,实现自己的功能
function Man() { Person.call(this); this.name = 'Jack'; this.age = 19 } var man = new Man();
// 借用Person的intro方法 man.intro(); // Jack is 19 years old
缺点:不能继承父级构造函数原型,每次创建实例要多运行一个构造函数
3、通过原型共享实现继承
即子级原型等于父级原型
function Man(name) { this.name = name; } //构造函数原型共享 Man.prototype = Person.prototype; var man = new Man('Jack'); //子级实例可调用共享的原型上的方法speak man.speak('Chinese'); // Jack can speak Chinese
缺点:共享原型,一个修改原型属性和方法,另一个会同步
4、堪称完美的圣杯模式
通过在一个立即执行函数中定义一个临时构造函数,来中转源构造函数到目标构造函数的原型链,这样修改目标构造函数的原型不会直接影响到源构造函数原型,
同时执行完毕立即销毁,减少内存开销。
var inherit = (function () { var Temp = function () {}; // 定义临时构造函数用于原型链的中转 return function (Target, Origin) { Temp.prototype = Origin.prototype; // Temp继承Origin原型 Target.prototype = new Temp(); // Target继承Temp对象原型 Target.prototype.constructor = Target; // 改写Target原型上的构造器指向 Target.prototype.ancestor = Origin.prototype; // 标记Target真正继承的原型 } }());