concatで配列化できなくなった?
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 配列っぽいのの配列化はsliceだけ
◆ 配列のシャローコピーならconcatでも
◆ 配列のシャローコピーならconcatでも
前にも思ったような気がするようなしないようなですが
だと bの要素を書き換えるとaも変わります
反対にaを変えても bも変わります
なので concat や slice を使ってコピーします
aを変えてもbやcは変化なしです
配列の1階層目だけをディープコピーですが2階層目以降はシャローコピーなので今回みたいなプリミティブ値のときはいいですが 中にオブジェクト(配列も)が来たときにオブジェクトの中の変更は他のにも反映されます
使う方法は3つあります(もっとあるかも)
(1) __proto__を書き換えて継承させる
(2) callでむりやり
(3) 一端配列に変換
ここからが今回の言いたかったこと!
(3)は(1)や(2)の方法で配列メソッドで行えます
私はよくsliceでやってます
slice(a,b)は 配列のa番目からb番目の要素を取り出して配列として返すメソッドです
引数がなければそのままの配列が返って来ます
配列っぽいのに使うと配列として返って来ます
配列のコピーのときにsliceじゃなくて気分でconcatにしたりもするので なんとなくconcatで配列へ変換しようとすると
変換されてないです
concatだと無理なようです
配列コピーと混ざって使ってしまわないように配列コピーもsliceに統一するといいかもです
色々なメソッドで配列に変換してみる
○concatは上に書いたとおり配列以外の要素だと その要素を配列の0番目の要素にして何かを追加する動作になります
なので変換はできてません
○filterは全部return trueにすることで元通りの配列になります
○forEachは何も返せないので一時変数tを用意してそこにforEach内でpushを使って追加して 最後にtを返すようにしてます
forEach+pushです
○joinはjoin+splitしてます
絶対に来ない文字列を探さないとダメです
また splitしてるので文字列になります
本来の形に戻せないものもあります
○mapはそのまま値を返します
○reduceはどんどんpushしていきます
最初だけ配列作る必要があります
初期値を渡せたらいいんですけどね
○sliceは一番簡単に出来る方法です
配列をコピーする方法
a = [1,2,3]
b = a
だと bの要素を書き換えるとaも変わります
反対にaを変えても bも変わります
b[1] = 0
a
// [1, 0, 3]
a.push(4)
b
// [1, 0, 3, 4]
なので concat や slice を使ってコピーします
a = [1, 2, 3]
b = a.concat()
c = a.slice()
a[1] = 0
b
// [1, 2, 3]
c
// [1, 2, 3]
aを変えてもbやcは変化なしです
配列の1階層目だけをディープコピーですが2階層目以降はシャローコピーなので今回みたいなプリミティブ値のときはいいですが 中にオブジェクト(配列も)が来たときにオブジェクトの中の変更は他のにも反映されます
配列っぽいオブジェクトの配列化
NodeListやHTMLCollectionやargumentsなどの配列っぽいオブジェクトはArray.prototypeを継承していなくて配列メソッドが使えません使う方法は3つあります(もっとあるかも)
(1) __proto__を書き換えて継承させる
a = document.getElementsByTagName("a")
a.__proto__ = Array.prototype
a.map(function(e){return e.href})
この方法だと 元々のメソッド(この例だとgetElementsByTagNameで持ってくるHTMLCollectionのメソッド)が使えなくなるので使わないほうが良い時もあります(2) callでむりやり
a = document.getElementsByTagName("a");
[].map.call(a, function(e){return e.href})
(ここだけセミコロンあるのは"["や"("から次の行が始まるとJavaScriptはつなげて1つの文にしようとするからです)(3) 一端配列に変換
a = document.getElementsByTagName("a")
arr = []
for(var i=0;i<a.length;i++){
arr[i] = a[i]
}
arr.map(function(e){return e.href})
ここからが今回の言いたかったこと!
(3)は(1)や(2)の方法で配列メソッドで行えます
私はよくsliceでやってます
slice(a,b)は 配列のa番目からb番目の要素を取り出して配列として返すメソッドです
引数がなければそのままの配列が返って来ます
配列っぽいのに使うと配列として返って来ます
a = document.getElementsByTagName("a")
arr = [].slice.call(a)
arr instanceof Array
//true
配列のコピーのときにsliceじゃなくて気分でconcatにしたりもするので なんとなくconcatで配列へ変換しようとすると
[HTMLCollection[73]]
となりました変換されてないです
concatだと無理なようです
配列コピーと混ざって使ってしまわないように配列コピーもsliceに統一するといいかもです
おまけ
色々なメソッドで配列に変換してみる
function arg2arr(){
var t = [] // for forEach method
return {
concat : [].concat.call(arguments),
filter : [].filter.call(arguments, function(){return true}),
forEach : ([].forEach.call(arguments, function(e){t.push(e)}), t),
join : [].join.call(arguments, "絶対に来ない文字列!!!").split("絶対に来ない文字列!!!"),
map : [].map.call(arguments, function(e){return e}),
reduce : [].reduce.call(arguments, function(a,b,c){return c===1?[a,b]:(a.push(b),a)}),
slice : [].slice.call(arguments),
}
}
arg2arr(1,2,3)
concat: Array[1]
0: Arguments[3]
length: 1
__proto__: Array[0]
filter: Array[3]
0: 1
1: 2
2: 3
length: 3
__proto__: Array[0]
forEach: Array[3]
0: 1
1: 2
2: 3
length: 3
__proto__: Array[0]
join: Array[3]
0: "1"
1: "2"
2: "3"
length: 3
__proto__: Array[0]
map: Array[3]
0: 1
1: 2
2: 3
length: 3
__proto__: Array[0]
reduce: Array[3]
0: 1
1: 2
2: 3
length: 3
__proto__: Array[0]
slice: Array[3]
0: 1
1: 2
2: 3
length: 3
__proto__: Array[0]
__proto__: Object
○concatは上に書いたとおり配列以外の要素だと その要素を配列の0番目の要素にして何かを追加する動作になります
なので変換はできてません
○filterは全部return trueにすることで元通りの配列になります
○forEachは何も返せないので一時変数tを用意してそこにforEach内でpushを使って追加して 最後にtを返すようにしてます
forEach+pushです
○joinはjoin+splitしてます
絶対に来ない文字列を探さないとダメです
また splitしてるので文字列になります
本来の形に戻せないものもあります
○mapはそのまま値を返します
○reduceはどんどんpushしていきます
最初だけ配列作る必要があります
初期値を渡せたらいいんですけどね
○sliceは一番簡単に出来る方法です