2012-05-07 3 views
15

Mozilla заявила, что удалит __proto__ некоторое время назад (~ 2008), и она все еще находится в браузере. Неужели это будет устаревшим? Он работает в Opera, (думаю, Safari) и Chrome. Мне не нужно беспокоиться об IE, поэтому я хотел бы продолжать использовать его.__proto__, когда он исчезнет? Альтернативы?

Однако, я не хочу, чтобы мой код, чтобы перестать работать один день, так что на мой вопрос:

__proto__ позволяет мертвому простого наследования:

a.__proto__ = {'a':'test'} 

Есть в любом случае я могу повторить это в соответствии со стандартами? Я знаю, что существует функциональное наследование, это уродливо, и это слишком усложняет тот факт, что я просто хочу создать цепочку прототипов. Просто интересно, решили ли какие-либо мастера.

Благодаря

+3

Похоже, что '__proto__' может быть стандартизован в следующем ECMAScript. Не может быть абсолютно уверен, пока не будет завершена. http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts ... по крайней мере, это указано в текущем проекте в Приложении B «Дополнительные функции для веб-браузеров». –

ответ

22

Примечание: Считается неправильной практикой, чтобы изменить значение __proto__. Этому сильно препятствует Брендан Эйх, создатель JavaScript, среди прочих. Фактически свойство __proto__ было полностью удалено из нескольких движков JavaScript, таких как Rhino. Если вы хотите знать, зачем тогда читать following comment Брендан Эйх.

Обновление: Браузеры не собираются удалить объект __proto__. Фактически, ECMAScript Harmony теперь стандартизовал как свойство __proto__, так и функцию setPrototypeOf. Свойство __proto__ поддерживается только по наследству. Вам настоятельно рекомендуется использовать setPrototypeOf и getPrototypeOf вместо __proto__.

Предупреждение: Хотя setPrototypeOf теперь является стандартным, вы по-прежнему не рекомендуется использовать его, потому что мутирует прототип объекта неизменно убивает оптимизаций и делает ваш код медленнее. Кроме того, использование setPrototypeOf обычно является показателем низкого качества кода.


Вам не нужно беспокоиться о том, что существующий код не работает в один прекрасный день. Объект __proto__ находится здесь.

Теперь вопрос под рукой. Мы хотим сделать что-то похожее на это в соответствии с требованиями стандарта образом:

var a = { 
    b: "ok" 
}; 

a.__proto__ = { 
    a: "test" 
}; 

alert(a.a); // alerts test 
alert(a.b); // alerts ok 

Очевидно, что вы не можете использовать Object.create для достижения этой цели, так как мы не создаем новый объект. Мы просто пытаемся изменить внутреннее свойство [[proto]] данного объекта. Проблема в том, что невозможно изменить внутреннее свойство [[proto]] объекта после его создания (за исключением использования __proto__, которого мы пытаемся избежать).

Итак, чтобы решить эту проблему, я написал простую функцию (обратите внимание, что он работает для всех объектов, за исключением функций):

function setPrototypeOf(obj, proto) { 
    var result = Object.create(proto); 
    var names = Object.getOwnPropertyNames(obj); 
    var getProp = Object.getOwnPropertyDescriptor; 
    var setProp = Object.defineProperty; 
    var length = names.length; 
    var index = 0; 

    while (index < length) { 
     var name = names[index++]; 
     setProp(result, name, getProp(obj, name)); 
    } 

    return result; 
} 

Таким образом, мы можем теперь изменить прототип любого объекта после его создания следующим образом (обратите внимание, что мы фактически не изменяя внутреннюю [[proto]] свойства объекта, но вместо того, чтобы создать новый объект с теми же свойствами данного объекта и который наследует от данного прототипа):

var a = { 
 
    b: "ok" 
 
}; 
 

 
a = setPrototypeOf(a, { 
 
    a: "test" 
 
}); 
 

 
alert(a.a); // alerts test 
 
alert(a.b); // alerts ok
<script> 
 
function setPrototypeOf(obj, proto) { 
 
    var result = Object.create(proto); 
 
    var names = Object.getOwnPropertyNames(obj); 
 
    var getProp = Object.getOwnPropertyDescriptor; 
 
    var setProp = Object.defineProperty; 
 
    var length = names.length; 
 
    var index = 0; 
 

 
    while (index < length) { 
 
     var name = names[index++]; 
 
     setProp(result, name, getProp(obj, name)); 
 
    } 
 

 
    return result; 
 
} 
 
