続・多階層のオブジェクトのアクセスへの挑戦
階層が深くなったJavaScriptオブジェクトのプロパティが定義されているかどうか判定するに続いて、紆余曲折ありまして、FuelPHPのArr::get()をJavaScriptで実装してみることにしました。FuelPHPのArrクラスには色々と便利なメソッドがあり、JavaScriptにも応用できそうなものがあったりするので今後も何か変換してみようかなとか思っていたりします。使うかどうか不明ですが...。
ソース
fuel.js
/**
* @fileoverview FuelPHPのcoreクラスをJS化して使えるようにするためのクラス群
* 作成した時点でのFuelPHPのバージョンは 1.7.1
*/
var FuelJS = FuelJS || {};
FuelJS.core = FuelJS.core || {};
/**
* \Fuelクラス(の一部)
*
* @constructor
*/
FuelJS.core.Fuel = (function () {
return {
/**
* Takes a value and checks if it is a Closure or not, if it is it
* will return the result of the closure, if not, it will simply return the
* value.
*
* @param {*} value
* @return {*}
*/
value : function (value) {
return typeof value === 'function' ? value() : value;
}
};
}());
/**
* \Arrクラス(の一部)
*
* @constructor
*/
FuelJS.core.Arr = (function () {
var _private = {
isArray : function (value) {
return (Array.isArray ?
Array.isArray(value)
: Object.prototype.toString.call(value) === '[object Array]') ||
Object.prototype.toString.call(value) === '[object Object]';
}
};
var _this = {
/**
* Gets a dot-notated key from an array, with a default value if it does
* not exist.
*
* @param {!Object|Array} array The search array
* @param {string|Array} key The dot-notated key or array of keys
* @param {*} defaultVal The default value
* @return {*}
*/
get : function (array, key, defaultVal) {
if ( ! _private.isArray(array)) {
throw new TypeError('First parameter must be an array or ArrayAccess object.');
}
if (typeof key === 'undefined' || key === null) {
return array;
}
if (_private.isArray(key)) {
var ret = {};
for (var i = 0, len = key.length; i < len; i++) {
ret[key[i]] = self.get(array, key[i], defaultVal);
}
return ret;
}
if (array.hasOwnProperty(key)) {
return array[key];
}
for (var i = 0, keys = (key).toString().split('.'), len = keys.length; i < len; i++) {
if ((_private.isArray(array) && typeof array[keys[i]] !== 'undefined') === false) {
if ( ! _private.isArray(array) || ! array.hasOwnProperty(keys[i])) {
return FuelJS.core.Fuel.value(defaultVal || null);
}
}
array = array[keys[i]];
}
return array;
},
};
var self = _this;
return self;
}());
動作確認
sample
// Alias
var log = function (arg) { console.log(arg); };
var array = [9, 8, 7, {a: {b: {c : {d : {}}}}}];
log(FuelJS.core.Arr.get(array)); // [9, 8, 7, {a: {b: {c : {d : {}}}}}]
log(FuelJS.core.Arr.get(array, null)); // [9, 8, 7, {a: {b: {c : {d : {}}}}}]
log(FuelJS.core.Arr.get(array, ['0', '2', '5'])); // {0: 9, 2: 7, 5: null}
log(FuelJS.core.Arr.get(array, '0')); // 9
log(FuelJS.core.Arr.get(array, '1')); // 8
log(FuelJS.core.Arr.get(array, '2')); // 7
log(FuelJS.core.Arr.get(array, '4')); // null
log(FuelJS.core.Arr.get(array, '4', 'defaultValue!')); // defaultValue!
log(FuelJS.core.Arr.get(array, '3')); // {a: {b: {c : {d : {}}}}}
log(FuelJS.core.Arr.get(array, '3.a')); // {b: {c : {d : {}}}}
log(FuelJS.core.Arr.get(array, '3.a.b')); // {c : {d : {}}}
log(FuelJS.core.Arr.get(array, '3.a.b.c')); // {d : {}}
log(FuelJS.core.Arr.get(array, '3.a.b.c.d')); // {}
log(FuelJS.core.Arr.get(array, '3.a.b.c.d.e')); // null
軽く説明とか
FuelJS.core.Fuel
のクラスを用意したのはデフォルト値の扱いをFuelに近づけておきたかったからです。\Arr::get()
は該当する要素がなかった場合の対応として第三引数にデフォルト値を設定できます。これには関数を渡すことも可能で、要素がなければ渡した関数の実行結果が返ります。FuelJS.core.Arr
の_private.isArray
は配列とオブジェクトを許容します。
JavaScript標準のArray.isArray
を使用してしまうと、せっかくのメソッドでObjectが使用できなくなってしまうので拡張しています。
言い訳
- JavaScriptの名前空間とかほぼわかっていません。この辺は無視していただけると...。
- 突貫工事で作成したのでちゃんとテストしてませんごめんなさい(スライディング土下座