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继承是通过 class 丶 extends 关键字来实现继承 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,从而实现继承。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 028.Python面向对象继承(单继承,多继承,super,菱形继承)
- PHP类继承、接口继承关系概述
- 面向对象:理解 Python 类的单继承与多继承
- java入门第二季--继承--java中的继承初始化顺序
- 前端基本功(七):javascript中的继承(原型、原型链、继承的实现方式)
- 组合还是继承,这是一个问题?——由模式谈面向对象的原则之多用组合、少用继承
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。