So then I tried this:
object.__proto__ = SmartObject.prototype
...
Is this a proper and acceptable way to add the prototype to my object? Or is this breaking object oriented patterns and considered bad practice and should I continue doing like I did (using the constructor).
我建议不要这样做,因为:
在当前的JavaScript引擎中,修改对象原型会破坏其性能。
这种做法不太常见,会使你的代码对其他人来说有些陌生。
它是与浏览器相关的;__proto__
属性仅在JavaScript规范的附录中定义,并且仅适用于浏览器(尽管规范要求浏览器实现它)。非与浏览器相关的方法是Object.setPrototypeOf(object, SmartObject.prototype);
但请参考#1和#2。
你可能会担心这会在编码层面或内存层面造成冗余或重复(我不确定)。如果你从一开始就使用SmartObject
而不是先创建object
再添加智能,则不会出现这种情况:
var SmartObject = function(name, description, properties) {
this.name = name;
this.description = description;
this.properties = properties;
};
SmartObject.prototype.getName = function(){
return this.name;
};
SmartObject.prototype.getDescription = function(){
return this.description;
};
SmartObject.prototype.getProperies = function(){
return this.properties;
};
var object = new SmartObject(
"object name",
"object description",
[
{ name: "first", value: "1" },
{ name: "second", value: "2" },
{ name: "third", value: "3" }
]
);
var anotherObject = new SmartObject(
);
var yetAnotherObject = new SmartObject(
);
或者更好的方式是使用ES2015(您可以使用像Babel这样的转译器):
class SmartObject {
constructor() {
this.name = name;
this.description = description;
this.properties = properties;
}
getName() {
return this.name;
}
getDescription() {
return this.description;
}
getProperies(){
return this.properties;
}
}
let object = new SmartObject(
"object name",
"object description",
[
{ name: "first", value: "1" },
{ name: "second", value: "2" },
{ name: "third", value: "3" }
]
);
let anotherObject = new SmartObject(
);
let yetAnotherObject = new SmartObject(
);
您曾表示无法从一开始就使用 SmartObject
,因为它们来自 JSON 数据源。在这种情况下,您可以使用 reviver 函数将您的 SmartObject
合并到 JSON 解析中:
var objects = JSON.parse(json, function(k, v) {
if (typeof v === "object" && v.name && v.description && v.properties) {
v = new SmartObject(v.name, v.description, v.properties);
}
return v;
});
虽然这意味着对象首先被创建,然后再被重新创建,但创建对象是一项非常便宜的操作;以下示例显示了使用和不使用reviver解析20k个对象时的时间差异:
var json = '[';
for (var n = 0; n < 20000; ++n) {
if (n > 0) {
json += ',';
}
json += '{' +
' "name": "obj' + n + '",' +
' "description": "Object ' + n + '",' +
' "properties": [' +
' {' +
' "name": "first",' +
' "value": "' + Math.random() + '"' +
' },' +
' {' +
' "name": "second",' +
' "value": "' + Math.random() + '"' +
' }' +
' ]' +
'}';
}
json += ']';
var SmartObject = function(name, description, properties) {
this.name = name;
this.description = description;
this.properties = properties;
};
SmartObject.prototype.getName = function() {
return this.name;
};
SmartObject.prototype.getDescription = function() {
return this.description;
};
SmartObject.prototype.getProperies = function() {
return this.properties;
};
console.time("parse without reviver");
console.log("count:", JSON.parse(json).length);
console.timeEnd("parse without reviver");
console.time("parse with reviver");
var objects = JSON.parse(json, function(k, v) {
if (typeof v === "object" && v.name && v.description && v.properties) {
v = new SmartObject(v.name, v.description, v.properties);
}
return v;
});
console.log("count:", objects.length);
console.timeEnd("parse with reviver");
console.log("Name of first:", objects[0].getName());
在我的电脑上,这将大致使时间翻倍,但我们谈论的是从大约60毫秒到大约120毫秒,因此从绝对角度来看,这并不值得担心 - 而且这是针对20k个对象的。
或者,您可以将您的方法混合在一起,而不是使用原型:
var smartObjectMethods = {
getName() {
return this.name;
},
getDescription() {
return this.description;
},
getProperies() {
return this.properties;
}
};
var smartObjectMethodNames = Object.keys(smartObjectMethods);
objects.forEach(function(v) {
smartObjectMethodNames.forEach(function(name) {
v[name] = smartObjectMethods[name];
});
});
ES2015有Object.assign
,您可以使用它来替代smartObjectMethodNames
和内部的forEach
:
objects.forEach(function(v) {
Object.assign(v, smartObjectMethods);
});
无论哪种方式,它都略微不够内存效率,因为每个对象最终都有自己的getName
、getDescription
和getProperties
属性(函数不会重复,但是用于引用它们的属性会重复)。虽然这几乎不可能成为问题。
这里再举一个包含20k个对象的例子:
var json = '[';
for (var n = 0; n < 20000; ++n) {
if (n > 0) {
json += ',';
}
json += '{' +
' "name": "obj' + n + '",' +
' "description": "Object ' + n + '",' +
' "properties": [' +
' {' +
' "name": "first",' +
' "value": "' + Math.random() + '"' +
' },' +
' {' +
' "name": "second",' +
' "value": "' + Math.random() + '"' +
' }' +
' ]' +
'}';
}
json += ']';
var smartObjectMethods = {
getName() {
return this.name;
},
getDescription() {
return this.description;
},
getProperies() {
return this.properties;
}
};
var smartObjectMethodNames = Object.keys(smartObjectMethods);
console.time("without adding methods");
console.log("count:", JSON.parse(json).length);
console.timeEnd("without adding methods");
console.time("with adding methods");
var objects = JSON.parse(json);
objects.forEach(function(v) {
smartObjectMethodNames.forEach(function(name) {
v[name] = smartObjectMethods[name];
});
});
console.log("count:", objects.length);
console.timeEnd("with adding methods");
if (Object.assign) {
console.time("with assign");
var objects = JSON.parse(json);
objects.forEach(function(v) {
Object.assign(v, smartObjectMethods);
});
console.log("count:", objects.length);
console.timeEnd("with assign");
}
console.log("Name of first:", objects[0].getName());
Object.assign()
方法吗?将你所需的方法放在另一个对象methods = { getName : function() {}, etc:etc}
中,然后使用Object.assign(object, methods)
。 - nnnnnnobject1.prototype = Object.create(SmartObject.prototype)
...有趣。 - bloodyKnuckles