一、原型链基础
JavaScript的原型链是一种从对象到另一个对象的委托关系。它在一个对象的属性找不到时,会沿着原型链往上找指定名称的属性或方法。我们来看一个例子:
function Animal(name){ this.name = name; } Animal.prototype.getName = function(){ return this.name; } function Dog(name){ this.name = name; } Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; var dog = new Dog('Henry'); console.log(dog.getName()); //输出:Henry
在此代码中,我们定义了Animal和Dog两个构造函数,并且让Dog的原型指向Animal的实例。当我们调用dog.getName()时,JavaScript引擎会在dog对象上查找getName方法,没有找到时会沿着原型链往上找Animal.prototype上的getName方法。
二、改变原型链
在上一个例子中,我们让Dog的原型指向了Animal的实例,在Dog.prototype上定义getName方法。这种方式同样有一些问题:
function Animal(name){ this.name = name; } Animal.prototype.getName = function(){ return this.name; } function Dog(name){ this.name = name; } Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; Dog.prototype.getName = function(){ return 'My name is ' + this.name; } var dog = new Dog('Henry'); console.log(dog.getName()); //输出:My name is Henry var animal = new Animal('Oscar'); console.log(animal.getName()); //输出:Oscar
在此代码中,我们在Dog.prototype上定义了一个新的getName方法,这会影响到它的父对象Animal.prototype上的getName方法,而且animal对象也会受到影响。
为了避免这种相互污染的问题,我们可以使用Object.create()方法,创建一个新的对象作为原型,并将其赋值给Dog.prototype。
function Animal(name){ this.name = name; } Animal.prototype.getName = function(){ return this.name; } function Dog(name){ this.name = name; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.getName = function(){ return 'My name is ' + this.name; } var dog = new Dog('Henry'); console.log(dog.getName()); //输出:My name is Henry var animal = new Animal('Oscar'); console.log(animal.getName()); //输出:Oscar
三、继承的多种方式
除了使用原型链实现继承之外,我们还可以使用其他几种方式:构造函数、组合继承、寄生构造函数和组合继承。
1. 构造函数
构造函数是最基本的一种继承方式。它有一些局限性,例如无法访问父对象的属性和方法,但在一些情况下,由于其简单易用,还是有着很大的用武之地。
function Animal(name){ this.name = name; this.getName = function(){ return this.name; } } function Dog(name, type){ this.type = type; this.getType = function(){ return this.type; } Animal.call(this, name); } var dog = new Dog('Henry', 'Poodle'); console.log(dog.getName()); //输出:Henry console.log(dog.getType()); //输出:Poodle
2. 组合继承
组合继承是指同时采用构造函数和原型链的继承方式。使用该方法,可以避免构造函数和原型链各自的局限性和缺陷。
function Animal(name){ this.name = name; } Animal.prototype.getName = function(){ return this.name; } function Dog(name, type){ this.type = type; Animal.call(this, name); } Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; Dog.prototype.getType = function(){ return this.type; } var dog = new Dog('Henry', 'Poodle'); console.log(dog.getName()); //输出:Henry console.log(dog.getType()); //输出:Poodle
3. 寄生构造函数
寄生构造函数是指在另一个构造函数的基础上增加一些方法或属性,从而达到继承的目的。与传统的构造函数继承相比,寄生构造函数继承的优势在于可以使用闭包,对属性和方法进行封装,从而达到不污染父对象的目的。
function Animal(name){ this.name = name; } Animal.prototype.getName = function(){ return this.name; } function Dog(name, type){ var self = new Animal(name); self.type = type; self.getType = function(){ return this.type; } return self; } var dog = new Dog('Henry', 'Poodle'); console.log(dog.getName()); //输出:Henry console.log(dog.getType()); //输出:Poodle
4. 组合继承
寄生组合继承也是一种很好的继承方式,它通过借用构造函数继承父对象的属性和方法,和通过原型链继承父对象的原型,达到继承的目的。
function Animal(name){ this.name = name; } Animal.prototype.getName = function(){ return this.name; } function Dog(name, type){ Animal.call(this, name); this.type = type; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.getType = function(){ return this.type; } var dog = new Dog('Henry', 'Poodle'); console.log(dog.getName()); //输出:Henry console.log(dog.getType()); //输出:Poodle