ES6内建对象的继承

在ES6之前, JS是无法通过继承的方式创建属于自己的特殊数组的, 也就是说可以使用原型链来继承数组的一些方法, 但是某些返回一个数组的方法返回的值还是一个Array的实例, 例如slice, length等, 还是Array的实例属性, 和当前原型链末尾的函数没什么关系, 只是借用了Array的方法。

 1 // 实例:
 2 // Array的行为
 3 let colors = [];
 4 colors[0] = ‘red‘;
 5 console.log(colors.length); // 1
 6 colors.length = 0;
 7 console.log(colors[0]); // undefined
 8
 9 // 使用ES5方式继承数组
10 function MyArray() {
11   Array.apply(this, arguments);
12 }
13 MyArray.prototype = Object.create(Array.prototype, {
14   constructor: {
15     value: MyArray,
16     writable: true,
17     configurable: true,
18     enumerable: true
19   }
20 });
21 colors = new MyArray();
22 colors[0] = ‘red‘;
23 console.log(colors.length); // 0
24 colors.length = 0;
25 console.log(colors[0]); // ‘red‘
26 colors = new MyArray();
27 colors.push(1, 2, 3, 4, 5);
28 let cs = colors.slice(2, 3);
29 console.log(cs instanceof MyArray); // false

从上面代码可以看出, MyArray虽然可以使用数组上面的方法和属性, 但是其表现行为和Array是不一致的, 因为传统JS继承形式实现的数组继承没有从Array.apply或原型赋值中继承相关功能。

ES6和ES5继承的区别:

在ES5中继承是先由派生类型创建this的值, 然后调用基类型的构造函数, 也就是说this的值开始指向的是MyArray的实例, 但是随后被来自Array的其他属性所修饰。

在ES6中则正好相反, 先由基类创建this的值, 然后派生类的构造函数再修改这个值, 所有一开始可以通过this访问基类的所有内建功能, 然后再添加自己的功能, 这就是为什么派生类的constructor必须先写super() 方法。

 1 // 实例:
 2 class MyArray extends Array {
 3
 4 }
 5
 6 let colors = new MyArray();
 7 colors[0] = ‘red‘;
 8 console.log(colors.length); // 1
 9 colors.length = 0;
10 console.log(colors[0]); // undefined
11 colors.push(1, 2, 3, 4, 5);
12 let cs = colors.slice(2, 3);
13 console.log(cs instanceof MyArray); // true

由上面的例子可以看出, MyArray的实例不但可以使用数组的属性和方法, 还具有了数组的特性, slice返回的实例还是自身的实例。

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

 1 // 内建类内部的大致实现方式
 2 class MyClass {
 3   static get[Symbol.species]() {
 4     return this;
 5   }
 6
 7
 8   constructor(value) {
 9     this.value = value;
10   }
11
12
13   clone() {
14     return new this.constructor[Symbol.species](this.value);
15   }
16 }

下面看一个栗子:

 1 class c1 {
 2   constructor(c) {
 3     this.c = c;
 4   }
 5   static get[Symbol.species]() {
 6     return this;
 7   }
 8   cc(c) {
 9     console.log(this.constructor);
10     console.log(this.constructor[Symbol.species]);
11     return new this.constructor[Symbol.species](c);
12   }
13 }
14
15
16 // 如果c2想要自己控制自己所继承自c1的cc方法返回的是自己还是c1,那么就要通过Symbol.species来告
17 // 诉c1,我要返回什么,而c1给的默认选项是返回c2,也就是继承的属性,如果直接使用
18 // this.constructor,那么当我想要返回c1的实例,而不是c2自己时,我是没有办法改变的
19 // 此时就可以通过重写Symbol.species,来达到返回c1的实例的效果
20 class c2 extends c1 {
21   constructor(c) {
22     super(c);
23   }
24   static get[Symbol.species]() {
25     return c1;
26   }
27 }
28
29
30 let c = new c2(‘c2‘);
31 console.log(c.c);
32 console.log(c.cc(‘c22‘));
33
34
35 /*
36 c2
37 [Function: c2]
38 [Function: c1]
39 c1 { c: ‘c22‘ }
40 */

通过上面栗子发现, 直接返回this.constructor也没有什么问题, 原先父类返回自身的方法能够返回现在的派生类, 而不是父类自己, 那么为什么不直接使用this.constructor呢? 因为这个必须要通过一种方式让子类可以进行自由控制, 写死之后如果我确实不想要返回自身的实例, 而是返回父类的实例呢? 那么写死后就没有任何办法进行修改了, 如果MyArray继承了Array, 那么slice永远都只能返回MyArray, 而不能返回一个Array的实例。

在下面的内建类中都已定义Symbol.species属性:

Array、 ArrayBuffer、 Map、 Promise、 Regexp、 Set、 Typed arrays

关于Symbol、 内部Symbol、 class等概念和详细用法, 请看《 深入理解ES6》 一书, 此文出处也是这本书。

关于原型链的继承相关知识请看《JavaScript高级教程第三版》其中有详细的讲解。

