У меня есть следующий код:
class Polygon {
constructor() {
this.name = "Polygon";
}
}
class Rectangle {
constructor() {
this.name = "Rectangle";
}
}
class Square extends Polygon {
constructor() {
super();
}
}
Object.setPrototypeOf(Square, Rectangle);
const instance = new Square();
console.info(instance.name); // Rectangle
Мое понимание:
Instance__proto__
: Указывает на Square.prototype для наследования методов экземпляра.Subclass.__proto__
: Указывает на Rectangle для наследования статических методов и свойств.Square.prototype.__proto__
: указывает на Polygon.prototype для наследования методов экземпляра родительского класса.Мой вопрос:
В приведенном выше коде после использования Object.setPrototypeOf(Square, Rectangle)
Square._proto_
теперь указывает на Rectangle, а не на Polygon для всех статических свойств. А для наследования всех остальных методов Square.prototype._proto_
указывает на Polygon.prototype
. Итак, я ожидаю, что super()
в Square вызовет конструктор Polygon, поскольку Square расширяет Polygon. Однако похоже, что super() вместо этого вызывает конструктор Rectangle. Теперь я в замешательстве: что все меняется при запуске Object.setPrototypeOf(Square, Rectangle)
, похоже, в моем понимании есть пробел.
См. Сопоставляется ли super() с __proto__ под капотом?
Чего не хватает, так это того, откуда берется значение super
.
Базовый класс (то есть не расширяющий другой класс) создается на Function.prototype
. Например
Object.getPrototype(Polygon) === Function.prototype
Класс, расширяющий другой, приводит к двум вещам:
Объект расширенного класса создается на основе расширяемого класса, а не на Function.prototype
. Например
Object.getPrototypeOf(Square) === Polygon
Свойство prototype
расширенного класса создано на основе свойства prototype
расширенного класса. Например
Object.getPrototypOf(Square.prototype) === Polygon.prototype
Обратите внимание, что значение constructor
, унаследованное от prototype
расширенного класса, не изменяется и остается установленным для объекта расширенного класса. И.Е.
Square.prototype.constructor === Square
Это имеет последствия: суперкласс расширенного объекта не может быть определен на основе свойства constructor
, унаследованного экземплярами расширенного класса.
super
для поиска метода конструктора объекта суперкласса.Этот код показывает последствия изменения объекта, на котором создан прототип объекта класса, в экспериментальных целях, чтобы увидеть, что произойдет. Это не то, что нужно делать в реальном программировании.
class Polygon {
static ClassName = "Polygon";
constructor() { this.name = "Polygon instance" }
inheritsFrom() {return "Polygon"}
}
class Rectangle {
static ClassName = "Rectangle";
constructor() { this.name = "Rectangle instance"; }
inheritsFrom() {return "Rectangle"}
}
class Square extends Polygon {
constructor() {
super();
}
}
console.info("class Polygon is prototyped on Function.prototype: ",
Object.getPrototypeOf(Polygon) === Function.prototype, " (1) true expected");
console.info(Square.ClassName, " (2) static value 'Polygon' expected");
const instance = new Square();
console.info( instance.name, " (3) 'Polygon instance' expected")
console.info( instance.constructor.name, " (4) 'Square' expected")
// overwrite the object (the super class) on which Square is prototyped:
Object.setPrototypeOf(Square, Rectangle);
console.info( Square.ClassName, " (5) static value 'Rectangle' expected");
const instance2 = new Square();
console.info(instance2.name, " (6) 'Rectangle instance' expected");
console.info( instance2.constructor.name, " (7) 'Square' expected")
// instances of Square still inherit from Polygon.prototype
console.info("instance instanceof Polygon is ", instance instanceof Polygon,
" (8) true expected");
console.info("instance instanceof Rectangle is ",
instance instanceof Rectangle, " (9) false expected)");
console.info("instance inherits from ", instance.inheritsFrom(), " (10) 'Polygon' expected");
console.info("instance2 is an instanceOf Polygon ", instance2 instanceof Polygon,
" (11) true expected");
console.info("instance2.instanceof Rectangle is ",
instance2 instanceof Rectangle, " (12) false expected");
console.info("instance2 inherits from ", instance2.inheritsFrom(),
" (13) 'Polygon' expected");
Обновление: использование __proto__
в качестве унаследованного метода получения для возврата значения null или объект, для которого был прототипирован другой объект, был помечен как «НОРМАТИВНЫЙ ДОПОЛНИТЕЛЬНЫЙ, СТАРЫЙ» в ECMAScript и, следовательно, считается устаревшим: использование __proto__
было удален из тестового кода.
Во всем вышеперечисленном
Polygon
и Rectangle
в тестовом коде объявляют статическое свойство ClassName
и метод класса с именем inheritsFrom
ClassName
наследуется Square
от объекта, для которого Square
был создан прототип в момент Square.ClassName
оценки.name
— это свойство экземпляра, заданное конструктором, называемым super()
.constructor.name
наследуется экземпляром класса от первого объекта в их цепочке наследования. Цепочка наследования экземпляров Square
начинается с Square.prototype
и возвращается обратно к Polygon.prototype
, ни один из которых не изменяется тестовым кодом.inheritsFrom
— это метод класса, определенный в объявлениях классов для Polygon
и Rectangle
в тестовом коде.Означает ли это: 1. Object.setPrototypeOf(Square, Rectangle); меняет прототип класса Square, а не Square.prototype? 2. После этого изменения экземпляры Square создаются с использованием конструктора Rectangle, что приводит к тому, что свойства задаются конструктором Rectangle? 3. Сам Square.prototype продолжает указывать на Polygon.prototype?
Пожалуйста, используйте Object.getPrototypeOf
вместо устаревшего __proto__
.
Джимо 1. да, Square.prototype никак не изменяется ни вашим, ни моим кодом. 2. да, 3. да. Я обновил ответ и тестовый код, чтобы лучше это отразить.
Обновление ответа @Bergi в ответ - спасибо.