Подтвердить что ты не робот

Добавление прототипа конструктора к объекту javascript

У меня есть несколько объектов javascript, например:

var object = {
    name: "object name",
    description: "object description",
    properties: [
        { name: "first", value: "1" },
        { name: "second", value: "2" },
        { name: "third", value: "3" }
    ]
};

Теперь я хотел изменить эти объекты на более умные объекты (добавив некоторые методы и т.д.).
Сначала я создал такую ​​конструкторскую функцию:

SmartObject = function( object ){

    this.name = object.name;

    this.description = object.description;

    this.properties = object.properties;

};

SmartObject.prototype.getName = function(){
    return this.name;
};

SmartObject.prototype.getDescription = function(){
    return this.description;
};

SmartObject.prototype.getProperies = function(){
    return this.properties;
};

И затем я использовал этот конструктор для изменения моего объекта на экземпляр SmartObject следующим образом:

var smartObject = new SmartObject( object );

Для этого кажется правильным объектно-ориентированным javascript-кодом, но это слишком сложно, поскольку все, что я действительно хочу сделать, это добавить некоторые методы, и теперь я копирую все свойства из object в SmartObject в SmartObject в конструктор.

В этом примере есть только 3 свойства и некоторые простые методы, но в моем проекте есть несколько десятков (вложенных) свойств и более сложные методы.

Итак, я пробовал это:

object.__proto__ = SmartObject.prototype;

И это, по-видимому, приводит к точному такому же результату и кажется намного проще (проверьте эта скрипка для этого пример).

Является ли это правильным и приемлемым способом добавления прототипа к моему объекту? Или - это ломающиеся объектно-ориентированные шаблоны и считается плохой практикой, и я должен продолжать делать то, что сделал (используя конструктор).

Или может быть еще один приемлемый способ добавления методов к моему существующему объекту, не вытягивая его через конструктор?


Примечание. Я попытался найти такой пример в StackOverflow, но при поиске я всегда заканчиваю примерами, расширяющими прототип существующих классов javascript. Если есть такой вопрос, не стесняйтесь отмечать это как дубликат, и я снова закрою свой вопрос.

4b9b3361

Ответ 1

Как я уже упоминал ранее, изменение прототипа объекта окажет серьезное влияние на производительность вашего кода. (tbh, я никогда не занимал время, чтобы измерить воздействие). Эта страница MDN объясняет.

Однако, если речь идет о шаблоне, вы можете легко создать общий factory для своих объектов, например:

function genericFactory(proto, source) {
    return Object.keys(source).reduce(function(target, key) {
        target[key] = source[key];
        return target;
    }, Object.create(proto));
}

И теперь вы можете использовать его, передав SmartObject.prototype и object в качестве аргументов следующим образом:

var smartObject = genericFactory(SmartObject.prototype, object);

Ответ 2

Сочетание Object.create() от @SebastienDaniel его ответа и @bloodyKnuckles его комментарий и метод Object.assign(), предложенный @nnnnnn в его комментарий мне удалось с помощью следующего простого кода сделать именно то, что я хотел:

var smartObject = Object.assign( Object.create( SmartObject.prototype ), object );

Отметьте обновленную скрипку здесь

Ответ 3

Итак, я пробовал это:

object.__proto__ = SmartObject.prototype;

...

Является ли это правильным и приемлемым способом добавления прототипа к моему объекту? Или это разбитые объектно-ориентированные шаблоны и считается плохой практикой, и я должен продолжать делать, как я сделал (используя конструктор).

Я рекомендую против него, потому что:

  • Изменение прототипа объекта после факта разрушает его производительность в современных механизмах JavaScript.

  • Это необычно, и, таким образом, ваш код немного чужд кому-либо еще, что вы могли бы его сохранить.

  • Он зависит от браузера; свойство __proto__ определяется только в Приложении к спецификации JavaScript и только для браузеров (спецификация требует, чтобы браузеры реализовали его, хотя). Не-браузерный способ - Object.setPrototypeOf(object, SmartObject.prototype);, но смотрите # 1 и # 2.

Вы видите, что он избыточный или повторяющийся, либо на уровне кодирования, либо на уровне памяти (я не уверен). Это не значит, что вы принимаете SmartObject с самого начала, а не сначала создаете object, а затем добавляете smarts позже:

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;
});

Хотя это означает, что объекты сначала создаются, а затем воссоздаются, создание объектов - очень дешевая операция; здесь пример, показывающий разницу во времени при анализе объектов 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());

Ответ 4

Учитывая, что ваш объект аналогичен объекту SmartObject в свойствах, вы можете найти такие вещи:

var obj = {
    name: "object name",
    description: "object description",
    properties: [
        { name: "first", value: "1" },
        { name: "second", value: "2" },
        { name: "third", value: "3" }
    ]
}, 
SmartObject = function( object ){

    this.name = object.name;

    this.description = object.description;

    this.properties = object.properties;

};

SmartObject.prototype.getName = function(){
    return this.name;
};

SmartObject.prototype.getDescription = function(){
    return this.description;
};

SmartObject.prototype.getProperties = function(){
    return this.properties;
};
obj.constructor = SmartObject;
obj.__proto__ = obj.constructor.prototype;
console.log(obj.getName());