摘要:當(dāng)且僅當(dāng)使用至少兩個(gè)參數(shù)調(diào)用構(gòu)造函數(shù)時(shí),此描述才適用。斷言的值為返回。還有是定義指定長(zhǎng)度數(shù)組時(shí)會(huì)出現(xiàn)什么事呢在源碼中有個(gè)的方法。轉(zhuǎn)換為數(shù)字對(duì)象轉(zhuǎn)換為數(shù)字的規(guī)則如下表參數(shù)類(lèi)型結(jié)果返回。不同的是,可以選擇自定義上下文環(huán)境。
JS中的Array作者:陳大魚(yú)頭
github: KRISACHAN
Array()ecma-262中的定義:
Array對(duì)象是一種特殊對(duì)象,它會(huì)對(duì)數(shù)組索引屬性鍵進(jìn)行特殊處理。
每個(gè)Array對(duì)象都有一個(gè)不可配置的length屬性,其最大值是232 - 1。
當(dāng)且僅當(dāng)不帶參數(shù)調(diào)用Array構(gòu)造函數(shù)時(shí),此描述才適用。
執(zhí)行過(guò)程:
定義 numberOfArgs 傳遞給此函數(shù)的調(diào)用的實(shí)參數(shù)量;
斷言: numberOfArgs 為 0;
如果 NewTarget 為 undefined ,就設(shè)置 newTarget 為 活動(dòng)函數(shù)對(duì)象(active-function-object,正在運(yùn)行的執(zhí)行上下文的函數(shù)組件) ,并且讓 newTarget 成為 NewTarget ;
原型 proto 怎么辦?通過(guò)原生方法GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")來(lái)構(gòu)造;
返回原生方法ArrayCreate(0, proto)。
魚(yú)頭注:NewTarget是啥?NewTarget是原生Class FunctionCallbackInfo(函數(shù)調(diào)用的callback上下文的信息)內(nèi)的一個(gè)不變量,用來(lái)定義構(gòu)造調(diào)用時(shí)的返回值(new.target)。
Array(len)當(dāng)且僅當(dāng)使用一個(gè)參數(shù)調(diào)用Array構(gòu)造函數(shù)時(shí),此描述才適用。
執(zhí)行過(guò)程:
定義 numberOfArgs 為傳遞給此函數(shù)的調(diào)用的實(shí)參數(shù)量;
斷言: numberOfArgs 為1;
如果 NewTarget 為 undefined ,就設(shè)置 newTarget 為 活動(dòng)函數(shù)對(duì)象 ,并且讓 newTarget 成為 NewTarget ;
原型 proto 怎么辦?通過(guò)原生方法GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")來(lái)構(gòu)造;
然后定義array 為ArrayCreate(0, proto);
如果 len 的類(lèi)型不是個(gè)Number,則:
定義 defineStatus 為CreateDataProperty(array, "0", len);
斷言:defineStatus為真;
讓 intLen(初始化長(zhǎng)度) 為1。
或者:
定義intLen為ToUint32(len)(原生方法,將len轉(zhuǎn)換成0到232-1之間的整數(shù)值);
如果intLen不等于len,拋出RangeError異常。
執(zhí)行Set(array, "length", intLen, true)(原生方法,給對(duì)象的屬性賦值);
返回array。
當(dāng)且僅當(dāng)使用至少兩個(gè)參數(shù)調(diào)用Array構(gòu)造函數(shù)時(shí),此描述才適用。
執(zhí)行過(guò)程:
定義 numberOfArgs 為傳遞給此函數(shù)的調(diào)用的實(shí)參數(shù)量;
斷言: numberOfArgs 大于等于2;
如果 NewTarget 為 undefined ,就設(shè)置 newTarget 為 活動(dòng)函數(shù)對(duì)象 ,并且讓 newTarget 成為 NewTarget ;
原型 proto 怎么辦?通過(guò)原生方法GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")來(lái)構(gòu)造;
然后定義array 為ArrayCreate(numberOfArgs, proto);
定義 k 為0;
定義 items為 正序傳入?yún)?shù)的 零源(zero-origined) 列表;
重復(fù),當(dāng) k 小于 numberOfArgs
定義 Pk 為 ToSting(k);
定義 itemK 為 item[k];
定義 defineStatus 為CreateDataProperty(array, Pk, itemK);
斷言:defineStatus為真;
k 加1。
斷言: array的 length 值為 numberOfArgs;
返回 array。
上面的三種情況以上便是構(gòu)造Array()時(shí)不同情況的具體實(shí)現(xiàn)。但是我們從上面的斷言可以知道,構(gòu)造結(jié)果有可能為真,有可能為假。還有是定義指定長(zhǎng)度數(shù)組時(shí)會(huì)出現(xiàn)什么事呢?
在V8源碼 3.28.71(node0.12.18)中 Array 有個(gè)CloneElementAt的方法。定義如下:
在指定索引處克隆元素時(shí),如果克隆失敗,則返回一個(gè)空句柄(任何原因)。
從這句話我們可以知道,當(dāng)我們構(gòu)造一個(gè)指定長(zhǎng)度的 Array 時(shí),由于有長(zhǎng)度,所以會(huì)開(kāi)辟相應(yīng)下標(biāo)的空間,但是因?yàn)樵撓聵?biāo)并沒(méi)有元素,所以就會(huì)返回empty,任何原因構(gòu)造數(shù)組元素失敗時(shí),都會(huì)返回一個(gè)empty。
示例如下:
var arr = new Array(10);
arr // [empty × 10]
以上總結(jié)
上面是 ECMA 上的定義以及 V8 源碼的容錯(cuò)處理,其實(shí)簡(jiǎn)單來(lái)說(shuō)就是:
調(diào)用 Array(args) 時(shí):
用原生方法 GetPrototypeFromConstructor 生成原型 proto ;
判斷 args 的類(lèi)型;
如果為 undefined,則直接返回創(chuàng)建數(shù)組的原生方法 ArrayCreate;
如果為 number,則用原生方法 Set 創(chuàng)建 args 長(zhǎng)度的數(shù)組,并通過(guò)原生方法 CloneElementAt 來(lái)創(chuàng)建 args 個(gè) empty 作為數(shù)組元素,如果args 大于 232 - 1 的話,會(huì)報(bào)錯(cuò);
如果為其他類(lèi)型,則把 args 變成數(shù)組元素,并用 原生方法 CreateDataProperty 創(chuàng)建參數(shù),然后返回創(chuàng)建數(shù)組的原生方法 ArrayCreate。
類(lèi)型轉(zhuǎn)換是一個(gè)經(jīng)常出現(xiàn)在一些網(wǎng)上常見(jiàn)面試題或者奇技淫巧中的內(nèi)容。那么關(guān)于數(shù)組的類(lèi)型轉(zhuǎn)換,又是怎樣的呢?
首先我們要知道,在 JS 中類(lèi)型轉(zhuǎn)換只有三種情況,分別是:
轉(zhuǎn)換為布爾值
轉(zhuǎn)換為數(shù)字
轉(zhuǎn)換為字符串
轉(zhuǎn)換為原始類(lèi)型對(duì)象在轉(zhuǎn)換類(lèi)型的時(shí)候,會(huì)執(zhí)行原生方法ToPrimitive。
其算法如下:
如果已經(jīng)是 原始類(lèi)型,則返回當(dāng)前值;
如果需要轉(zhuǎn) 字符串 則先調(diào)用toSting方法,如果此時(shí)是 原始類(lèi)型 則直接返回,否則再調(diào)用valueOf方法并返回結(jié)果;
如果不是 字符串,則先調(diào)用valueOf方法,如果此時(shí)是 原始類(lèi)型 則直接返回,否則再調(diào)用toString方法并返回結(jié)果;
如果都沒(méi)有 原始類(lèi)型 返回,則拋出 TypeError類(lèi)型錯(cuò)誤。
當(dāng)然,我們可以通過(guò)重寫(xiě)Symbol.toPrimitive來(lái)制定轉(zhuǎn)換規(guī)則,此方法在轉(zhuǎn)原始類(lèi)型時(shí)調(diào)用優(yōu)先級(jí)最高。
// 下面例子來(lái)自YCK的小冊(cè)
const data = {
valueOf () {
return 1;
},
toString () {
return "1";
},
[Symbol.toPrimitive]() {
return 2;
}
};
data + 1 // 3
轉(zhuǎn)換為布爾值
對(duì)象轉(zhuǎn)換為布爾值的規(guī)則如下表:
參數(shù)類(lèi)型 | 結(jié)果 |
---|---|
Undefined | 返回 false。 |
Null | 返回 false。 |
Boolean | 返回 當(dāng)前參數(shù)。 |
Number | 如果參數(shù)為+0、-0或NaN,則返回 false;其他情況則返回 true。 |
String | 如果參數(shù)為空字符串,則返回 false;否則返回 true。 |
Symbol | 返回 true。 |
Object | 返回 true。 |
對(duì)象轉(zhuǎn)換為數(shù)字的規(guī)則如下表:
參數(shù)類(lèi)型 | 結(jié)果 |
---|---|
Undefined | 返回 NaN。 |
Null | Return +0. |
Boolean | 如果參數(shù)為 true,則返回 1;false則返回 +0。 |
Number | 返回當(dāng)前參數(shù)。 |
String | 先調(diào)用 ToPrimitive,再調(diào)用 ToNumber,然后返回結(jié)果。 |
Symbol | 拋出 TypeError錯(cuò)誤。 |
Object | 先調(diào)用 ToPrimitive,再調(diào)用 ToNumber,然后返回結(jié)果。 |
對(duì)象轉(zhuǎn)換為字符串的規(guī)則如下表:
參數(shù)類(lèi)型 | 結(jié)果 |
---|---|
Undefined | 返回 "undefined"。 |
Null | 返回 "null"。 |
Boolean | 如果參數(shù)為 true ,則返回 "true";否則返回 "false"。 |
Number | 調(diào)用 NumberToString,然后返回結(jié)果。 |
String | 返回 當(dāng)前參數(shù)。 |
Symbol | 拋出 TypeError錯(cuò)誤。 |
Object | 先調(diào)用 ToPrimitive,再調(diào)用 ToString,然后返回結(jié)果。 |
所以通過(guò)上面的轉(zhuǎn)換規(guī)則,我們是否能夠輕松地看懂以下的隱式轉(zhuǎn)換呢?
[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"
所以各位是否理解上述隱式轉(zhuǎn)換的答案呢?
關(guān)于API使用的一些經(jīng)驗(yàn)與思考loopsJS數(shù)組自帶了很多的方法,在現(xiàn)代工程化數(shù)據(jù)驅(qū)動(dòng)的理念下,這些方法都是非常重要的。
forEach是 Array 方法中比較基本的一個(gè),作用也很簡(jiǎn)單,與for,就是遍歷,循環(huán)。不同的是,forEach可以選擇自定義上下文環(huán)境。例子如下:
var arr1 = [1, 2, 3];
var arr2 = [5, 6, 7];
arr1.forEach(function (e, i, a) {
console.log(e, this); // this為arr2
}, arr2);
但是如果forEach的回調(diào)函數(shù)是用箭頭函數(shù)定義的,那么就無(wú)法改變它原本指向的上下文環(huán)境。例子如下:
var arr1 = [1, 2, 3];
var arr2 = [5, 6, 7];
arr1.forEach((e, i, a) => {
console.log(e, this); // this為window對(duì)象
}, arr2);
所以如果我們要實(shí)現(xiàn)以下這個(gè)功能:
<ul>
<li class="1">1li>
<li class="2">2li>
<li class="3">3li>
<li class="4">4li>
<li class="5">5li>
ul>
<script>
"use strict";
var ul = document.querySelector("ul");
ul.onClick = event => {
var cls = event.target.className;
ul.querySelectorAll("li").forEach(el => {
el.style.color = (cls === el.className ");"#FFF" : "#FF0");
});
};
script>
在ES6以前的環(huán)境中,如果直接用for循環(huán),會(huì)出現(xiàn)只能獲取到最后一個(gè)元素的問(wèn)題,但是用forEach則沒(méi)有這個(gè)問(wèn)題。
reduceArray ES5 API reduce跟reduceRight,可以輕松實(shí)現(xiàn)并歸元素的功能,例子如下:
如果我們需要實(shí)現(xiàn)一個(gè)這樣的對(duì)象:
{
a: 1,
b: 2,
c: 3
...
};
那么用reduce就會(huì)變得很簡(jiǎn)單:
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");
性能
上面演示了通過(guò)JS數(shù)組API實(shí)現(xiàn)的一些功能,所以與for循環(huán)比性能如何呢?
var arr = new Array(100);
arr.forEach(data => {
console.log(data);
});
for (var i = 0; i < arr.length; ++i) {
console.log(arr[i]);
};
所以哪個(gè)更耗時(shí)間呢?
在公布結(jié)果之前,其實(shí)網(wǎng)上一直流傳著for循環(huán)性能比f(wàn)orEach性能好,考慮性能少用forEach的言論,其實(shí)以前的瀏覽器也是這種情況。
詳情可以看知乎的這篇評(píng)論:www.zhihu.com/question/54…
性能對(duì)比如下:
以下代碼測(cè)試環(huán)境為:Chrome 55.0.2883 / Windows 7 0.0.0
所以在9012年的如今,結(jié)果又會(huì)是如何呢?
以下代碼測(cè)試環(huán)境為:Chrome 73.0.3683 / Windows 10 0.0.0
通過(guò)上面的對(duì)比,結(jié)果已經(jīng)很明顯了,我們要知道,現(xiàn)代的瀏覽器性能優(yōu)化已經(jīng)做得比以前好很多了,再加上電子設(shè)備本身的硬件也越來(lái)越好,所以代碼塊的性能不是我們首要的考慮因素。
在跟同行溝通的過(guò)程中,經(jīng)常會(huì)看到有人為了扣那么一個(gè)兩個(gè)表達(dá)式的性能而煩惱,其實(shí)是這是沒(méi)有任何必要,原因也如上,我們應(yīng)該優(yōu)化的是我們表達(dá)式是否清晰明了,是否適合后期維護(hù)或拓展。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7296.html
摘要:從這句話我們可以知道,當(dāng)我們構(gòu)造一個(gè)指定長(zhǎng)度的時(shí),由于有長(zhǎng)度,所以會(huì)開(kāi)辟相應(yīng)下標(biāo)的空間,但是因?yàn)樵撓聵?biāo)并沒(méi)有元素,所以就會(huì)返回,任何原因構(gòu)造數(shù)組元素失敗時(shí),都會(huì)返回一個(gè)。 作者:陳大魚(yú)頭 github: KRISACHAN JS中的Array ecma-262中的定義:Array對(duì)象是一種特殊對(duì)象,它會(huì)對(duì)數(shù)組索引屬性鍵進(jìn)行特殊處理。 每個(gè)Array對(duì)象都有一個(gè)不可配置的lengt...
摘要:另外,適配器模式和其它幾個(gè)模式可能容易讓人迷惑,這里說(shuō)一下大概的區(qū)別適配器和橋接模式雖然類(lèi)似,但橋接的出發(fā)點(diǎn)不同,橋接的目的是將接口部分和實(shí)現(xiàn)部分分離,從而對(duì)他們可以更為容易也相對(duì)獨(dú)立的加以改變。 1. 簡(jiǎn)介 適配器模式(Adapter)是將一個(gè)類(lèi)(對(duì)象)的接口(方法或?qū)傩裕┺D(zhuǎn)化成客戶(hù)希望的另外一個(gè)接口(方法或?qū)傩裕?,適配器模式使得原本由于接口不兼容而不能一起工作的那些類(lèi)(對(duì)象)可以一...
摘要:簡(jiǎn)單就意味著更快的開(kāi)發(fā)速度,更小的維護(hù)成本,同時(shí)往往具有更好的體驗(yàn)下面我介紹哪些或許你不知道小技巧。默認(rèn)為,此時(shí)陰影與元素同樣大。如果沒(méi)有指定,則由瀏覽器決定通常是的值,不過(guò)目前取透明。首先,我們要明白這里的最小寬度值是什么意思。 暑假實(shí)習(xí)的時(shí)候帶我的師傅,告訴我要注重基礎(chǔ),底層實(shí)現(xiàn)原理。才能在日新月異的技術(shù)行業(yè)站住腳跟,以不變應(yīng)萬(wàn)變,萬(wàn)丈高樓平地起,所以我們應(yīng)該不斷的去學(xué)習(xí),去交流。...
摘要:簡(jiǎn)單就意味著更快的開(kāi)發(fā)速度,更小的維護(hù)成本,同時(shí)往往具有更好的體驗(yàn)下面我介紹哪些或許你不知道小技巧。默認(rèn)為,此時(shí)陰影與元素同樣大。如果沒(méi)有指定,則由瀏覽器決定通常是的值,不過(guò)目前取透明。首先,我們要明白這里的最小寬度值是什么意思。 暑假實(shí)習(xí)的時(shí)候帶我的師傅,告訴我要注重基礎(chǔ),底層實(shí)現(xiàn)原理。才能在日新月異的技術(shù)行業(yè)站住腳跟,以不變應(yīng)萬(wàn)變,萬(wàn)丈高樓平地起,所以我們應(yīng)該不斷的去學(xué)習(xí),去交流。...
摘要:簡(jiǎn)單就意味著更快的開(kāi)發(fā)速度,更小的維護(hù)成本,同時(shí)往往具有更好的體驗(yàn)下面我介紹哪些或許你不知道小技巧。默認(rèn)為,此時(shí)陰影與元素同樣大。如果沒(méi)有指定,則由瀏覽器決定通常是的值,不過(guò)目前取透明。首先,我們要明白這里的最小寬度值是什么意思。 暑假實(shí)習(xí)的時(shí)候帶我的師傅,告訴我要注重基礎(chǔ),底層實(shí)現(xiàn)原理。才能在日新月異的技術(shù)行業(yè)站住腳跟,以不變應(yīng)萬(wàn)變,萬(wàn)丈高樓平地起,所以我們應(yīng)該不斷的去學(xué)習(xí),去交流。...
閱讀 2433·2021-10-11 10:57
閱讀 1284·2021-10-09 09:59
閱讀 1999·2019-08-30 15:53
閱讀 3215·2019-08-30 15:53
閱讀 1013·2019-08-30 15:45
閱讀 741·2019-08-30 15:44
閱讀 3447·2019-08-30 14:24
閱讀 955·2019-08-30 14:21