在下面的代码中,我创建了一个名为Slates的对象,并为其添加了一些函数。
"use strict";
function Slates() {
this.canvas;
this.ctx;
this.width;
this.height;
}
Slates.prototype.init = function() {
this.canvas = document.getElementById("canvas");
this.ctx = this.canvas.getContext("2d");
this.resize();
};
Slates.prototype.resize = function() {
this.width = window.innerWidth;
this.height = window.innerHeight;
this.canvas.width = this.width;
this.canvas.height = this.height;
console.log("resizing");
};
Slates.prototype.render = function() {
this.ctx.clearRect(0, 0, this.width, this.height);
this.ctx.fillStyle = "rgba(0,0,100,.5)";
this.ctx.fillRect(0, 0, this.width, this.height);
};
window.onload = function() {
var slates = new Slates();
slates.init();
window.onresize = slates.resize;
window.requestAnimationFrame(slates.render);
};
问题是当我将函数slates.render
作为回调函数传递给函数时
例如window.requestAnimationFrame
this
关键字引用window
对象而不是Slates
实例,因此我得到了
未捕获的TypeError:无法读取未定义的属性'clearRect'
如何解决此问题,以便它引用我的对象的实例?这是指向codepen的链接。
答案 0 :(得分:5)
例如,您可以使用Function.prototype.bind
,它将返回一个函数,但该值保持不变:
window.requestAnimationFrame(slates.render.bind(slates))
或将此调用包装到另一个函数中:
window.requestAnimationFrame(function() { slates.render() })
答案 1 :(得分:4)
L值和r值
在编程中,有两种表达式:l值(" l"左)和r值(" r"右)。 l值可以分配给,而r值是我们可以分配到其他东西的东西。例如,假设我们有以下HTML
<div id="my-div" style="background-color: blue;"></div>
以及以下JS:
var myDiv = document.getElementById("my-div");
然后我们可以使用myDiv.style.backgroundColor
作为l值(即为其赋值)
myDiv.style.backgroundColor = "black"; // myDiv.style.backgroundColor is an l-value
或作为r值(将其指定/传递给其他人):
var newDiv = document.createElement("div");
newDiv.style.backgroundColor = myDiv.style.backgroundColor // now both are "black"
当我们有一个&#34;点链&#34;与上面的myDiv.style.backgroundColor
一样,它在表达式中的值等于链中 last 链接的值。因此,当我们在表达式中使用myDiv.style.backgroundColor
时,我们只是使用backgroundColor
。将此分配给新元素(例如newDiv
)不会在myDiv.style
和newDiv.style
之间创建任何类型的链接。
同样,当您将slates.render
分配给window.onload
时,您只需分配render
功能 - 不再与slates
建立任何关联。
您可能想知道......好吧,但不应该this
成为slates
的链接吗?好吧,想想看,你从未在this
函数中为render
分配任何内容,对吗?你刚才用过它。语言引擎为您提供this
的值。 JS中的普通变量/属性,例如slates
或slates.render
可以是l值或 r值,具体取决于上下文。但是,this
关键字仅作为r-value
有效。如果我们尝试直接分配到this
关键字,我们会看到如下错误:
错误说明了我们已经知道的事情 - this
不是有效的l值。那么它从哪里获得价值?
这让我们讨论了JS的范围,以及为什么this
与其他变量的重要区别。
词汇范围(变量如何工作)
在JS中,变量是词法范围,这意味着函数调用中变量的值不是由调用的范围决定的,而是由函数的范围决定的。定义。
var x = {name: "outer"};
function y() {
var x = {name: "inner"};
return function() {
console.log(x.name);
};
}
var z = y(); // z is a function here
z(); // logs "inner", not "outer" because JS is lexically scoped
在这个例子中,我们定义了一个函数y
,它本身返回一个被赋值给z
的函数。当z
执行时,它会console.log(x.name)
,但问题是...... x
应该引用哪个?如果JS是动态范围的,那么我们可能期望它打印&#34;外部&#34;,因为这是在同一&#34;范围&#34;中定义的x
。作为变量z
。但它并没有。它打印&#34;内部&#34;因为JS是词法范围的,这意味着x
等于定义时范围内的x
。
动态范围(this
关键字如何运作)
如果this
关键字的词法范围与JS中的其他变量一样,那么您帖子中的代码就可以了。但事实并非如此。虽然JS中的常规变量是词法范围的,但this
关键字不是词法范围。它具有动态范围。意味着它的值不取决于定义函数的上下文,而是取决于函数执行的上下文。你在什么上下文中执行它?窗口对象!如果您想象加载事件被调用为window.onload()
,那么这将变得更加清晰。
事实上,这就是我们想要的行为。因为如果您在按钮上设置了单击事件,那么您可能会执行以下操作:
var inputBox = document.getElementById("my-input-box");
function myFunc() {
alert("Uh oh, you typed " + this.value);
}
inputBox.onchange = myFunc;
这是相同的行为。有些令人困惑的是,window
也是默认 this
值。 (更多内容见下文)。
除了一些例外情况 - 包括new
运算符和bind
,call
和apply
方法 - 传递{的唯一方法{1}} JS中的函数值是在对象上调用它,如下所示:
this
类比
这是一个真实世界的比喻。说你有一个球。在球上定义一个名为object.method(); // this now refers to object within `method`
的方法。该方法定义为kick
。 &#34;它&#34;该短语与JavaScript中的move your leg to exert force on *it*
非常相似。它的含义取决于使用它的上下文,或者在这种情况下,函数被称为。没有球,this
仍有意义,但它引用的kick
未定义。我踢的不明。
当你将一个方法传递给window.onload处理程序时,你只传递it
函数,它本身与kick
没有任何联系。只有当我们将其称为ball
ball.kick()
时才会kick
获得this
值ball
。在现实生活中,没有&#34;默认&#34;它的价值。如果我们不确定,我们不会认为它指的是地球。然而,与现实生活不同,JS 具有默认的it
/ this
值。该默认值是window
对象。试图说明默认值为window
的原因可能是徒劳的 - 它只是一个实施决策。
一个解决方案
在您的情况下,最简单的方法是使用bind
函数。 bind
函数(在JS的最新版本中引入)允许您将某个this
值绑定到函数,返回新的this
绑定函数作为其返回值。例如,如果你有一个像
Ball.prototype.kick = function() {
this.touch();
this.move();
console.log(this);
}
你做了
myBall = new Ball();
kickBall = ball.kick.bind(myBall); // returns a new function, doesn't affect the original ball.kick
然后,您可以在没有正常kickBall
语法的情况下拨打ball.kick()
,其this
值将为myBall
。
kickBall(); // myBall
window.setTimeout(myBall.kick, 1000); // window
更简单的解决方案
更简单(也更常见)的方法是只分配一个自己调用slates.render()
到window.onload
的函数,如下所示:
window.onload = function() {
slates.render(); // this works because doing obj.method() passes obj as a this value to method
};
进一步阅读
这里有一篇关于this
在JS中的含义的更好的文章:https://stackoverflow.com/a/3127440/2407870