摘要:从这句话我们可以知道,当我们构造一个指定长度的时,由于有长度,所以会开辟相应下标的空间,但是因为该下标并没有元素,所以就会返回,任何原因构造数组元素失败时,都会返回一个。
JS中的Array作者:陈大鱼头
github: KRISACHAN
ecma-262中的定义:Array()Array对象是一种特殊对象,它会对数组索引属性键进行特殊处理。
每个Array对象都有一个不可配置的length属性,其最大值是232 - 1。
当且仅当不带参数调用Array构造函数时,此描述才适用。
执行过程:
定义 numberOfArgs 传递给此函数的调用的实参数量;
断言: numberOfArgs 为 0;
如果 NewTarget 为 undefined ,就设置 newTarget 为 活动函数对象(active-function-object,正在运行的执行上下文的函数组件) ,并且让 newTarget 成为 NewTarget ;
原型 proto 怎么办?通过原生方法GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")来构造;
返回原生方法ArrayCreate(0, proto)。
鱼头注:NewTarget是啥?NewTarget是原生Class FunctionCallbackInfo(函数调用的callback上下文的信息)内的一个不变量,用来定义构造调用时的返回值(new.target)。
Array(len)当且仅当使用一个参数调用Array构造函数时,此描述才适用。
执行过程:
定义 numberOfArgs 为传递给此函数的调用的实参数量;
断言: numberOfArgs 为1;
如果 NewTarget 为 undefined ,就设置 newTarget 为 活动函数对象 ,并且让 newTarget 成为 NewTarget ;
原型 proto 怎么办?通过原生方法GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")来构造;
然后定义array 为ArrayCreate(0, proto);
如果 len 的类型不是个Number,则:
定义 defineStatus 为CreateDataProperty(array, "0", len);
断言:defineStatus为真;
让 intLen(初始化长度) 为1。
或者:
定义intLen为ToUint32(len)(原生方法,将len转换成0到232-1之间的整数值);
如果intLen不等于len,抛出RangeError异常。
执行Set(array, "length", intLen, true)(原生方法,给对象的属性赋值);
返回array。
Array(...items)当且仅当使用至少两个参数调用Array构造函数时,此描述才适用。
执行过程:
定义 numberOfArgs 为传递给此函数的调用的实参数量;
断言: numberOfArgs 大于等于2;
如果 NewTarget 为 undefined ,就设置 newTarget 为 活动函数对象 ,并且让 newTarget 成为 NewTarget ;
原型 proto 怎么办?通过原生方法GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")来构造;
然后定义array 为ArrayCreate(numberOfArgs, proto);
定义 k 为0;
定义 items为 正序传入参数的 零源(zero-origined) 列表;
重复,当 k 小于 numberOfArgs
定义 Pk 为 ToSting(k);
定义 itemK 为 item[k];
定义 defineStatus 为CreateDataProperty(array, Pk, itemK);
断言:defineStatus为真;
k 加1。
断言: array的 length 值为 numberOfArgs;
返回 array。
empty上面的三种情况以上便是构造Array()时不同情况的具体实现。但是我们从上面的断言可以知道,构造结果有可能为真,有可能为假。还有是定义指定长度数组时会出现什么事呢?
在V8源码 3.28.71(node0.12.18)中 Array 有个CloneElementAt的方法。定义如下:
在指定索引处克隆元素时,如果克隆失败,则返回一个空句柄(任何原因)。
从这句话我们可以知道,当我们构造一个指定长度的 Array 时,由于有长度,所以会开辟相应下标的空间,但是因为该下标并没有元素,所以就会返回empty,任何原因构造数组元素失败时,都会返回一个empty。
示例如下:
var arr = new Array(10); arr // [empty × 10]以上总结
上面是 ECMA 上的定义以及 V8 源码的容错处理,其实简单来说就是:
调用 Array(args) 时:
用原生方法 GetPrototypeFromConstructor 生成原型 proto ;
判断 args 的类型;
如果为 undefined,则直接返回创建数组的原生方法 ArrayCreate;
如果为 number,则用原生方法 Set 创建 args 长度的数组,并通过原生方法 CloneElementAt 来创建 args 个 empty 作为数组元素,如果args 大于 232 - 1 的话,会报错;
如果为其他类型,则把 args 变成数组元素,并用 原生方法 CreateDataProperty 创建参数,然后返回创建数组的原生方法 ArrayCreate。
类型转换类型转换是一个经常出现在一些网上常见面试题或者奇技淫巧中的内容。那么关于数组的类型转换,又是怎样的呢?
首先我们要知道,在 JS 中类型转换只有三种情况,分别是:
转换为布尔值
转换为数字
转换为字符串
转换为原始类型对象在转换类型的时候,会执行原生方法ToPrimitive。
其算法如下:
如果已经是 原始类型,则返回当前值;
如果需要转 字符串 则先调用toSting方法,如果此时是 原始类型 则直接返回,否则再调用valueOf方法并返回结果;
如果不是 字符串,则先调用valueOf方法,如果此时是 原始类型 则直接返回,否则再调用toString方法并返回结果;
如果都没有 原始类型 返回,则抛出 TypeError类型错误。
当然,我们可以通过重写Symbol.toPrimitive来制定转换规则,此方法在转原始类型时调用优先级最高。
// 下面例子来自YCK的小册 const data = { valueOf () { return 1; }, toString () { return "1"; }, [Symbol.toPrimitive]() { return 2; } }; data + 1 // 3转换为布尔值
对象转换为布尔值的规则如下表:
参数类型 | 结果 |
---|---|
Undefined | 返回 false。 |
Null | 返回 false。 |
Boolean | 返回 当前参数。 |
Number | 如果参数为+0、-0或NaN,则返回 false;其他情况则返回 true。 |
String | 如果参数为空字符串,则返回 false;否则返回 true。 |
Symbol | 返回 true。 |
Object | 返回 true。 |
对象转换为数字的规则如下表:
参数类型 | 结果 |
---|---|
Undefined | 返回 NaN。 |
Null | Return +0. |
Boolean | 如果参数为 true,则返回 1;false则返回 +0。 |
Number | 返回当前参数。 |
String | 先调用 ToPrimitive,再调用 ToNumber,然后返回结果。 |
Symbol | 抛出 TypeError错误。 |
Object | 先调用 ToPrimitive,再调用 ToNumber,然后返回结果。 |
对象转换为字符串的规则如下表:
参数类型 | 结果 |
---|---|
Undefined | 返回 "undefined"。 |
Null | 返回 "null"。 |
Boolean | 如果参数为 true ,则返回 "true";否则返回 "false"。 |
Number | 调用 NumberToString,然后返回结果。 |
String | 返回 当前参数。 |
Symbol | 抛出 TypeError错误。 |
Object | 先调用 ToPrimitive,再调用 ToString,然后返回结果。 |
所以通过上面的转换规则,我们是否能够轻松地看懂以下的隐式转换呢?
[1,2,3] + {a: 1, b: 2} // "1,2,3[object Object]" [1,2,3] + 1 // "1,2,31" [1,2,3] + true // "1,2,3true" [1,2,3] + undefined // "1,2,3undefined" [1,2,3] + null // "1,2,3null" [1,2,3] + "123" // "1,2,3123" [1,2,3] + Symbol("biu") // "Uncaught TypeError"
所以各位是否理解上述隐式转换的答案呢?
关于API使用的一些经验与思考JS数组自带了很多的方法,在现代工程化数据驱动的理念下,这些方法都是非常重要的。loops
forEach是 Array 方法中比较基本的一个,作用也很简单,与for,就是遍历,循环。不同的是,forEach可以选择自定义上下文环境。例子如下:
var arr1 = [1, 2, 3]; var arr2 = [5, 6, 7]; arr1.forEach(function (e, i, a) { console.log(e, this); // this为arr2 }, arr2);
但是如果forEach的回调函数是用箭头函数定义的,那么就无法改变它原本指向的上下文环境。例子如下:
var arr1 = [1, 2, 3]; var arr2 = [5, 6, 7]; arr1.forEach((e, i, a) => { console.log(e, this); // this为window对象 }, arr2);
所以如果我们要实现以下这个功能:
在ES6以前的环境中,如果直接用for循环,会出现只能获取到最后一个元素的问题,但是用forEach则没有这个问题。
reduceArray ES5 API reduce跟reduceRight,可以轻松实现并归元素的功能,例子如下:
如果我们需要实现一个这样的对象:
{ a: 1, b: 2, c: 3 ... };
那么用reduce就会变得很简单:
var newArr = "a,b,c,d,e,f".split(",").reduce((acc, cur, idx) => { let o = {}; if (Object.prototype.toString.call(acc) !== "[object Object]") { o[cur] = idx; } else { let newO = {}; newO[cur] = idx; o = { ...acc, ...newO, }; }; return o; }, "a");性能
上面演示了通过JS数组API实现的一些功能,所以与for循环比性能如何呢?
var arr = new Array(100); arr.forEach(data => { console.log(data); }); for (var i = 0; i < arr.length; ++i) { console.log(arr[i]); };
所以哪个更耗时间呢?
在公布结果之前,其实网上一直流传着for循环性能比forEach性能好,考虑性能少用forEach的言论,其实以前的浏览器也是这种情况。
详情可以看知乎的这篇评论:https://www.zhihu.com/question/54637225/answer/140362071
性能对比如下:
以下代码测试环境为:Chrome 55.0.2883 / Windows 7 0.0.0
所以在9012年的如今,结果又会是如何呢?
以下代码测试环境为:Chrome 73.0.3683 / Windows 10 0.0.0
通过上面的对比,结果已经很明显了,我们要知道,现代的浏览器性能优化已经做得比以前好很多了,再加上电子设备本身的硬件也越来越好,所以代码块的性能不是我们首要的考虑因素。
在跟同行沟通的过程中,经常会看到有人为了扣那么一个两个表达式的性能而烦恼,其实是这是没有任何必要,原因也如上,我们应该优化的是我们表达式是否清晰明了,是否适合后期维护或拓展。
如果你也喜欢探讨技术,或者对本文,本系列有任何的意见或建议,你可以扫描下方二维码,关注微信公众号“ 鱼头的Web海洋 ”,随时与鱼头互动。欢迎!衷心希望可以遇见你。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105872.html
摘要:当且仅当使用至少两个参数调用构造函数时,此描述才适用。断言的值为返回。还有是定义指定长度数组时会出现什么事呢在源码中有个的方法。转换为数字对象转换为数字的规则如下表参数类型结果返回。不同的是,可以选择自定义上下文环境。 作者:陈大鱼头 github: KRISACHAN JS中的Array ecma-262中的定义: Array对象是一种特殊对象,它会对数组索引属性键进行特殊处理。 ...
摘要:简单就意味着更快的开发速度,更小的维护成本,同时往往具有更好的体验下面我介绍哪些或许你不知道小技巧。默认为,此时阴影与元素同样大。如果没有指定,则由浏览器决定通常是的值,不过目前取透明。首先,我们要明白这里的最小宽度值是什么意思。 暑假实习的时候带我的师傅,告诉我要注重基础,底层实现原理。才能在日新月异的技术行业站住脚跟,以不变应万变,万丈高楼平地起,所以我们应该不断的去学习,去交流。...
摘要:简单就意味着更快的开发速度,更小的维护成本,同时往往具有更好的体验下面我介绍哪些或许你不知道小技巧。默认为,此时阴影与元素同样大。如果没有指定,则由浏览器决定通常是的值,不过目前取透明。首先,我们要明白这里的最小宽度值是什么意思。 暑假实习的时候带我的师傅,告诉我要注重基础,底层实现原理。才能在日新月异的技术行业站住脚跟,以不变应万变,万丈高楼平地起,所以我们应该不断的去学习,去交流。...
摘要:简单就意味着更快的开发速度,更小的维护成本,同时往往具有更好的体验下面我介绍哪些或许你不知道小技巧。默认为,此时阴影与元素同样大。如果没有指定,则由浏览器决定通常是的值,不过目前取透明。首先,我们要明白这里的最小宽度值是什么意思。 暑假实习的时候带我的师傅,告诉我要注重基础,底层实现原理。才能在日新月异的技术行业站住脚跟,以不变应万变,万丈高楼平地起,所以我们应该不断的去学习,去交流。...
阅读 3176·2023-04-25 20:35
阅读 3569·2019-08-30 15:54
阅读 1948·2019-08-30 15:43
阅读 2140·2019-08-29 15:14
阅读 1861·2019-08-29 11:17
阅读 3356·2019-08-26 13:36
阅读 663·2019-08-26 10:15
阅读 2751·2019-08-23 15:41