1. 程式人生 > >奇妙JS程式碼系列(三)一道有趣的題(建立一個長度為x陣列)

奇妙JS程式碼系列(三)一道有趣的題(建立一個長度為x陣列)

原題描述:

不使用loop迴圈,建立一個長度為100的陣列,並且每個元素的值等於它的下標

這個問題的一些變種例如給一個長度length和value,返回長度為length值全為value的陣列等。

1.誤區

最容易掉入陷阱的:

var arr = new Array(100);
arr = arr.map(function(item, index) {
return index;
});

如果你的答案是這個,恭喜你成功掉入出題人的坑了。

2.解釋

坑在Array(100),可以看看MDN的規範,
這裡寫圖片描述
new Array(100)返回的是一個什麼都沒有的“稀疏陣列”,其實我覺得這個答案可以看做是瀏覽器顯示處理的Bug。

在47版本Chrome下(或者早些的Chrome版本),new Array(10)會返回[undefined,undefined,undefined,.......(100)],但這個undefined又跟你直接宣告的[undefined,undefined,undefined,.......(100)]不同(都是47版本的Chrome),測試:
這裡寫圖片描述

這裡寫圖片描述

看出來了把,這個就是以前瀏覽器的顯示Bug,後來修復了也就是現在顯示的empty,用來區別undefined,表明這個和undefined是不一樣的。

上面提到的Array()建立的是稀疏陣列(Sparse Array),在一些資料中是這樣說的,但是在js裡隨便建立個數組都可以是「稀疏」的吧(因為實際上就是 Object ?)

a = new Array(2); a[1] = 1; a;  // (2) [undefined × 1, 1]
b = []; b[4] = 5; b[9] = 10;
b;  // (10) [undefined × 4, 5, undefined × 4, 10]
而 JS 對於這類陣列,由於上述規範的定義,遍歷的時候會跳過空的地方
a.map(e => 2*e)  //(2) [undefined × 1, 2]

JS 引擎對於稀鬆陣列是用字典儲存的,能省一些空間,但是在索引方面肯定是比 Full Array 吃虧了。所以 JavaScript 不把 new Array 設計成全用 undefined 填滿,有點反直覺。

以上是一種解釋,另一種不用稀疏陣列來解釋:

對於 new Array(2),它實際上返回的是一個長度為 2,裡面什麼都沒有的陣列。即對應empty字面表現的意義。

根據 ECMA-262 6th edition,即 ES6 規範 22.1.1.2 小節 Array (len), Array(len) 僅僅就是給新陣列設了個 length 屬性而已,其他什麼也沒做……而對於 JS 陣列的索引,就算是陣列越界了,也都是返回 undefined:

new Array(2)[999] === undefined // true

能影響到 Array.prototype.map() 的原因是因為,根據規範 22.1.3.15 小節 Array.prototype.map ( callbackfn [ , thisArg ] ),map 函式會先檢查 HasProperty。要 map,首先這個元素/屬性要存在。可是:

new Array(2).hasOwnProperty(0) // false
[undefined, undefined].hasOwnProperty(0) // true

所以,傳給 map 的 callback 其實根本就沒執行,可以測試

new Array(2).map(e => console.log('done'))  

會發現啥也沒 log 出來。

這個“bug”在edge和Safari也發現過。

3.正確的解決

扯了那麼多,那麼這道題應該怎麼做呢。

其實這道題的面試官當時是說不用loop的情況下的解決方案,用Array方法的話其實都是屬於loop,目的是考察遞迴:

1. 遞迴+自執行函式
var arr = [];

(function dfs(i) {
  if (i < 100) {
    arr.push(i);
    dfs(++i);
  }
}(0));
2. 轉化成字串
Array(100).join(",").split(",").map(function(key,index){return index;})
3. apply轉換一下,可以看到js的大多數坑的解決都有apply的身影。。。
Array.apply(null,Array(100)).map((item,index)=>index)

apply在es5可以加類陣列物件,在傳入的時候,由於每一項的值都是不存在的,相當於進去的是 undefined(這裡的undefined是真真切切的undefined。。。)

