“this"关键字工作在一个函数?

我刚刚在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
}
99468 次浏览

这是定义的行为吗?它是 跨浏览器的安全吗?< / p >

是的。是的。

有什么潜在的原因吗 它就是这样的…

this的含义很容易推导:

  1. 如果this在构造函数中使用,并且该函数是通过new关键字调用的,则this指的是将要创建的对象。this将继续表示对象,即使在公共方法中也是如此。
  2. 如果this在其他任何地方被使用,包括嵌套的受保护的函数,它引用全局作用域(在浏览器的情况下是窗口对象)。

第二种情况显然是一个设计缺陷,但是通过使用闭包可以很容易地解决它。

在这种情况下,内部this被绑定到全局对象,而不是外部函数的this变量。 这是语言的设计方式。

请参阅Douglas Crockford所著的“JavaScript: The Good Parts”。

从我的另一篇文章中摘录,这里有比你想知道的更多关于的信息。

在我开始之前,这里有关于Javascript最重要的事情要记住,当它没有意义的时候可以重复一遍。Javascript没有类(ES6 class语法糖)。如果某个东西看起来像一个类,这是一个聪明的技巧。Javascript有对象功能。(这不是100%准确,函数只是对象,但有时将它们视为独立的事物会有所帮助)

变量被附加到函数。无论何时调用函数,都会被赋予一个特定的值,这取决于你如何调用函数。这通常称为调用模式。

在javascript中有四种调用函数的方法。可以以方法函数构造函数应用的形式调用该函数。

作为方法

方法是附加到对象上的函数

var foo = {};
foo.someMethod = function(){
alert(this);
}

当作为方法调用时,将被绑定到函数/方法所属的对象。在本例中,this将被绑定到foo。

作为功能

如果你有一个独立的函数,变量将绑定到"global"对象,几乎总是浏览器上下文中的窗口对象。

 var foo = function(){
alert(this);
}
foo();
 

这可能就是让你抓狂的原因,但是不要感觉不好。许多人认为这是一个糟糕的设计决策。由于回调是作为函数而不是作为方法调用的,这就是为什么您会看到不一致的行为。

很多人通过这样的方式来解决这个问题

var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}

定义一个变量,它指向。闭包(它自己的一个主题)保留,所以如果你调用bar作为回调,它仍然有一个引用。

注意:在use strict模式下,如果用作函数,this不会绑定到全局。(它是undefined)。

作为构造函数

还可以将函数作为构造函数调用。根据你正在使用的命名约定(TestObject),这个也是也许是你正在做的事,是什么让你陷入困境

使用new关键字作为构造函数调用函数。

function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();

当作为构造函数调用时,将创建一个新Object,并且将绑定到该对象。同样,如果你有内部函数并且它们被用作回调函数,你将把它们作为函数调用,并且将被绑定到全局对象。使用var that =这个技巧/模式。

有些人认为构造函数/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);


 

函数调用

函数只是Object的一种类型。

所有Function对象都有调用应用方法,用于执行被调用的Function对象。

当调用时,这些方法的第一个参数指定在函数执行期间将由this关键字引用的对象——如果它是nullundefined,则全局对象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"

call的附加参数作为函数调用的参数传递,而apply的单个附加参数可以将函数调用的参数指定为类似array的对象。

因此,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
};

...通过对象访问函数的引用并用括号调用它——obj.foo()——等价于foo.call(obj)foo.apply(obj)

然而,作为对象属性持有的函数并不“绑定”到这些对象。正如你在上面obj的定义中所看到的,由于函数只是对象的一种类型,所以它们可以被引用(因此可以通过引用传递给函数调用或通过引用从函数调用返回)。当传递对函数的引用时,不会携带关于传递位置的附加信息,这就是为什么会发生以下情况:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

对Function引用的调用baz没有为调用提供任何上下文,因此它实际上与baz.call(undefined)相同,因此this最终引用了window。如果我们想让baz知道它属于obj,我们需要在调用baz时以某种方式提供该信息,这是callapply的第一个参数和闭包发挥作用的地方。

作用域链

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的作用域不会“消失”。

考虑到以上所有内容,你现在应该能够理解以下示例中的作用域是如何工作的,以及为什么在调用时传递带有特定值this的“预绑定”函数的技术是有效的:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"

我找到了一个关于ECMAScript这的很好的教程

A该值是一个与执行相关的特殊对象 上下文。因此,它可以被命名为上下文对象(即一个对象) 执行上下文在其中被激活的对象)

任何对象都可以用作上下文的这个值。

a此值是执行上下文的属性,但不是 变量对象的属性

这个特性非常重要,因为与变量相反,这个值从不参与标识符解析过程。也就是说,当在代码中访问this时,它的值直接从执行上下文中获取,而不需要任何作用域链查找。this的值在进入上下文时只确定一次。

在全局上下文中,this值是全局对象本身(这意味着,这里的this值等于变量对象)。

对于函数上下文,这个值在每个单独的函数调用中都可能是不同的

引用Javascript-the-coreChapter-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 -解构对象- '设置为global或undefined,而不是object,它实际上与对象解构无关,而是与如何调用c()有关,但在这里不容易看穿它。

中数说“箭头函数表达式最适合非方法函数”;但是箭头函数在这里成立。

this在JS:

有三种类型的函数,其中this具有不同的含义。最好通过例子来解释:

  1. 构造函数

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

  1. 正常的函数或方法

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

  1. 事件监听器

在事件处理程序的函数内部,this将引用检测到事件的DOM元素。请看这个问题:在事件处理程序中使用它