我希望找到一个“this”关键字的明确解释,以及如何正确使用它。

它的行为似乎很奇怪,我不完全明白为什么。

这是如何工作的,何时使用?


当前回答

我对这个问题的看法与我希望有帮助的其他答案不同。

查看JavaScript的一种方法是看到只有一种方法可以调用函数1。它是

functionObject.call(objectForThis, arg0, arg1, arg2, ...);

始终为objectForThis提供一些值。

其他一切都是functionObject.call的语法糖

因此,其他一切都可以通过它如何转换为functionObject.call来描述。

如果你只是调用一个函数,那么这就是“全局对象”,在浏览器中就是窗口

函数foo(){console.log(此);}foo();//这是窗口对象

换句话说,

foo();

被有效地转化为

foo.call(window);

请注意,如果使用严格模式,则这将是未定义的

“使用严格”;函数foo(){console.log(此);}foo();//这是窗口对象

这意味着

换句话说,

foo();

被有效地转化为

foo.call(undefined);

在JavaScript中有+、-和*等运算符。还有点运算符。

这个运算符与右侧的函数和左侧的对象一起使用时,实际上意味着“将对象作为this传递给函数”。

实例

常量bar={name:'bar',foo(){console.log(此);},};bar.foo();//这是酒吧

换句话说,bar.foo()转换为consttemp=bar.foo;临时呼叫(bar);

注意,函数的创建方式并不重要(主要是…)。所有这些都会产生相同的结果

常量bar={name:'bar',fn1(){console.log(this);},fn2:function(){console.log(this);},fn3:其他函数,};函数otherFunction(){console.log(this)};bar.fn1();//这是酒吧bar.fn2();//这是酒吧bar.fn3();//这是酒吧

同样,这些都只是语法糖

{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }

另一个褶皱是原型链。当您使用.b时,JavaScript首先查找属性b的a直接引用的对象。如果在对象上找不到b,则JavaScript将在对象的原型中查找b。

定义对象原型的方法多种多样,2019年最常见的是class关键字。出于这个目的,尽管这并不重要。重要的是,当它在对象a中查找属性b时,如果它在对象上找到属性b,或者在它的原型链中找到属性b(如果b最终是一个函数),那么上述规则同样适用。函数b引用将使用call方法调用,并将a作为objectForThis传递,如该答案顶部所示。

现在让我们想象一下,在调用另一个函数之前,我们创建了一个显式设置此值的函数,然后使用调用它。(点)运算符

函数foo(){console.log(此);}函数栏(){const objectForThis={name:“moo”}foo.call(objectForThis);//显式传递objectForThis}常量对象={酒吧};obj.bar();

在转换为使用调用之后,obj.bar()变为consttemp=obj.bar;临时调用(obj);。当我们进入bar函数时,我们调用foo,但我们显式地为objectForThis传递了另一个对象,所以当我们到达foo时,这是内部对象。

这是bind和=>函数有效的作用。它们是语法糖。他们有效地构建了一个新的不可见函数,就像上面的bar一样,它在调用指定的函数之前显式地设置了这个函数。在绑定的情况下,这将设置为传递给绑定的任何值。

函数foo(){console.log(此);}const bar=foo.bind({name:“moo”});//bind创建了一个新的不可见函数,该函数使用绑定对象调用foo。bar();//我们在这里传递给bar的objectForThis被忽略,因为//绑定创建的不可见函数将使用//我们在上面绑定的对象bar.call({name:“other”});

注意,如果functionObject.bind不存在,我们可以像这样创建自己的

function bind(fn, objectForThis) {
  return function(...args) {
    return fn.call(objectForthis, ...args);
  };
}

然后我们可以这样称呼它

function foo() {
  console.log(this);
}

const bar = bind(foo, {name:'abc'});

箭头函数,=>运算符是绑定的语法糖

const a = () => {console.log(this)};

const tempFn = function() {console.log(this)}; 
const a = tempFn.bind(this);

与bind一样,会创建一个新的不可见函数,该函数使用objectForThis的绑定值调用给定函数,但与bind不同的是,要绑定的对象是隐式的。当使用=>运算符时,这就是发生的情况。

所以,就像上面的规则一样

const a = () => { console.log(this); }  // this is the global object
'use strict';
const a = () => { console.log(this); }  // this is undefined
function foo() {
  return () => { console.log(this); }
}

const obj = {
  foo,
};
const b = obj.foo();
b();

obj.foo()转换为consttemp=obj.foo;临时调用(obj);这意味着foo中的箭头运算符将obj绑定到一个新的不可见函数,并返回分配给b.b()的新的不可视函数。该不可见函数忽略传递给它的this,并将obj作为objectForThis`传递给箭头函数。

上面的代码翻译为

function foo() {
  function tempFn() {
    console.log(this);
  }
  return tempFn.bind(this);
}

const obj = {
  foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);

1apply是另一个类似于调用的函数

functionName.apply(objectForThis, arrayOfArgs);

但从概念上讲,ES6甚至可以将其转换为

functionName.call(objectForThis, ...arrayOfArgs);

其他回答

关于此关键字的一些信息

让我们将此关键字记录到全局范围内的控制台中,无需任何代码

console.log(this)

在客户端/浏览器中,此关键字是一个全局对象,它是窗口

console.log(this === window) // true

and

在Server/Node/Javascript运行时中,此关键字也是一个全局对象,即module.exports

console.log(this === module.exports) // true
console.log(this === exports) // true

请记住,导出只是对module.exports的引用

“this”的值取决于执行函数的“上下文”。上下文可以是任何对象或全局对象,即窗口。

因此,“this”的语义不同于传统的OOP语言。这会导致问题:1.当一个函数传递给另一个变量时(很可能是回调);和2。当从类的成员方法调用闭包时。

在这两种情况下,这都设置为window。

这是我见过的最好的解释:清晰地理解JavaScripts

此引用总是指(并保持)object-一个单独的对象,通常用于函数或方法,尽管它可以在全局范围请注意,当我们使用strict模式时在全局函数和匿名函数中未定义绑定到任何对象。

有四种情况可能会令人困惑:

当我们传递一个方法(使用此方法)作为参数用作回调函数时。当我们使用内部函数(闭包)时。需要注意的是,闭包不能通过使用this关键字访问外部函数的this变量,因为this变量只能由函数本身访问,而不能由内部函数访问。当依赖于此的方法被分配给跨上下文的变量时,在这种情况下,它引用了另一个对象,而不是最初预期的对象。与bind、apply和call方法一起使用时。

他给出了代码示例、解释和解决方案,我认为这很有帮助。

JavaScript中的this总是指正在执行的函数的“所有者”。

如果未定义显式所有者,则引用最顶层的所有者窗口对象。

所以如果我做了

function someKindOfFunction() {
   this.style = 'foo';
}

element.onclick=someKindOfFunction;

这将引用元素对象。但是要小心,很多人都会犯这个错误。

<element onclick=“someKindOfFunction()”>

在后一种情况下,您只是引用函数,而不是将其交给元素。因此,这将引用窗口对象。

我对这个问题的看法与我希望有帮助的其他答案不同。

查看JavaScript的一种方法是看到只有一种方法可以调用函数1。它是

functionObject.call(objectForThis, arg0, arg1, arg2, ...);

始终为objectForThis提供一些值。

其他一切都是functionObject.call的语法糖

因此,其他一切都可以通过它如何转换为functionObject.call来描述。

如果你只是调用一个函数,那么这就是“全局对象”,在浏览器中就是窗口

函数foo(){console.log(此);}foo();//这是窗口对象

换句话说,

foo();

被有效地转化为

foo.call(window);

请注意,如果使用严格模式,则这将是未定义的

“使用严格”;函数foo(){console.log(此);}foo();//这是窗口对象

这意味着

换句话说,

foo();

被有效地转化为

foo.call(undefined);

在JavaScript中有+、-和*等运算符。还有点运算符。

这个运算符与右侧的函数和左侧的对象一起使用时,实际上意味着“将对象作为this传递给函数”。

实例

常量bar={name:'bar',foo(){console.log(此);},};bar.foo();//这是酒吧

换句话说,bar.foo()转换为consttemp=bar.foo;临时呼叫(bar);

注意,函数的创建方式并不重要(主要是…)。所有这些都会产生相同的结果

常量bar={name:'bar',fn1(){console.log(this);},fn2:function(){console.log(this);},fn3:其他函数,};函数otherFunction(){console.log(this)};bar.fn1();//这是酒吧bar.fn2();//这是酒吧bar.fn3();//这是酒吧

同样,这些都只是语法糖

{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }

另一个褶皱是原型链。当您使用.b时,JavaScript首先查找属性b的a直接引用的对象。如果在对象上找不到b,则JavaScript将在对象的原型中查找b。

定义对象原型的方法多种多样,2019年最常见的是class关键字。出于这个目的,尽管这并不重要。重要的是,当它在对象a中查找属性b时,如果它在对象上找到属性b,或者在它的原型链中找到属性b(如果b最终是一个函数),那么上述规则同样适用。函数b引用将使用call方法调用,并将a作为objectForThis传递,如该答案顶部所示。

现在让我们想象一下,在调用另一个函数之前,我们创建了一个显式设置此值的函数,然后使用调用它。(点)运算符

函数foo(){console.log(此);}函数栏(){const objectForThis={name:“moo”}foo.call(objectForThis);//显式传递objectForThis}常量对象={酒吧};obj.bar();

在转换为使用调用之后,obj.bar()变为consttemp=obj.bar;临时调用(obj);。当我们进入bar函数时,我们调用foo,但我们显式地为objectForThis传递了另一个对象,所以当我们到达foo时,这是内部对象。

这是bind和=>函数有效的作用。它们是语法糖。他们有效地构建了一个新的不可见函数,就像上面的bar一样,它在调用指定的函数之前显式地设置了这个函数。在绑定的情况下,这将设置为传递给绑定的任何值。

函数foo(){console.log(此);}const bar=foo.bind({name:“moo”});//bind创建了一个新的不可见函数,该函数使用绑定对象调用foo。bar();//我们在这里传递给bar的objectForThis被忽略,因为//绑定创建的不可见函数将使用//我们在上面绑定的对象bar.call({name:“other”});

注意,如果functionObject.bind不存在,我们可以像这样创建自己的

function bind(fn, objectForThis) {
  return function(...args) {
    return fn.call(objectForthis, ...args);
  };
}

然后我们可以这样称呼它

function foo() {
  console.log(this);
}

const bar = bind(foo, {name:'abc'});

箭头函数,=>运算符是绑定的语法糖

const a = () => {console.log(this)};

const tempFn = function() {console.log(this)}; 
const a = tempFn.bind(this);

与bind一样,会创建一个新的不可见函数,该函数使用objectForThis的绑定值调用给定函数,但与bind不同的是,要绑定的对象是隐式的。当使用=>运算符时,这就是发生的情况。

所以,就像上面的规则一样

const a = () => { console.log(this); }  // this is the global object
'use strict';
const a = () => { console.log(this); }  // this is undefined
function foo() {
  return () => { console.log(this); }
}

const obj = {
  foo,
};
const b = obj.foo();
b();

obj.foo()转换为consttemp=obj.foo;临时调用(obj);这意味着foo中的箭头运算符将obj绑定到一个新的不可见函数,并返回分配给b.b()的新的不可视函数。该不可见函数忽略传递给它的this,并将obj作为objectForThis`传递给箭头函数。

上面的代码翻译为

function foo() {
  function tempFn() {
    console.log(this);
  }
  return tempFn.bind(this);
}

const obj = {
  foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);

1apply是另一个类似于调用的函数

functionName.apply(objectForThis, arrayOfArgs);

但从概念上讲,ES6甚至可以将其转换为

functionName.call(objectForThis, ...arrayOfArgs);