>

六种继承方式,JavaScript继承方式

- 编辑:澳门新葡亰平台游戏 -

六种继承方式,JavaScript继承方式

JavaScript 四种持续情势

2017/06/20 · JavaScript · 继承

原稿出处: Xuthus Blog   

接轨是面向对象编制程序中又一百般首要的定义,JavaScript扶植促成持续,不协助接口世襲,完毕延续首要信任原型链来完成的。

这篇开头写多少个工具函数达成类的恢宏。每一个工具函数都以本着一定的写类方式(习贯)。这篇遵照构造函数情势写类:属性(字段)和办法都挂在this上。以下分别提供了个类,分别作为父类和子类。

近些日子温故了下JS OO之写类措施,从那篇最早我们看看JS OO之继续格局。

原型链

先是得要领悟怎么着是原型链,在风度翩翩篇随笔看懂proto和prototype的涉及及界别中讲得要命详尽

原型链世袭基本观念便是让一个原型对象指向另二个品种的实例

function SuperType() { this.property = true } SuperType.prototype.getSuperValue = function () { return this.property } function SubType() { this.subproperty = false } SubType.prototype = new SuperType() SubType.prototype.getSubValue = function () { return this.subproperty } var instance = new SubType() console.log(instance.getSuperValue()) // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function SuperType() {
  this.property = true
}
SuperType.prototype.getSuperValue = function () {
  return this.property
}
function SubType() {
  this.subproperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
  return this.subproperty
}
var instance = new SubType()
console.log(instance.getSuperValue()) // true

代码定义了四个品类SuperType和SubType,每种品种分别有五性格能和三个办法,SubType世袭了SuperType,而后续是通过创制SuperType的实例,并将该实例赋给SubType.prototype完结的。

福寿齐天的庐山面目目是重写原型对象,代之以叁个新品类的实例,那么存在SuperType的实例中的全部属性和措施,现在也存在于SubType.prototype中了。

大家了解,在创设二个实例的时候,实例对象中会有二个内部指针指向成立它的原型,进行关联起来,在这里处代码SubType.prototype = new SuperType(),也会在SubType.prototype制造三个里面指针,将SubType.prototype与SuperType关联起来。

于是instance指向SubType的原型,SubType的原型又指向SuperType的原型,进而在instance在调用getSuperValue(卡塔尔国方法的时候,会沿着这条链一贯往上找。

累积措施

在给SubType原型加多方法的时候,固然,父类上也可以有生龙活虎致的名字,SubType将会覆盖这几个艺术,达到重新的目标。 但是其一格局依旧留存于父类中。

记住无法以字面量的样式丰盛,因为,上边说过通超过实际例世襲本质上就是重写,再使用字面量情势,又是叁遍重写了,但此番重写未有跟父类有别的关系,所以就能招致原型链截断。

function SuperType() { this.property = true } SuperType.prototype.getSuperValue = function () { return this.property } function SubType() { this.subproperty = false } SubType.prototype = new SuperType() SubType.prototype = { getSubValue:function () { return this.subproperty } } var instance = new SubType() console.log(instance.getSuperValue()) // error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function SuperType() {
  this.property = true
}
SuperType.prototype.getSuperValue = function () {
  return this.property
}
function SubType() {
  this.subproperty = false
}
SubType.prototype = new SuperType()
SubType.prototype = {
  getSubValue:function () {
   return this.subproperty
  }
}
var instance = new SubType()
console.log(instance.getSuperValue())  // error

问题

生龙活虎味的运用原型链世袭,首要难题来高傲含引用类型值的原型。

function SuperType() { this.colors = ['red', 'blue', 'green'] } function SubType() { } SubType.prototype = new SuperType() var instance1 = new SubType() var instance2 = new SubType() instance1.colors.push('black') console.log(instance1.colors) // ["red", "blue", "green", "black"] console.log(instance2.colors) // ["red", "blue", "green", "black"]

