我在这里有以下代码:
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不高兴。我不是要从文件中读取内容,但不会将字符串作为参数。
答案 0 :(得分:2)
将其更改为:
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值;箭头函数遵循正常的变量查找规则。因此,在搜索当前范围中不存在的对象时,箭头功能最终从其所包含的范围中找到了此对象。
如果您将方法声明为箭头函数,那么箭头函数自动绑定也将很容易解决这些问题。 (这是实验性的,可能需要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);
或者,作为命令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));
希望这会有所帮助。如有任何疑问,很高兴对其进行更新。
答案 1 :(得分:0)
您可以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
关键字设置为所提供的值,并在调用新函数时提供给定的参数序列。 / p>
因此,在bind
方法内部未使用this
printColor
是未定义的,通过使用bind
我们只是告诉printColor
方法{{1} }这里应引用this
类,以便我们可以访问ColorPrinter
,