Итак, я попробовал это:
object.__proto__ = SmartObject.prototype;
...
Это правильный и приемлемый способ добавить прототип к моему объекту? Или это нарушает объектно-ориентированные шаблоны и считается плохой практикой, и я должен продолжать делать то, что делал (используя конструктор).
Я рекомендую против этого, потому что:
Изменение прототипа объекта постфактум снижает его производительность в современных движках 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. В этом случае вы можете включить свой SmartObject
в разбор JSON с помощью функции 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;
});
Хотя это и означает, что объекты сначала создаются, а затем повторносоздаются, создание объектов — очень дешевая операция; вот пример, показывающий разницу во времени при парсинге 20к объектов с оживителем и без него:
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 мс, так что в абсолютном выражении не о чем беспокоиться, и это для 20 000 объектов.
В качестве альтернативы вы можете смешивать свои методы, а не прототип:
// The methods to mix in
var smartObjectMethods = {
getName() {
return this.name;
},
getDescription() {
return this.description;
},
getProperies() {
return this.properties;
}
};
// Remember their names to make it faster adding them later
var smartObjectMethodNames = Object.keys(smartObjectMethods);
// Once we have the options, we update them all:
objects.forEach(function(v) {
smartObjectMethodNames.forEach(function(name) {
v[name] = smartObjectMethods[name];
});
});
ES2015 имеет Object.assign
, который вы можете использовать вместо smartObjectMethodNames
и внутреннего forEach
:
// Once we have the options, we update them all:
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) { // browser has it
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());
person
T.J. Crowder
schedule
17.06.2016
Object.assign()
? Поместите желаемый метод в другой объектmethods = { getName : function() {}, etc:etc}
, а затемObject.assign(object, methods)
. - person nnnnnn   schedule 17.06.2016assignObject = Object.assign( new SmartObject(), object );
не работает, поскольку конструктор пытается получить доступ к свойствам аргумента, который я не передаю. Я мог бы переработать конструктор, но интересно, будет ли это быстрее, чем использовать конструктор напрямую? - person Wilt   schedule 17.06.2016methods
для хранения всех ваших методов, а затемObject.assign()
для копирования методов в любой объект. - person nnnnnn   schedule 17.06.2016object1.prototype = Object.create(SmartObject.prototype)
...интересно. - person bloodyKnuckles   schedule 17.06.2016Object.assign()
вместе сObject.create()
в сочетании с хорошим решением! - person Wilt   schedule 17.06.2016object1
является конструктором. - person Bergi   schedule 18.06.2016