1
2
3
4
5
6
7
8
9
10
11
function SuperType() {
  this.colors = ['red', 'blue', 'green']
}
function SubType() {
}
SubType.prototype = new SuperType()
var instance1 = new SubType()
var instance2 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors)  // ["red", "blue", "green", "black"]
console.log(instance2.colors) // ["red", "blue", "green", "black"]

在SuperType结构函数定义了一个colors属性,当SubType通过原型链继承后,这几个本性就能够忍俊不禁SubType.prototype中,就跟特意创制了SubType.prototype.colors同样,所以会招致SubType的富有实例都会分享那么些个性,所以instance1改善colors这几个援引类型值,也会反映到instance第22中学。

//  父类Person
function Person(nationality) {
    this.nationality = nationality;
    this.setNationality = function(n) {this.nationality=n;};
    this.getNationality = function() {return this.nationality;};
}

// 类Man
function Man(name) {
    this.name = name;
    this.setName = function(n){this.name=n;};
    this.getName = function(){return this.name;};
}

面向对象的言语好些个都协助世襲,世襲最注重的亮点正是代码复用,进而塑造大型软件系统。假诺二个类能够重用另三个类的习性和或方法,就称为继承。从这一个角度来寻访JS的一而再方式。JS中世袭格局与写类方式有关。差别的写类格局变成分化的接续格局。种种流行JS库世襲格局也各不相同。从最简便的复用初步。

借用架构函数

此格局为了消除原型中包涵援引类型值所推动的难点。

