JavaScript中的几种继承方法示例

原理: 子类原型指向父类实例对象实现原型共享,即Son.prototype = new Father()。

这里先简单介绍下原型

js中每个对象都有一个__proto__属性,这个属性指向的就是该对象的原型。js中每个函数都有一个prototype属性,这个属性指向该函数作为构造函数调用时创建的实例的原型。原型对象上有一个constructor属性,指向创建该对象的构造函数,该属性不可枚举。

var obj = {}; obj.__proto__ === Object.prototype; //true console.log(Object.prototype.constructor) // ƒ Object

当我们访问一个对象的属性或者方法时,如果找不到,则会通过原型向上寻找,若原型上也未找到,则会去原型的原型上面去找。比如我要调用obj.toString方法时,在自身并未找到toString方法,则会去原型上去寻找,即在Object.prototype上去寻找,找到后运行该方法。

var obj = {}; obj.toString(); obj.__proto__.toString(); //obj.__proto__和Object.prototype指向的是一个对象,自然就能访问Object.prototype上的toString方法啦

注意:原型链的终点是null,使用bind方法返回的函数没有prototype属性。

var obj = {}; function fn(){}; fn.bind(obj).prototype; // undefined Object.prototype.__proto__; // null

原型链接继承

function Father(age){ this.age = age; this.color = ['red','pink'] } Father.prototype.sayHello = function(){ console.log('hello') } function Son(sex){ this.sex = sex } console.log(Son.prototype.constructor) // ƒ Son Son.prototype = new Father(15) // 原型链继承关键 var son = new Son('男') son.color.push('black') var son2 = new Son('女') son.sayHello() // hello son.sayHello === son2.sayHello //true console.log(son2.color) // ['red','pink','black'] console.log(Son.prototype.constructor) // ƒ Father

可以看到通过原型链实现继承,原型上引用类型的值会被所有实例共享。子类的constructor指向会发生改变,而且在创建子类实例时不可以向父类构造函数传递参数。可以手动把子类constructor属性指回其构造函数。

//写法一 Son.prototype.constructor = Son // 这种写法有点缺点,它会让constructor属性变的可以枚举。 //写法二 Object.defineProperty(Son.prototype,'constructor',{ enumerable:false, // 设置不可枚举 value:Son })

2.构造函数继承

原理:在子类构造函数中通过apply或者call调用父类构造函数来继承属性或方法。

function Father(name){ this.color = ['red'] this.sayHello = function(){ console.log('hello') } } Father.prototype.sayName = function(){ console.log('zs') } function Son(num,name){ Father.call(this,name) //实现继承的关键代码 this.num = num } var son = new Son(10,'zs') var son2 = new Son(15,'ls') son.color.push('pink') console.log(son2.color) // ['red'] son.sayName() //报错 son.sayName is not a function console.log(son.sayHello === son2.sayHello) //false

可以看出通过构造函数实现继承,解决了原型链继承不能向父类传参以及引用类型值共享的问题。但这种继承方法却不能访问父类构造函数原型上的方法和属性,而且定义在父类构造函数中的方法也不能复用。

3.组合式继承

组合继承,有时候也叫伪经典继承,它是将原型链继承和构造函数继承结合到一起的一种继承模式。实现思路是通过原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。

function Father(name){ this.color = ['red'] } Father.prototype.sayName = function(){ console.log('zs') } function Son(num,name){ Father.call(this,name) //继承实例属性 this.num = num } Son.prototype = new Father() //继承原型上属性 Son.prototype.constructor = Son var son = new Son(10,'zs') var son2 = new Son(15,'ls') son.color.push('pink') console.log(son.color,son2.color) //['red','pink'] ['red'] son.sayName() // zs

组合式继承避免了原型链继承和构造函数继承的缺点,融合了它们的优点,成为JavaScript中常用的一种继承模式。

4.寄生式继承

寄生式继承与工厂模式类似,一般用来继承对象。即创建一个封装继承的函数,在函数内部复制一份该对象,对复制的对象进行处理,返回复制的对象。

function createAnother(obj){ var clone = Object.create(obj) clone.name = 'zs' clone.sayHello = function(){ console.log('hello') } return clone } var obj = {age:15} var newObj = createAnother(obj) // 15 console.log(newObj.name) // zs newObj.sayHello() // hello

5.寄生组合式继承

前面说到过组合式继承是Javascript中最常用的继承模式,不过这种模式也有自己的不足,它会调用两次父类构造函数。第一次是在将子类原型指向父类实例的时候,第二次是在子类构造函数中调用的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/86861f1149be2dfacc7eb23399d1cdca.html