Не удается прочитать свойство Undefined, Runtime Error - PullRequest
0 голосов
/ 29 марта 2020

У меня есть следующий код здесь:

class ColorPrinter {
    constructor(sourceFile, colors) {
      this.sourceFile = sourceFile 
      this.colors = colors
    }

    showColors() {
      this.colors.forEach(this.printColor);
    }

    printColor(colorObj) {
      console.log(this.sourceFile, colorObj.name, colorObj.hex);
    }
  } 


const colors = [{name: "red", hex: "#FF0000"}, {name: "yellow", hex: "#FFFF00"}, {name: "cyan", hex: "#0000FF"}];
const cp = new ColorPrinter('colors.csv', colors);
cp.showColors();

Однако, когда я запускаю этот прогон, я получаю ошибку времени выполнения:

"Ошибка типа: Не удается прочитать свойство ' sourceFile 'of undefined "

Я не понимаю, почему JS не устраивает. Я не пытаюсь читать из файла, но он не будет принимать строку в качестве параметра.

Ответы [ 2 ]

2 голосов
/ 29 марта 2020

Измените его на:

this.colors.forEach((x) => this.printColor(x));

Причина, по которой исходный код не работает должным образом, связана с тем, как scope работает в javascript, особенно с тем, как значение this ограничено.

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

wookie.eatBanana(); // inside the eatBanana method, this === wookie

Но если вы вызываете метод отдельно от объекта, this в конечном итоге становится undefined:

const detachedFn = wookie.eatBanana;

// same function, but now this === undefined
detachedFn(); 

И что вы делаете, когда проходите this.printColor в forEach передает саму функцию , которая в конечном итоге вызывается без привязки к вашему объекту:

const detachedFn = this.printColor;
this.colors.forEach((x) => detachedFn(x)); // this === undefined inside detachedFn, because it's invoked standalone

Внутри реализации forEach она просто вызывает Функция была дана. Фактически:

// pseudocode
function forEach(fn) {
  fn(); // no reference to your class instance; fn is just a regular function.
}

Определение новой функции, которая вызывает this.printColor(), сохраняет область:

this.colors.forEach((x) => this.printColor(x));

function forEach(fn) {
  fn(); // inside this function your method is called *on your object*, preserving the 'this' binding.
}

Функции стрелок Автоматическое связывание с родительской областью :

Функция стрелки не имеет этого. Используется значение this лексической области видимости; Функции стрелок следуют нормальным правилам поиска переменных. Таким образом, при поиске этого, которого нет в текущей области видимости, функция стрелки в конечном итоге находит его из входящей в него области.

Функция автоматической привязки функции стрелки также пригодится для решения этих проблем, если вы объявите методы как функции стрелок для начала. (Это экспериментально и может потребоваться плагин свойств класса babel .) bind :

// make a copy of printColor that's explicitly bound to 'this'.
const explicitlyBoundFn = this.printColor.bind(this);

// works
this.colors.forEach(explicitlyBoundFn);

Вы также можете выполнить sh это через вызов или применить , которые оба позволяют передавать в область (хотя в этом случае нет причин делать это).

// no reason to do this, but it works.
const detachedFn = this.printColor;
this.colors.forEach((x) => detachedFn.call(this, x));

Надеюсь, это поможет. Рад обновить его, если что-то требует разъяснений.

0 голосов
/ 29 марта 2020

Вы можете связать текущий this также как:

this.colors.forEach(this.printColor.bind(this));

class ColorPrinter {
    constructor(sourceFile, colors) {
      this.sourceFile = sourceFile 
      this.colors = colors
    }

    showColors() {
      this.colors.forEach(this.printColor.bind(this));
    }

    printColor(colorObj) {
      console.log(this.sourceFile, colorObj.name, colorObj.hex);
    }
  } 


const colors = [{name: "red", hex: "#FF0000"}, {name: "yellow", hex: "#FFFF00"}, {name: "cyan", hex: "#0000FF"}];
const cp = new ColorPrinter('colors.csv', colors);
cp.showColors();

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

Итак, без использования bind this внутри printColor метод не определен, а с помощью bind мы просто сообщаем методу printColor, что this здесь должен ссылаться на ColorPrinter класс, чтобы мы могли получить доступ к this.sourceFile,

...