这种办法的出主意正是在子类布局函数的当中调用父类布局函数,能够依附apply(卡塔尔(英语:State of Qatar)和call(卡塔尔国方法来退换指标的推行上下文

function SuperType() { this.colors = ['red', 'blue', 'green'] } function SubType() { // 继承SuperType SuperType.call(this) } var instance1 = new SubType() var instance2 = new SubType() instance1.colors.push('black') console.log(instance1.colors) // ["red", "blue", "green", "black"] console.log(instance2.colors) // ["red", "blue", "green"]

1
2
3
4
5
6
7
8
9
10
11
12
function SuperType() {
  this.colors = ['red', 'blue', 'green']
}
function SubType() {
  // 继承SuperType
  SuperType.call(this)
}
var instance1 = new SubType()
var instance2 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors)  // ["red", "blue", "green", "black"]
console.log(instance2.colors) // ["red", "blue", "green"]

在新建SubType实例是调用了SuperType构造函数,这样的话,就能在新SubType目的上实行SuperType函数中定义的持有目的初叶化代码。

结果,SubType的种种实例就可以有着温馨的colors属性的别本了。

传送参数

依附结构函数还或许有二个优势就是能够传递参数

function SuperType(name) { this.name = name } function SubType() { // 继承SuperType SuperType.call(this, 'Jiang') this.job = 'student' } var instance = new SubType() console.log(instance.name) // Jiang console.log(instance.job) // student

1
2
3
4
5
6
7
8
9
10
11
12
function SuperType(name) {
  this.name = name
}
function SubType() {
  // 继承SuperType
  SuperType.call(this, 'Jiang')
 
  this.job = 'student'
}
var instance = new SubType()
console.log(instance.name)  // Jiang
console.log(instance.job)   // student

问题

万生龙活虎仅仅注重布局函数,方法都在布局函数中定义,因而函数无法完毕复用

 

 

整合世袭(原型链+布局函数卡塔尔国

组合世襲是将原型链世襲和构造函数结合起来,进而发挥两岸之长的风流倜傥种方式。

思路便是采纳原型链完成对原型属性和议程的一而再再三再四,而通过借用布局函数来完毕对实例属性的接续。

这样,既通过在原型上定义方法达成了函数复用,又能够保障每种实例都有它和睦的性质。

function SuperType(name) { this.name = name this.colors = ['red', 'blue', 'green'] } SuperType.prototype.sayName = function (卡塔尔国 { console.log(this.name卡塔尔(英语:State of Qatar) } function SubType(name, job卡塔尔(英语:State of Qatar) { // 世襲属性 SuperType.call(this, name卡塔尔(英语:State of Qatar) this.job = job } // 世袭方法 SubType.prototype = new SuperType(卡塔尔 SubType.prototype.constructor = SuperType SubType.prototype.sayJob = function(卡塔尔(قطر‎ { console.log(this.job卡塔尔(قطر‎} var instance1 = new SubType('Jiang', 'student'卡塔尔instance1.colors.push('black'卡塔尔 console.log(instance1.colors卡塔尔(英语:State of Qatar) //["red", "blue", "green", "black"] instance1.sayName() // 'Jiang' instance1.sayJob() // 'student' var instance2 = new SubType('J', 'doctor') console.log(instance2.colors) // //["red", "blue", "green"] instance2.sayName() // 'J' instance2.sayJob() // 'doctor'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function SuperType(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
  console.log(this.name)
}
function SubType(name, job) {
  // 继承属性
  SuperType.call(this, name)
 
  this.job = job
}
// 继承方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SuperType
SubType.prototype.sayJob = function() {
  console.log(this.job)
}
var instance1 = new SubType('Jiang', 'student')
instance1.colors.push('black')
console.log(instance1.colors) //["red", "blue", "green", "black"]
instance1.sayName() // 'Jiang'
instance1.sayJob()  // 'student'
var instance2 = new SubType('J', 'doctor')
console.log(instance2.colors) // //["red", "blue", "green"]
instance2.sayName()  // 'J'
instance2.sayJob()  // 'doctor'

这种情势制止了原型链和布局函数继承的短处,融合了她们的优点,是最常用的生机勃勃种持续格局。

1,继承工具函数风度翩翩

1、布局函数形式写类,通过艺术调用复制父类属性/字段到子类 完毕延续

原型式世袭

依附原型能够依据已部分对象成立新目的,同期还不用为此创立自定义类型。

function object(o) { function F() {} F.prototype = o return new F() }

1
2
3
4
5
function object(o) {
  function F() {}
  F.prototype = o
  return new F()
}

在object函数内部,先创设叁个近来的布局函数,然后将盛传的对象作为这一个布局函数的原型,最终回到那些一时类型的三个新实例。

实为上来讲,object对传播个中的对象进行了叁次浅复制。

var person = { name: 'Jiang', friends: ['Shelby', 'Court'] } var anotherPerson = object(person) console.log(anotherPerson.friends) // ['Shelby', 'Court']

1
2
3
4
5
6
var person = {
  name: 'Jiang',
  friends: ['Shelby', 'Court']
}
var anotherPerson = object(person)
console.log(anotherPerson.friends)  // ['Shelby', 'Court']

这种形式要去你必需有二个对象作为另叁个指标的根底。

在此个事例中,person作为另三个目的的底蕴,把person传入object中,该函数就能再次来到一个新的目的。

本条新对象将person作为原型,所以它的原型中就包蕴叁当中央项目和一个引用类型。

故而意味着黄金年代旦还应该有其它一个对象关联了person,anotherPerson矫正数组friends的时候,也会反映在此个目标中。

Object.create()方法

ES5经过Object.create(卡塔尔(قطر‎方法则范了原型式世袭,能够接收七个参数,叁个是用作新对象原型的靶子和二个可选的为新目的定义额外属性的对象,行为相像,基本用法和方面的object相通,除了object不能够担任第二个参数以外。

var person = { name: 'Jiang', friends: ['Shelby', 'Court'] } var anotherPerson = Object.create(person) console.log(anotherPerson.friends) // ['Shelby', 'Court']

1
2
3
4
5
6
var person = {
  name: 'Jiang',
  friends: ['Shelby', 'Court']
}
var anotherPerson = Object.create(person)
console.log(anotherPerson.friends)  // ['Shelby', 'Court']
/**
 * @param {Function} subCls 子类
 * @param {Function} superCls 父类
 * @param {Object} param 父类构造参数
 */
function extend(subCls, superCls, param) {
    superCls.call(subCls.prototype, param);
}

这里父类,子类都使用构造函数格局写,不用原型。子类调用父类函数来复制父类的性情。

寄生式世襲

寄生式世袭的思路与寄生布局函数和工厂情势雷同,即成立叁个仅用于封装世襲进度的函数。

function createAnother(o卡塔尔(قطر‎ { var clone = Object.create(o卡塔尔国 // 创制三个新对象 clone.sayHi = function(卡塔尔国 { // 增加格局 console.log('hi'卡塔尔国} return clone // 再次回到这一个指标 } var person = { name: 'Jiang' } var anotherPeson = createAnother(person卡塔尔(قطر‎ anotherPeson.sayHi(卡塔尔国

1
2
3
4
5
6
7
8
9
10
11
12
function createAnother(o) {
  var clone = Object.create(o) // 创建一个新对象
  clone.sayHi = function() { // 添加方法
    console.log('hi')
  }
  return clone  // 返回这个对象
}
var person = {
  name: 'Jiang'
}
var anotherPeson = createAnother(person)
anotherPeson.sayHi()

依据person再次来到了一个新指标anotherPeson,新目的不仅仅全数了person的习性和议程,还恐怕有温馨的sayHi方法。

在主要思虑对象并不是自定义类型和布局函数之处下,那是一个实用的情势。

利用如下

/**
 * 父类Polygon:多边形
 * @param {Object} sides
 */
function Polygon(sides) {
    this.sides = sides;
    this.setSides = function(s) {this.sides=s;}
}

/**
 * 子类Triangle:三角形
 */
function Triangle() {
    this.tempfun = Polygon;//父类引用赋值给子类的一个属性tempfun
    this.tempfun(3);//调用
    delete this.tempfun;//删除该属性
    this.getArea = function(){};
}

//new个对象 
var tri = new Triangle();
console.log(tri.sides);//继承的属性
console.log(tri.setSides);//继承的方法
console.log(tri.getArea);//自有的方法

//缺点是对于Triangle的实例对象用instanceof为父类Polygon时是false
console.log(tri instanceof Triangle);//true
console.log(tri instanceof Polygon);//false

寄生组合式世袭

在前头说的三结合情势(原型链+布局函数卡塔尔国中,世袭的时候供给调用两遍父类布局函数。

父类

function SuperType(name) { this.name = name this.colors = ['red', 'blue', 'green'] }

1
2
3
4
function SuperType(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}

第一遍在子类布局函数中

function SubType(name, job卡塔尔国 { // 继承属性 SuperType.call(this, name卡塔尔(قطر‎this.job = job }

1
2
3
4
5
6
function SubType(name, job) {
  // 继承属性
  SuperType.call(this, name)
 
  this.job = job
}

第三遍将子类的原型指向父类的实例

// 世襲方法 SubType.prototype = new SuperType(卡塔尔国

1
2
// 继承方法
SubType.prototype = new SuperType()

当使用var instance = new SubType()的时候,会时有发生两组name和color属性,豆蔻年华组在SubType实例上,风姿罗曼蒂克组在SubType原型上,只不超过实际例上的隐蔽了原型上的。

采纳寄生式组合格局,可以躲藏那一个主题材料。

这种格局通过借用构造函数来世袭属性,通过原型链的混成格局来持续方法。

基本思路:不必为了钦赐子类型的原型而调用父类的构造函数,大家须要的意气风发味正是父类原型的三个别本。

真相上正是利用寄生式世襲来世袭父类的原型,在将结果钦命给子类型的原型。

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

1
2
3
4
5
function inheritPrototype(subType, superType) {
  var prototype = Object.create(superType.prototype)
  prototype.constructor = subType
  subType.prototype = prototype
}

该函数完成了寄生组合继承的最简便易行款式。

那么些函数选拔多个参数,一个子类,叁个父类。

首先步成立父类原型的别本,第二步将创造的别本增加constructor属性,第三部将子类的原型指向那些别本。

function SuperType(name) { this.name = name this.colors = ['red', 'blue', 'green'] } SuperType.prototype.sayName = function (卡塔尔国 { console.log(this.name卡塔尔(قطر‎ } function SubType(name, job卡塔尔 { // 世襲属性 SuperType.call(this, name卡塔尔国 this.job = job } // 世襲inheritPrototype(SubType, SuperType卡塔尔 var instance = new SubType('Jiang', 'student'卡塔尔国 instance.sayName(卡塔尔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function SuperType(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
  console.log(this.name)
}
function SubType(name, job) {
  // 继承属性
  SuperType.call(this, name)
 
  this.job = job
}
// 继承
inheritPrototype(SubType, SuperType)
var instance = new SubType('Jiang', 'student')
instance.sayName()

增补:直接利用Object.create来得以达成,其实就是将地点封装的函数拆开,这样演示可以更便于通晓。

function SuperType(name) { this.name = name this.colors = ['red', 'blue', 'green'] } SuperType.prototype.sayName = function (卡塔尔(قطر‎ { console.log(this.name卡塔尔(قطر‎ } function SubType(name, job卡塔尔(قطر‎ { // 世襲属性 SuperType.call(this, name卡塔尔(قطر‎ this.job = job } // 世袭 SubType.prototype = Object.create(SuperType.prototype卡塔尔(قطر‎ // 修复constructor SubType.prototype.constructor = SubType var instance = new SubType('Jiang', 'student'卡塔尔 instance.sayName(卡塔尔(英语:State of Qatar)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function SuperType(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
  console.log(this.name)
}
function SubType(name, job) {
  // 继承属性
  SuperType.call(this, name)
 
  this.job = job
}
// 继承
SubType.prototype = Object.create(SuperType.prototype)
// 修复constructor
SubType.prototype.constructor = SubType
var instance = new SubType('Jiang', 'student')
instance.sayName()

ES6新添了三个主意,Object.setPrototypeOf,能够直接创立关联,何况不要手动增多constructor属性。

// 继承 Object.setPrototypeOf(SubType.prototype, SuperType.prototype) console.log(SubType.prototype.constructor === SubType) // true

1
2
3
// 继承
Object.setPrototypeOf(SubType.prototype, SuperType.prototype)
console.log(SubType.prototype.constructor === SubType) // true

1 赞 2 收藏 评论

澳门新葡亰平台游戏 1

extend(Man, Person, 'China');
var m = new Man('jack');
console.log(m.nationality);//China
console.log(m.setNationality('Japan'));
console.log(m.getNationality('Japan'));//Japan

因为 JavaScript中具名函数的三种调用情势,子类还足以有以下的各个落到实处格局。只是在子类中调用父类方法不相同而已。

出口能够看来Man世襲了Person的质量及持有办法。这种持续情势于java的很分裂等啊,

function Triangle() {
    Polygon.call(this,3); //call方式调用父类
    this.getArea = function(){};    
}
function Triangle() {
    Polygon.apply(this,[3]); //apply方式调用父类
    this.getArea = function(){};
}
function Triangle() {
    var temp = new Polygon(3); //new方式调用父类
    for(atr in temp) { //全部复制给子类
        this[atr] = temp[atr];
    }   
    this.getArea = function(){};
}
class Animal {
    int legs;   
    Animal(int l) {
        legs = l;
    }
    int getLegs() {
        return legs;
    }
}
public class Person extends Animal{
    //属性(字段)
    String name;    
    //构造方法(函数)
    Person(int legs, String name) {
        super(legs);//调用父类构造器
        this.name = name;
    }   
    //方法
    String getName() {
        return this.name;
    }
    public static void main(String[] args) {

        Person p = new Person(2,"jack");        
        System.out.println(p.legs);
    }
}

这种情势的缺欠是子类的实例对象用instanceof检查父类时总是false。那与java中继续"is a "的涉嫌是反其道而行之的。

Java中,子类Person在作者布局方法中调用父类布局方法super(legs卡塔尔国,创制对象的时候一向将父类布局参数legs:2传进去,不独有只传自个儿的name:jack。上面JavaScript世襲是在extend时传父类布局参数(extend函数的第七个参数),并不是在new Man时将父类布局参数字传送过去。好,模拟Java来落实下extend,这里玄妙的在子类上暂存了父类援用。

 

 

2、原型情势写类,原型方式三番五次

2,继承工具函数二

core JS本人的目的系统就是使用原型形式(prototype based卡塔尔(英语:State of Qatar)世袭的。大概说core JS未有运用大范围的类世襲(class based卡塔尔(قطر‎系统,而是利用原型世袭来贯彻和煦的靶子系统。工作中大家也足以用原型情势来促成持续,代码复用以营造和煦的功效模块。 

/**
 * @param {Function} subCls
 * @param {Function} superCls
 */
function extend(subCls, superCls) { 
    subCls.supr = superCls;
} 
/**
 * 父类Polygon:多边形
 * 
 */
function Polygon() {}
Polygon.prototype.sides = 0;
Polygon.prototype.setSides = function(s) {this.sides=s;}

/**
 * 子类Triangle:三角形
 */
function Triangle() {}
Triangle.prototype = new Polygon(); //这是原型继承关键的一句
Triangle.prototype.getArea = function(){}

//new个对象
var tri = new Triangle();
console.log(tri.sides);//继承的属性
console.log(tri.setSides);//继承的方法
console.log(tri.getArea);//自有方法

//instanceof测试
console.log(tri instanceof Triangle);//true,表明该对象是三角形
console.log(tri instanceof Polygon);//true,表明三角形也是多边形

要么以Person为父类,来贯彻子类Woman

固然从输出能够见见子类世袭了父类Polygon的品质sides和办法setSides,但sides是0,怎会是三角形呢。还得调用下tri.setSides(3卡塔尔(قطر‎使之造成三角形。那样仿佛非常不便利。无法传参数,便是原型方式的宿疾。优点是没有错的维护了"is a"的关系。

function Woman(nationality, name) {
    Woman.supr.call(this, nationality);//和java有点类似哦,在子类中调用父类构造器
    this.name = name;
    this.setName = function(n){this.name=n;};
    this.getName = function(){return this.name;};
}
extend(Woman, Person);

 

最终,成立对象的议程和java也就好像,即new的时候还要将父类结构参数(nationality:Japan卡塔尔(英语:State of Qatar)传进去。

3、组合结构函数/原型形式写类,采纳前边种格局继续

var w = new Woman('Japan', 'lily');
console.log(w.nationality);//Japan
w.setNationality('U.S.A');
console.log(w.getNationality());//U.S.A

澳门新葡亰平台游戏,这种办法父类,子类的习性都挂在布局函数里,方法都挂在原型上。

/**
 * 父类Polygon:多边形
 */
function Polygon(sides) {
    this.sides = sides;
}
Polygon.prototype.setSides = function(s) {this.sides=s;}

/**
 * Triangle 三角形
 * @param {Object} base 底
 * @param {Object} height 高
 */
function Triangle(base,height) {
    Polygon.call(this,3);//复制父类属性给自己
    this.base = base;
    this.height = height;
}
Triangle.prototype = new Polygon();//复制父类方法给自己

Triangle.prototype.getArea = function(){ //最后定义自己的方法
    return this.base*this.height/2;
}

//new个对象
var tri = new Triangle(12,4);
console.log(tri.sides);//继承的属性
console.log(tri.setSides);//继承的方法
console.log(tri.base);//自有属性
console.log(tri.height);//自有属性
console.log(tri.getArea);//自有方法

//instanceof测试,表明正确的维护了"is a"的关系
console.log(tri instanceof Triangle);//true,表明该对象是三角形
console.log(tri instanceof Polygon);//true,表明三角形也是多边形

 

相关:

JavaScript世襲情势(2) 

JavaScript继承方式(3)

JavaScript继承格局(4)

snandy Class.js

本文由前端php发布,转载请注明来源:六种继承方式,JavaScript继承方式