詳解JavaScript如何優(yōu)雅地實(shí)現(xiàn)創(chuàng)建多維數(shù)組
前言
已經(jīng)堅(jiān)持力扣刷題 80 天了,其中經(jīng)常需要?jiǎng)?chuàng)建多維數(shù)組
比如給你兩個(gè)需求:
- 創(chuàng)建一個(gè) 10 * 10 的數(shù)組,初值為 0
- 創(chuàng)建一個(gè) 10 * 10 的數(shù)組,值為 0-99
想知道掘友們會(huì)如何創(chuàng)建這兩個(gè)數(shù)組呢?
常規(guī)方法
在這里展示一下常見的三種創(chuàng)建多維數(shù)組的方法
for 遍歷
最經(jīng)典的方法就是 for 循環(huán)
下面是需求一的代碼
const arr = []
for (let i = 0; i < 10; i++) {
arr[i] = []
for (let j = 0; j < 10; j++) {
arr[i][j] = 0
}
}如果要實(shí)現(xiàn)需求二,只需將 arr[i][j] = 0 改為 arr[i][j] = i * 10 + j 就好了
map
我習(xí)慣使用 map 創(chuàng)建
需求一
const arr1 = new Array(10).fill(0).map(() => new Array(10).fill(0))
需求二
const arr2 = new Array(10).fill(0).map((_, i) => new Array(10).fill(0).map((_, j) => i * 10 + j))
因?yàn)?map 不會(huì)處理空對(duì)象,所有創(chuàng)建數(shù)組后得先用 fill 賦值
Array.from
我看別人的題解經(jīng)??吹接?Array.from 初始化數(shù)組的
Array.from 能將一個(gè)類數(shù)組或可迭代對(duì)象轉(zhuǎn)換成真實(shí)的數(shù)組,可以給第二個(gè)參數(shù)傳遞一個(gè)函數(shù)來修改新數(shù)組的元素,就像 map 一樣
關(guān)于類數(shù)組,只要是有 length 屬性的對(duì)象,就被視為類數(shù)組
需求一
const arr1 = Array.from({ length: 10 }, () => Array.from({ length: 10 }, () => 0))需求二
const arr2 = Array.from({ length: 10 }, (_, i) => Array.from({ length: 10 }, (_, j) => i * 10 + j))不足
以上的三種方法 for 遍歷的代碼太多不想用,map 和 Array.from 有時(shí)不宜閱讀
然而其他強(qiáng)類型語言聲明多維數(shù)組都很方便,看起來也簡(jiǎn)單明了
int arr[10][10]
它們的 int 類型初值默認(rèn)為 0,當(dāng)然想實(shí)現(xiàn)需求二也得再做處理
所以,我就想實(shí)現(xiàn)一個(gè)函數(shù),簡(jiǎn)單明了地創(chuàng)建與設(shè)置多維數(shù)組的初值
遞歸函數(shù)實(shí)現(xiàn)
我們先明確函數(shù)的需求
fun(0, false, 10) // 創(chuàng)建一個(gè)長(zhǎng)度為10,初值為0的數(shù)組 fun(0, true, 10, 10) // 創(chuàng)建一個(gè)10 * 10的數(shù)組,初值為0-99
設(shè)計(jì)函數(shù)接收的參數(shù)
1.數(shù)組的初值
2.設(shè)置初值時(shí)是否需要遞增
3.剩余參數(shù),表示每個(gè)維度的數(shù)組的長(zhǎng)度
代碼也不難,直接展示了:
如果是一維數(shù)組,就賦值,賦值時(shí)看一下要不要遞增
如果是多維,就遞歸創(chuàng)建,在需要遞增時(shí)得計(jì)算下傳入的初值
function fun(value, inc, ...dimensions) {
// 取首元素,創(chuàng)建數(shù)組
const length = dimensions.shift()
const arr = new Array(length)
if (dimensions.length == 0) {
// 一維數(shù)組 賦值
for (let i = 0; i < length; i++) {
arr[i] = inc ? value++ : value
}
} else {
// 多維數(shù)組 遞歸創(chuàng)建
let gap = dimensions.reduce((a, b) => a * b, 1) // 初值的間隔
for (let i = 0; i < length; i++) {
arr[i] = fun(value + (inc ? i * gap : 0), inc, ...dimensions)
}
}
return arr
}鏈?zhǔn)胶瘮?shù)實(shí)現(xiàn)
失敗的嘗試
其實(shí)呢,個(gè)人對(duì)先傳初值的方式略感不爽
本來我是想創(chuàng)建一個(gè)鏈?zhǔn)降暮瘮?shù),和其他語言定義多維數(shù)組的語法一致并改造升級(jí)
fun(10) // 創(chuàng)建一個(gè)長(zhǎng)度為10,不設(shè)置初值 fun(10)(10, 0) // 創(chuàng)建一個(gè)10 * 10的數(shù)組,初值為0 fun(10)(10)(10, 0, true) // 創(chuàng)建一個(gè)10 * 10的數(shù)組,初值為0-999
但是出現(xiàn)了一個(gè)問題,fun 的返回值是一個(gè)數(shù)組,但它又需要作為函數(shù)調(diào)用
我嘗試通過設(shè)置原型的方式讓函數(shù)具有數(shù)組的功能
Object.setPrototypeOf(fun, Array.prototype)
但是函數(shù)自帶不可配置的 length 屬性,作為一個(gè)數(shù)組不能正確讀取 length,那就無法正常使用
處理辦法
要解決這個(gè)返回值的問題,那就只能根據(jù)參數(shù)進(jìn)行區(qū)分,判斷是返回函數(shù)還是返回?cái)?shù)組
- 當(dāng)傳入數(shù)字為正數(shù)時(shí),視為長(zhǎng)度,處理數(shù)組并返回函數(shù)自身
- 當(dāng)傳入數(shù)字為非正數(shù)時(shí),視為初值,將之前處理的數(shù)組賦值并返回
在數(shù)組處理時(shí)用了一些技巧,將樹形的數(shù)組結(jié)構(gòu)鋪平了,下面是代碼
let root = [] // 根數(shù)組 root[0] 將會(huì)是返回的結(jié)果
let tile = [root] // 平鋪數(shù)組
// 函數(shù)實(shí)現(xiàn)
function fun(length) {
if (length <= 0) {
// 結(jié)束標(biāo)志 非正數(shù)
try {
// 空屬性會(huì)被JSON轉(zhuǎn)換成null,然后再替換為初值
let res = JSON.stringify(root[0])
res = res.replaceAll('null', Math.abs(length))
return JSON.parse(res)
} finally {
// 恢復(fù)變量
root = []
tile = [root]
}
} else {
const next = [] // 新的平鋪數(shù)組
// 遍歷平鋪數(shù)組,里面的元素是倒數(shù)第二層
for (const two of tile) {
// 首次調(diào)用two.length為0,但需要執(zhí)行一次
for (let i = 0; i < (two.length || 1); i++) {
const one = new Array(length) // 最后一層
two[i] = one
next.push(one)
}
}
// 更替平鋪數(shù)組
tile = next
}
return fun
}
// 函數(shù)調(diào)用
fun(10)(0) // [0,0,0,0,0,0,0,0,0,0]
fun(2)(3)(-1) // [[1,1,1],[1,1,1]]
fun(2)(2)(2)(-2) // [[[2,2],[2,2]],[[2,2],[2,2]]]如果想要實(shí)現(xiàn)初值遞增,那就是再加一個(gè)參數(shù)或再加一種情況,留作讀者自行實(shí)現(xiàn)了
使用 Proxy 實(shí)現(xiàn)
至此還不知足,我們可以借助 Proxy,將函數(shù)調(diào)用改為屬性訪問,更接近強(qiáng)類型語言的習(xí)慣
實(shí)現(xiàn)一個(gè) int 代理對(duì)象
let root = []
let tile = [root]
// 攔截器
const handers = {
get(target, key) {
key = parseInt(key)
if (key <= 0) {
try {
let res = JSON.stringify(root[0])
res = res.replaceAll('null', Math.abs(key))
return JSON.parse(res)
} finally {
root = []
tile = [root]
}
} else {
const next = []
for (const two of tile) {
for (let i = 0; i < (two.length || 1); i++) {
const one = new Array(key)
two[i] = one
next.push(one)
}
}
tile = next
}
return int
},
}
// 用 proxy 創(chuàng)建 int
const int = new Proxy({}, handers)至此,我們就可以像那些強(qiáng)類型語言一樣,簡(jiǎn)單明了的定義多維數(shù)組了
const arr = int[10][10][0] // 10*10 初值為 0 的數(shù)組
到此這篇關(guān)于詳解JavaScript如何優(yōu)雅地實(shí)現(xiàn)創(chuàng)建多維數(shù)組的文章就介紹到這了,更多相關(guān)JavaScript多維數(shù)組內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript結(jié)合html5 canvas實(shí)現(xiàn)(可調(diào)畫筆顏色/粗細(xì)/橡皮)的涂鴉板
js+html5 canvas實(shí)現(xiàn)的涂鴉畫板特效,可調(diào)畫筆顏色|粗細(xì)|橡皮,可以保存涂鴉效果為圖片編碼,測(cè)試了下還不錯(cuò),感興趣的朋友可以參考下2013-04-04
微信小程序復(fù)選框?qū)崿F(xiàn)多選一功能過程解析
這篇文章主要介紹了微信小程序復(fù)選框?qū)崿F(xiàn)多選一功能過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Javascript中實(shí)現(xiàn)String.startsWith和endsWith方法
這篇文章主要介紹了Javascript中實(shí)現(xiàn)String.startsWith和endsWith方法,這兩個(gè)很好用的方法在JS中沒有,本文就自己編碼實(shí)現(xiàn)了這兩個(gè)方法,需要的朋友可以參考下2015-06-06
JS+HTML5實(shí)現(xiàn)圖片在線預(yù)覽功能
這篇文章主要為大家詳細(xì)介紹了JS+HTML5實(shí)現(xiàn)圖片在線預(yù)覽功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
微信小程序?qū)崿F(xiàn)點(diǎn)擊頁面出現(xiàn)文字
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)點(diǎn)擊頁面出現(xiàn)文字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09
js中console在一行內(nèi)打印字符串和對(duì)象的方法
這篇文章主要介紹了js中console在一行內(nèi)打印字符串和對(duì)象的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09
JavaScript去掉數(shù)組重復(fù)項(xiàng)的方法分析【測(cè)試可用】
這篇文章主要介紹了JavaScript去掉數(shù)組重復(fù)項(xiàng)的方法,結(jié)合實(shí)例形式分析了javascript使用object特性實(shí)現(xiàn)數(shù)組去除重復(fù)項(xiàng)功能的相關(guān)操作技巧,需要的朋友可以參考下2018-07-07

