ES5 和 ES6 的继承

虚树磷火
• 阅读 974

  • ES5中的类是通过function定义的
function Person (name, age) {
    this.name = name;
    this.age = age;
}

const p = new Person()
  • ES6中的类是通过class定义的
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

const p = new Person()
  • 可以看到class定义的类,内部的constructor函数和function定义的Person是一样的,通过new进行实例化的时候,function是调用他自己,而class则是调用其内部定义的constructor函数;
  • 另外class的写法上,Person后面不带括号,实例化时的参数是在constructor里面进行传递的;

继承

  • 继承在JS里的含义即,子类可以访问到父类的属性
  • ES5里的继承是通过修改原型链实现的
function Person (name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.getAge = function () {
    return this.age;
};

function Teacher (name, age, salary) {
    const obj = Object.create(new Person(name, age));
    obj.salary = salary;
    return obj;
}

const t = new Teacher(...);
  • 如上通过将实例化后的父类作为子类实例化的原型,即t可以通过t.__proto__.__proto__访问到Person的原型,从而访问到getAge方法,而name和age是直接挂在p上面的,t也可以访问到
  • es6的继承是通过extends进行的,系统底层帮你关联了类的原型,无需自己手动修改
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

class Teacher extends Person {
    constructor(name, age, salary) {
        super(name, age);
        this.salary = salary;
    }
}

const t = new Teacher(...)
  • 注意这里出现了一个super,这是ES6内置的函数,只能在class的构造函数constructor上面定义,其本质代表父类的构造函数;
  • 我们可以通过babel转译上面的代码来理解ES6的继承,babel官网提供在线转码功能,方便平时对照,转换后的代码如下:
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

class Teacher extends Person {
    constructor(name, age, salary) { }
}
-------------------------------------
"use strict";

function _inheritsLoose(subClass, superClass) {
    subClass.prototype = Object.create(superClass.prototype);
    subClass.prototype.constructor = subClass;
    subClass.__proto__ = superClass;
}

var Person = function Person(name, age) {
  this.name = name;
  this.age = age;
};

var Teacher = /*#__PURE__*/function (_Person) {
  _inheritsLoose(Teacher, _Person);

  function Teacher(name, age, salary) {
    var _this;

    _this = _Person.call(this, name, age) || this;
    _this.salary = salary;
    return _this;
  }

  return Teacher;
}(Person);
  • 首先可以看到,转译后,父类被转换成了普通的函数,而子类首先执行了_inheritsLoose方法,做了以下三个处理:

    • 将父类的原型对象设置成了子类原型对象的原型
    • 设置子类的原型对象上的构造函数指向
    • 将子类和父类通过__proto__关联了起来
  • 这三个操作和ES5最大的不同在于将子类和父类通过__proto__关联了起来
  • 处理完原型链,子类作为一个函数被返回,该函数内,会以调用该函数的对象作为上下文调用一遍父类的构造函数,并在其返回上加上自己内部的属性,最终返回这个对象
  • 因为最终类是通过new实例化的,结合new的原理,也就是最终子类会内部创建一个对象,作为this的指向,并调用一遍父类,将父类的属性挂载上去,并返回,然后挂载上自己的属性,最后返回,这样就完成了继承
  • 这里需要注意一点_Person的结果没有返回的情况下,_this等于this,但如果_Person返回了一个对象,_this将会等于那个对象而非new的时候创建的this
  • 以上是有super的转码结果,试着把super去掉后转译得到:

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }

var Person = function Person(name, age) {
  this.name = name;
  this.age = age;
};

var Teacher = /*#__PURE__*/function (_Person) {
  _inheritsLoose(Teacher, _Person);

  function Teacher(name, age, salary) {
    var _this;

    _this.salary = salary;
    return _assertThisInitialized(_this);
  }

  return Teacher;
}(Person);
  • 可以看到super去掉之后,出现了一段抛出异常的方法,以及原本调用Person的代码不见了;
  • 抛错前的判断void 0 其实就是输出undefined,但因为js里面undifined是一个可被覆盖的变量而非常量或保留变量,所以通常使用void 0来获取undeifned
  • 同时也可以看出super就像对应与以当前上下文调用一遍父类函数,super的入参也是父类构造函数的入参
  • 这里可以看出ES6规定了如果使用了constructor并且使用了继承,就必须要在constructor里面调用super,并且调用super必须在调用this之前,因为没有super之前,_this等于undefined,无法在上面绑定属性,转译后代码会报错,而直接用class时会提示必须在调用this前调用super

super存在的意义

  • 以下这里是纯粹的个人理解
  • super没有自动执行,而是需要用户手动添加的原因是,程序无法知道调用父类构造函数时,使用哪个参数,而暴露super给用户也使得用户在传入参数之前可以做一些定制化的处理;
  • 另外super必须要在this前调用,一是因为上面说的没调用super前,_this是undefined,那我们可能会疑问为什么不直接先默认把this赋值给_this,那就不会有undefined的问题,但也如前所说,父类的构造函数是有可能返回一个对象的,当返回一个对象的时候,_this将会被重新赋值,那么之前对_this做的所有操作都是没有意义的,所以ES6硬性规定了,super必须在this前调用
  • 父类构造函数有可能返回非对象但是非空的值吗,是有可能的,这种情况下ES5转译出来的代码执行后,salary是永远也无法被赋值的,但实际上直接运行class的写法,发现salary可以正常被赋值,这是因为转义的代码是用es2015-loose转的,直接用es2015转的话发现,代码是会对父类构造函数返回结果判断是否为对象的,这也符合new一个function时,判断其是否为对象来作为最后结果的返回的逻辑,有兴趣的可以上babel官网看下es2015转出来的代码,会添加很多兼容性的判断,更为严谨一点

ES5/ES6继承区别

  • 写法不同,ES5要自己操作原型链,ES6直接帮你做了;
  • ES6,子类的构造函数可以通过__proto__访问到父类;
  • ES6,使用了构造函数且用了继承,必须在构造函数里用super
  • ES6,子类构造函数里的this,取决于父类,父类的构造函数返回一个对象的情况下,子类的实例就是这个返回的对象,es5的则是new阶段生成的对象
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
ZY ZY
4年前
js继承的几种方式
1.原型链继承原型链继承:想要继承,就必须要提供父类(继承谁,提供继承的属性)//父级functionPerson(name)//给构造函数添加参数this.namename;this.age10;this.sumfunction()console.log(this.name)//原
Easter79 Easter79
4年前
typescript类 继承 修饰符
//1、ts中类的定义/es5:functionPerson(name){this.namename;this.runfunction(){
Stella981 Stella981
4年前
List的Select 和Select().tolist()
List<PersondelpnewList<Person{newPerson{Id1,Name"小明1",Age11,Sign0},newPerson{Id2,Name"小明2",Age12,
Stella981 Stella981
4年前
Lua基础(对象)
:和.区别.   stu{id100,name"Tom",age21}成员变量   function stu.toString()成员函数    return stu.id .. stu.name .. stu.age   endprint(stu
Stella981 Stella981
4年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
4年前
ES6面向对象
ES6面向对象js中的面向对象functionUser(name,age){this.namename;//定义属性this.ageage;}//通过原型添加方法User.prototype.showNamefuncti
Stella981 Stella981
4年前
Javascript中,实现类与继承的方法和优缺点分析
Javascript是一种弱类型语言,不存在类的概念,但在js中可以模仿类似于JAVA中的类,实现类与继承第一种方法:利用Javascript中的原型链1//首先定义一个父类23functionAnimal(name,age){4//定义父类的属性5thi