От переводчика: поприще переводчика для меня ново, по этому прошу вас не судить строго.
Когда-то, читав Принципы Объектно-ориентированного JavaScript за авторством Nicholas Zakas, я наткнулся на совет об использовании Object.create(), для создания объектов унаследованных от null. Такой объект не унаследует Object.prototype и, соответственно, не будет иметь его, Object.prototype, методов. Zakaz предположил, что это может быть использовано для создания безопасного объекта «cache». Мне очень понравилась эта идея, но в соответствии с MDN (Mozilla Developer Network), Object.create() не поддерживается до IE9, что делает этот метод более требовательным при использовании в браузере. Но в Node.js, на сервере, этот метод поддерживается полностью.
Когда вы создаете объект в JavaScript, то этот объект автоматически наследует методы Object.prototype. Из-за этого объект становится «опасным» для проверки существования ключей с помощью оператора «in», т. к. в этом случае найдутся такие методы объекта как «valueOf» и «toString» (в результате подъема по иерархии прототипов). По этому люди предпочитают использовать Object.prototype.hasOwnProperty() вместо оператора «in».
Но если вы создадите объект унаследованный от null, используя Object.create(), то не будет иерархии наследования. Как таковой, этот объект, становится очень близок к объекту который не содержит предопределенных системой методов.
Что бы увидеть как это работает взгляните на следующий Node.js скрипт:
// Создание объекта без прототипа. Это позволит предотвратить наследование любых предопределенных методов
// имеющихся у объекта. Это означает, что ЛЮБОЙ и ВСЕ свойства этого объекта должны быть
// определены пользователем, что делает этот объект очень полезным для создания кэш контейнера.
var safeCache = Object.create(null);
// Давайте посмотрим, существуют ли какие-то стандартные методы в этом объекте.
["hasOwnProperty", "toString", "valueOf", "constructor", "__proto__"]
.forEach(
function iterator(key, index) {
console.log("[ %s ] exists: %s", key, ( key in safeCache ));
}
);
При запуске это скрипта мы получим следующий результат:
[ hasOwnProperty ] exists: false
[ toString ] exists: false
[ valueOf ] exists: false
[ constructor ] exists: false
[ __proto__ ] exists: true
Как вы видите, все «стандартные» свойства Object.prototype отсутствуют. Единственное, волшебное, свойство которое существует «__proto__». Это дает нам максимально близкую возможность, слепо, добавлять, удалять и проверять наличие ключей у данного объекта. Я предполагаю, что это допустимый уровень риска за возможность максимально простого кода.
Конечно, когда я говорю «простой код», отличие не так существенно. Что бы увидеть что-то конкретное, я попытался создать максимально простой кэш класс, инкапсулирующий хранение пар ключ-значение. Первый класс — хранилище реализованное на объекте унаследованном от null; второй класс — хранилище реализованное на обычном JavaScript объекте:
// В этой версии кэша мы будем использовать инкапсулированный
// объект унаследованный от null, и затем, предположим, что названия
// пользовательских ключей кэша будет конфликтовать с именами стандартных методов.
function SafeCache() {
var cache = Object.create(null);
// Reveal the public API.
return ({
get: get,
has: has,
remove: remove,
set: set
});
// ---
// PUBLIC METHODS.
// ---
function get(key) {
return ( cache[key] );
}
function has(key) {
return ( key in cache );
}
function remove(key) {
return ( delete( cache[key] ), this );
}
function set(key, value) {
return ( cache[key] = value, this );
}
}
var safeCache = new SafeCache()
.set("foo", "Bar")
.set("hello", "world")
.set("beep", "boop");
console.log("## Safe Cache ##");
console.log(safeCache.has("foo"));
console.log(safeCache.has("meep"));
console.log(safeCache.has("valueOf"));
console.log(safeCache.has("__proto__"));
// В этой версии кэша мы будем использовать классический объект,
// с применением специальных мер предосторожности для проверки
// существования метода или возвращения, определенного пользователем, значения.
function SaferCache() {
var cache = {};
// Reveal the public API.
return ({
get: get,
has: has,
remove: remove,
set: set
});
// ---
// PUBLIC METHODS.
// ---
function get(key) {
if (has(key)) {
return ( cache[key] );
}
}
function has(key) {
return ( cache.hasOwnProperty(key) );
}
function remove(key) {
return ( delete( cache[key] ), this );
}
function set(key, value) {
return ( cache[key] = value, this );
}
}
var saferCache = new SaferCache()
.set("foo", "Bar")
.set("hello", "world")
.set("beep", "boop");
console.log("## Safer Cache ##");
console.log(saferCache.has("foo"));
console.log(saferCache.has("meep"));
console.log(saferCache.has("valueOf"));
console.log(saferCache.has("__proto__"));
Если вы только бегло взглянули на этот код, вы могли даже не заметить разницы. Но она там есть, в методах get() и has().
Что бы быть честным, если вы собираетесь инкапсулировать реализацию кэша, вы могли бы использовать самое надежно — .hasOwnProperty() или, например, смешанный ключ. Но если вы не работает с неполным кэшем, использование объекта унаследованного от null может быть разумным методом баланса между простотой кода и риском. И если ничего из перечисленного, то это просто полезно знать, что вы можете создать объект унаследованный от null в JavaScript/Node.js.
Автор: edejin