在ES6之前, JS是无法通过继承的方式创建属于自己的特殊数组的, 也就是说可以使用原型链来继承数组的一些方法, 但是某些返回一个数组的方法返回的值还是一个Array的实例, 例如slice, length等, 还是Array的实例属性, 和当前原型链末尾的函数没什么关系, 只是借用了Array的方法。
 // 实例:
// Array的行为
let colors = [];
colors[0] = 'red';
console.log(colors.length); //
colors.length = 0;
console.log(colors[0]); // undefined // 使用ES5方式继承数组
function MyArray() {
Array.apply(this, arguments);
}
MyArray.prototype = Object.create(Array.prototype, {
constructor: {
value: MyArray,
writable: true,
configurable: true,
enumerable: true
}
});
colors = new MyArray();
colors[0] = 'red';
console.log(colors.length); //
colors.length = 0;
console.log(colors[0]); // 'red'
colors = new MyArray();
colors.push(1, 2, 3, 4, 5);
let cs = colors.slice(2, 3);
console.log(cs instanceof MyArray); // false
从上面代码可以看出, MyArray虽然可以使用数组上面的方法和属性, 但是其表现行为和Array是不一致的, 因为传统JS继承形式实现的数组继承没有从Array.apply或原型赋值中继承相关功能。
 
ES6和ES5继承的区别:
在ES5中继承是先由派生类型创建this的值, 然后调用基类型的构造函数, 也就是说this的值开始指向的是MyArray的实例, 但是随后被来自Array的其他属性所修饰。
在ES6中则正好相反, 先由基类创建this的值, 然后派生类的构造函数再修改这个值, 所有一开始可以通过this访问基类的所有内建功能, 然后再添加自己的功能, 这就是为什么派生类的constructor必须先写super() 方法。
 
 // 实例:
class MyArray extends Array { } let colors = new MyArray();
colors[0] = 'red';
console.log(colors.length); //
colors.length = 0;
console.log(colors[0]); // undefined
colors.push(1, 2, 3, 4, 5);
let cs = colors.slice(2, 3);
console.log(cs instanceof MyArray); // true
由上面的例子可以看出, MyArray的实例不但可以使用数组的属性和方法, 还具有了数组的特性, slice返回的实例还是自身的实例。

这一行为的改变是通过Symbol.species属性实现的, 他被用于定义返回函数的静态访问器属性, 其作用其实是让子类可以自由的控制要返回自己的实例还是父类的实例。

 // 内建类内部的大致实现方式
class MyClass {
static get[Symbol.species]() {
return this;
} constructor(value) {
this.value = value;
} clone() {
return new this.constructor[Symbol.species](this.value);
}
}

下面看一个栗子:

 class c1 {
constructor(c) {
this.c = c;
}
static get[Symbol.species]() {
return this;
}
cc(c) {
console.log(this.constructor);
console.log(this.constructor[Symbol.species]);
return new this.constructor[Symbol.species](c);
}
} // 如果c2想要自己控制自己所继承自c1的cc方法返回的是自己还是c1,那么就要通过Symbol.species来告
// 诉c1,我要返回什么,而c1给的默认选项是返回c2,也就是继承的属性,如果直接使用
// this.constructor,那么当我想要返回c1的实例,而不是c2自己时,我是没有办法改变的
// 此时就可以通过重写Symbol.species,来达到返回c1的实例的效果
class c2 extends c1 {
constructor(c) {
super(c);
}
static get[Symbol.species]() {
return c1;
}
} let c = new c2('c2');
console.log(c.c);
console.log(c.cc('c22')); /*
c2
[Function: c2]
[Function: c1]
c1 { c: 'c22' }
*/
通过上面栗子发现, 直接返回this.constructor也没有什么问题, 原先父类返回自身的方法能够返回现在的派生类, 而不是父类自己, 那么为什么不直接使用this.constructor呢? 因为这个必须要通过一种方式让子类可以进行自由控制, 写死之后如果我确实不想要返回自身的实例, 而是返回父类的实例呢? 那么写死后就没有任何办法进行修改了, 如果MyArray继承了Array, 那么slice永远都只能返回MyArray, 而不能返回一个Array的实例。
在下面的内建类中都已定义Symbol.species属性:
Array、 ArrayBuffer、 Map、 Promise、 Regexp、 Set、 Typed arrays
关于Symbol、 内部Symbol、 class等概念和详细用法, 请看《 深入理解ES6》 一书, 此文出处也是这本书。
关于原型链的继承相关知识请看《JavaScript高级教程第三版》其中有详细的讲解。

