ES5和ES6及继承机制

栏目: JavaScript · 发布时间: 6年前

内容简介:这几天在学习react的时候学习到ES6的在js万物皆对象,但事还是区分普通对象和函数对象,那大家需要知道是只有函数对象才有那这里就得出几个公式:

这几天在学习react的时候学习到ES6的 class extends 继承方式,就复习一下ES5的继承机制,并整理下来。

  • ES5继承机制
  • ES6继承机制
  • 两者的区别总结

ES5继承机制

在js万物皆对象,但事还是区分普通对象和函数对象,那大家需要知道是只有函数对象才有 prototype 属性,但所有对象都有__proto__属性

function A(){
}
var B = new A;
复制代码

那这里就得出几个公式:

B.__proto__== A.prototype; 
B.constructor == A;
a.prototype.constuctor = A;
复制代码

那这是实现继承的基础; 也就是说A.prototype是A的原型对象,A是构造函数,B是A的实例,原型对象(A.prototype)是 构造函数(A)的一个实例。而此时this指向是指向实例。 明白这个关系实现继承就很简单了!看一下下面的代码

原型链继承

通过重写子类的原型 等于 父类的一个实例,(父类的实例属相变成子类的原型属性)

function father() {
    this.faName = 'father';
}
father.prototype.getfaName = function() {
    console.log(this.faName);
};
function child() {
    this.chName = 'child';
}
child.prototype = new father();
child.prototype.constructor = child;
child.prototype.getchName = function() {
    console.log(this.chName);
};
复制代码

组合继承 (比较常用,也有缺点)

通过子类的原型对象指向父类实例的方式来实现继承,那我们不难发现原型链的形成是真正是靠__proto_ 而非prototype,子类的实例可以访问父类原型上的方法,是因为子类实例通过 _ proto 与父类的原型对象有连接

//先来个父类,带些属性
function Super(){
    this.flag = true;
}
//为了提高复用性,方法绑定在父类原型属性上
Super.prototype.getFlag = function(){
    return this.flag;
}
//来个子类
function Sub(){
    this.subFlag = false;
}
//实现继承
Sub.prototype = new Super;
//给子类添加子类特有的方法,注意顺序要在继承之后
Sub.prototype.getSubFlag = function(){
    return this.subFlag;
}
//构造实例
var es5 = new Sub;
复制代码

寄生组合继承 (业内比较提倡的方法)

所谓寄生组合式继承,即通过借助构造函数来继承属性,通过原型链的混成形式来继承方法。 其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

function inserit(son, father) {
    var obj = Object.create(father.prototype);
    son.prototype = obj;
    obj.constructor = son
}

function SuperType(name,colors) {
    this.name = name;
    this.colors = colors;
}
SuperType.prototype.sayName = function () {
    return this.name;
}

function SubType(job,name,color) {
    SuperType.call(this, name,color);
    this.job = job;
}
//核心方法
inserit(SubType, SuperType);
SubType.prototype.sayjob = function () {
    return this.job;
}
var instance= new SubType("doctor","John",["red","green"]);
console.log(instance.sayjob(),instance.sayName()) //doctor,John
复制代码
  • [x] 原型链继承缺点:父类包含引用类型的属性,那么子类所有实例都会共享该属性(包含引用类型的 原型 属性会被实例共享),在创建子类实例时,不能向父类的构造函数传递参数
  • [x] 组合继承的缺点:两次调用父类构造函数:(第一次是在创建子类原型的时候,第二次是在子类构造函数内部)子类继承父类的属性,一组在子类实例上,一组在子类原型上(在子类原型上创建不必要的多余的属性)(实例上的屏蔽原型上的同名属性)效率低
  • [x] 组合继承的优点:只调用一次父类的构造函数,避免了在子类原型上创建不必要的,多余的属性,原型链保持不变

ES5的继承机制总结

ES5的继承机制简单来说就是:实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))

ES6的继承机制

class Point {
    constructor(x) {
        this.x = 1;
        this.p = 2;
    }
    print() {
      return this.x;
    }
}
Point.prototype.z = '4'
class ColorPoint extends Point {
    constructor(x) {
        this.color = color; // ReferenceError
        super(x, y);
        this.x = x; // 正确
    }
    m() {
     super.print();
   }
}
复制代码

ES6继承是通过 classextends 关键字来实现继承 Point是父类,ColorPoint 是子类 通过 class 新建子类 extends继承父类的方式实现继承,方式比ES5简单的多。

constructor

constructor 方法是类的构造函数,是一个默认方法,通过 new 命令创建对象实例时,自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个默认的 consructor 方法会被默认添加。所以即使你没有添加构造函数,也是会有一个默认的构造函数的。一般 constructor 方法返回实例对象 this ,但是也可以指定 constructor 方法返回一个全新的对象,让返回的实例对象不是该类的实例。

class Points {
    constructor(x) {
        this.x = 1;
        this.p = 2;
    }
    print() {
      return this.x;
    }
    statc getname(){
      return new.target.name;
    }
}
等同于:
function Points(x) {
  this.x = x;
  this.p = 2;
}
Points.prototype.print = function() {
  return '(' + this.x +')';
}
复制代码

也就是说constructor就代表在父类上加属性,而在class对象加方法属性 等于在原型上的加。而这些属性方法 只有通过new出的实例 或者是extends 继承出来的实例才可以获取到 所以我们可以得到

new Points().__proto__.print() //可以调用到Points的print方法
new Points().x = 1 //可以调用到constructor 里面的 this.x=1
复制代码

super

super既可以当做函数使用,也可以当做对象使用两种使用的时候完全不一样, 函数用时 : 在 constructor 中必须调用 super 方法,因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工,而 super 就代表了父类的构造函数。super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B,因此 super() 在这里相当于

A.prototype.constructor.call(this, props)
复制代码

在 super() 执行时,它指向的是 子类 B 的构造函数,而不是父类 A 的构造函数。也就是说,super() 内部的 this 指向的是 B。所以在第一个es6的例子中子类的this指向的是自己。、

当做对象使用: 在普通方法中,指向父类的原型对象;在静态方法中,指向父类。 所以在子类的方法中super.print();指向的是父类原型上的方法。 但是因为super的两种用法,所以es6规定在使用必须要明确使用方式,像单独console.log(super) 就会报错。

static

顾名思义是静态方法的意思,类相当于实例的原型, 所有在类中定义的方法, 都会被实例继承。 如果在一个方法前, 加上static关键字, 就表示该方法不会被实例继承, 而是直接通过类来调用, 这就称为“ 静态方法”。静态方法调用直接在类上进行,而在类的实例上不可被调用。静态方法通常用于创建 实用/工具 函数。

new.target

new.target属性允许你检测函数或构造方法是否通过是通过new运算符被调用的。在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target 的值是undefined。

也就是说new.target的功能就是用来检测函数的调用是不是通过 new 去创建一个新对象的,而且new.target返回的是一个指向函数的引用,也就是说我们能够确定是哪个函数进行了new操作。

ES6的继承机制总结

先创建父类实例this 通过class丶extends丶super关键字定义子类,并改变this指向,super本身是指向父类的构造函数但做函数调用后返回的是子类的实例,实际上做了父类.prototype.constructor.call(this),做对象调用时指向父类.prototype,从而实现继承。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Domain-Driven Design

Domain-Driven Design

Eric Evans / Addison-Wesley Professional / 2003-8-30 / USD 74.99

"Eric Evans has written a fantastic book on how you can make the design of your software match your mental model of the problem domain you are addressing. "His book is very compatible with XP. It is n......一起来看看 《Domain-Driven Design》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具