JavaScript имеет очень скрученную форму прототипного наследования. Мне нравится называть это constructor pattern of prototypal inheritance. Существует еще одна модель прототипного наследования - the prototypal pattern of prototypal inheritance. Сначала я объясню это последним.
В объектах JavaScript наследуется от объектов. Нет необходимости в занятиях. Это хорошая вещь. Это облегчает жизнь. Например, у нас есть класс для линий:
class Line {
int x1, y1, x2, y2;
public:
Line(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
int length() {
int dx = x2 - x1;
int dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}
}
Да, это C++. Теперь, когда мы создали класс, мы теперь можем создавать объекты:
Line line1(0, 0, 0, 100);
Line line2(0, 100, 100, 100);
Line line3(100, 100, 100, 0);
Line line4(100, 0, 0, 0);
Эти четыре строки образуют квадрат.
У JavaScript нет классов.Он имеет прототипное наследование. Если вы хотите сделать то же самое, используя прототипичный рисунок вы могли бы сделать это:
var line = {
create: function (x1, y1, x2, y2) {
var line = Object.create(this);
line.x1 = x1;
line.y1 = y1;
line.x2 = x2;
line.y2 = y2;
return line;
},
length: function() {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
}
};
Затем вы создаете экземпляры объекта line
следующим образом:
var line1 = line.create(0, 0, 0, 100);
var line2 = line.create(0, 100, 100, 100);
var line3 = line.create(100, 100, 100, 0);
var line4 = line.create(100, 0, 0, 0);
Это все есть на него. Никаких запутанных конструкторских функций с объектами prototype
. Единственной функцией, необходимой для наследования, является Object.create
. Эта функция принимает объект (прототип) и возвращает другой объект, который наследуется от прототипа.
К сожалению, в отличие от Lua, JavaScript поддерживает шаблон конструктора прототипального наследования, что затрудняет понимание прототипного наследования. Шаблон конструктора является обратным шаблону прототипа.
- В прототипном шаблоне объекты имеют наибольшее значение. Следовательно, легко видеть, что объекты наследуются от других объектов.
- В конструкторе шаблоны функции имеют наибольшее значение. Следовательно, люди склонны думать, что конструкторы наследуются от других конструкторов. Это не верно.
выше программа будет выглядеть следующим образом при записи с помощью шаблона конструктора:
function Line(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype.length = function() {
var dx = this.x2 - this.x1;
var dy = this.y2 - this.y1;
return Math.sqrt(dx * dx + dy * dy);
};
Теперь вы можете создавать экземпляры Line.prototype
следующим образом:
var line1 = new Line(0, 0, 0, 100);
var line2 = new Line(0, 100, 100, 100);
var line3 = new Line(100, 100, 100, 0);
var line4 = new Line(100, 0, 0, 0);
Обратите внимание на сходство между конструктором шаблона и прототипа?
- В прототипном шаблоне мы просто создаем объект, который имеет метод
create
. В шаблоне конструктора мы создаем функцию, и JavaScript автоматически создает для нас объект prototype
.
- В прототипе у нас есть два метода -
create
и length
. В шаблоне конструктора тоже есть два метода: constructor
и length
.
Шаблон конструктора является обратным шаблону прототипа, потому что при создании функции JavaScript автоматически создает объект prototype
для функции. Объект prototype
имеет свойство, называемое constructor
который points back to the function itself:
Как сказал Эрик, причина console.log
знает выход Foo
происходит потому, что, когда вы проходите Foo.prototype
к console.log
:
- Он находит
Foo.prototype.constructor
, который Foo
.
- Каждая именованная функция в JavaScript имеет свойство, называемое
name
.
- Следовательно
Foo.name
является "Foo"
.Поэтому он находит строку "Foo"
на Foo.prototype.constructor.name
.
Edit: Хорошо, я понимаю, что у вас есть проблемы с переопределение prototype.constructor
собственности в JavaScript. Чтобы понять проблему, давайте сначала поймем, как работает оператор new
.
- Во-первых, я хочу, чтобы вы хорошо рассмотрели диаграмму, которую я показал вам выше.
- На приведенной выше диаграмме у нас есть функция-конструктор, объект-прототип и экземпляр.
- Когда мы создаем экземпляр, используя ключевое слово
new
, перед конструктором JS создается новый объект.
- Внутренняя собственность
[[proto]]
этого нового объекта равна нулю constructor.prototype
указывает на на момент создания объекта.
Что это подразумевает? Рассмотрим следующую программу:
function Foo() {}
function Bar() {}
var foo = new Foo;
Foo.prototype = Bar.prototype;
var bar = new Foo;
alert(foo.constructor.name); // Foo
alert(bar.constructor.name); // Bar
Смотрите вывод здесь: http://jsfiddle.net/z6b8w/
- Экземпляр
foo
наследует от Foo.prototype
.
- Следовательно
foo.constructor.name
отображает "Foo"
.
- Затем мы установили
Foo.prototype
в Bar.prototype
.
- Следовательно
bar
наследует от Bar.prototype
, хотя он был создан new Foo
.
- Таким образом,
bar.constructor.name
является "Bar"
.
В JS fiddle вы условии, что вы создали функцию Foo
, а затем установить Foo.prototype.constructor
в function Bar() {}
:
function Foo() {}
Foo.prototype.constructor = function Bar() {};
var f = new Foo;
console.log(f.hasOwnProperty("constructor"));
console.log(f.constructor);
console.log(f);
Поскольку вы изменили свойство Foo.prototype
каждый экземпляр Foo.prototype
будет отражать эти изменения. Следовательно, f.constructor
- function Bar() {}
. Таким образом, f.constructor.name
является "Bar"
, а не "Foo"
.
Смотрите сами - f.constructor.name
это "Bar"
.
Хром, как известно, делает такие странные вещи. Важно понимать, что Chrome - это утилита для отладки, а console.log
в основном используется для целей отладки.
Следовательно, при создании нового экземпляра Chrome, вероятно, записывает исходный конструктор во внутреннее свойство, к которому обращается console.log
. Таким образом, он отображает Foo
, а не Bar
.
Это не фактическое поведение JavaScript. Согласно спецификации при перезаписывании свойства prototype.constructor
нет никакой связи между экземпляром и исходным конструктором.
Другие реализации JavaScript (например, консоль Opera, node.js и RingoJS) делают правильные вещи и отображают Bar
. Следовательно, поведение Chrome нестандартно и зависит от браузера, поэтому не паникуйте.
Что важно понимать, что хотя Chrome отображает Foo
вместо Bar
constructor
свойство объекта еще function Bar() {}
как и с другими реализациями:
Я предполагаю, что свойство 'prototype' функций и объект' __proto__' - это одно и то же. Вот почему вы видите результат, который видите. – millimoose
В принципе, всякий раз, когда вы делаете 'new Foo()', результирующий объект '__proto__' устанавливается в' prototype' функции конструктора. Я сомневаюсь, что есть более глубокое объяснение, кроме «как работает Javascript OO». (Независимо от ссылок на спецификацию ECMAScript.) Альтернативно: всякий раз, когда вы определяете функцию, Javascript также внутренне определяет объект-прототип для объектов, созданных с использованием этой функции в качестве конструктора. – millimoose
Вы прочитали суть в конце вопроса? Вопрос об официальном/унифицированном объяснении поведения Chrome остается нерешенным. Основной ответ ниже: «Не беспокойтесь об этом», «не паникуйте» и т. Д. Я не делаю ни того, ни другого. Я поддержал ваш ответ на том основании, что он дает хороший обзор прототипов, но на самом деле он не затрагивает вопрос о поведении Chrome. –