美文网首页
JavaScript继承

JavaScript继承

作者: 海山城 | 来源:发表于2017-12-05 15:24 被阅读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"
  • Object.create(person.prototype)方法创建了一个对象,该对象的__proto__指向了传入的参数person.prototype,然后将该对象赋值给子类的原型(male.prototype),最终实现了父类的原型加入到了原型链上
  • 修改子类的prototype一定要在创建子类实例之前,否则会报错(上述代码①和②位置交换就会报错)
  • 修改子类的prototype一定要在给子类的prototype添加自己的方法之前,否则子类自己的方法会被覆盖掉(printAge方法需要在①后面②之前定义)
  • 之所以将父类prototype加入原型链,而不是直接赋给子类的prototype,是防止修改子类的prototype也会修改父类的prototype

上面子类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"
  • 核心思想和上面一样,是把父类的prototype放到子类实例的原型链上,这样子类的实例就可以掉用父类的方法了,这种方法也比较符合继承的理念

补充

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()会出现一个小问题,问题如下。

  • new person()创造出来的对象中,会包含person的属性(即name、age。即使你构造时没传参数,也会有,只不过两个的值为undefined),把它赋给male的prototype。也就是说,male.prototype上面会有name和age两个属性,这显然不是我们想看到的。我们期待的male的prototype,只有一个__proto__指向person.prototype,还有一个constructor指向male自身,不在有多余属性。因此,可以将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)
  }
}

相关文章

  • 前端面试题目(二)

    javascript对象的几种创建方式 javascript继承的6种方法 详情:[JavaScript继承方式详...

  • 函数的原型对象

    什么是原型? 原型是Javascript中的继承的继承,JavaScript的继承就是基于原型的继承。 函数的原型...

  • 005|JavaScript ES6新特性之Classes

    在过去,需要像 053|JavaScript 继承详解 那样实现继承。JavaScript这种继承实现方式与其它面...

  • Web前端经典面试试题及答案2

    javascript面向对象中继承实现? 面向对象的基本特征有:封闭、继承、多态。在JavaScript中实现继承...

  • 一文带你彻底理解 JavaScript 原型对象

    一、什么是原型 原型是Javascript中的继承的基础,JavaScript的继承就是基于原型的继承。 1.1 ...

  • javascript代码积累

    一、javascript实现继承 1.基于原型链实现继承 2.基于属性和方法复制实现继承 二、javascript...

  • 理解 JavaScript 中的原型链

    JavaScript 作为一门面对对象语言,但是却不支持接口继承,只支持实现继承。JavaScript 中实现继承...

  • Javascript原型和原型链

    JavaScript在ES6之前没有类似class,extend的继承机制,JavaScript的继承主要是通过原...

  • JavaScript--对象创建和继承方法

    JavaScript创建对象方法总结精彩博文javascript继承讲解精彩博文于江水 继承讲解 JavaScri...

  • JavaScript 继承

    继承是JS中非常内容,原因就是JS没有地道的继承方式,我们只能通过各种方式来模拟面向对象中的继承。下面介绍几种常见...

网友评论

      本文标题:JavaScript继承

      本文链接:https://www.haomeiwen.com/subject/yqlwbxtx.html