Страницы

Поиск по вопросам

Показаны сообщения с ярлыком прототипное-наследование. Показать все сообщения
Показаны сообщения с ярлыком прототипное-наследование. Показать все сообщения

суббота, 7 декабря 2019 г.

В чем отличие __proto__ от prototype?

#javascript #прототипное_наследование


В чем различие этих 2 способов прототипного наследования и какой из них более правильнее
использовать?



function Cat() {
    this.__proto__.type = 'кот';
}


var cat = new Cat();
console.log(cat.type); // кот


function CatTwo() {
}
CatTwo.prototype.type = 'тоже кот';

var catTwo = new Cat();
console.log(catTwo.type); // тоже кот



    


Ответы

Ответ 1



На самом деле в примере в вопросе нет наследования. __proto__ до недавнего времени не был стандартизирован. В последних спецификациях можно найти, что getter __proto__, обертка над вызовом внутренней функции [[GetPrototypeOf]]. Точно такая же, как и функция Object.getPrototypeOf Если применительно к приведенному коду: в первом случае значение прототипа меняется каждый раз при создании объекта, во втором - устанавливается всего один раз. Отсюда можно сделать вывод, что если есть доступ к конструктору, прототип лучше менять непосредственное у него. Пример, когда приведенный код выдаст разные значения: function Cat(type) { this.__proto__.type = type; } var cat = new Cat('кот'); console.log(cat.type); // кот var cat2 = new Cat('кот2'); console.log(cat.type); // кот2 console.log(cat2.type); // кот2 function CatTwo() {} CatTwo.prototype.type = 'тоже кот'; var catTwo = new CatTwo(); console.log(catTwo.type); // тоже кот

Ответ 2



Свойство __proto__ Абсолютно любой объект в JavaScript имеет свойство proto. Это скрытое системное свойство, и не во всех реализациях языка оно доступно пользователю. При обращении к любому свойству объекта, оно в первую очередь ищется в самом объекте: var obj = {ownProperty: 1}; console.log(obj.ownProperty);// 1 Но если его там нет, поиск происходит в свойстве proto: obj.__proto__ = {propertyOfProto: 2}; console.log(obj.propertyOfProto);// 2 Если его нет и там, оно ищется дальше по цепочке: obj.__proto__.__proto__ = {propertyOfProtosProto: 3}; console.log(obj.propertyOfProtosProto);// 3 Эта цепочка называется цепочкой прототипов (prototype chain). proto любого значения (кроме null и undefined) ссылается на prototype соответствующего ему типу данных: (0).__proto__ === Number.prototype false.__proto__ === Boolean.prototype "string".__proto__ === String.prototype (new Date).__proto__ === Date.prototype (function(){}/* new Function */).__proto__ === Function.prototype Все типы данных наследуются от Object, это означает что: Number.prototype.__proto__ === Object.prototype И наконец, завершение цепочки: Object.prototype.__proto__ === null Свойство prototype Это обычное свойство, ничем не отличающиеся от любых других свойств. За исключением двух особенностей: 1) Функции в JavaScript имеют свойство prototype. Оно по умолчанию является объектом с единственным свойством constructor, которое ссылается на саму функцию. 2) Свойство prototype используется при создании новых объектов оператором new. Этот оператор делает следущее: Создает пустой объект: var instance = {}; Устанавливает proto этому объекту ссылкой на prototype функции-класса: instance.__proto__ = FnClass.prototype; Применяет функцию-класс к нашему новосозданному объекту: constructorReturns = FnClass.apply(instance, arguments); т.е. исполняет функцию FnClass, передавая ей instance в качестве this и аргументы в виде массива arguments. Возвращает экземпляр функции-класса, но если FnClass нам вернул обьект, тогда его: return constructorReturns instanceof Object ? constructorReturns : instance; Функцией-классом я называю функцию, к которой впоследствии ожидается применение оператора new. Такие функции принято именовать с заглавной буквы.

