Pull to refresh

Comments 44

Кстати, для желающих реально разобраться в теме — очень рекомендую почитать ветку комментов от dsCode:
habrahabr.ru/post/111393/#comment_3555912

Начиная с «В ECMAScript нет (пока) сахара для классов».
Очень классно и познавательно. Там можно и разобраться, что такое __proto__, prototype и new на нормальных, понятных примерах.
В консоли Хрома:

> function Test(){} var q=new Test(); console.log(q.constructor.prototype===q.__proto__);
true


т.е. __proto__ равно constructor.prototype?
Только для объектов, созданных «простыми» конструкторами.

> function Test() {} Test.prototype = {}; var q = new Test(); console.log(q.constructor.prototype===q.__proto__);

false
UFO just landed and posted this here
А как это на практике можно использовать?
Вы про __proto__?
Ну например если в переопределенном методе какого либо класса вам нужно вызвать этод же метод родительского, тогда можете сделать так:
Object.getPrototypeOf(this).myMethod.apply(this, arguments);
Неверно. Данный подход зациклится при использовании сложных иерархий (начиная с трех уровней) — можете проверить.

Пусть иерархия выглядит так:

var a = {};
var b = Object.create(a);
var c = Object.create(b);

a.name = «a»;
b.name = «b»;
c.name = «c»;

b.some_func = function() { console.log(Object.getPrototypeOf(this).name); }
c.some_func(); // выведет b, т.е. нам не удалось получить a как прототип this
так все же верно,c inherits b inherits a, вызов c::parent вернет b, вызов c::parent::parent вернет 'a'.
Хорошо, вот другой пример.

var a = {};
var b = Object.create(a);
var c = Object.create(b);

a.some_func = function() { console.log(«a»); }
b.some_func = function() { console.log(«b»); Object.getPrototypeOf(this).some_func.apply(this, arguments); }
c.some_func = function() { console.log(«c»); Object.getPrototypeOf(this).some_func.apply(this, arguments); }

a.some_func(); //a
b.some_func(); //b a
c.some_func(); //c b b b b…

Это я к чему? Да к тому, что конструкция Object.getPrototypeOf(this).myMethod.apply(this, arguments); делает нечто, совершенно отличающиеся от вызова родительского метода.
Угу, получается надо юзать Object.getPrototypeOf(this).some_func.apply(Object.getPrototypeOf(this), arguments);?
Для метода, не меняющего состояние объекта — пойдет.

Если метод меняет состояние, то первым аргументом apply может быть только this.

Для решения данной проблемы надо вспомнить, что вообще-то мы знаем, от какого объекта унаследовались. То есть правильный вариант таков:

var a = {};
var b = Object.create(a);
var c = Object.create(b);

a.some_func = function() { console.log(«a»); }
b.some_func = function() { console.log(«b»); a.some_func.apply(this, arguments); }
c.some_func = function() { console.log(«c»); b.some_func.apply(this, arguments); }

a.some_func(); //a
b.some_func(); //b a
c.some_func(); //c b a

На случай неизвестной заранее иерархии или для дополнительной защиты, можно накрутить что-нибудь с замыканиями.
Но Object.getPrototypeOf(this) использовать для подобной цели нельзя.
>поиск происходит в другом объекте, свойстве __proto__
>оно [свойство prototype] по умолчанию является объектом

Такие формулировки вводят в заблуждение. Свойство __proto__ не содержит никаких объектов, оно содержит ссылку на объект. Это важно.

>поиск происходит в другом объекте, на который ссылается свойство __proto__
>оно [свойство prototype] по умолчанию ссылается на объект
В JS Вообще все переменные содержат ссылку, кроме простых, так что в данном случае это не важно.
Мне вот только одно не понятно, почему вы не поискали подобные темы на хабре, имхо уже топиков 20 обсудало эту тему.
habrahabr.ru/post/108915/
habrahabr.ru/post/133034/
habrahabr.ru/post/120193/

И таких тем еще куча, тема очередной дубль своими словами о том что уже давно написали, разжевали.
Да ладно Вам, никто из читающих не будет искать, а так хоть узнаю что-то новое.
Если бы новое — ладно, но как сказал TheShock тему обсосали 200 раз, в свое время на хабре на эту тему по 1 посту в день писали. А так такую статью уже каждый написать может открываешь гугл ищешь готовую статью копируешь, меняешь пару предложение — все.
Искал. Но так и не нашел простого и понятного объяснения свойства __proto__.