原文地址:https://www.cnblogs.com/jyuf/p/8289087.html

时间: 2024-10-11 03:46:35

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

JavaScript内建对象

JavaScript内建对象包括: String.Array.Math.Date.Boolean.Number.Event.RegExp. 使用内建对象的属性和方法需要使用new关键字和构造函数创建内建对象的实例,对象实例继承对象的属性和方法. String 定义了所有处理字符串的操作. Array 提供一个数组模型,存储大量有序数据. Math 定义了所有数学运算. Date 定义了所有处理日期和时间的操作. Boolean 定义了处理布尔值的操作. Number 定义了处理数字的操作. Ev

javascript 对象初探 (四)--- 内建对象Array

 我们不要去纠结神马是内建对象,神马是內建构造器.到后来你们便会发现其实她们都是对象. Array()是一个构建数组的內建构造器函数: var arr = new Array(); 与下面的是等效的: var arr = []; //数组文本表识法 无论数组是以神马方式创建的,我们都可以照常向里面添加元素. arr[0] = 1; arr[1] = 2; console.log(arr) // [1, 2] 当我们使用构造器函数Array()创建数组时,也可以通过传值的方式为其添加元素: var

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

包括内建对象的所有对象都是Object对象的子对象. 1.Array():构建数组的内建构造器函数 例:创建数组方式有两种: 2.Boolean:是对象,与基本数据类型 布尔值 不相同 例:创建Boolean对象. 上例中 a 是对象而不是布尔值.可以通过valueOf()获取其布尔值.通常 Boolean 对象不会用于创建对象,而是利用该对象强制转换数据类型. 在强制转换时,以下内容将被转换为 false:空字符串'';null;undefined;数字0;NaN;false; 例: 即:Bo

ASP基础教程:ASP内建对象Response

在上一篇中作者给大家详细介绍了 ASP 内建对象之一 Request 的使用方法,相信各位通过一系列的实践已经能够熟练掌握,本篇将继续给大家介绍 ASP 的另一个内建对象 Response. 最近,有很多朋友来“妹儿”催我加快 ASP 篇一文的写作速度,并急切地询问哪里有关于 ASP 的教材.我深深地被大家的学习热情所打动,因此决定将自己平时所搜集的一些 ASP 信息资料拿出来和大家共享,也希望所有的朋友能慷慨地将自己搜集的有关 ASP 的资料告诉作者,谢谢.由于目前国内有关 ASP 的中文教材

ASP基础教程:ASP内建对象Application和Session

在上一篇中作者给大家详细介绍了 ASP 内建对象 Response 的使用方法,在这一篇中作者将继续给大家介绍另两个非常实用且重要的 ASP 的内建对象 Application 和 Session. 在 ASP 的内建对象中除了用于发送.接收和处理数据的对象外,还有一些非常实用的代表 Active Server 应用程序和单个用户信息的对象. 让我们先来看看 Application 对象.在同一虚拟目录及其子目录下的所有 .asp 文件构成了 ASP 应用程序.我们非但可以使用 Applicat

ASP基础教程:ASP内建对象Server

通过前九篇的理论和实践,相信大家已经对 ASP 有了系统的了解,虽然至今为止,我们只学了 ASP 的 4 个内建对象,但已经完全能够写出一些实用的小程序了.今天,作者将继续给大家讲解最后一个 ASP 内建对象——Server. 在开始本次课程之前,我仍要在这里回答一些朋友们提出的比较普遍的问题.最近仍有不少朋友来信问我,如何构建服务器端的 Active Server Page 环境.我想可能是我在前几篇中没有讲清楚,因此,在本篇的开头有必要把这个问题详细阐述一遍. ASP 的应用完全是基于 Mi

内建对象

内建对象是指由ECMAScript事先提供的.不依赖于宿主环境的对象,这些对象在程序运行之前就已经存在了.可以直接在程序中任何地方任何时候拿来使用. 1. 内建全局单例对象   Global.Math.JSON. 在整个执行环境中只有一个对象实例,这些对象没有内部属性[[Construct]]和[[Call]],所以不能使用new来创建,也不能作为函数来调用,而是直接使用对象名称来引用其属性和方法(对于全局对象,则可以直接使用属性和方法名). 1.1  常用内建全局单例对象方法 Global对象

JS的内建对象

JS中内置了17个对象,常用的是Array对象.Date对象.正则表达式对象.string对象.Global对象,即js已经创建好了,你不用自己再创建的对象,直接就可以调用. Object 是 JavaScript 中所有对象的父对象数据封装类对象:Object.Array.Boolean.Number 和 String其他对象:Function.Arguments.Math.Date.RegExp.Error Array对象Concat():表示把几个数组合并成一个数组.Join():返回字符

【笔记】javascript扩展内建对象举例

一.在php中有一个叫做in_array()的函数,主要用于查询数组中是否存在某个特定的值. javascript中则没有一个叫做inArray()的方法.因此,通过Array.prototype来实现一个这样的功能: Array.prototype.inArray = function(needle){ for(var i = 0,len = this.length; i < len; i++){ if(this[i] === needle){ return true; } } return