4. Int8Array
new Int8Array(100).map((item,index)=>index);

Int8Array的規範看相關文件把

5.討論的時候B大給的兩個最騷的解法。。。

這裡寫圖片描述

這裡寫圖片描述

第二種都看得懂把,第一種新手還是不好理解的,這裡解釋一下:

1、 js 裡的 + 號在不同表示式裡有不同含義
2、在這裡,是加號
3、加號後面跟一個數組,觸發了隱式轉換
4、陣列的隱式轉換包含兩個,toString 和 valueOf,根據陣列的隱式轉換規則,這裡呼叫 valueOf
5、但是 ary 的 valueOf 被改寫了,改成了看到的那個函式,那個函式裡每次會給 ary 新加一個與下標相同的元素
6、 如果下標還沒到 100,就再 +ary 一次,相當於又呼叫了一次 valueOf
7、於是就形成了遞迴

其他的
Object.keys(Array(100).toString().split(","))

function genArr(i, arr){
    if(i < 10){
        arr[i] = i++
        return genArr(i, arr);
    } else {
        return arr;
    }
}
var arr = genArr(0, [])

var a =[],b= function () { return a.length<100?(a.push(a.length)&&arguments.callee()):a }(); console.log(a,b);


[...Array(10).keys()]

Array(100).fill().map((v, i) => i)

4.問題的升級

題目改成100w,百萬級的陣列,怎麼生成快?
關鍵詞:弄個 getter setter ,惰性求值

5.求是求真

以上的問題可以看V8的原始碼,不看原始碼的時候,只能確定行為,誰解釋都有一套。
v8原始碼連結:連結
stackoverflow的討論:連結

相關推薦

奇妙JS程式碼系列一道有趣(建立一個長度x陣列)

原題描述: 不使用loop迴圈,建立一個長度為100的陣列,並且每個元素的值等於它的下標 這個問題的一些變種例如給一個長度length和value,返回長度為length值全為value的陣列等。 1.誤區 最容易掉入陷阱的: var arr = new Array(100); arr = arr.map(f

Vue.js學習系列-- Vue.js樣式繫結

class 與 style 是 HTML 元素的屬性,用於設定元素的樣式,我們可以用v-bind來設定樣式屬性。Vue.js v-bind 在處理 class 和 style 時,專門增強了它。表示式的結果型別除了字串之外, 1. Vue.js class 1.1 繫結

分布式緩存技術redis學習系列——redis高級應用主從、事務與鎖、持久化

master ica not ood www working can 出了 owin 上文《詳細講解redis數據結構(內存模型)以及常用命令》介紹了redis的數據類型以及常用命令,本文我們來學習下redis的一些高級特性。 回到頂部 安全性設置 設置客戶端操作秘密

數據結構系列線性表

復雜 -o -type 復雜度 順序結構 之前 包含 替換 鏈式存儲結構 線性表是什麽 零個或多個數據元素的有序序列 線性存儲結構 例如 java中的數組,每次都申請固定長度內存空間,並且長度不可變 而arraylist則是長度可變的數組,這是java在底層對數組

【原創】源碼角度分析Android的消息機制系列——ThreadLocal的工作原理

沒有 cit gen 管理 pre 靜態 bsp 允許 clas ι 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 先看Android源碼(API24)中對ThreadLocal的定義: public class ThreadLocal<T>

源碼分析系列x264_deblocking_dataflow

像素 色度 結構 inf blank 水平 frame 垂直 左右 http://www.cnblogs.com/xkfz007/articles/2616157.html 去塊濾波(Deblocking)部分關鍵函數 3.1 deblocking_filter_ed

Windows Server 2012 AD 站點和域部署系列創建站點、子網及鏈接

子網 站點 windows server 2012 配置域 本章博文開始在根域ds01 端創建BJ、SH、GZ站點 ,配置子網及相關站點間的鏈接 。創建站點:1、重命名默認站點:登陸ds01,打開“Active Directory 站點和服務”,右鍵點擊默認的站點Default-First-Sit

PHP系列PHP數組與數據結構

php數組與數據結構 PHP數組與數據結構數組是把若幹變量按有序的形式組織起來的一種形式。這些數據元素的集數組分為一維二維三維、索引數組(數組索引是整數)和關聯數組 (1)數組的聲明1、一個數組中存的多個內容、數組中的內容叫作“元素”2、每個元素都是由健和值組成的Key/

Linux系統運維常見面試簡答題系列9

connect 切換 -a ip) 整理 程序 strong ack 自己 1. 寫一個sed命令,修改/tmp/input.txt文件的內容,要求:(1) 刪除所有空行;(2) 一行中,如果包含”11111″,則在”11111″前面插入”AAA”,在”11111″後面插入

