在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. 2019-ACM-ICPC-南京区网络赛-E. K Sum-杜教筛+欧拉定理

    2019-ACM-ICPC-南京区网络赛-E. K Sum-杜教筛+欧拉定理 [Problem Description] 令\(f_n(k)=\sum_{l_1=1}^n\sum_{l_2=1}^n\ ...

  2. python3 jieba分词

    一.jieba库用于分词,https://github.com/fxsjy/jieba 二.分词:分词精细:全局(文本分析)<精确(快速成词)<搜素(搜素引擎分词) #分词 str=r'今 ...

  3. G1垃圾收集器原理剖析【官方解读】

    继续基于上一次https://www.cnblogs.com/webor2006/p/11135005.html的官方G1文档进行解读,上一次分析到了这: 话不多说,继续往前读: When perfo ...

  4. .NET Core SignalR 和 .NET SignalR 区别

    由于要转 .NET Core ,对于以前用到的一些进行迁移. 在迁移 SignalR 的时候发现 .NET Core 下的和 .NET 下的区别还是挺大的. 功能差异 自定重新连接 .NET 下的 S ...

  5. python_面向对象——类方法和静态方法

    1.类方法不能访问实例变量,只能访问类变量. class Dog(object): name = 'wdc' def __init__(self,name): self.name = name def ...

  6. Go 语言解释器 Yaegi

    Yaegi 是一个优雅的 Go 语言解释器,可以执行 Go 脚本和插件. 特性 完整支持 Go 语言规范 用 Go 编写,只使用标准库 简单的解释器 API: New(), Eval(), Use() ...

  7. WEB知识补充 支付宝 支付

  8. chrome 調試 node 代碼

    (1)node --inspect-brk debug/demo (2)通过 chrome 进行调试    (3) chrome://inspect 进入chrome调试界面    --inspect ...

  9. AirTest与模拟器连接(二)

    如果我们手边没有可用的Android真机,又想进行Android应用自动化测试,这时候就要使用AirtestIDE的Android模拟器自动化测试功能了. AirtestIDE所支持的模拟器包括 An ...

  10. TensorFlow(十二):使用RNN实现手写数字识别

    上代码: import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #载入数据集 mnist ...