вторник, 25 июня 2019 г.

Имитация множественного наследования

Есть класс и функция-конструктор. Делается попытка реализовать класс, являющийся чем-то типа потомка обоих. Точнее, методы из прототипа функции-конструктора копируются в прототип класса-потомка, унаследованный от класса.
Да, я понимаю, что это не полноценное наследование, но для решения задачи этого хватает.
Проблема в другом. Как заставить тайпскрипт воспринимать копируемые методы?
class First { someMethod() { console.log('someMethod from First'); } }
function Second() { console.log('Second'); }
Second.prototype.doSmth = function () { console.log('doSmth from Second'); }
interface IBoth { someMethod() doSmth() }
class Both extends First /* implements IBoth */ { constructor() { console.log('constructor of Both'); super(); Second.call(this); } }
for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; }
На самом деле, надо обеспечить видимость методов ещё на уровень дальше
class Final extends Both { doIt() { this.someMethod(); //this.doSmth(); // Надо заставить видеть метод тут (this as any as IBoth).doSmth(); // Компилируется, но это ужас } }
если при этом методы не будут видны из самого Both, то это годится.
Вот что я уже пробовал:
При попытке написать
class Both extends First implements IBoth {
возникает ошибка, что я не реализую методы интерфейса. При переименовании Both в _Both и использовании
var Both = _Both as typeof _Both;
всё остаётся как было, что логично, поскольку тут никак не используется First При переименовании Both в _Both и использовании
var Both = _Both as typeof IBoth;
говорит, что не может найти имя IBoth
Пробовал ещё несколько вариантов, но они совсем бредовые. Что ещё можно сделать?

Попробовать можно тут: http://www.typescriptlang.org/Playground Полный код для проверки
Запустить (код из правой панели) после добавления строки:
(new Final).doIt();
Вывод при запуске при раскомментированной строке this.doSmth();
constructor of Both Second someMethod from First doSmth from Second doSmth from Second

PS: Этот вопрос на английском.


Ответ

Интерфейс вообще не нужен, нужно просто объявить прототипное поле с нужным типом:
doSmth: () => void
Она видима как свойство, а не как метод, но это непринципиально.
Полный код:
class First { someMethod() { console.log('someMethod from First'); } }
function Second() { console.log('Second'); }
Second.prototype.doSmth = function () { console.log('doSmth from Second'); }
class Both extends First { constructor() { console.log('constructor of Both'); super(); Second.call(this); }
doSmth: () => void }
for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; }
class Final extends Both { doIt() { this.someMethod(); this.doSmth(); //Both.prototype.doSmth(); // ok //Final.prototype.doSmth(); // ok } }
PS: Надо было гуглить не всяческие варианты с наследованием, а typescript class prototype variable - сразу нашёл подходящий вариант.

четверг, 11 октября 2018 г.

В чем отличие __proto__ от prototype?

В чем различие этих 2 способов прототипного наследования и какой из них более правильнее использовать?
function Cat() { this.__proto__.type = 'кот'; } var cat = new Cat(); console.log(cat.type); // кот function CatTwo() { } CatTwo.prototype.type = 'тоже кот'; var catTwo = new Cat(); console.log(catTwo.type); // тоже кот


Ответ

На самом деле в примере в вопросе нет наследования.

__proto__ до недавнего времени не был стандартизирован.
В последних спецификациях можно найти, что getter __proto__, обертка над вызовом внутренней функции [[GetPrototypeOf]]
Точно такая же, как и функция Object.getPrototypeOf

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

Пример, когда приведенный код выдаст разные значения:
function Cat(type) { this.__proto__.type = type; } var cat = new Cat('кот'); console.log(cat.type); // кот var cat2 = new Cat('кот2'); console.log(cat.type); // кот2 console.log(cat2.type); // кот2 function CatTwo() {} CatTwo.prototype.type = 'тоже кот'; var catTwo = new CatTwo(); console.log(catTwo.type); // тоже кот