Прототипы(Наследование) JS
Столкнулся с таким кодом на сайте https://learn.javascript.ru/ в тестах.
function MyArray() { }
MyArray.prototype = [];
let arr = new MyArray();
arr.push(1, 2, 3);
alert(arr.length);
Можете пожалуйста объяснить, что происходит в этом коде? И каким образом к переменной arr можно применить метод массива push?
Ответы (2 шт):
Идея прототипов проста. Если метода или свойства нет у класса он ищет его у прототипа.
В вашем случае создаётся объект класса MyArray у которого не своего push, a прототип у него array посмотрите способы создания массивов. И у array уже есть метод push.
Сложность небольшая появляется когда вы создадите 2 объекта и вызовите push у каждого и посмотрите на результат.
У объектов есть внутреннее свойство [[prototype]]
У функций есть свойство prototype (не путать одно с другим)
Когда функцию вызывают через new, она создает объект, у которого (при условии, что prototype функции - тоже объект) свойство [[prototype]] (в консоли браузера отображается как __proto__ **) будет ссылаться на prototype функции-конструктора.
** при этом у самого объекта нет такого свойства, это геттер, унаследованный от Object.prototype: __proto__
Каждый раз, когда у объекта запрашивают свойство / метод:
- JS сперва проверяет его наличие в самом объекте. Если не нашлось,
- По ссылке
[[prototype]]ищет его вprototypeфункции-конструктора. Если и там не найдется, prototypeфункции - тоже объект, у него тоже есть[[prototype]]- посмотрит там. Так по "цепочке" идет вверх, пока не найдет свойство, или пока не закончится "лесенка" из[[prototype]].
function MyArray() {}
MyArray.prototype = [];
let arr = new MyArray();
console.log( arr ); // {} есть push? Нет. ► arr.__proto__
console.log( arr.__proto__ === MyArray.prototype ); // true
console.log( MyArray.prototype ); // [] есть push? Нет. ► MyArray.prototype.__proto__
console.log( MyArray.prototype.__proto__ === Array.prototype ); // true
console.log( arr.__proto__.__proto__.hasOwnProperty("push") ); // true
console.log( arr.__proto__.__proto__ === Array.prototype ); // true
// Метод берется отсюда
arr.push(1, 2, 3);
console.log(arr.length); // 3
console.log(MyArray.prototype.length); // 0
console.log( arr );
console.log( arr.hasOwnProperty("length") ); // true
.as-console-wrapper { min-height: 100vh; }
.as-console-wrapper code { white-space: nowrap; }
length сперва берется из MyArray.prototype, но дальше записывается и продолжает увеличивается у объекта arr.
* MyArray.prototype = []; — это короткая запись MyArray.prototype = new Array();
И каким образом к переменной arr можно применить метод массива push?
Внутреннее устройство push не знаю, можно предположить, что ему всё равно, это массив, объект или кто. Если нашлось свойство length, добавляет переданное значение под индексом length и увеличивает length на один.
let test_1 = {
"0": 0,
"1": 1,
length: 0
};
Array.prototype.push.call(test_1, "bubu");
console.log(test_1); // Оппа, { "0": "bubu", "1": 1, "length": 1 }
let test_2 = {};
Array.prototype.push.call(test_1, "bubu");
console.log(test_2); // {} — нет length, не сработал.