Skip to content

Instantly share code, notes, and snippets.

@steel1990
Last active January 8, 2020 03:46
Show Gist options
  • Save steel1990/9868106cc3b2f61ee735 to your computer and use it in GitHub Desktop.
Save steel1990/9868106cc3b2f61ee735 to your computer and use it in GitHub Desktop.
/**
* 通过字符串访问对象属性
*
* @param {Object} context 需要查找的对象
* @param {string} path 如 'a.b.c'
* @param {*} value 路径上需要设置的值
* @param {boolean} dontRewrite 不允许重写最后设置的值
* @return {*} 返回查找到的值
*/
var visit = function () {
var isValidObj = function (obj) {
var type = typeof obj;
return /^(?:object|function|array)$/.test(type) && obj !== null;
};
var isVoid = function (obj) {
return obj === null || obj === undefined;
};
return function (context, path, value, dontRewrite) {
path = path.split('.');
var obj = context;
var isGet = typeof value === 'undefined';
var name;
while (path.length > 1) {
name = path.shift();
if (isVoid(obj[name]) && isGet) {
return obj[name];
} else if (!isValidObj(obj[name]) && !isGet) {
obj[name] = {};
}
obj = obj[name];
}
name = path[0];
if (!isGet && (isVoid(obj[name]) || !dontRewrite)) {
obj[name] = value;
}
return obj[name];
};
}();
// UT
// GET
var obj = {
a: 1,
b: {
c: 2,
d: [3, 4],
e: 'str'
}
};
console.assert(visit(obj, 'a') === 1, 'simple path "a"');
console.assert(visit(obj, 'b') === obj.b, 'simple path "b" => obj');
console.assert(visit(obj, 'b.c') === 2, 'two path "b.c"');
console.assert(visit(obj, 'b.d.0') === 3, 'three path with array "b.c.0"');
console.assert(visit(obj, 'b.d.1') === 4, 'three path with array "b.c.1"');
console.assert(
visit(obj, 'b.d.length') === 2,
'three path with array legnth "b.c.length"'
);
console.assert(visit(obj, 'b.e') === 'str', 'two path str "b.e"');
console.assert(visit(obj, 'b.e.length') === 3, 'three path str legnth "b.e"');
console.assert(visit(obj, 'b.e.0') === 's', 'three path str index "b.e.0"');
console.assert(visit(obj, 'b.f') === undefined, 'no such path "b.f"');
console.assert(visit(obj, 'b.f.g') === undefined, 'no such path "b.f.g"');
console.assert(visit(obj, 'x.x.x.x.x.x') === undefined, 'no such path "x.x.x.x.x.x"');
// SET
obj = {
a: 1,
b: {
c: 2,
d: [3, 4],
e: 'str'
}
};
visit(obj, 'a', 2);
console.assert(obj.a === 2, 'simple set path "a"');
visit(obj, 'c.c', 2);
console.assert(obj.c.c === 2, 'simple set path "c.c"');
visit(obj, 'x.x.x.x', 2);
console.assert(obj.x.x.x.x === 2, 'simple set path "x.x.x.x"');
visit(obj, 'a.b', 2);
console.assert(obj.a.b === 2, 'rewrite number type "a.b"');
visit(obj, 'a.b.c', 2);
console.assert(obj.a.b.c === 2, 'rewrite number type "a.b.c"');
visit(obj, 'a.b.c.d', 0);
console.assert(obj.a.b.c.d === 0, 'set to 0 for "a.b.c.d"');
visit(obj, 'a.b.c.d', null);
console.assert(obj.a.b.c.d === null, 'set to null for "a.b.c.d"');
var fn = function () {};
visit(obj, 'a.b.c.d', fn);
console.assert(obj.a.b.c.d === fn, 'set to function for "a.b.c.d"');
visit(obj, 'a.b.c.d.e', 2);
console.assert(obj.a.b.c.d.e === 2, 'set to property to function for "a.b.c.d.e"');
var reg = /x/;
visit(obj, 'a.b.c.d.e', reg);
console.assert(obj.a.b.c.d.e === reg, 'set to RegExp property to function for "a.b.c.d.e"');
visit(obj, 'a.b.c.d.e.f', 2);
console.assert(obj.a.b.c.d.e.f === 2, 'set to property to RegExp for "a.b.c.d.e.f"');
visit(obj, 'b.d.0', 2);
console.assert(obj.b.d[0] === 2, 'rewrite array index "b.d.0"');
visit(obj, 'b.d.2', 2);
console.assert(obj.b.d[2] === 2, 'array push like index "b.d.2"');
console.assert(obj.b.d.length === 3, 'array length should be 3 for "b.d.length"');
visit(obj, 'g', 2, true);
console.assert(obj.g === 2, 'normal set "g"');
visit(obj, 'g', 3, true);
console.assert(obj.g === 2, 'dont rewrite set "g"');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment