简体   繁体   English

无法读取未定义的属性,运行时错误

[英]Cannot read property of Undefined, Runtime Error

I have the following code here:我这里有以下代码:

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();

However when run I run this, I get the run time error of :但是,当我运行它时,我得到以下运行时错误:

"TypeError: Cannot read property 'sourceFile' of undefined" “类型错误:无法读取未定义的属性‘sourceFile’”

I don't understand why JS isn't happy.我不明白为什么 JS 不高兴。 I am not trying to read from a file, but it won't take the string as a parameter.我不是试图从文件中读取,但它不会将字符串作为参数。

Change it to:将其更改为:

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

The reason your original code doesn't work as you expected has to do with the way scope works in javascript, specifically how the value of this is bound.您的原始代码没有按预期工作的原因与作用域在 javascript 中的工作方式有关,特别是this的值是如何绑定的。

The main thing to understand is that (in general) when you invoke a function, this is set to the object on which the method was invoked .要理解的主要事情是(通常)当您调用一个函数时, this设置为调用该方法的对象

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

But if you invoke the method separately from the object, this ends up being undefined :但是,如果您与对象分开调用该方法, this最终会是undefined

const detachedFn = wookie.eatBanana;

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

And what you're doing when you pass this.printColor to forEach is passing the function itself , which ends up getting invoked without being bound to your object:当您将this.printColor传递给forEach时,您正在做的是传递函数本身,最终会被调用而不会绑定到您的对象:

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

Inside the forEach implementation it's just invoking the function it was given.forEach实现中,它只是调用给定的函数。 Effectively:有效地:

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

Defining a new function that invokes this.printColor() preserves the scope:定义一个调用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.
}

Arrow functions auto-bind to the parent scope :箭头函数自动绑定到父作用域

An arrow function does not have its own this.箭头函数没有自己的 this。 The this value of the enclosing lexical scope is used;使用封闭词法范围的 this 值; arrow functions follow the normal variable lookup rules.箭头函数遵循正常的变量查找规则。 So while searching for this which is not present in current scope, an arrow function ends up finding the this from its enclosing scope.因此,在搜索当前作用域中不存在的 this 时,箭头函数最终会从其封闭的作用域中找到 this。

Arrow function auto-binding also comes in handy to solve these problems if you declare the methods as arrow functions to begin with.如果您首先将方法声明为箭头函数,箭头函数自动绑定也可以派上用场来解决这些问题。 (This is experimental and might require the babel class properties plugin ). (这是实验性的,可能需要 babel 类属性插件)。

// declaring this method as an arrow function causes it to bind to
// the parent scope (the class instance) which means you can invoke
// it independently of the instance.

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

// now this is fine because printColor is already bound to 'this'
this.colors.forEach(this.printColor);

Or, as palaѕн pointed out , with an explicit call to bind :或者,正如 palasн指出的那样,显式调用bind

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

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

You can also accomplish this via call or apply , which both allow you to pass in a scope (although there's no reason to do so in this case).您也可以通过callapply完成此操作,这两者都允许您传入范围(尽管在这种情况下没有理由这样做)。

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

Hope this helps.希望这可以帮助。 Happy to update it if anything needs clarification.如果有任何需要澄清的地方,很高兴更新它。

You can bind the current this also like:您也可以像这样绑定当前的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();

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. bind()方法创建一个新函数,在调用this函数时,将其this关键字设置为提供的值,并在调用新函数时在任何提供的参数之前提供给定的参数序列。

So, without using bind this inside printColor method is undefined and by using bind we are just telling the printColor method that this here should refer to ColorPrinter class so that we can access this.sourceFile ,因此,如果没有在printColor方法中使用bind this是未定义的,通过使用bind我们只是告诉printColor方法this应该引用ColorPrinter类,以便我们可以访问this.sourceFile

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM