ca881亚洲城娱乐:JavaScript继承方式,六种继承方式

JavaScript 七种持续格局

2017/06/20 · JavaScript
· 继承

原稿出处: Xuthus
Blog   

继续是面向对象编制程序中又叁万分首要的概念,JavaScript帮忙促成持续,不帮忙接口继承,完成一连首要依靠原型链来完成的。

3,继承工具函数3

那篇起头写多少个工具函数达成类的壮大。每一种工具函数都是针对一定的写类情势(习惯)。那篇依据构造函数格局写类:属性(字段)和格局都挂在this上。以下分别提供了个类,分别作为父类和子类。

前段时间温故了下JS OO之写类措施,从那篇开端大家看看JS OO之继续格局。

原型链

率先得要清楚怎么样是原型链,在1篇小说看懂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落成的。

实现的原形是重写原型对象,代之以3个新品类的实例,那么存在SuperType的实例中的全体属性和方式,今后也设有于SubType.prototype中了。

咱俩知道,在开创二个实例的时候,实例对象中会有1个内部指针指向创设它的原型,进行关联起来,在此处代码SubType.prototype = new SuperType(),也会在SubType.prototype创造1个里面指针,将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中学。

view sourceprint?1 /** 

//  父类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()和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属性的副本了。

传送参数

依傍构造函数还有二个优势正是能够传递参数

ca881亚洲城娱乐:JavaScript继承方式,六种继承方式。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

问题

假设只是凭借构造函数,方法都在构造函数中定义,由此函数无法达到规定的标准复用

2  * @param {Function} subCls 

 

 

组合继承(原型链+构造函数)

组成继承是将原型链继承和构造函数结合起来,从而发挥双方之长的壹种形式。

思路就是应用原型链完毕对原型属性和艺术的一而再,而通过借用构造函数来促成对实例属性的一而再。

如此,既通过在原型上定义方法达成了函数复用,又能够确定保障每一种实例都有它和谐的品质。

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 instance一 = new SubType(‘Jiang’, ‘student’)
instance1.colors.push(‘black’) console.log(instance一.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
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’

那种格局防止了原型链和构造函数继承的通病,融合了他们的亮点,是最常用的壹种持续格局。

3  * @param {Function} superCls 

一,继承工具函数壹

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作为原型,所以它的原型中就富含2其中坚项目和一个引用类型。

因此意味着即使还有其它三个指标关系了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’]

4  */

/**
 * @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) //
创设3个新目的 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方法。

在重点思考对象而不是自定义类型和构造函数的情事下,那是二个立见成效的形式。

5 function extend(subCls,superCls) { 

行使如下

/**
 * 父类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实例上,1组在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
}

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

本条函数接受多少个参数,三个子类,一个父类。

先是步成立父类原型的副本,第3步将创造的副本添加constructor属性,第1部将子类的原型指向这些副本。

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()

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()

ES陆新增了一个办法,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 收藏
评论

ca881亚洲城娱乐 1

6     subCls.prototype = new superCls();   

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中具名函数的有余调用格局,子类还足以有以下的有余达成情势。只是在子类中调用父类方法差别而已。

7 }

出口能够看来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 “的关系是违背的。

view sourceprint?1 /** 

Java中,子类Person在本身构造方法中调用父类构造方法super(legs),成立对象的时候一向将父类构造参数legs:二传进来,不仅仅只传本身的name:jack。上边JavaScript继承是在extend时传父类构造参数(extend函数的第11个参数),而不是在new
Man时将父类构造参数字传送过去。好,模拟Java来落到实处下extend,那里巧妙的在子类上暂存了父类引用。

 

2  *  父类Person 

 

2、原型格局写类,原型情势持续

3  */

2,继承工具函数二

core JS自个儿的目的系统正是利用原型方式(prototype based)继承的。或然说core
JS未有利用大面积的类继承(class
based)系统,而是采用原型继承来贯彻自己的靶子系统。工作中大家也足以用原型格局来促成持续,代码复用以营造筑组织调的作用模块。 

4 function Person(){} 

/**
 * @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,表明三角形也是多边形

5 Person.prototype.nationality = China; 

只怕以Person为父类,来落到实处子类Woman

虽说从出口能够见到子类继承了父类Polygon的属性sides和办法setSides,但sides是0,怎么会是三角形呢。还得调用下tri.setSides(三)使之成为三角形。那样就如很不便宜。不可能传参数,正是原型情势的老毛病。优点是不错的维护了”is
a”的涉嫌。

6 Person.prototype.getNationality = function() {return
this.nationality;} 

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);

 

7 Person.prototype.setNationality = function(n) { this.nationality = n;}

最终,创制对象的主意和java也接近,即new的时候还要将父类构造参数(nationality:Japan)传进去。

叁、组合构造函数/原型方式写类,选择前边种方法持续

子类继承与父类

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

那种办法父类,子类的习性都挂在构造函数里,方法都挂在原型上。

view sourceprint?1 function Man() {} 

/**
 * 父类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,表明三角形也是多边形

2 extend(Man,Person);

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图