我希望找到一个“this”关键字的明确解释,以及如何正确使用它。
它的行为似乎很奇怪,我不完全明白为什么。
这是如何工作的,何时使用?
我希望找到一个“this”关键字的明确解释,以及如何正确使用它。
它的行为似乎很奇怪,我不完全明白为什么。
这是如何工作的,何时使用?
当前回答
Javascript就是这个
简单函数调用
考虑以下功能:
function foo() {
console.log("bar");
console.log(this);
}
foo(); // calling the function
请注意,我们在正常模式下运行,即不使用严格模式。
在浏览器中运行时,此值将作为窗口记录。这是因为window是web浏览器范围内的全局变量。
如果在node.js这样的环境中运行相同的代码,这将引用应用程序中的全局变量。
现在,如果我们通过添加语句“usestrict”在严格模式下运行此操作;在函数声明的开头,这将不再引用这两个环境中的全局变量。这样做是为了避免严格模式中的混淆。在这种情况下,这将只是log undefined,因为这就是它,它没有被定义。
在下面的例子中,我们将看到如何操纵这个值。
对对象调用函数
有不同的方法可以做到这一点。如果您在Javascript中调用了本机方法,如forEach和slice,那么您应该已经知道,在这种情况下,this变量指的是调用该函数的Object(注意,在Javascript中,几乎所有的东西都是Object,包括数组和函数)。以以下代码为例。
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
如果对象包含包含函数的属性,则该属性称为方法。调用此方法时,将始终将此变量设置为与其关联的对象。对于严格模式和非严格模式都是如此。
请注意,如果一个方法存储(或复制)在另一个变量中,则对该方法的引用将不再保留在新变量中。例如:
// continuing with the previous code snippet
var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation
考虑更常见的实际情况:
var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself
新关键字
考虑Javascript中的构造函数:
function Person (name) {
this.name = name;
this.sayHello = function () {
console.log ("Hello", this);
}
}
var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`
这是如何工作的?好吧,让我们看看当我们使用新关键字时会发生什么。
使用new关键字调用函数将立即初始化Person类型的Object。此对象的构造函数将其构造函数设置为Person。此外,请注意,typeof awal将仅返回Object。这个新对象将被分配Person.prototype的原型。这意味着Person原型中的任何方法或属性都可用于Person的所有实例,包括awal。现在调用函数Person本身;这是对新构建的对象awal的引用。
很简单,嗯?
请注意,官方的ECMAScript规范中没有说明此类函数是实际的构造函数。它们只是普通函数,新函数可以用于任何函数。只不过我们是这样使用它们的,所以我们只是这样称呼它们。
在函数上调用函数:调用和应用
所以,是的,因为函数也是Object(实际上是Javascript中的第一类变量),所以即使函数也有方法。。。嗯,功能本身。
所有函数都继承自全局函数,它的许多方法中有两个是调用和应用,这两个方法都可以用于在调用它们的函数中操纵此值。
function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);
这是使用call的典型示例。它基本上接受第一个参数,并在函数foo中将其设置为对thisArg的引用。传递给调用的所有其他参数都作为参数传递给函数foo。因此,上面的代码将在控制台中记录{myObj:“is cool”},[1,2,3]。在任何函数中更改此值的非常好的方法。
apply与callaccept几乎相同,它只接受两个参数:thisArg和一个包含要传递给函数的参数的数组。因此,可以将上述调用转换为如下应用:
foo.apply(thisArg, [1,2,3])
请注意,调用和应用可以覆盖我们在第二个项目中讨论的点方法调用集的值。足够简单:)
呈现。。。。绑定
bind是应召而来的兄弟。它也是Javascript中所有函数从全局函数构造函数继承的方法。绑定和调用/应用之间的区别在于,调用和应用实际上都会调用函数。另一方面,bind会返回一个预设了thisArg和参数的新函数。让我们举个例子来更好地理解这一点:
function foo (a, b) {
console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */
bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`
看到三者之间的区别了吗?这很微妙,但它们的用法不同。与调用和应用一样,bind也将通过点方法调用覆盖此集合的值。
还要注意,这三个函数都没有对原始函数进行任何更改。call和apply将返回新构造的函数的值,而bind将返回新构建的函数本身,以便调用。
额外的东西,复制这个
有时,您不喜欢这样一个事实,即这会随范围而改变,尤其是嵌套范围。看看下面的例子。
var myObj = {
hello: function () {
return "world"
},
myMethod: function () {
// copy this, variable names are case-sensitive
var that = this;
// callbacks ftw \o/
foo.bar("args", function () {
// I want to call `hello` here
this.hello(); // error
// but `this` references to `foo` damn!
// oh wait we have a backup \o/
that.hello(); // "world"
});
}
};
在上面的代码中,我们看到这个值随嵌套范围而改变,但我们希望从原始范围中获得这个值。所以我们将这个“复制”到那个,并使用副本代替这个。聪明,嗯?
索引:
默认情况下,此中包含什么?如果我们将函数作为一个带有Object点符号的方法调用呢?如果我们使用新关键字呢?我们如何通过调用和应用来处理这一点?使用bind。复制此以解决嵌套范围问题。
其他回答
这是我见过的最好的解释:清晰地理解JavaScripts
此引用总是指(并保持)object-一个单独的对象,通常用于函数或方法,尽管它可以在全局范围请注意,当我们使用strict模式时在全局函数和匿名函数中未定义绑定到任何对象。
有四种情况可能会令人困惑:
当我们传递一个方法(使用此方法)作为参数用作回调函数时。当我们使用内部函数(闭包)时。需要注意的是,闭包不能通过使用this关键字访问外部函数的this变量,因为this变量只能由函数本身访问,而不能由内部函数访问。当依赖于此的方法被分配给跨上下文的变量时,在这种情况下,它引用了另一个对象,而不是最初预期的对象。与bind、apply和call方法一起使用时。
他给出了代码示例、解释和解决方案,我认为这很有帮助。
JavaScript中的“this”这是执行上下文的财产之一。
每次执行函数时都会创建此属性,而不是在此之前。它的价值不是静态的,而是取决于它的使用方式。获取一个指向其所在函数所有者的值习惯于
“this”关键字有不同的使用方式,下面是它的示例(方法、正则函数、箭头函数、事件监听器、显式函数绑定)。
1.方法内部。
this==(对调用方法的对象)。
在上面的示例中,方法“fullName()”由对象“person”调用,因此方法“fullName()”中的值将等于对象“person”。
2.函数内部。
i) 函数声明/表达式
在松散模式下,此==窗口(对象)
在严格模式下,this==未定义
注意:使用函数声明或函数表达式方法定义函数时,此属性的工作原理相同。
ii)箭头功能:
箭头函数没有自己的此属性,它们将此属性的值作为其周围的函数。如果周围函数不存在,即如果它们是在全局级别定义的,则此==窗口(对象)
3.事件侦听器this==附加处理程序的对象。单击事件绑定到Document对象
在上面的示例中,由于单击处理程序附加到“document”对象,因此它将等于“document“对象
4.显式函数绑定(调用、应用、绑定)
call()和apply()方法是预定义的JavaScript方法。
它们都可以用来调用另一个对象作为参数的对象方法。
在上面的示例中,“printFullDetails()”中的这一点通过传递作为调用方法的第一个参数显式设置为personObj1和personObj2。
您可以在这里探索有关调用、应用和绑定方法的更多信息。
关于这一点,最详细和最全面的文章可能是以下内容:
JavaScript中“this”关键字的温和解释
这背后的想法是理解函数调用类型对设置该值具有重要意义。
当难以识别时,不要问自己:
这是从哪里来的?
但一定要问问自己:
如何调用函数?
对于箭头函数(上下文透明的特殊情况),问问自己:
在定义箭头函数的地方有什么值?
在处理这个问题时,这种心态是正确的,会让你免于头痛。
这里有一个很好的JavaScript源代码。
以下是总结:
全局this在浏览器中,在全局范围内,这是windowobject<script type=“text/javascript”>console.log(此==窗口);//真的var foo=“bar”;console.log(this.foo);//“条形图”console.log(window.foo);//“条形图”在使用repl的节点中,这是顶级命名空间。您可以将其称为全局。>这个{ArrayBuffer:[Function:ArrayBuffer],Int8Array:{[Function:Int8Array]BYTES_PER_ELEMENT:1},Uint8Array:{[Function:Uint8Array]BYTES_PER_ELEMENT:1},...>全局==此真的在从脚本执行的节点中,全局范围中的这个对象以空对象开始。它与全球不同\\测试.jsconsole.log(此);\\{}console.log(this==全局);\\时尚函数this
除了在DOM事件处理程序的情况下,或者当提供了thisArg时(请参见下文),在节点和浏览器中都会在不使用新引用调用全局范围的函数中使用此函数…
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis();
console.log(this.foo); //logs "foo"
</script>
如果使用use strict;,在这种情况下,这将是未定义的
<script type="text/javascript">
foo = "bar";
function testThis() {
"use strict";
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined
</script>
若使用new调用函数,这将是一个新上下文,它将不会引用全局this。
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
new testThis();
console.log(this.foo); //logs "bar"
console.log(new testThis().foo); //logs "foo"
</script>
原型
您创建的函数将成为函数对象。它们会自动获得一个特殊的原型属性,这是您可以为其赋值的属性。当您使用new调用函数来创建实例时,您可以访问分配给原型属性的值。您可以使用此访问这些值。
function Thing() {
console.log(this.foo);
}
Thing.prototype.foo = "bar";
var thing = new Thing(); //logs "bar"
console.log(thing.foo); //logs "bar"
在原型上分配数组或对象通常是错误的。如果希望每个实例都有自己的数组,请在函数中创建它们,而不是在原型中创建它们。
function Thing() {
this.things = [];
}
var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
反对这个
您可以在对象的任何函数中使用它来引用该对象的其他财产。这与使用new创建的实例不同。
var obj = {
foo: "bar",
logFoo: function () {
console.log(this.foo);
}
};
obj.logFoo(); //logs "bar"
DOM事件this
在HTML DOM事件处理程序中,这始终是对事件附加到的DOM元素的引用
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick);
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs "<div id="foo"></div>"
}
var listener = new Listener();
document.getElementById("foo").click();
除非您绑定上下文
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs Listener {handleClick: function}
}
var listener = new Listener();
document.getElementById("foo").click();
HTML此
在可以放入JavaScript的HTML属性中,这是对元素的引用。
<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
评估此
您可以使用eval访问此。
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
eval("console.log(this.foo)"); //logs "bar"
}
var thing = new Thing();
thing.logFoo();
用这个
您可以使用with将其添加到当前范围中,以读取和写入其值,而无需显式引用它。
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
with (this) {
console.log(foo);
foo = "foo";
}
}
var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
jQuery this
jQuery在很多地方都会引用DOM元素。
<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
this.click();
});
</script>
我对这个问题的看法与我希望有帮助的其他答案不同。
查看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);