Зачем использовать это ключевое слово в функции конструктора

сравнить код 1 и код 2, какой из них правильный?

function Rectangle(height, width) {
 this.height = height;
 this.width = width;
 this.calcArea = function() { // why use this here?
 return this.height * this.width;
 };

}

код 2 Я подумал, что это нормально:

function Rectangle(height, width) {
 this.height = height;
 this.width = width;
 calcArea = function() {
 return this.height * this.width;
 };

 }
4 ответа

Если вы не используете this, calcArea не будет доступна через объект Rectangle. Когда ты говоришь,

this.calcArea = function () ...

вы создаете новую переменную в текущем объекте (this), и функция будет назначена ему, чтобы объект имел доступ к ней.

Попробуйте эти утверждения, с этим и без this. Вы поймете лучше.

var a = new Rectangle(1, 2);
console.log(a.calcArea());


какой из них правильный?

Это зависит от того, как вы смотрите "правильно":

  • Неправильно ли будет выполняться какая-либо декларация?
    • Нет, оба действительны JavaScript.
  • Какой из них будет вычислять calcArea?
    • Код 1 будет правильно вычислять его, а код 2 не создает функцию-член класса Rectangle но вы можете правильно вычислить его с небольшим перенаправлением рекламы. Смотри ниже.
  • Является ли одна хорошая практика для создания классов?
    • Нет, ни один из них. См. Внизу.

Код 1 - calcArea()

Если вы создадите новый экземпляр Rectangle в коде 1, тогда:

function Rectangle(height, width) {
 this.height = height;
 this.width = width;
 this.calcArea = function() { // why use this here?
 return this.height * this.width;
 }; 
}

var rect = new Rectangle( 3, 4 );
console.log( rect.calcArea() );

Будет правильно выводить 12

Код 2 - calcArea()

Если вы создадите новый экземпляр Rectangle в коде 2, тогда:

function Rectangle(height, width) {
 this.height = height;
 this.width = width;
 calcArea = function() {
 return this.height * this.width;
 };
}

var rect = new Rectangle( 3, 4 );
console.log( rect.calcArea() );

TypeError: rect.calcArea is not a function

calcArea вместо этого привязана к глобальному охвату, поэтому мы можем:

console.log(calcArea());

calcArea NaN как calcArea в части глобальной области, поэтому не знает ни одного экземпляра класса Rectangle а глобальная область не имеет атрибута height или width.

Если мы это сделаем:

var rect = new Rectangle( 3, 4 );
width = 7; // Set in the global scope.
height = 10; // Set in the global scope.
console.log( calcArea() );

Затем он вернет 70 (а не 12 так как в пределах calcArea() this ссылается на глобальную область, а не на объект rect).

Если мы изменим то, что this означает, используя .call() чтобы вызвать функцию:

var rect = new Rectangle( 3, 4 );
width = 7; // Set in the global scope.
height = 10; // Set in the global scope.
console.log( calcArea.call( rect ) );

Затем он выведет 12 (поскольку теперь this относится к объекту rect а не к глобальной области).

Вероятно, вы не хотите делать это каждый раз, когда хотите использовать calcArea().

Почему код 1 не является оптимальным

Код 1 будет работать, но не является оптимальным решением, потому что каждый раз, когда вы создаете новый объект Rectangle он будет создавать атрибут calcArea этого объекта, который является другой функцией для любых атрибутов calcArea любого другого объекта Rectangle.

Вы можете увидеть это, если вы это сделаете:

function Rectangle(height, width) {
 this.height = height;
 this.width = width;
 this.calcArea = function() { // why use this here?
 return this.height * this.width;
 }; 
}

var r1 = new Rectangle( 3, 4 ),
 r2 = new Rectangle( 6, 7 );

console.log( r1.calcArea.toString() === r2.calcArea.toString() ); // Line 1
console.log( r1.calcArea === r2.calcArea ); // Line 2

Который будет выдавать true при тестировании строкового представления функций, идентичных, но false при проверке идентичности функций.

Что это значит? Если вы создадите 10 000 экземпляров Rectangle тогда у вас будет также 10 000 различных экземпляров атрибута calcArea и каждая копия потребует дополнительной памяти (плюс время для выделения этой памяти и сбора мусора в конце).

Что лучше?

function Rectangle(height, width) {
 this.setHeight( height );
 this.setWidth( width );
}
Rectangle.prototype.setHeight = function( height ){ this.height = height; }
Rectangle.prototype.setWidth = function( width ){ this.width = width; }
Rectangle.prototype.calcArea = function(){ return this.height * this.width; }

Тогда, если вы это сделаете:

var r1 = new Rectangle( 3, 4 ),
 r2 = new Rectangle( 6, 7 );

console.log( r1.calcArea.toString() === r2.calcArea.toString() ); // Line 1
console.log( r1.calcArea === r2.calcArea ); // Line 2

Он вернет true для обоих - это означает, что r1.calcArea и r2.calcArea относятся к одинаковой функции и независимо от количества экземпляров Rectangle.


Вторая версия будет устанавливать глобальную переменную calcArea, чтобы делать вещи, специфичные для вашего объекта, когда создается экземпляр вашего объекта. Для этого необходимо установить свойства вашего конкретного объекта.


Когда вы предикаете свои методы и свойства с помощью "this" в своем конструкторе, они позволяют создавать любые новые объекты с этим конструктором для использования этих свойств и методов, и эти свойства и методы указывают на вновь созданный объект.

Если вы создаете новый объект на основе своей версии конструктора Rectangle, который не использует "this" в качестве предисловия к calcArea и посмотрите на отладчик chrome, вы получите следующую ошибку:

Object #<rectangle> has no method 'calcArea' 
</rectangle>

Короче говоря, это просто не признано.

Другими аспектами не использования "этого" является то, что метод становится "глобальным".

Вот пример кода для демонстрации:

function Rectangle(height, width) {
 this.height = height;
 this.width = width;
 calcArea = function() { // Not prefaced with 'this'
 return 'hello world';
 };

}


var aNewObj = new Rectangle(10,10);

console.log(calcArea()) // Notice this is not aNewObj.calcArea() ...just calcArea() but its still accessible.

licensed under cc by-sa 3.0 with attribution.