В чем отличие метода класса от функции свойства от функции стрелки свойства в машинописи? - PullRequest
6 голосов
/ 09 мая 2019

Мне было интересно - в чем разница между методом класса, свойством класса, которое является функцией, и свойством класса, которое является функцией стрелки?Ведет ли себя ключевое слово this по-разному в разных вариантах метода?

class Greeter {

  constructor() {
    this.greet();
    this.greet2();
    this.greet3();
  }

  greet() {
    console.log('greet1', this);
  }

  greet2 = () => {
    console.log('greet2', this);
  }

  greet3 = function() {
    console.log('greet3', this);
  }
}


let bla = new Greeter();

Редактировать: вывод javascript из скомпилированной машинописи:

var Greeter = /** @class */ (function () {
function Greeter() {
    var _this = this;
    this.greet2 = function () {
        console.log('greet2', _this);
    };
    this.greet3 = function () {
        console.log('greet3', this);
    };
    this.greet();
    this.greet2();
    this.greet3();
}
Greeter.prototype.greet = function () {
    console.log('greet1', this);
};
return Greeter;
}());
var bla = new Greeter();

Моя версия TypeScript 3.4.4

Ответы [ 2 ]

3 голосов
/ 09 мая 2019

Есть различия между всеми 3 версиями.Это различие в 3 областях:

  1. Кто this во время выполнения
  2. Где назначена функция
  3. Каков тип this в машинописи.

Давайте начнем с того, где они работают точно так же.Рассмотрим этот класс с полем класса:

class Greeter {
  constructor(private x: string) {
  }
  greet() {
    console.log('greet1', this.x);
  }

  greet2 = () => {
    console.log('greet2', this.x);
  }

  greet3 = function () {    
    // this is typed as any 
    console.log('greet3', this.x);
  }
}

let bla = new Greeter(" me");

С этим классом все 3 вызова функций будут печататься, как и ожидалось: 'greet* me' при вызове bla

bla.greet()
bla.greet2()
bla.greet3()

Кто это во время выполнения

Функции стрелок захватывают this из контекста объявления, поэтому this в greet2 всегда гарантированно будет экземпляром класса, который создал эту функцию.Другие версии (метод и функция) не дают таких гарантий.

Так что в этом коде не все 3 печатают один и тот же текст:

function call(fn: () => void) {
  fn();
}

call(bla.greet) // greet1 undefined 
call(bla.greet2) //greet2 me
call(bla.greet3) // greet3 undefined

Это особенно важно при передаче функции какобработчик событий для другого компонента.

Если функция назначена

Методы класса (например, greet) назначаются в прототипе, инициализация поля (например,greet2 и greet3) назначаются в конструкторе.Это означает, что greet2 и greet3 будут иметь больший объем памяти, поскольку они требуют выделения нового замыкания каждый раз, когда создается экземпляр Greeter.

Какого типа это в машинописи.

Typescript будет печатать this как экземпляр Greeter как в методе (greet), так и в функции стрелки (greet2), но будет печатать this как любой вgreet3.Это приведет к ошибке, если вы попытаетесь использовать this в greet3 в noImplictAny

Когда их использовать

  1. Используйте синтаксис метода, если эта функция не будет передана в качестве обработчика события другому компоненту (если только вы не используете bind или что-то еще, чтобы this оставался экземпляром класса)

  2. Используйте синтаксис функции стрелки, когда ваша функция будет передана другим компонентам, и вам нужен доступ к this внутри функции.

  3. Не могу придумать хороший вариант использованиядля этого вообще избегайте.

1 голос
/ 09 мая 2019

this разница ключевых слов:

Выше все три имеют одинаковые this, но вы увидите разницу, когда передадите метод другим функциям.

class Greeter {
  constructor() {
  }
  greet() {
    console.log(this);
  }

  greet2 = () => {
    console.log(this);
  }

  greet3 = function() {
    console.log(this);
  }
}


let bla = new Greeter();
function wrapper(f){
  f();
}
wrapper(bla.greet) //undefined
wrapper(bla.greet2) //Greeter 
wrapper(bla.greet3) //undefined

Но есть еще одно отличие: первый метод использует prototype из class, а два других - нет.Они являются методом экземпляра объекта.

class Greeter {
  constructor() {
  }
  greet() {
    console.log('greet1', this);
  }

  greet2 = () => {
    console.log('greet2', this);
  }

  greet3 = function() {
    console.log('greet3', this);
  }
}

let bla = new Greeter();
console.log(Object.getOwnPropertyNames(Greeter.prototype))

Если у меня в классе -> str = "my string"; и во всех 3 методах я могу сказать console.log(this.str), и он выводит «мою строку».Но мне интересно - действительно ли это одно и то же

Нет, это не одно и то же.Как я уже говорил, greet2 и greet3 не будут включены в Greeter.prototype, вместо этого они будут в самом экземпляре.Это означает, что если вы создадите 1000 экземпляров Greeter, их будет 1000 различных методов (greet2 и greet3), сохраненных в памяти для 1000 различных экземпляров.Но будет единый метод greet для всех экземпляров.

См. Фрагмент ниже с двумя экземплярами Greeter()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...