</script>

Простой и эффективный (и мы не использовали свойство __proto__).

+0

Отличный ответ. Благодаря! – jdw

+2

Вы буквально говорите людям, чтобы они писали сломанный код, против явного совета как органов стандартов, так и разработчиков JS. В зависимости от таких ошибок, как API-интерфейсы и внутренние интерфейсы Windows оказались настолько беспорядочными. Если вы можете избежать этого, вы действительно не должны. И я действительно не думаю, что вы должны рекомендовать его без каких-либо оговорок. Ни вы, ни я не являетесь высшим авторитетом, чем спецификация или разработчики языка. Помните: на самом деле нет версии JavaScript, которая включает в себя '__proto__' - это внутренняя деталь, которая может быть реализована некоторыми реализациями. – Chuck

+7

@Chuck, свойство '__proto__' считается стандартным в следующей версии JavaScript, как указано в @cliffsofinsanity. Разработчики советуют не использовать его, потому что это еще не стандарт. Следовательно, это может не работать во всех машинах JavaScript. Всегда полезно понять, почему определенная функция не рекомендуется использовать, а не просто критиковать людей. Использование свойства '__proto__' в сети отлично работает, так как все основные браузеры поддерживают его. Я не вижу никаких оснований использовать его, кроме того, что он не будет работать в Rhino. Это не имеет ничего общего с Windows. –

1

Я не вижу, как:

var a = {}; 
a.__proto__ = A; // A is object because no constructor is needed 

проще:

var a = new A(); // A is Constructor function 

Теперь настройка в последнем случае может быть более многословным, если вы не» t нужна функция конструктора, но в этом случае вы можете автоматизировать ее так же просто:

var A = Constructor({ 
    method: function(){} 
}); // A is function, meant to be used with new 

По сравнению с:

var A = { 
    method: function(){} 
}; //A is object, meant to be set as __proto__ 

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

+0

Для логики инициализации можно использовать фабрику вместо того, чтобы полагаться на конструктор. Вам нужно копать немного глубже, чтобы увидеть значение. Если у ваших двух версий были прототипы, Object.create немного проще. См.: Function Constructor() {}; Constructor.prototype = {/ * методы * /}; var newObject = new Constructor(); ** VS ** var proto = {/ * методы * /}; var newObject = Object.create (proto) ;. Я просто создал новый прототипный объект в меньшем количестве кода, чем определение моего классического наследования. – Drew

+1

@Drew Прежде всего, первый не использует классическое наследование. Во-вторых, только одноразовая стоимость установки требует больше кода, фактическое создание объектов - это не только меньше кода, но и позволяет инициализировать логику без создания отдельной фабрики. Помните, что конструктор в JS работает как любая другая функция, он не ограничен, как конструкторы на классическом языке, поэтому многие причины создания фабрики на этих языках не применимы к js. – Esailija

7

Я думаю, что фактический момент, который хотел сделать Mozilla, заключается в том, что он нестандартен, поэтому разработчики будут в полной мере устранять его права.

Более чистый способ изготовления прототипных цепей - Object.create. Эквивалент кода, чтобы создать объект a с прототипом {'a': 'test'}, является:

a = Object.create({'a':'test'}) 

Есть также регулировочные прокладки, чтобы имитировать эту функцию в браузерах, которые не поддерживают его, если вы когда-нибудь понадобится, чтобы работать с одним , что является еще одним преимуществом перед непосредственным возиться с __proto__.

+0

Я не думаю, что OP хочет создать новый объект с данным прототипом. Я думаю, он хочет изменить прототип существующего объекта без изменения '__proto__'. –

+1

@AaditMShah: Его описание состояло в том, что он «просто хочет [s] создать цепочку прототипов« в отличие от «функционального наследования». Это говорит о том, что он хочет наследование чистого прототипа, для чего предназначен объект Object.create. Динамически сплетенные прототипы полезны, но не так часто необходимы. – Chuck

+0

Кроме того, мутирующий объект путем переназначения прототипа намного медленнее и обескуражен. Object.create намного быстрее. –