Вы хотите сказать что статья эта никому не нужна, и лучше бы ее не было?
Однако уже больше сотни человек добавили ее в избранное.
Но так и не нашел простого и понятного объяснения свойства __proto__.

Вы мне напомнили этот комикс:
Русская версия, на всяк случай:
я же не стандарт предлагаю, а просто хочу помочь некоторым людям разобраться в javascript
Расшифрую)
— Ситуация: есть 14 разных топиков, объясняющих __proto__ просто и понятно
— 14?! Долбануться! Нужно написать один универсальный топик, объясняющий __proto__ реально просто и понятно
— Ситуация: есть 15 разных топиков, объясняющих __proto__ просто и понятно
Та ни в чём. Он просто не нужен)
Вам уже не нужен (т.к. вы уже полностью разобрались в теме), а кому-то еще нужен
Мне кажется чем больше литературы по определенной теме, тем лучше — есть выбор.
С другой стороны — осложняется поиск)
UFO just landed and posted this here
Просто реально — есть столько не раскрытых или плохо раскрытых тем. Лучше эту энергию затратить на что-то полезное, а не на очередной клон той же статьи.
Без определенного фундамента — сложно раскрыть более сложные темы.
О чём вы говорите? Этим фундаментом уже заложено всё, что можно. Давным давно пора строить дом, а не продолжать в десятый раз ложить фундамент того же размера, но под другим углом.
Если рекомендуете для старых браузеров эмулировать функции `Object.create` и `Object.getPrototypeOf`, то, лучше уж, давайте ссылку на
github.com/kriskowal/es5-shim — тут эти методы описаны максимально по стандарту и учитывают очень много разных нюансов.
Например: функция `Object.create` задаёт свойство `__proto__` у созданного объекта, а в функция `Object.getPrototypeOf` несколько универсальные приведённого вами кода:

Object.getPrototypeOf = function getPrototypeOf(object) {
        return object.__proto__ || (
            object.constructor
                ? object.constructor.prototype
                : prototypeOfObject
        );
    };
ок. спасибо за ссылочку, не знал раньше про эту библиотеку
Как корректно поменять __proto__ объекта, чтобы превратить его в массив (серверный JS)? Такой код работает:
a.__proto__=Array.prototype
Но вы говорите, что использовать его некорректно. Есть ли какой-нибудь корректный способ без копирования элементов массива?
var MyArray = function () {};
MyArray.prototype = new Array();
var a = new MyArray();

// same as
// var a = {};
// a.__proto__=Array.prototype

a.push(42);
console.log(a.length); // 1

Если просто копировать аттрибуты прототипа массивы, то сломается length
А разве не надо ещё и конструктор прототипу прописать?
MyArray.prototype.constructor = MyArray;
А то получится, что конструктор у нас будет системным массивом, а не нашей функцией.
Если будет использоваться в instanceof — пишите. Лучше не изобретать свой массив (не наследовать), а написать обертку над существующим (см комент ниже).
Основная проблема такого подхода вот в чём:
var MyArray = function () {};
MyArray.prototype = new Array();
var a = new MyArray();
a[1] = 123;
a.length; // 0
Угу, так же как и с a.__proto__=Array.prototype
Таким образом, вы сделали массив из пустого объекта. А если имеется объект типа {0:'x', 1:'y'}, то его можно превратить в «почти-массив» таким кодом:
obj.__proto__ = Array.prototype;
obj.length = <длина массива>; // Допустим, в нашей задаче она известна


И такой подход точно работает (неправильно отрабатывает конструкция типа for (var i in obj), но ее можно заменить методами forEach и т. д.
Есть ли способ проделать то же самое, не трогая свойство __proto__?
Эта конструкция всегда неправильно работает: for (var i in obj).
То, что вы хотите — можно сделать так:

var obj = {0:'x', 1:'y'};
obj.length = 2;
console.log( [].slice.call(obj) ); // ['x', 'y' ]


Но если вас интересует теоретическая возможность, то я думаю, что кроссбраузерно и стандартно — это врядли реально.
Метод slice копирует элементы массива и создает новый массив. Соответственно, тратятся ресурсы на ненужную операцию.
А кроссбраузерность не нужна — речь шла о серверном JS. На клиенте можно было бы, например, воспользоваться методом jQuery.makeArray (который работает примерно так же, как предложенный вами вариант).
Вообщем, пока я решил проблему хаком указанным выше, правильного варианта, по-видимому, не существует.
Поставил плюс, но, пожалуйста, в следующий раз выбирайте тему, не обсосаную 200 раз.
Sign up to leave a comment.

Articles