Когда вы объявляете ссылочную переменную (т. е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int
:
int x;
x = 10;
В этом примере переменная x является int
, и Java инициализирует ее для 0. Когда вы назначаете его 10 во второй строке, ваше значение 10 записывается в ячейку памяти, на которую указывает x.
Но когда вы пытаетесь объявить ссылочный тип, произойдет что-то другое. Возьмите следующий код:
Integer num;
num = new Integer(10);
Первая строка объявляет переменную с именем num
, но она не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integer
является ссылочным типом). Поскольку вы еще не указали, что указать на Java, он устанавливает значение null, что означает «Я ничего не указываю».
Во второй строке ключевое слово new
используется для создания экземпляра (или создания ) объекту типа Integer и переменной указателя num
присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования .
(точка).
Exception
, о котором вы просили, возникает, когда вы объявляете переменную, но не создавали объект. Если вы попытаетесь разыменовать num
. Перед созданием объекта вы получите NullPointerException
. В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что «num не может быть инициализирован», но иногда вы пишете код, который непосредственно не создает объект.
Например, вы можете имеют следующий метод:
public void doSomething(SomeObject obj) {
//do something to obj
}
В этом случае вы не создаете объект obj
, скорее предполагая, что он был создан до вызова метода doSomething
. К сожалению, этот метод можно вызвать следующим образом:
doSomething(null);
В этом случае obj
имеет значение null. Если метод предназначен для того, чтобы что-то сделать для переданного объекта, целесообразно бросить NullPointerException
, потому что это ошибка программиста, и программисту понадобится эта информация для целей отладки.
Альтернативно, там могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething
может быть записано как:
/**
* @param obj An optional foo for ____. May be null, in which case
* the result will be ____.
*/
public void doSomething(SomeObject obj) {
if(obj != null) {
//do something
} else {
//do something else
}
}
Наконец, Как определить исключение & amp; причина использования Трассировки стека
Это не всегда необходимо, но у него есть свои возможности. Предположим, мы хотели создать метод копирования на базовом классе Person
. Например:
// define the Person Class
function Person(name) {
this.name = name;
}
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
// define the Student class
function Student(name) {
Person.call(this, name);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
Теперь, что происходит, когда мы создаем новый Student
и копируем его?
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false
Копия не является экземпляром Student
. Это связано с тем, что (без явных проверок) мы не смогли бы вернуть копию Student
из «базового» класса. Мы можем вернуть только Person
. Однако, если бы мы сбросили конструктор:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
... тогда все работает так, как ожидалось:
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true
До сих пор путаница все еще существует.
Следуя исходному примеру, поскольку у вас есть существующий объект student1
как:
var student1 = new Student("Janet", "Applied Physics");
Предположим, вы не хотите знаете, как student1
создан, вам просто нужен другой объект, как он, вы можете использовать свойство конструктора для student1
, например:
var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");
Здесь он не сможет получить свойства из Student
если свойство конструктора не задано. Скорее это создаст объект Person
.
Да, это необходимо. Когда вы выполняете
,Student.prototype = new Person();
Student.prototype.constructor
становится Person
. Поэтому вызов Student()
возвращает объект, созданный Person
. Если вы затем выполняете
Student.prototype.constructor = Student;
Student.prototype.constructor
, сбрасывается на Student
. Теперь, когда вы вызываете Student()
, он выполняет Student
, который вызывает родительский конструктор Parent()
, он возвращает правильно наследуемый объект. Если вы не сбросили Student.prototype.constructor
перед его вызовом, вы получили бы объект, у которого не было бы никаких свойств, установленных в Student()
.
TLDR; Не очень необходимо, но, вероятно, поможет в долгосрочной перспективе, и это более точно.
ПРИМЕЧАНИЕ. Многое редактировалось, поскольку мой предыдущий ответ был смущенно написан и имел некоторые ошибки, которые я пропустил в моей спешке, чтобы ответить. Спасибо тем, кто указал на некоторые вопиющие ошибки.
В принципе, для правильного подключения к подклассу в Javascript. Когда мы подклассом, мы должны сделать некоторые напуганные вещи, чтобы убедиться, что прототипная делеция работает правильно, включая перезапись объекта prototype
. Перезапись объекта prototype
включает в себя constructor
, поэтому нам нужно исправить ссылку.
Давайте быстро рассмотрим, как работают «классы» в ES5.
Допустим, вы имеют конструкторскую функцию и ее прототип:
//Constructor Function
var Person = function(name, age) {
this.name = name;
this.age = age;
}
//Prototype Object - shared between all instances of Person
Person.prototype = {
species: 'human',
}
Когда вы вызываете конструктор для создания экземпляра, скажем Adam
:
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
Ключевое слово new
вызывается с помощью «Человек» 'в основном будет запускать конструктор Person с несколькими дополнительными строками кода:
function Person (name, age) {
// This additional line is automatically added by the keyword 'new'
// it sets up the relationship between the instance and the prototype object
// So that the instance will delegate to the Prototype object
this = Object.create(Person.prototype);
this.name = name;
this.age = age;
return this;
}
/* So 'adam' will be an object that looks like this:
* {
* name: 'Adam',
* age: 19
* }
*/
Если мы console.log(adam.species)
, поиск не будет выполняться на экземпляре adam
и будет искать прототипную цепочку для его .prototype
, который есть Person.prototype
- и Person.prototype
, имеет свойство a .species
, поэтому поиск будет успешным на Person.prototype
. Затем он будет записывать 'human'
.
Здесь Person.prototype.constructor
будет правильно указывать на Person
.
Итак, теперь интересная часть - так называемое «подклассификация». Если мы хотим создать класс Student
, являющийся подклассом класса Person
с некоторыми дополнительными изменениями, нам нужно убедиться, что Student.prototype.constructor
указывает на точность для ученика.
Это не делается само по себе. При подклассе код выглядит следующим образом:
var Student = function(name, age, school) {
// Calls the 'super' class, as every student is an instance of a Person
Person.call(this, name, age);
// This is what makes the Student instances different
this.school = school
}
var eve = new Student('Eve', 20, 'UCSF');
console.log(Student.prototype); // this will be an empty object: {}
Вызов new Student()
здесь возвращает объект со всеми требуемыми свойствами. Здесь, если мы проверим eve instanceof Person
, он вернется false
. Если мы попытаемся получить доступ к eve.species
, он вернет undefined
.
Другими словами, нам нужно связать делегирование, чтобы eve instanceof Person
возвращало true и чтобы экземпляры делегата Student
правильно Student.prototype
, а затем Person.prototype
.
НО, так как мы вызываем его с ключевым словом new
, помните, что добавляет этот призыв? Он назовет Object.create(Student.prototype)
, как мы установили это делегиальное отношение между Student
и Student.prototype
. Обратите внимание, что прямо сейчас Student.prototype
пуст. Таким образом, при поиске .species
экземпляр Student
потерпит неудачу, поскольку он делегирует только Student.prototype
, а свойство .species
не существует на Student.prototype
.
Когда мы присваиваем Student.prototype
Object.create(Person.prototype)
, сам Student.prototype
делегирует Person.prototype
, и поиск eve.species
вернет human
, как мы ожидаем. Предположительно, мы хотели бы, чтобы он наследовал от Student.prototype AND Person.prototype. Поэтому нам нужно все это исправить.
/* This sets up the prototypal delegation correctly
*so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
*This also allows us to add more things to Student.prototype
*that Person.prototype may not have
*So now a failed lookup on an instance of Student
*will first look at Student.prototype,
*and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);
Теперь делегация работает, но мы переписываем Student.prototype
с помощью функции Person.prototype
. Поэтому, если мы назовем Student.prototype.constructor
, он будет указывать на Person
вместо Student
. Вот почему мы должны это исправить.
// Now we fix what the .constructor property is pointing to
Student.prototype.constructor = Student
// If we check instanceof here
console.log(eve instanceof Person) // true
В ES5 наше свойство constructor
является ссылкой, которая ссылается на функцию, которую мы написали с намерением стать «конструктором». Помимо того, что дает нам ключевое слово new
, конструктор в противном случае является «простой» функцией.
В ES6 constructor
теперь встроен в способ, которым мы пишем классы - как в, он предоставляется как метод, когда мы объявляем класс. Это просто синтаксический сахар, но он предоставляет нам некоторые удобства, такие как доступ к super
, когда мы расширяем существующий класс. Поэтому мы должны написать вышеприведенный код следующим образом:
class Person {
// constructor function here
constructor(name, age) {
this.name = name;
this.age = age;
}
// static getter instead of a static property
static get species() {
return 'human';
}
}
class Student extends Person {
constructor(name, age, school) {
// calling the superclass constructor
super(name, age);
this.school = school;
}
}
eve instanceof Student
возвращен true
. См. stackoverflow.com/questions/35537995/… для объяснения. Также, когда вы говорите which is, at the moment, nothing
, о чем вы говорите? Каждая функция имеет прототип, поэтому, если я проверю Student.prototype
, это что-то.
– Aseem Bansal
27 February 2016 в 02:28
Object.create(Person.prototype)
, Student.prototype
пуст. Поэтому, если мы запишем eve.species
, он не будет делегировать должным образом своему суперклассу Person, и он не будет записывать 'human'
. Предположительно, мы хотим, чтобы каждый подкласс наследовал от своего прототипа, а также прототипа супер.
– bthehuman
27 February 2016 в 18:49
which is, at the moment, nothing
я имел в виду, что объект Student.prototype
пуст.
– bthehuman
27 February 2016 в 18:55
Student.prototype
- Object.create(Person.prototype)
- который, если вы помните, так же, как все экземпляры Person настроены на делегирование Person.prototype
- поиск свойства на экземпляре Student
делегирует только только i> Student.prototype
. Таким образом, eve.species
не сможет выполнить поиск. Если мы его назначим, Student.prototype
сам делегирует Person.prototype
, и поиск eve.species
вернет human
.
– bthehuman
27 February 2016 в 19:02
instance
, «подкласс» Constructor , он будет точным. & quot; i> Нет, instanceof
не использует constructor
. "Однако, если мы посмотрим на .prototype.constructor, он все равно укажет на Person & quot; i> Nope, это будет Student
. Я не понимаю смысла этого примера. Вызов функции в конструкторе не является наследованием. "В ES6 конструктор теперь является фактической функцией вместо ссылки на функцию" i> Что это?
– Felix Kling
27 February 2016 в 19:03
Это огромная ошибка, если вы написали
Student.prototype.constructor = Student;
, но тогда, если бы существовал Учитель, чей прототип был также Person, и вы написали
Teacher.prototype.constructor = Teacher;
, тогда Студент конструктор теперь учитель!
Редактировать: вы можете избежать этого, убедившись, что вы установили прототипы ученика и учителя, используя новые экземпляры класса Person, созданные с использованием Object.create, как в примере Mozilla.
Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
Student.prototype = Object.create(...)
предполагается в этом вопросе. Этот ответ не добавляет ничего, кроме возможной путаницы.
– André Chalella
6 November 2015 в 22:14
Object.create(...)
используется в статье MDN, которая породила вопрос, но не в самом вопросе. Я уверен, что многие люди не щелкают.
– Alex Ross
9 January 2016 в 03:10
Для простой функции конструктора:
function Person(){
this.name = 'test';
}
console.log(Person.prototype.constructor) // function Person(){...}
Person.prototype = { //constructor in this case is Object
sayName: function(){
return this.name;
}
}
var person = new Person();
console.log(person instanceof Person); //true
console.log(person.sayName()); //test
console.log(Person.prototype.constructor) // function Object(){...}
По умолчанию (из спецификации https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects / Object / constructor ), все прототипы автоматически получают свойство, называемое конструктором, которое указывает на функцию, на которой это свойство. В зависимости от конструктора к прототипу могут быть добавлены другие свойства и методы, которые не являются очень распространенной практикой, но все же разрешены для расширений.
Итак, просто отвечая: нам нужно убедиться, что значение в prototype.constructor правильно установлено, поскольку предполагается, что спецификация будет.
Должны ли мы всегда правильно устанавливать это стоимость? Это помогает при отладке и делает внутреннюю структуру совместимой со спецификацией. Мы должны определенно, когда наш API будет использоваться третьими сторонами, но не совсем, когда код будет окончательно выполнен во время выполнения.
Это необходимо, если вам нужна альтернатива toString
без monkeypatching:
//Local
foo = [];
foo.toUpperCase = String(foo).toUpperCase;
foo.push("a");
foo.toUpperCase();
//Global
foo = [];
window.toUpperCase = function (obj) {return String(obj).toUpperCase();}
foo.push("a");
toUpperCase(foo);
//Prototype
foo = [];
Array.prototype.toUpperCase = String.prototype.toUpperCase;
foo.push("a");
foo.toUpperCase();
//toString alternative via Prototype constructor
foo = [];
Array.prototype.constructor = String.prototype.toUpperCase;
foo.push("a,b");
foo.constructor();
//toString override
var foo = [];
foo.push("a");
var bar = String(foo);
foo.toString = function() { return bar.toUpperCase(); }
foo.toString();
//Object prototype as a function
Math.prototype = function(char){return Math.prototype[char]};
Math.prototype.constructor = function()
{
var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
while (i < max)
{
Math.prototype[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);
i = i + 1;
}
}
Math.prototype.constructor();
console.log(Math.prototype("a") );
console.log(Math.prototype["a"] );
console.log(Math.prototype("a") === Math.prototype["a"]);
Это необязательно. Это всего лишь одна из многих традиций, чемпионы ООП, чтобы попытаться превратить прототипное наследование JavaScript в классическое наследование. Единственное, что делает следующий
Student.prototype.constructor = Student;
, это то, что теперь у вас есть ссылка на текущий «конструктор».
В ответе Уэйн, который был отмечен как правильный, вы можете сделать то же самое, что следующий код делает
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
с кодом ниже (просто замените this.constructor на Person)
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new Person(this.name);
};
Слава Богу, что с ES6 classic Натураторы-пуристы могут использовать собственные операторы языка, такие как класс, extends и super, и нам не нужно видеть, как исправления prototype.constructor и родительские рефери.
Я бы не согласился. Нет необходимости устанавливать прототип. Возьмите тот же самый код, но удалите строку prototype.constructor. Что-нибудь изменилось? Нет. Теперь сделайте следующие изменения:
Person = function () {
this.favoriteColor = 'black';
}
Student = function () {
Person.call(this);
this.favoriteColor = 'blue';
}
и в конце тестового кода ...
alert(student1.favoriteColor);
Цвет будет синим.
Изменение в prototype.constructor, по моему опыту, мало что делает, если вы не делаете очень конкретные, очень сложные вещи, которые, вероятно, не являются хорошей практикой:)
Редактировать: После того, как несколько раз выкарабкались по сети и делали некоторые эксперименты, похоже, что люди устанавливают конструктор так, что он «выглядит» как вещь, которая строится с помощью «нового». Думаю, я бы сказал, что проблема в том, что javascript - это прототип языка - нет такой вещи, как наследование. Но большинство программистов исходят из программирования, который накладывает наследование как «путь». Поэтому мы придумываем всевозможные вещи, чтобы попытаться сделать этот прототип языка «классическим» языком, например, расширением «классов». Действительно, в примере, который они дали, новый студент - это человек - он не «распространяется» от другого ученика. Студент - это все о человеке, и независимо от того, кем является ученик, также. Расширьте учащегося, и все, что вы расширили, - это студент в глубине души, но настроен в соответствии с вашими потребностями.
Крокфорд немного сумасшедший и переусердствующий, но немного почитал некоторые вещи, которые он написал .. это заставит вас взглянуть на этот материал совсем по-другому.
Выполняет ли это какую-либо важную цель?
Да и нет.
В ES5 и ранее JavaScript сам не использовал
constructor
для что-нибудь. Он определил, что объект по умолчанию для свойства функцииprototype
будет иметь его и что он будет ссылаться на функцию, а - на .Это изменилось в ES2015 (ES6), который начал использовать его по отношению к иерархиям наследования. Например,
Promise#then
использует свойствоconstructor
обещанного вами обещания (через SpeciesConstructor ) при создании нового обещания для возврата. Он также участвует в подтипировании массивов (через ArraySpeciesCreate ).Вне самого языка иногда люди использовали его при попытке создания общих функций «клонировать» или просто вообще, когда они хотел ссылаться на то, что, по их мнению, будет функцией конструктора объекта. Мой опыт в том, что использование его редко, но иногда люди его используют.
Можно ли его опустить?
Это по умолчанию, вы нужно вернуть его, когда вы замените объект на свойство функции
prototype
:Student.prototype = Object.create(Person.prototype);
Если вы этого не сделаете:
Student.prototype.constructor = Student;
... тогда
Student.prototype.constructor
наследует отPerson.prototype
, который (предположительно) имеетconstructor = Person
. Так что это вводит в заблуждение. И, конечно, если вы подклассифицируете что-то, что использует его (например,Promise
илиArray
), а не используяclass
¹ (который обрабатывает это для вас), вам нужно убедиться, что вы правильно настроили его. Итак, в основном: это хорошая идея.Все в порядке, если ничто в коде (или используемом вами библиотечном коде) не использует его. Я всегда гарантировал, что он правильно подключен.
Конечно, с ключевым словом ES2015 (иначе ES6)
class
, большую часть времени мы бы его использовали, нам не нужно потому что он обрабатывается для нас, когда мы делаемclass Student extends Person { }
¹ "... если вы подклассифицируете что-то, что его использует (например,
Promise
илиArray
), и не используяclass
... " & nbsp; & mdash; Это возможно сделать это, но это настоящая боль (и немного глупо). Вы должны использоватьReflect.construct
.
Не нужно использовать «классы классов» или «Новый» в наши дни. Используйте литералы объектов.
Прототип объекта уже является «классом». Когда вы определяете литерал объекта, он уже является экземпляром прототипа Object. Они также могут выступать в качестве прототипа другого объекта и т. Д.
const Person = {
name: '[Person.name]',
greeting: function() {
console.log( `My name is ${ this.name || '[Name not assigned]' }` );
}
};
// Person.greeting = function() {...} // or define outside the obj if you must
// Object.create version
const john = Object.create( Person );
john.name = 'John';
console.log( john.name ); // John
john.greeting(); // My name is John
// Define new greeting method
john.greeting = function() {
console.log( `Hi, my name is ${ this.name }` )
};
john.greeting(); // Hi, my name is John
// Object.assign version
const jane = Object.assign( Person, { name: 'Jane' } );
console.log( jane.name ); // Jane
// Original greeting
jane.greeting(); // My name is Jane
// Original Person obj is unaffected
console.log( Person.name ); // [Person.name]
console.log( Person.greeting() ); // My name is [Person.name]
Объектно-ориентированное на основе класса Языки, такие как Java и C ++, основаны на концепции двух разных сущностей: классов и экземпляров.
...
Язык, на основе прототипа, такой как JavaScript, не делайте этого различия: у него просто есть объекты. Язык, основанный на прототипе, имеет понятие прототипического объекта, объект, используемый в качестве шаблона, из которого можно получить начальные свойства для нового объекта. Любой объект может определять свои собственные свойства либо при его создании, либо во время выполнения. Кроме того, любой объект может быть связан как прототип для другого объекта, позволяя второму объекту совместно использовать свойства первого объекта
Получил хороший пример кода, почему действительно необходимо установить конструктор прототипов ..
function CarFactory(name){
this.name=name;
}
CarFactory.prototype.CreateNewCar = function(){
return new this.constructor("New Car "+ this.name);
}
CarFactory.prototype.toString=function(){
return 'Car Factory ' + this.name;
}
AudiFactory.prototype = new CarFactory(); // Here's where the inheritance occurs
AudiFactory.prototype.constructor=AudiFactory; // Otherwise instances of Audi would have a constructor of Car
function AudiFactory(name){
this.name=name;
}
AudiFactory.prototype.toString=function(){
return 'Audi Factory ' + this.name;
}
var myAudiFactory = new AudiFactory('');
alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');
var newCar = myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory
alert(newCar);
/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class ).. Dont we want our new car from Audi factory ????
*/
createNewCar
создает фабрики !? Также похоже, что он должен был использоваться как var audiFactory = new CarFactory("Audi")
, а не использовать наследование.
– Bergi
15 June 2015 в 02:17
this.constructor
внутри, поэтому неудивительно, что он должен быть установлен. У вас есть какой-либо пример без него?
– Dmitri Zaitsev
23 May 2016 в 11:53
constructor
не имеет особого значения в JS, поэтому вы можете также назвать егоbananashake
. Единственное различие заключается в том, что двигатель автоматически инициализируетconstructor
наf.prototype
всякий раз, когда вы объявляете функциюf
. Однако он может быть перезаписан в любое время. – user123444555621 12 December 2011 в 23:11constructor
, означает, что имеет i> особый смысл в JS, в значительной степени по определению. – Wayne Burkett 19 January 2012 в 17:29return new this.constructor(this.name);
вместоreturn new Person(this.name);
. Поскольку функцияthis.constructor
является функциейStudent
(поскольку вы устанавливаете ее с помощьюStudent.prototype.constructor = Student;
), функцияcopy
завершает вызов функцииStudent
. Я не уверен, что вы сделали с комментарием//just as bad
. – CEGRD 8 December 2012 в 01:16Student
добавил дополнительный аргумент, например:Student(name, id)
? Нужно ли нам переопределить функциюcopy
, вызвав из нее версиюPerson
, а затем также скопировать дополнительное свойствоid
? – snapfractalpop 7 March 2014 в 04:50