提问人:rmeador 提问时间:9/25/2008 最后编辑:Willem van der Veenrmeador 更新时间:12/6/2022 访问量:101451
“this”关键字如何在函数中工作?
How does "this" keyword work within a function?
问:
我刚刚在 JavaScript 中遇到了一个有趣的情况。我有一个类,其方法使用对象文字表示法定义多个对象。在这些对象中,指针正在使用。从程序的行为中,我推断出指针指的是调用该方法的类,而不是由文字创建的对象。this
this
这似乎是武断的,尽管这是我期望它的工作方式。这是定义的行为吗?跨浏览器安全吗?是否有任何理由可以解释为什么它超出了“规范所说的”(例如,它是某些更广泛的设计决策/理念的结果)?精简代码示例:
// inside class definition, itself an object literal, we have this function:
onRender: function() {
this.menuItems = this.menuItems.concat([
{
text: 'Group by Module',
rptletdiv: this
},
{
text: 'Group by Status',
rptletdiv: this
}]);
// etc
}
答:
这是定义的行为吗?是吗 跨浏览器安全?
是的。是的。
有什么理由可以解释为什么 事情就是这样......
的含义很简单:this
- 如果在构造函数中使用,并且该函数是使用 关键字调用的,则指将要创建的对象。 即使在公共方法中,也将继续表示对象。
this
new
this
this
- 如果在其他任何地方使用,包括嵌套的受保护函数,则它指的是全局范围(在浏览器的情况下是窗口对象)。
this
第二种情况显然是设计缺陷,但通过使用闭包可以很容易地解决它。
在这种情况下,内部函数绑定到全局对象,而不是外部函数的变量。
这就是语言的设计方式。this
this
参见 Douglas Crockford 的“JavaScript: The Good Parts”一文。
从我的另一篇帖子中蚕食,这里比你想知道的要多。
在我开始之前,这里有一件关于 Javascript 的最重要的事情要记住,当它没有意义时,要对自己重复一遍。Javascript 没有类(ES6 是语法糖)。如果某样东西看起来像一个类,这是一个聪明的伎俩。Javascript 有对象和函数。(这不是 100% 准确的,函数只是对象,但有时将它们视为单独的事物会有所帮助)class
this 变量附加到函数。每当调用函数时,都会为其指定一个值,具体取决于调用函数的方式。这通常称为调用模式。
在 javascript 中调用函数有四种方法。您可以将函数作为方法、函数、构造函数和 apply 调用。
作为一种方法
方法是附加到对象的函数
var foo = {};
foo.someMethod = function(){
alert(this);
}
当作为方法调用时,这将绑定到函数/方法所属的对象。在此示例中,这将绑定到 foo。
作为函数
如果你有一个独立的函数,这个变量将绑定到“全局”对象,几乎总是浏览器上下文中的窗口对象。
var foo = function(){
alert(this);
}
foo();
这可能是让你绊倒的原因,但不要感到难过。许多人认为这是一个糟糕的设计决策。由于回调是作为函数而不是方法调用的,因此您会看到看似不一致的行为。
许多人通过做这样的事情来解决这个问题,嗯,这个
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
您定义了一个指向此的变量。闭包(一个独立的主题)保留了它,所以如果你调用 bar 作为回调,它仍然有一个引用。
注意:In 模式如果用作函数,则不绑定到全局。(它是)。use strict
this
undefined
作为构造函数
还可以调用函数作为构造函数。根据你使用的命名约定 (TestObject),这也可能是你正在做的事情,也是让你绊倒的原因。
使用 new 关键字将函数作为构造函数调用。
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
当作为构造函数调用时,将创建一个新对象,并将其绑定到该对象。同样,如果你有内部函数,并且它们被用作回调,你将把它们作为函数调用,这将绑定到全局对象。使用那个 var that = this trick/pattern。
有些人认为 constructor/new 关键字是扔给 Java/传统 OOP 程序员的骨头,作为创建类似于类的东西的一种方式。
使用 Apply 方法
最后,每个函数都有一个名为“apply”的方法(是的,函数是 Javascript 中的对象)。Apply 允许您确定其值,还允许您传入参数数组。这是一个无用的例子。
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
评论
this
this.confusing = 'hell yeah';
var confusing = 'hell yeah';
myObject.confusing
this
function Foo(thought){ this.confusing = thought; }
var myObject = new Foo("hell yeah");
函数调用
函数只是对象的一种类型。
所有 Function 对象都具有 call 和 apply 方法,这些方法执行调用它们的 Function 对象。
调用时,这些方法的第一个参数指定在执行函数期间关键字将引用的对象 - 如果它是 或 ,则全局对象 , 用于 。this
null
undefined
window
this
因此,调用函数...
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
...带括号的 - - 等价于 或 ,这实际上与 或 相同。foo()
foo.call(undefined)
foo.apply(undefined)
foo.call(window)
foo.apply(window)
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
其他参数 to 作为参数传递给函数调用,而单个附加参数 to 可以将函数调用的参数指定为类似 Array 的对象。call
apply
因此,等价于 或 。foo(1, 2, 3)
foo.call(null, 1, 2, 3)
foo.apply(null, [1, 2, 3])
>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"
如果函数是对象的属性...
var obj =
{
whereAmI: "obj",
foo: foo
};
...通过对象访问对 Function 的引用并用括号 - - 调用它等效于 或 。obj.foo()
foo.call(obj)
foo.apply(obj)
但是,作为对象属性保存的函数不会“绑定”到这些对象。正如您在上面的定义中看到的,由于 Functions 只是 Object 的一种类型,因此可以引用它们(因此可以通过引用传递给 Function 调用或通过引用从 Function 调用返回)。传递对函数的引用时,不会携带有关其传递位置的其他信息,这就是发生以下情况的原因:obj
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
对函数引用 的调用 ,不会为调用提供任何上下文,因此它实际上与 相同,因此最终引用了 。如果我们想知道它属于 ,我们需要在调用时以某种方式提供该信息,这是 or 和闭包的第一个参数发挥作用的地方。baz
baz.call(undefined)
this
window
baz
obj
baz
call
apply
示波器链
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
执行函数时,它会创建一个新作用域,并具有对任何封闭作用域的引用。在上面的示例中创建匿名函数时,它引用了创建它的作用域,即 的作用域。这称为“闭包”。bind
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
当您尝试访问变量时,将遍历此“作用域链”以查找具有给定名称的变量 - 如果当前作用域不包含该变量,则查看链中的下一个作用域,依此类推,直到到达全局作用域。当匿名函数返回并完成执行时,匿名函数仍然具有对 的引用,因此 的作用域不会“消失”。bind
bind
bind
鉴于上述所有内容,您现在应该能够理解以下示例中作用域的工作原理,以及为什么在调用函数时,将具有特定值的函数传递到“pre-bound”周围的技术会起作用:this
>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
评论
我找到了一个关于 ECMAScript 的好教程
答:此值是与执行相关的特殊对象 上下文。因此,它可以被命名为上下文对象(即 对象,在哪个上下文中激活执行上下文)。
任何对象都可以用作上下文的此值。
a 此值是执行上下文的属性,但不是 变量对象的属性。
此功能非常重要,因为与变量相反,此值从不参与标识符解析过程。也就是说,当在代码中访问它时,它的值直接从执行上下文中获取,无需任何范围链查找。在进入上下文时,此值仅确定一次。
在全局上下文中,this 值是全局对象本身(这意味着,此处的此值等于变量 object)
对于函数上下文,每个函数调用中的此值可能不同
参考 Javascript-the-core 和 Chapter-3-this
评论
这里的所有答案都非常有帮助,但我仍然很难弄清楚在我的情况下该说什么,这涉及到对象解构。因此,我想使用我的代码的简化版本再添加一个答案,this
let testThis = {
x: 12,
y: 20,
add({ a, b, c }) {
let d = a + b + c()
console.log(d)
},
test() {
//the result is NaN
this.add({
a: this.x,
b: this.y,
c: () => {
//this here is testThis, NOT the object literal here
return this.a + this.b
},
})
},
test2() {
//64 as expected
this.add({
a: this.x,
b: this.y,
c: () => {
return this.x + this.y
},
})
},
test3() {
//NaN
this.add({
a: this.x,
b: this.y,
c: function () {
//this here is the global object
return this.x + this.y
},
})
},
}
正如这里解释的那样,Javascript - 解构对象 - “this”设置为全局或未定义,而不是对象,它实际上与对象解构无关,而是与c()的调用方式无关,但在这里不容易看穿它。
MDN 说“箭头函数表达式最适合非方法函数”,但箭头函数在这里有效。
this
在 JS 中:
有 3 种类型的函数具有不同的含义。最好通过示例来解释它们:this
- 构造 函数
// In a constructor function this refers to newly created object
// Every function can be a constructor function in JavaScript e.g.
function Dog(color){
this.color = color;
}
// constructor functions are invoked by putting new in front of the function call
const myDog = new Dog('red');
// logs Dog has color red
console.log('Dog has color ' + myDog.color);
- 正常功能或方法
// Browswer example:
console.log(this === window) // true
function myFn(){
console.log(this === window)
}
myFn(); // logs true
// The value of this depends on the context object.
// In this case the context from where the function is called is global.
// For the global context in the browser the context object is window.
const myObj = {fn: myFn}
myObj.fn() // logs false
// In this case the context from where the function is called is myObj.
// Therefore, false is logged.
myObj.fn2 = function myFn(){
console.log(this === myObj)
}
myObj.fn2() // logs true
// In this case the context from where the function is called is myObj.
// Therefore, true is logged.
- 事件侦听器
在事件处理程序的函数内部,将引用检测到事件的 DOM 元素。请参阅此问题:在事件处理程序中使用它this
评论
var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};