Python操作rabbitmq系列:多個接收端消費消息

name 連接 logs http clas header 消費者 exclusive pub 接著上一章。這一章,我們要將同一個消息發給多個客戶端。這就是發布訂閱模式。直接看代碼: 發送端: import pikaimport sysconnection = pika.B

Flask 學習系列---Jinjia2使用過濾器

ide 數位 指定字段 模板 小數 else capital 12px 效果圖 再Jinjia2中過濾器是一種轉變變量輸出內容的技術。··過濾器通過管道符號“|與變量鏈接,並且可以通過圓括號傳遞參數” 。舉例說明: {{my_variable|default(‘my_var

Docker系列容器管理

mozilla http 格式 file tor centos determine dia 進程 3.1 新建容器並啟動所需要的命令主要為docker run [root@localhost ~]# docker run centos /bin/echo "syavi

wifi認證Portal開發系列:portal協議

tro spa size http log ron 認證 gin auto 中國移動WLAN業務PORTAL協議規範介紹 wifi認證Portal開發系列(三):portal協議

詳解YUV系列----YUV420

roc 根據 系列 watermark 存儲方式 圖片 images src fff 前兩講詳細講解了YUV444以及YUV422兩種格式,實際中這兩種格式使用的相對較少,使用比較多的便是本節要梳理的YUV420格式嘍,同樣,老辦法,老套路嘍。 一、文字描述:YUV

C# 多線程系列

job row 空閑 最好 方式 不同的 運行時 作業 tun 線程池 創建線程需要時間,如果有不同的小任務要完成,就可以事先創建許多線程,在應完成這些任務時發出請求。這個線程數最好在需要更多線程時增加,在需要釋放資源時減少。 不需要自己創建這樣的一個列表。該列表由T

Docker入門與應用系列容器管理

輸出 clear tag 程序 ipaddr one 停止 1.2 標準 一、啟動容器   啟動容器有兩種方式,一種是基於鏡像新建一個容器並啟動,另一個是將終止狀態的容器重新啟動。 1.1 新建並啟動 主要命令為 docker run 下面的命令輸出一個&rd

Java Thread系列線程安全

AI 資源 習慣 get string tar rup end 就是 Java Thread系列(三)線程安全 一、什麽是線程安全 線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個類始終都能表現出正確的行為,那麽這個類(對象或方法)就是線程安全的。 線程安全來

Greeplum 系列 基本用法

ont price createdb version access tex sin 方便 清空表 Greeplum 系列(三) 基本用法 《PostgreSQL 教程》:https://www.yiibai.com/postgresql 一、Greeplum 登陸與創建

EntityFramework系列---上下文線程內唯一

ext.get 推薦 方法 隨著 ack 問題 IE public == 為什麽要讓DbContext線程內唯一   在使用EF的情況下,我們通常把SaveChange這個方法提到業務邏輯層,如果在用到DbContext的時候就new一個出來的話,不能保證同一個業務邏輯使用

yum 系列 離線部署

基於 位置 text yum 倉庫 機器 不安裝 軟件包 --nodeps led yum 系列(三) 離線部署 一、下載 rpm 依賴包 準備一臺 全新的 CentOS7 mini 虛擬機 ,聯網下載所有所需的 rpm 包和其依賴, yum install -y --do