Создание новых объектов из замороженных родительских объектов

Этот пример создает объект, замораживает его, а затем создает новый объект из замороженного объекта. Если второй объект попытается изменить тестовое свойство, он не сможет. Он остается замороженным со значением первого объекта 10.

//Create an object and freeze it

var first = {
    test: 10
};
Object.freeze(first);

//Create a second object from the first one and
//try and change the new test property (you can't)

var second = Object.create(first);
second.test = 20;
console.log(second.test); //10

Вот мои вопросы:

Является ли second.test новым свойством нового объекта или это просто ссылка на свойство в замороженном первом объекте?
Можно ли использовать замороженное first.test в качестве значения по умолчанию, но позволить second.test перезаписать его, если это необходимо? ?

Причина, по которой я спрашиваю, заключается в том, что я хочу сделать неизменяемый базовый объект в качестве шаблона со значениями по умолчанию, а затем использовать его для создания новых объектов, которые я могу настроить. Какой лучший подход для этого?

Спасибо!


person d13    schedule 31.10.2013    source источник


Ответы (4)


second на самом деле является новым объектом, а first является прототипом second. Причина почему

second.test = 20;

не работает, потому что при назначении он будет искать настройки в прототипе (например, configurable, enumerable, writable, [[Extensible]]) и не назначать их экземпляру, если какие-либо из них ложны1. Чтобы назначить непосредственно экземпляру, вам нужно будет использовать Object.defineProperty на second:

var first = {
    test: 10
};
Object.freeze(first);

var second = Object.create(first);
Object.defineProperty(second, 'test', { value: 20, enumerable: true, configurable: true, writable: true });
console.log(second.test); // 20

1:[[Put]]: спецификация ECMAScript , §8.12.5

person Qantas 94 Heavy    schedule 31.10.2013
comment
Тогда кажется, что «второй» ведет себя так, как будто он тоже был заморожен. Он наследует свою замороженность. Но если вы вызовете Object.isFrozen(второй), это вернет false, я думаю - person Panu Logic; 02.07.2020

Используйте Object.assign

         var first = {
            test: 10
        };
        Object.freeze(first);

        //Create a second object from the first one and
        //try and change the new test property (you can't)

        var second = Object.assign({}, first, {
            test: 20
        });
        console.log(second.test); //20
person Gorilla    schedule 01.01.2016
comment
Довольно необычный случай, но обратите внимание, что это все равно не будет работать, если замороженный объект содержит ссылки на другие замороженные объекты, и вы пытаетесь перезаписать их свойства. Все еще довольно полезный метод для простых структур данных, просто никто не понимает, что это надежный способ создания неизменяемых. - person Dtipson; 18.01.2016

В вашем случае second является ссылкой на first (как вы и предполагали). Решением будет клонирование вашего объекта. Нет встроенного способа клонирования объектов - вы должны сделать это самостоятельно, вот как (источник):

function clone(obj){
   if(obj == null || typeof(obj) != 'object')
      return obj;

   var temp = obj.constructor();

   for(var key in obj)
       temp[key] = clone(obj[key]);
   return temp;
}

Затем вы используете его следующим образом:

var first = {
    test: 10
};
Object.freeze(first);

// clone it into a new one
var second = clone(first);
second.test = 20;
console.log(second.test); // 20 where the first is locked
person Dimitar Dimitrov    schedule 31.10.2013
comment
Нет встроенного способа клонирования? Я бы сказал, что Object.assign в значительной степени является клонером. - person ; 16.03.2018
comment
@loldrup хорошо, на этот вопрос был дан ответ в 2013 году, и я считаю, что Object.assign был представлен в спецификации ECMAScript2015, он даже сейчас требует полифиллов, например, для IE11. В любом случае, я согласен, что ответ, вероятно, следует обновить / отредактировать, чтобы включить его в качестве опции и идти в ногу со временем. - person Dimitar Dimitrov; 16.03.2018
comment
Чтобы было ясно, это неправда, что ... второй является ссылкой на первый. Как сказано в принятом ответе, первый является прототипом второго - person Panu Logic; 02.07.2020
comment
@PanuLogic, хотя используемая вами терминология действительно лучше в контексте JS (я согласен). Однако, насколько я понимаю, эти 2 фразы означают одно и то же? Есть смысловая разница? Я имею в виду, что в JS быть «прототипом» означает «иметь ссылку на», не так ли? - person Dimitar Dimitrov; 02.07.2020
comment
Да, слова могут иметь много разных значений. Насколько я понимаю, когда у вас есть ссылка, вы, по сути, говорите об одном и том же. Принимая во внимание, что прототипом объекта является другой объект, отдельный от объекта, прототипом которого он является. Прототип может иметь меньше свойств, чем объект, прототипом которого он является, и значения свойств могут быть другими. Объект A, прототипом которого является object :B, имеет ссылку на B. Но B не имеет ссылки на A. - person Panu Logic; 13.07.2020

В новых версиях javascript вы можете использовать деструктуризацию объекта для создания нового объекта со свойствами старого.

const first = {
    test: 10
};
Object.freeze(first);

//Clone the object
const second = {...first};
second.test = 20;
console.log(second.test); // 20 where the first is locked
person Alexander Damaso Perez    schedule 03.11.2020