JavaScript继承

2017-12-05  本文已影响0人  海山城

继承是指一个对象能够直接使用另一个对象的属性方法

JS不提供原生的继承机制,下面来实现自定义继承
先定义两个类

function person(name, age){
  this.name = name
  this.age = age
}

person.prototype.printName = function(){
  console.log(this.name)
}

function male(sex){
  this.sex = sex
}

male.prototype.printSex = function(){
  console.log(this.sex)
}

属性的继承

属性获取的思路是在一个类中执行另一个类的构造函数,并且在执行时把另一个类中的this换成自己的(通过call来改变this),才能将这个属性继承到自己身上。
下面实现male类继承person的属性

function male(name, age, sex){
  person.call(this, name, age)
  this.sex = sex
}

male类实例化,看看属性中有没有person类中的name和age属性

var m = new male('hsc', 25, 'man')
console.log(m.name) // "hsc"
console.log(m.age) // 25

方法的继承

类的方法一般都定义在prototype中,想要实现方法的继承,需要将父类的原型放到原型链上即可

male.prototype = Object.create(person.prototype) //①
var m = new male('hsc', 25, 'man') //②
m.printName() //"hsc"

上面子类male的实例是可以调用父类person的printName方法了,相当于实现了方法的继承了,但是还是有点缺陷,我们知道prototype对象中有一个constructor属性指向其类型,但是我们复制的父元素的prototype,这时候constructor属性指向是不对的,是指向父类型person的。

console.log(male.prototype.constructor === person); //true

因此还需要修改一下constructor

male.prototype.constructor = male
console.log(male.prototype.constructor === person); //false
console.log(male.prototype.constructor === male); //true

代码归总

function inherit(superType, subType){
  var _prototype = Object.create(superType.prototype)
  _prototype.constructor = subType
  subType.prototype = _prototype
}

function person(name, age){
  this.name = name
  this.age = age
}

person.prototype.printName = function(){
  console.log(this.name)
}

function male(name, age, sex){
  person.call(this, name, age)
  this.sex = sex
}

inherit(person, male)
//子类方法的定义要在实现继承之后
male.prototype.printSex = function(){
  console.log(this.sex)
}

var m = new male('hsc', 25, 'man')
m.printName()              //"hsc"
console.log(m.age)         //25
m.printSex()               //"man"

有个问题,Object.create()是ES5的函数,如果不用ES5的写法的话,如下实现

function person(name, age){
  this.name = name
  this.age = age
}
person.prototype.printName = function(){
  console.log(this.name)
}

function male(name, age, sex){
  person.call(this, name, age)
  this.sex = sex
}

male.prototype = new person() 
//new person()相当于创建了一个对象,并且该对象的__proto__指向了person.prototype。
//该对象赋值给了male.prototype,所以male.prototype.__proto__ === person.prototype。
//即最终person的prototype被加入到了原型链上

male.prototype.printSex = function(){
  console.log(this.sex)
}
male.prototype.constructor =  male

var m = new male('hsc', 25, 'man')
m.printName()              //"hsc"
console.log(m.age)         //25
m.printSex()               //"man"

补充

hasOwnPerperty是Object.prototype的一个方法,可以判断一个对象是否包含自定义属性(属性是否是自己的),而不是原型链上的属性,hasOwnProperty是JavaScript中唯一一个处理属性但是不查找原型链的函数

m.hasOwnProperty('name');         // true,因为对象m有name这个属性
m.hasOwnProperty('sex');          // true,因为对象m有sex这个属性
m.hasOwnProperty('printName');    // false,因为对象m没有printName这个方法
m.hasOwnProperty('printSex');     // false,因为对象m没有printSex这个方法
m.__proto__.hasOwnProperty('printName');   // false,因为对象m.__proto__没有printName这个方法
m.__proto__.hasOwnProperty('printSex');    // true,因为对象m.__proto__有printSex这个方法
m.__proto__.__proto__.hasOwnProperty('printName');  // true,printName在m.__proto__.__proto__,即person.prototype上
person.prototype.hasOwnProperty('printName');       // true

最后附上一张手绘实现继承的图


继承

ps: 2018/7/12补充

发现一个问题

最近时间发现一个问题,使用male.prototype = Object.create(person.prototype)是没有问题的,而使用male.prototype = new person()会出现一个小问题,问题如下。

var fn = function(){}
fn.prototype = person.prototype
male.prototype = new fn()

上述修改的原理是创建一个不带属性的空函数,然后把person的prototype赋给这个空函数的prototype。最后在把这个空函数new出来的对象赋给male.prototype。这样实现了只继承person的prototype中的方法,属性。而不会把person自身的属性加入到male的prototype中。

最后补充一下es6的类的写法

其实就是上面方法的语法糖

class Person{
  constructor(name, age){
    this.name = name
    this.age = age
  }
  printName(){
    console.log(this.name)
  }
}
class Male extends Person{
  constructor(name, age, sex){
    super(name, age)
    this.sex= sex
  }
  printSex(){
    console.log(this.sex)
  }
}
上一篇 下一篇

猜你喜欢

热点阅读