zoukankan      html  css  js  c++  java
  • ES5与ES6的继承

    JavaScript本身是一种神马语言:

    提到继承,我们常常会联想到C#、java等面向对象的高级语言(当然还有C++),因为存在类的概念使得这些语言在实际的使用中抽象成为一个对象,即面向对象。JavaScript这门语言本身就是作为浏览器脚本语言的弱语言,伴随着没有类的概念,JavaScript就成为了一种基于对象的语言而不是面向对象的语言,面向对象就会存在继承,那么基于对象的JavaScript是如何继承的。

    ES5规则

    JavaScript的4种继承方式:

    (1)原型继承

    栗子:

     1 function Animal() {
     2     this.name = "Animal";
     3     this.actions = ['eat', 'drink']
     4     this.eat = function () {
     5         console.log('eat');
     6     };
     7 }
     8 
     9 Animal.prototype = {
    10     age: '6years',
    11     hobbies: []
    12 }
    13 
    14 function Dog() {
    15     //do someting
    16 }
    17 Dog.prototype = new Animal();
    18 
    19 function Cat() {
    20     //do someting
    21 }
    22 Cat.prototype = new Animal();
    23 
    24 var dog = new Dog();
    25 var cat = new Cat();

    结果:

    可以看到cat跟dog都继承了animal的实例对象,原型继承原型继承是最基础的继承方式,核心就是重写子类原型,是父类实例对象充当子类原型。

    如果此时作如下操作会有什么情况发生

    1 dog.name = 'dog'
    2 dog.actions.push('look');
    3 dog.age = '7years';
    4 dog.hobbies.push('sleep');

    cat的结果:

    结果已经很明显了,原型上的引用类型会被共享。

    原因就是操作数组时,首先会在对象下找当前数组,如果有就会更改对象下的数组,如果没有就会到原型里面找数组,由于dog跟cat的原型是同一个animal所以修改的就是同一个数组,如果是简单类型,查找对象内没有此属性,重新生成一个属性,并且不会继续使用原型内部的属性,即原型共享。

    结论:优点:最基本的继承方式,简单;缺点:原型中的引用类型共享。

    到这里是不是有一种原型链的感觉了呢~

    (2)改变上下文的继承

     1 function Animal(name) {
     2     this.name = name;
     3     this.actions = ['eat', 'drink']
     4     this.eat = function () {
     5         console.log('eat');
     6     };
     7 }
     8 
     9 function Dog() {
    10     Animal.call(this, 'dog')
    11 }
    12 
    13 function Cat() {
    14     Animal.call(this, 'cat')
    15 }
    16 
    17 var dog = new Dog();
    18 var cat = new Cat();

    结果:

    利用call(apply,bind)方式改变了Animal函数内部this的指向,使this指向分别指向了Dog和Cat

    优点:摒弃了原型,避免了原型共享;解决了向父类构造函数传参的问题。

    缺点:没生成一个新对象,都会重新定义一次function,严重影响内存。

    这里是不是有一种多态的感觉了呢~

    (3)把前两种结合起来继承:

    栗子:

     1 function Animal(name) {
     2     this.name = name;
     3     this.actions = ['eat', 'drink']
     4 }
     5 
     6 Animal.prototype = {
     7     eat: function () {
     8         console.log('eat');
     9     }
    10 }
    11 
    12 function Dog(name) {
    13     Animal.call(this, name)
    14 }
    15 Dog.prototype = new Animal();
    16 
    17 function Cat(name) {
    18     Animal.call(this, name)
    19 }
    20 Cat.prototype = new Animal();
    21 
    22 var dog = new Dog('dog');
    23 var cat = new Cat('cat');

    结果:

    优点:这种方式成功的避免了重复定义function的尴尬情况,同时解决了原型共享的问题。

    缺点:如果有两个子类继承父类,但是父类的属性有一个子类不用,怎么搞?这个是没法避免的,而且父类的属性全部在子类的原型上,很不美观。

    这里是不是又仿佛看见了new的原理了呢~

    (4)寄生组合继承:

    为了扣掉组合继承中原型中不需要的属性,看到为了满足这一点,可不可以介样:

     1 function Animal(name) {
     2     this.name = name;
     3     this.actions = ['eat', 'drink']
     4 }
     5 
     6 Animal.prototype = {
     7     eat: function () {
     8         console.log('eat');
     9     }
    10 }
    11 
    12 function Dog(name) {
    13     Animal.call(this, name)
    14 }
    15 Dog.prototype = Animal.prototype;
    16 
    17 function Cat(name) {
    18     Animal.call(this, name)
    19 }
    20 Cat.prototype = Animal.prototype;
    21 
    22 var dog = new Dog('dog');
    23 var cat = new Cat('cat');

    结果:

    是不是达到了原型中的属性被消灭的效果了呢。这里我们可以联想到什么呢,那就是js的new关键字

    回顾一下:

    1 var
    2     Demo = function () {
    3         var
    4             self = this;
    5     };
    6 
    7 var demo = {};
    8 demo.__proto__ = Demo.prototype;
    9 Demo.call(demo);

    区别在于将Demo.prototype是给对象的原型赋值,一个是给方法的原型赋值。

    接着上面的栗子来,乍一看好像对,实际Child中的__proto__为Object,并不是Parent,已经背离了Child继承Parent的目的。为啥呢?因为prototype就是Object,js里一切皆为对象。

    我们可以自己控制对象的原型

    改进:

     1 function Animal(name) {
     2     this.name = name;
     3     this.actions = ['eat', 'drink']
     4 }
     5 
     6 Animal.prototype.eat = function () {
     7     console.log('eat');
     8 }
     9 
    10 function Dog(name) {
    11     Animal.call(this, name)
    12 }
    13 
    14 function Cat(name) {
    15     Animal.call(this, name)
    16 }
    17 
    18 function initObject(obj) {
    19     var
    20         F = function () { };
    21     F.prototype = obj;
    22     return new F();
    23 }
    24 
    25 var
    26     dogPrototype = initObject(Animal.prototype),
    27     catPrototype = initObject(Animal.prototype);
    28 dogPrototype.constructor = Dog;
    29 catPrototype.constructor = Cat;
    30 Dog.prototype = dogPrototype;
    31 Cat.prototype = catPrototype;
    32 var dog = new Dog('dog');
    33 var cat = new Cat('cat');

    结果:

    可以看到cat与dog的原型已经是Animal了。ES5 over~

    ES6规则

     1 class Animal {
     2     constructor(name) {
     3         this.name = name;
     4     };
     5 
     6     eat() {
     7         console.log('eat')
     8     };
     9 }
    10 
    11 class Dog extends Animal {
    12     constructor() {
    13         super();
    14     };
    15 
    16     eat() {
    17         super.eat();
    18     };
    19 }
    20 var dog = new Dog();

    ES6很大程度优化了ES5的继承方式,而且constructor也暴露出来,利用super可以直接调用父级函数以及属性,相当地方便。

  • 相关阅读:
    net core 使用 rabbitmq
    asp.net core WebApi 返回 HttpResponseMessage
    asp.net core 2.1 WebApi 快速入门
    JQuery EasyUI combobox动态添加option
    php截取字符去掉最后一个字符
    JQuery EasyUI Combobox的onChange事件
    对于不返回任何键列信息的 selectcommand 不支持 updatecommand 的动态 sql 生成
    Access2007 操作或事件已被禁用模式阻止解决办法
    Easyui 中 Tabsr的常用方法
    Win 7 IE11不能下载文件,右键另存为也不行
  • 原文地址:https://www.cnblogs.com/moran1992/p/6274032.html
Copyright © 2011-2022 走看看