Попытка понять разницу между прототипом и конструктором в JavaScript
Я новичок в JavaScript, чтобы понять эту концепцию, я прочитал много статей о прототипе и конструкторах, но где бы я ни был, я остался с путаницей.
Путаница возникает, когда люди говорят о конструкторе и прототипе одновременно.
В следующем примере
var employee = function Emp(name) {
this.name = name;
}
var jack = new employee("Jack Dwain");
employee.constructor //gives Function()
employee.prototype // gives Emp {}
employee.prototype.constructor //gives Emp(name)
jack.constructor //gives Emp(name)
jack.prototype //gives undefined
-
прототип - это способ, которым JS достигает наследования, поскольку Emp(name)
- это прототип базовой функции, который ссылается на ту же самую функцию. Это то, что произошло?
-
Чем отличаются employee.constructor
и employee.prototype.constructor
?
-
Почему jack.prototype
был undefined
i.e Если он наследует от функции Emp(name)
, почему он не ссылается на эту функцию?
-
Как я могу четко предсказать себя, не набрав в консоли то, что прототип, или конструктор, или prototype.constructor...... дает
Ответы
Ответ 1
Это довольно сложная задача, если вы привыкли к простоте расширения объектов на других языках ООП, но я сделаю все возможное, чтобы объяснить их использование и что к чему. Я предполагаю, что вы знакомы с другими языками ООП. Поправьте меня если я ошибаюсь.
Все функции имеют прототип Function(). Они наследуют все базовые функциональные возможности от Function, такие как toString() и valueOf().
Тогда есть конструктор. Это то, что вы используете для инициализации объекта.
p = new Foo();
Так что в этом случае у нас есть две вещи.
-
function Foo
с Function
качестве прототипа (Foo) - Объект
Function
с Foo()
качестве конструктора (p)
(следите за мной еще?)
Конструктор Foo()
может переопределить некоторые базовые функциональные возможности конструктора Function
, но также оставить его как есть и эффективно его использовать.
Если вы знакомы с принципами ООП, то прототип - это базовый класс, конструктор - ваш текущий класс. в ООП выше будет class Foo extends Function
Вы также можете начать наследование с помощью всей этой установки прототипа и конструктора, делающего более сложные объекты по мере использования функциональности.
Например это:
// make a object initialiser extending Function. in oop 'class Foo extends Function'
function Foo(bar) {
this.baz = bar;
}
Foo.prototype.append = function(what) {
this.baz += " " + what;
};
Foo.prototype.get() {
return this.baz
}
Теперь давайте скажем, что мы хотим по-разному получить базу оттуда. один для регистрации консоли и один для размещения его в строке заголовка. Мы могли бы сделать большую вещь в нашем классе Foo, но мы этого не делаем, потому что нам нужно делать совершенно разные вещи с новыми классами, но они созданы для разных реализаций. Единственное, что им нужно поделиться - это база, а также сеттеры и геттеры.
Поэтому нам нужно расширить его, чтобы использовать термин ООП. в OOp это будет желаемый конечный class Title extends Foo(){}
результатов class Title extends Foo(){}
. Итак, давайте посмотрим, как туда добраться.
function Title(what) {
this.message = what;
}
На данный момент функция Title выглядит следующим образом:
- Функция прототипа
- Название конструктора
Итак, чтобы расширить его, нам нужно изменить прототип.
Title.prototype = new Foo();
- прототип Foo
- конструктор Фу
Это делается путем инициализации нового объекта Foo() против прототипа. Теперь это в основном объект Foo с названием Title. Это не то, что мы хотим, потому что теперь мы не можем получить доступ к части сообщения в заголовке. Мы можем заставить его правильно расширять Foo(), сбрасывая конструктор в Title
Title.prototype.constructor = Title;
- прототип Foo
- Название конструктора
Теперь мы столкнулись с еще одной проблемой. Конструктор Foo не инициализируется, поэтому мы получаем неопределенный this.baz
Чтобы решить это нам нужно позвонить родителю. В Java вы можете сделать это с помощью super(vars)
, в php $parent->__construct($vars)
.
В javascript мы должны изменить конструктор класса Title для вызова конструктора родительского объекта.
Таким образом, конструктор класса Title станет
function Title(what) {
Foo.call(this,what);
this.message = what;
}
Используя свойство объекта Function, унаследованное от Foo, мы можем инициализировать объект Foo в объекте Title.
И теперь у вас есть правильно унаследованный объект.
Таким образом, вместо использования такого ключевого слова, как extend
как в других языках ООП, он использует prototype
и constructor
.
Ответ 2
employee.constructor//дает функцию()
В JavaScript функции также являются объектами, которые могут быть построены с использованием собственного конструктора Function. Таким образом, вы можете написать следующий код, чтобы получить экземпляр функции.
var employee2 = new Function('a', 'b', 'return a+b');
То же самое происходит, когда вы создаете функцию, используя литерал функции, как в вашем случае. И свойство конструктора этого объекта также относится к одному и тому же собственному объекту/классу функции.
employee.prototype//дает Emp {}
Каждый объект в JavaScript имеет прототип, связанный с ним. Хотя прототип объектов объектов напрямую доступен с помощью .prototype
. Этот же прототип копируется на прототипе объектов, когда вы создаете объекты с ключевым словом new
. В первую очередь это копирование отвечает за наследование/расширение. Несмотря на то, что прототип скопирован, он не имеет прямого доступа, как в случае объектов Function. Он доступен нестандартным способом с помощью .__proto__
. Следующий код вернет true.
jack.__proto__==employee.prototype
employee.prototype.constructor//дает Emp (name)
Как сказано в документации Object.prototype.constructor. Это возвращает ссылку на функцию Object, которая создала прототип экземпляра. Здесь ссылается объект employee.prototype и not employee
. Это немного сложно, но прототип объекта employee.prototype был создан функцией Emp (name)
jack.constructor//дает Emp (имя)
Как было сказано в предыдущем пункте, этот прототип объектов был создан функцией Emp (name), когда вы создали объект, используя новые Emp(),
jack.prototype//дает undefined
Разъем не является функциональным объектом, поэтому вы не можете получить доступ к его прототипу. Вы можете получить доступ (не стандартный способ) прототипа разъема, например, следующего.
jack.__proto__
Ответ 3
Если вы хотите создать javascript объект, вы можете просто объявить новый объект и присвоить ему свойства (я решил объективировать себя):
var myself= {
name:"Niddro",
age:32
};
Этот метод позволяет создать один объект. Если вы хотите иметь прототип , описывающий человека вообще, где вы можете объявить нескольких людей с одинаковой настройкой. Чтобы создать прототип, вы можете использовать конструктор , который показан ниже:
//Constructor
function generalNameForObject(param1, param2,...) {
//Give the object some properties...
}
У меня есть прототип (приемник) в виду, что я хочу позвонить человеку, и он должен содержать имя и возраст свойств, и я буду использовать конструктор, чтобы сделать это:
function person(name,age) {
this.name=name;
this.age=age;
}
Вышеупомянутая функция построения описывает прототип для моих личных объектов.
Создайте нового человека, вызвав функцию construct:
var myself = new person("Niddro",31);
var OP = new person("rajashekar thirumala",23);
Проходит некоторое время, и я понимаю, что у меня был день рождения, поэтому мне нужно изменить свойство прототипа:
myself.age=32;
Если вы хотите добавить свойства в конструкцию, вам необходимо вручную добавить ее в конструктор:
function person(name,age,rep) {
this.name=name;
this.age=age;
this.reputation=rep;
}
Вместо этого вы можете добавить свойства прототипу, выполнив следующие действия (здесь "prototype" - действительная команда, а не просто имя):
function person(name,age,rep) {
this.name=name;
this.age=age;
}
person.prototype.reputation=105;
обратите внимание, что это добавит репутацию 105 для всех созданных объектов.
Надеюсь, это дало вам более глубокое понимание взаимосвязи между конструктором и прототипом.
Ответ 4
Конструктор:
function Foo(x) {
this.x =x;
}
Foo
является конструктором. Конструктор - это функция.
Существует два способа использования этого конструктора Foo
.
"Объекты создаются с использованием конструкторов в новых выражениях, для Например, новая дата (2009,11) создает новый объект Date. Вызов конструктор без использования новых имеет последствия, которые зависят от конструктор. Например, Date() создает строковое представление текущую дату и время, а не объект."
Источник ECMA-262
Это означает, что если Foo
возвращает что-то (через return "somevalue";
), то typeof Foo()
- тип возвращаемого значения.
С другой стороны, когда вы вызываете
var o = new Foo();
JavaScript фактически просто
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
Прототип:
Когда вы вызываете o.a
, тогда javascript сначала проверяет, является ли a
собственное свойство объекта o
. Если javascript не ищет цепочку свойств, найдите a
.
Для получения дополнительной информации о цепочке свойств см. mdn.
Конструкция prototype
конструктора имеет действительно мощную функцию, которая недоступна в классах. Если это полезно, это еще одна дискуссия. Конструкция конструктора prototype
может изменять свойства каждого экземпляра, который ссылается на этот прототип в своей прототипной цепочке.
Резюме:
Примечание. Это не точное определение, цель сводки - просто дать вам представление об участниках и прототипах.
Если вы используете конструктор с ключевым словом new
, то конструкторы и прототипы имеют сходную цель, даже жесткую, они совершенно разные. Конструктор инициализирует свойства объекта, поэтому он предоставляет свойства. Прототип также предоставляет свойства через цепочку свойств (наследование на основе прототипа).
Ответ 5
Но правда в том, что такой подход может быть неправильным во многих ситуациях. В Javascript, когда вы привязываете метод к ключевому слову this, вы предоставляете этот метод только этому конкретному экземпляру, и он на самом деле не имеет никакого отношения к экземпляру объекта этого конструктора, почти как статический метод. Помня о том, что функции являются первоклассными гражданами в Javascript, мы можем работать с ними так же, как с объектами, в этом случае мы только добавляем свойство к экземпляру объекта функции. Это только часть истории, вы также должны знать, что любой метод, связанный с этим, будет повторно объявлен для каждого нового экземпляра, который мы создаем, что может негативно повлиять на использование памяти приложением, если мы хотим создать так много экземпляров.