【问题标题】:Cannot read property of Undefined, Runtime Error无法读取未定义的属性,运行时错误
【发布时间】:2020-03-29 05:29:36
【问题描述】:

我这里有以下代码:

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

但是当我运行它时,我得到了运行时错误:

“TypeError: 无法读取未定义的属性‘sourceFile’”

我不明白为什么 JS 不高兴。我不想从文件中读取,但它不会将字符串作为参数。

【问题讨论】:

    标签: javascript undefined


    【解决方案1】:

    改成:

    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.
    }
    

    箭头函数auto-bind to the parent scope:

    箭头函数没有自己的 this。使用封闭词法范围的 this 值;箭头函数遵循正常的变量查找规则。因此,在搜索当前作用域中不存在的 this 时,箭头函数最终会从其封闭作用域中找到 this。

    如果您将方法声明为箭头函数,箭头函数自动绑定也可以方便地解决这些问题。 (这是实验性的,可能需要 babel class properties plugin)。

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

    或者,作为 palaѕн pointed out,显式调用 bind:

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

    您也可以通过call 或apply 完成此操作,它们都允许您传入一个范围(尽管在这种情况下没有理由这样做)。

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

    希望这会有所帮助。如果有任何需要澄清的地方,很高兴更新它。

    【讨论】:

    • 效果很好,为什么这里需要箭头功能?
    • 感谢您阐明 JavsScript 中的绑定和范围!
    【解决方案2】:

    你可以bind当前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 关键字设置为提供的值,并在调用新函数时在提供的任何参数之前提供给定的参数序列。

    所以,如果在 printColor 中不使用 bind this 方法是未定义的,使用 bind 我们只是告诉 printColor 方法 this 这里应该引用 ColorPrinter 类,以便我们可以访问this.sourceFile,

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-13
      • 1970-01-01
      • 1970-01-01
      • 2019-01-20
      • 1970-01-01
      • 2014-09-18
      相关资源
      最近更新 更多