ES6内建对象的继承的更多相关文章

  1. javascript 对象初探 (四)--- 内建对象之旅之Array

     我们不要去纠结神马是内建对象,神马是內建构造器.到后来你们便会发现其实她们都是对象. Array()是一个构建数组的內建构造器函数: var arr = new Array(); 与下面的是等效的: ...

  2. javascript内建对象

    内建对象等价于内建构造器内建对象大致分为三类:数据封装类对象--Object.Array.Boolean.Number和String工具类对象--Math.Date.RegExp等用于提供遍历的对象错 ...

  3. 哪个HTML5内建对象用于在画布上绘制?()

    哪个HTML5内建对象用于在画布上绘制?() getContent getContext getGraphics getCanvas 我的理解: A.C.D不存在HTML5,,js方法中 HTML 5 ...

  4. javascript——对象的概念——内建对象

    包括内建对象的所有对象都是Object对象的子对象. 1.Array():构建数组的内建构造器函数 例:创建数组方式有两种: 2.Boolean:是对象,与基本数据类型 布尔值 不相同 例:创建Boo ...

  5. 18第一章 ASP.Net内建对象

    第一章        ASP.Net内建对象 第一章        ASP.Net内建对象 ASP.Net为保持用户的数据和信息,内建了许多对象,包括Application.Response.Requ ...

  6. JSP内建对象

    ① out - javax.servlet.jsp.jspWriter    out对象用于把结果输出到网页上. 方法: 1. void clear() ;    清除输出缓冲区的内容,可是不输出到c ...

  7. ASP内建对象

    Active Server Pages 提供内建对象,这些对象使用户更容易收集通过浏览器请求发送的信息.响应浏览器以及存储用户信息(如用户首选项).本文简要说明每一个对象.有关每个对象的详细信息,请参 ...

  8. Python源码剖析——01内建对象

    <Python源码剖析>笔记 第一章:对象初识 对象是Python中的核心概念,面向对象中的"类"和"对象"在Python中的概念都为对象,具体分为 ...

  9. javascript 对象初探 (四)--- 内建对象之旅之Boolean

    var a = new Boolean() 我们要明白一点在这里的b是一个对象而不是一个基本数据类型的布尔值.如果想将b转化成基本数据类型的布尔值,我们可以调用她的valueof()方法(继承自Obj ...

随机推荐

  1. Nginx基本参数调优

    Nginx基本参数 #运行用户 user nobody; #worker进程的个数:通常应该为物理CPU核数减1: #可以为”auto”,实现自动设定(worker_processes  auto) ...

  2. P4393 [BOI2007]Sequence 序列问题[贪心]

    题目描述 对于一个给定的序列a1, -, an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和ai+1用一个元素max(ai,ai+1)替代,这样得到一个比原来序列短的新序列.这一 ...

  3. python基础--切片、迭代、列表生成式

    原文地址:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143175684 ...

  4. js闭包随记

    理解闭包可以将以上代码分解如下: function outerFunction() {     ;     function innerFunction(){         return count ...

  5. [HDU 5608]Function(莫比乌斯反演 + 杜教筛)

    题目描述 有N2−3N+2=∑d∣Nf(d)N^2-3N+2=\sum_{d|N} f(d)N2−3N+2=∑d∣N​f(d) 求∑i=1Nf(i)\sum_{i=1}^{N} f(i)∑i=1N​f ...

  6. 两数最大公约数 GCD

    原理辗转相除法. public int gcd(int i, int j) { if (i == 0 || j == 0) { return 0; } int a = 0, b = 0; if (i ...

  7. icpc 银川 I. Base62 任意进制的转换 短除法

    Problem Description As we already know, base64 is a common binary-to-text encoding scheme. Here we d ...

  8. Jupyter开发环境搭建

    小书匠kindle 目录: 1.Jupyter 介绍 2.Jupyter安装 3.notedown插件安装 4.扩展包安装 5.运行Jupyter 6.在远端服务器上运行jupyter 1.Jupyt ...

  9. OpenJudge 1.5.14 人口增长问题

    描述 我国现有x亿人口,按照每年0.1%的增长速度,n年后将有多少人? 输入一行,包含两个整数x和n,分别是人口基数和年数,以单个空格分隔.输出输出最后的人口数,以亿为单位,保留到小数点后四位.1 & ...

  10. springboot项目整合swagger2出现的问题

    swagger需要开放以下uri:/swagger-ui.html/swagger-resources/webjars/csrf/v2 添加swagger后项目报错 Failed to start b ...