javascript中常量(理想情况下是文字)圆形对象的表示
Representation of constant (ideally literal) circular objects in javascript
我有一些相当循环结构的Javascript对象,它们的计算成本相对较高。我希望能够将它们在源代码中表示为(几乎)字面量,以便它们不需要重新计算。这里有一个库函数的例子,我在后面的几个段落中寻找。
曾经有一些建议的符号,锐利变量,但它们只在旧版本的Firefox中支持。
当然,对象不能表示为字面量,如果他们有任何循环引用,所以我想是一个Javascript对象转换成一个小函数来创建它的一些代码。然后可以手动将其复制到要使用的源文件中。
为了便于示例,我将调用我正在寻找的函数print
。我想要的行为大致如下:
console.log(print({a:1,b:{c:2,d:3}})); // {a:1,b:{c:2,d:3}} (a string)
var obj = [null,null];
obj[0]=obj[1]=obj; //with sharp variables: obj = #1=[#1#,#1#]
console.log(print(obj));
// e.g. (function(){var u=void 0,a1=[u,u];a1[0]=a1;a1[1]=a1;return a1;})()
var obj = {a:1,
some:{b:2,
nested:{c:3,
cyclic:{d:4}}}};
obj.some.nested.cyclic.structure = obj;
//obj = #1={a:1,some:{b:2,nested:{c:3,cyclic:{d:4,structure:#1#}}}}
console.log(print(obj));
//e.g. (function(){var u=void 0,a1={a:1,some:{b:2,nested:{c:3,cyclic:{'
// d:4,structure:u};a1.some.nested.cyclic.structure=a1;return a1;})()
//OR e.g. (function(){var u=void 0,a1={a:1,some:u},a2={b:2,nested:u},...
// a1.some=a2;a2.nested=a3;a3.cyclic=a4;a4.structure=a1; return a1;})()
本质上,对于任何仅由JS原语/普通对象/数组组成的对象,我们应该让eval(print(x))
在结构上(即深度上)等于x
,但不完全相同。换句话说,eval(print(x))
将是一种(愚蠢的)方式来对x
进行深度复制(但尊重循环)
比起第二个选项,我更喜欢第一个选项。大概也可以实现一些漂亮的打印,但这是可选的。此外,我真的不太关心像使用void 0
而不是undefined这样的小细节。
我知道存在可以序列化循环对象的库,但是它们在正常的树结构javascript对象中对一些自定义符号进行序列化,因此它们需要额外的代码来反序列化。我不需要这个,所以我希望避免它。
我认为,如果您能够将对象打印为尖锐的变量表示法,则可以相当容易地将其转换为如上所示的形式:
- 打印尖锐变量(不重复,例如
#1=
等) - 计算使用的不同尖锐变量的数量。
- 为每一个Javascript变量命名,例如
a1
,也可以为根命名。 - 将所有
#n#
替换为undefined,跟踪它们在树中的位置 为每个 - 为root添加返回语句。
#n#
生成如下代码:an['path'][1]['to']['object'] = am
然而,比起直接打印代码,似乎更不可能存在一些库来打印带有尖锐变量的对象。
我认为实现尖锐变量是复杂的,并没有使计算更容易。我会用函数实现这样的行为:
var sharps={};
Object.prototype.sharp=function(str){
sharps[str]=this;
}
function gs(str){
return function(){return sharps[str];};
}
你可以这样写:
var obj1={a:{b:gs("1");}
obj1.sharp("1");
alert(obj1.a.b());//returns obj1
我知道这不是你真正想要的。
这里有一个答案:
function isPrimitive(x){
switch(typeof x){
case "number":
case "string":
case "boolean":
case "undefined":
return true;
}
return false;
}
function isSerialisable(x){
switch(typeof x){
case "function":
case "symbol":
return false;
case "number":
case "string":
case "object":
case "boolean":
case "undefined":
return true;
}
return false;
}
function isValidIdentifier(string){
//this is *really* stupid
//TODO: operate brain
if(/[] ,;'".+=-()[]!*/.test(string)) return false;
//don't want anything too stupid
try{
//this throws a syntax error if our identifier is invalid!
//note that whilst e.g. `var in = ...` is invalid, `foo.in` is valid
void new Function("x", "return x." + string + ";")
} catch(e) { return false;}
return true;
}
function serialise(object){
var seenObjects = new Map();
var places = [];
//this function traverses object in depth-first order,
//simultaneously finding recursive references and
//building up a tree-shaped near-deep-copy of object that can be
//JSON.stringified
function process(object, path){
if(!isSerialisable(object)) throw "Object is not serialisable";
if(isPrimitive(object)) return object;
//object really is an object now
if(seenObjects.has(object)){
places.push({path:path.slice(),from:seenObjects.get(object)});
return null; //so we don't have cycles.
//we use null so it is included by JSON.stringify so that the
//order of the keys is preserved.
} else {
//iterate over the own properties
var ret = Array.isArray(object) ? [] : {} //maybe Object.create(null); doesn't really matter
seenObjects.set(object, path.slice()); //so we can find it again
for(var prop in object){
if(Object.prototype.hasOwnProperty.call(object, prop)){
var p = +prop;
prop = (p == prop && prop !== "") ? p : prop;
path.push(prop);
ret[prop] = process(object[prop], path);
console.assert(prop == path.pop(), "Path stack not maintained");
}
}
return ret;
}
}
function dotPath(path){
return path.map(function(x){
if(isValidIdentifier(x)){
return "." + x;
} else {
return "[" + JSON.stringify(x) + "]";
}}).join("");
//we use JSON.stringify to properly escape strings
//therefore we hope that they do not contain the types of vertical space
//which JSON ignores.
}
var tree = process(object, []);
if(places.length == 0){
//object not cyclic
return JSON.stringify(tree);
}
//object is cyclic
var result = "(function(){x=" + JSON.stringify(tree) + ";"
return result + places.map(function(obj){
//obj = {path:..., from:...}
return "x" + dotPath(obj.path) + "=x" + dotPath(obj.from);
}).join(";") + ";return x;})()";
}
一些辅助函数特别糟糕,但主要部分基本上是可以的,如果对于大对象来说内存有点重。也许使用conses为路径创建一个链表可以减少一点内存使用。
- 文字与原型对象表示法的数据结构
- JavaScript对象表示嵌套对象未定义
- ES6对象简写表示法可以与常规对象表示法相结合吗
- Javascript错误消息,自定义对象表示
- Javascript - 对象表示法脚本与面向对象脚本
- JavaScript对象表示法是正确的JSON吗
- 如何用对象表示法调用内部javascript函数
- 在webpack中创建多个块时,数组表示法和对象表示法之间的区别是什么
- 如何将可为null的json对象表示为单个字符串
- Javascript函数转换为传递变量的对象表示法
- jQuery选择器到JSON对象表示
- HTML输入名称中的Js对象表示法
- 不同Javascript对象表示法的名称
- 这个字符串作为Javascript对象表示什么?
- 使用JavaScript对象表示法访问json值
- 将多层PHP数组转换为Javascript对象表示法
- Javascript对象表示格式
- 图形化地图对象表示网站
- DOM元素的对象表示法
- 使用对象表示法向对象添加方法