必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

Extends的结果与寄生组合继承基本一致,必威需要

2019-09-21 03:00 来源:未知

几种继承的细微区别

虽然上述提到的三种方法都可以达到继承Date的目的-混合法严格说不能算继承,只不过是另类实现。

于是,将所有能打印的主要信息都打印出来,分析几种继承的区别,大致场景是这样的:

可以参考:( 请进入调试模式)

从上往下,1, 2, 3, 4四种继承实现分别是:(排出了混合法)

  • ES6的Class大法
  • 经典组合寄生继承法
  • 本文中的取巧做法,Date构造实例,然后更改__proto__的那种
  • ES6的Class大法,Babel打包后的实现(无法正常调用的)

~~以下是MyDate们的prototype~~~ Date {constructor: ƒ, getTest: ƒ} Date {constructor: ƒ, getTest: ƒ} Date {getTest: ƒ, constructor: ƒ} Date {constructor: ƒ, getTest: ƒ} ~~以下是new出的对象~~~ Sat Jan 13 2018 21:58:55 GMT+0800 (CST) MyDate2 {abc: 1} Sat Jan 13 2018 21:58:55 GMT+0800 (CST) MyDate {abc: 1} ~~以下是new出的对象的Object.prototype.toString.call~~~ [object Date] [object Object] [object Date] [object Object] ~~以下是MyDate们的__proto__~~~ ƒ Date() { [native code] } ƒ () { [native code] } ƒ () { [native code] } ƒ Date() { [native code] } ~~以下是new出的对象的__proto__~~~ Date {constructor: ƒ, getTest: ƒ} Date {constructor: ƒ, getTest: ƒ} Date {getTest: ƒ, constructor: ƒ} Date {constructor: ƒ, getTest: ƒ} ~~以下是对象的__proto__与MyDate们的prototype比较~~~ true true true true

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
29
30
31
32
33
34
35
~~~~以下是MyDate们的prototype~~~~~~~~~
Date {constructor: ƒ, getTest: ƒ}
Date {constructor: ƒ, getTest: ƒ}
Date {getTest: ƒ, constructor: ƒ}
Date {constructor: ƒ, getTest: ƒ}
 
~~~~以下是new出的对象~~~~~~~~~
Sat Jan 13 2018 21:58:55 GMT+0800 (CST)
MyDate2 {abc: 1}
Sat Jan 13 2018 21:58:55 GMT+0800 (CST)
MyDate {abc: 1}
 
~~~~以下是new出的对象的Object.prototype.toString.call~~~~~~~~~
[object Date]
[object Object]
[object Date]
[object Object]
 
~~~~以下是MyDate们的__proto__~~~~~~~~~
ƒ Date() { [native code] }
ƒ () { [native code] }
ƒ () { [native code] }
ƒ Date() { [native code] }
 
~~~~以下是new出的对象的__proto__~~~~~~~~~
Date {constructor: ƒ, getTest: ƒ}
Date {constructor: ƒ, getTest: ƒ}
Date {getTest: ƒ, constructor: ƒ}
Date {constructor: ƒ, getTest: ƒ}
 
~~~~以下是对象的__proto__与MyDate们的prototype比较~~~~~~~~~
true
true
true
true

看出,主要差别有几点:

  1. MyDate们的__proto__指向不一样
  2. Object.prototype.toString.call的输出不一样
  3. 对象本质不一样,可以正常调用的1, 3都是Date构造出的,而其它的则是MyDate构造出的

我们上文中得出的一个结论是:由于调用的对象不是由Date构造出的实例,所以不允许调用,就算是自己的原型链上有Date.prototype也不行

但是这里有两个变量:分别是底层构造实例的方法不一样,以及对象的Object.prototype.toString.call的输出不一样
(另一个MyDate.__proto__可以排除,因为原型链回溯肯定与它无关)

万一它的判断是根据Object.prototype.toString.call来的呢?那这样结论不就有误差了?

于是,根据ES6中的,Symbol.toStringTag,使用黑魔法,动态的修改下它,排除下干扰:

// 分别可以给date2,date3设置 Object.defineProperty(date2, Symbol.toStringTag, { get: function() { return "Date"; } });

1
2
3
4
5
6
// 分别可以给date2,date3设置
Object.defineProperty(date2, Symbol.toStringTag, {
    get: function() {
        return "Date";
    }
});

然后在打印下看看,变成这样了:

[object Date] [object Date] [object Date] [object Object]

1
2
3
4
[object Date]
[object Date]
[object Date]
[object Object]

可以看到,第二个的MyDate2构造出的实例,虽然打印出来是[object Date],但是调用Date方法仍然是有错误

必威 1

此时我们可以更加准确一点的确认:由于调用的对象不是由Date构造出的实例,所以不允许调用

而且我们可以看到,就算通过黑魔法修改Object.prototype.toString.call,内部的[[Class]]标识位也是无法修改的。
(这块知识点大概是Object.prototype.toString.call可以输出内部的[[Class]],但无法改变它,由于不是重点,这里不赘述)。

ES5 面向对象

4.原型式继承

核心思想:返回一个临时类型的一个新实例,现提出了规范的原型式继承,使用Object.create()方法。

var person={name:"xiaoming",age:16}

var anotherperson=Object.create(person,{name:"xiaowang"})

ES6继承与ES5继承的区别

从上午中的分析可以看到一点:ES6的Class写法继承是没问题的。但是换成ES5写法就不行了。

所以ES6的继承大法和ES5肯定是有区别的,那么究竟是哪里不同呢?(主要是结合的本文继承Date来说)

区别:(以SubClassSuperClassinstance为例)

  • ES5中继承的实质是:(那种经典组合寄生继承法)

    • 先由子类(SubClass)构造出实例对象this

    • 然后在子类的构造函数中,将父类(SuperClass)的属性添加到this上,SuperClass.apply(this, arguments)

    • 子类原型(SubClass.prototype)指向父类原型(SuperClass.prototype

    • 所以instance是子类(SubClass)构造出的(所以没有父类的[[Class]]关键标志)

    • 所以,instanceSubClassSuperClass的所有实例属性,以及可以通过原型链回溯,获取SubClassSuperClass原型上的方法

  • ES6中继承的实质是:

    • 先由父类(SuperClass)构造出实例对象this,这也是为什么必须先调用父类的super()方法(子类没有自己的this对象,需先由父类构造)

    • 然后在子类的构造函数中,修改this(进行加工),譬如让它指向子类原型(SubClass.prototype),这一步很关键,否则无法找到子类原型(注,子类构造中加工这一步的实际做法是推测出的,从最终效果来推测

    • 然后同样,子类原型(SubClass.prototype)指向父类原型(SuperClass.prototype

    • 所以instance是父类(SuperClass)构造出的(所以有着父类的[[Class]]关键标志)

    • 所以,instanceSubClassSuperClass的所有实例属性,以及可以通过原型链回溯,获取SubClassSuperClass原型上的方法

以上⬆就列举了些重要信息,其它的如静态方法的继承没有赘述。(静态方法继承实质上只需要更改下SubClass.__proto__SuperClass即可)

可以看着这张图快速理解:

必威 2

有没有发现呢:**ES6中的步骤和本文中取巧继承Date的方法一模一样,不同的是ES6是语言底层的做法,有它的底层优化之处,而本文中的直接修改__proto__容易影响性能**

ES6中在super中构建this的好处?

因为ES6中允许我们继承内置的类,如Date,Array,Error等。如果this先被创建出来,在传给Array等系统内置类的构造函数,这些内置类的构造函数是不认这个this的。
所以需要现在super中构建出来,这样才能有着super中关键的[[Class]]标志,才能被允许调用。(否则就算继承了,也无法调用这些内置类的方法)

参考文章:

[1]《js继承、构造函数继承、原型链继承、组合继承、组合继承优化、寄生组合继承》

[2]《JavaScript高级编程》

1 赞 收藏 评论

必威 3

如何继承 Date 对象?由一道题彻底弄懂 JS 继承

2018/01/25 · JavaScript · Date, 继承

原文出处: 撒网要见鱼   

类的继承(两种方式)

一、原型链继承

        对于什么是原型链?

        每个构造函数都有一个原型对象,原型对象的constructor指向这个构造函数本身,而实例的__proto__属性又指向原型对象。这个假设一个实例的__proto__内部指针指向其原型,而它的原型又是另一个类型的实例,那么它的原型又将指向另一个原型,另一个原型也包含一个指向它的构造函数的指针,假设另一个原型又是另一个类型的实例,这样层层递进,就构成了实例与原型的链条,这就是原型链的基本概念。

实现原型链的继承方式基本如下:

function Father () {

      this.appearance = "beautiful"

}

Father.prototype.sayHappy = function () {

        alert("快乐")

}

function Child () {

          this.name= "Jhon"

}

Child.prototype= new Father()        //  继承了父类的方法和属性

Child.prototype.addArr= [1,2,3,4,5]

var child= new Child()
child.sayHappy()          //  弹出“快乐”
child.appearance        //  "beautiful"

child.addArr                      //  [1,2,3,4,5]

原型链继承的缺点:①  不能传参  ② 若原型上的方法时引用类型的话,不小心被修改了的话会影响其他实例。


二、借助构造函数继承(利用calll和apply改变this指针)

基本思路:在子类型构造函数的内部调用超类型的构造函数。

function Father (Hobby){

      this.hobby= Hobby

}

Father.prototype.sayHappy = function () {

      alert("快乐")

}

function Child () {

      this.name= "Jhon"

      Father.call(this,"Play Games")          //  或者Father.apply(this,["Play Games"]),继承了Father的属性和方法

}

var child =  new Child()
child.sayHappy                // 没有反应,原型上的方法和属性不会继承
child.hobby                      //  "Play Games"

借助构造函数继承的缺点:①  方法都在构造函数中定义,函数的复用无从谈起    ②  超类中的方法对子类不可见。


三、组合继承(也叫经典继承,将原型链和借助构造函数继承相结合)

思路:1.原型链实现对原型属性和方法的继承;

            2.构造函数实现对实例属性的继承,且调用基类的构造函数;

function Father(Hobby) {

          this.hobby= Hobby;

          this.exGF = ['cuihua', 'erya']

}

Father.prototype.sayHappy = function () {

          alert("快乐")

}

function Child () {

          this.name= "Jhon"

          Father.call(this,"Play Games")          //  或者Father.apply(this,["Play Games"]),继承了Father的属性和方法

}

Child.prototype= new Father()

Student.prototype.sayName= function () {

          alert(this.name);

}

var liHua= new Child()

liHua.sayHappy()

liHua.sayName()


检测对象属性的两种方法:

object.hasOwnProperty(属性名),这个方法检测的是对象实例的属性(若是返回true),不能检测原型上的属性。

in操作符,检测对象所有的属性,包含原型和实例上的额,有的话就返回true.


判断一个原型是否在某个实例的原型链上:

Person.prototype.isPropotypeOf(personOne)    //  true

Object.prototype.isPropotypeOf(personOne)      //  true

判断一个构造函数是否在实例的原型链中出现过:

personOne instanceof Person                //  true

personOne instanceof Object                //  true


super

刚才有说到构造函数里面有super(x,y),方法里面有super.toString(),也就是说super有两种意义

1,父类的构造函数

然而这个super方法是在子类构造函数里面使用的,所以它应当返回一个子类的实例,所以super里面的this应该指向子类。super()在这里相当于A.prototype.constructor.call(this)。

super()只能用在子类的构造函数之中,用在其他地方会报错。

2,与父类相关的对象

super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

如何快速判断是否继承?

其实,在判断继承时,没有那么多的技巧,就只有关键的一点:[[prototype]]__ptoto__)的指向关系

譬如:

console.log(instance instanceof SubClass);
console.log(instance instanceof SuperClass);

实质上就是:

  • SubClass.prototype是否出现在instance的原型链上

  • SuperClass.prototype是否出现在instance的原型链上

然后,对照本文中列举的一些图,一目了然就可以看清关系。有时候,完全没有必要弄的太复杂。

一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

2018/08/02 · JavaScript · 继承

原文出处: 这是你的玩具车吗   

说实在话,以前我只需要知道“寄生组合继承”是最好的,有个祖传代码模版用就行。最近因为一些事情,几个星期以来一直心心念念想整理出来。本文以《JavaScript高级程序设计》上的内容为骨架,补充了ES6 Class的相关内容,从我认为更容易理解的角度将继承这件事叙述出来,希望大家能有所收获。

前言

见解有限,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正。

———-长文+多图预警,需要花费一定时间———-

故事是从一次实际需求中开始的。。。

某天,某人向我寻求了一次帮助,要协助写一个日期工具类,要求:

  • 此类继承自Date,拥有Date的所有属性和对象
  • 此类可以自由拓展方法

形象点描述,就是要求可以这样:

// 假设最终的类是 MyDate,有一个getTest拓展方法 let date = new MyDate(); // 调用Date的方法,输出GMT绝对毫秒数 console.log(date.getTime()); // 调用拓展的方法,随便输出什么,譬如helloworld! console.log(date.getTest());

1
2
3
4
5
6
7
// 假设最终的类是 MyDate,有一个getTest拓展方法
let date = new MyDate();
 
// 调用Date的方法,输出GMT绝对毫秒数
console.log(date.getTime());
// 调用拓展的方法,随便输出什么,譬如helloworld!
console.log(date.getTest());

于是,随手用JS中经典的组合寄生法写了一个继承,然后,刚准备完美收工,一运行,却出现了以下的情景:

必威 4

但是的心情是这样的:

TAG标签:
版权声明:本文由必威发布于必威-前端,转载请注明出处:Extends的结果与寄生组合继承基本一致,必威需要