我希望找到一个“this”关键字的明确解释,以及如何正确使用它。
它的行为似乎很奇怪,我不完全明白为什么。
这是如何工作的,何时使用?
我希望找到一个“this”关键字的明确解释,以及如何正确使用它。
它的行为似乎很奇怪,我不完全明白为什么。
这是如何工作的,何时使用?
当前回答
“这”是关于范围的。每个函数都有自己的作用域,因为JS中的所有内容都是一个对象,所以即使是一个函数也可以使用“this”将一些值存储到自身中。OOP 101教导“这”仅适用于对象的实例。因此,每次函数执行时,该函数的新“实例”都具有“this”的新含义。
大多数人在尝试在匿名闭包函数中使用“this”时会感到困惑,例如:
(function(value) { this.value = value; $('.some-elements').each(function(elt){ elt.innerHTML = this.value; // uh oh!! possibly undefined }); })(2);
所以这里,在each()内部,“this”不包含您期望的“value”(从this.value=value;在其上方)。因此,为了克服这个(并非双关语)问题,开发人员可以:
(function(value) { var self = this; // small change self.value = value; $('.some-elements').each(function(elt){ elt.innerHTML = self.value; // phew!! == 2 }); })(2);
试试看;你会开始喜欢这种编程模式
其他回答
总结此Javascript:
它的值取决于函数的调用方式,而不是创建位置!通常,该值由点左侧的对象确定。(全球空间中的窗口)在事件侦听器中,this的值引用调用事件的DOM元素。当使用new关键字调用函数时,this的值引用新创建的对象您可以使用以下函数操纵此值:call、apply、bind
例子:
let对象={prop1:function(){console.log(this);}}object.prop1();//对象位于点的左侧,因此这是对象constmyFunction=object.prop1//我们将函数存储在变量myFunction中myFunction();//我们在全球空间//myFunction是全局对象的属性//因此,它记录窗口对象
事件侦听器示例:
document.querySelector('.foo').addEventListener('click',函数(){console.log(this);//这是指调用eventListener的DOM元素})document.querySelector('.foo').addEventListener('click',()=>{console.log(this);//提示,es6箭头函数没有自己的绑定到此v})//因此,这将记录全局对象.foo:悬停{颜色:红色;光标:指针;}<div class=“foo”>单击我</div>
示例构造函数:
职能人员(姓名){this.name=名称;}const me=新人('Willem');//当使用new关键字时,构造函数中的this将引用新创建的对象console.log(me.name);//因此,name属性被放置在使用new关键字创建的对象上。
javascript中的每个执行上下文都有一个this参数,该参数由以下参数设置:
如何调用函数(包括作为对象方法、调用和应用的使用、new的使用)绑定的使用箭头函数的词汇(它们采用外部执行上下文的this)代码是严格模式还是非严格模式是否使用eval调用代码
可以使用func.all、func.apply或func.bind设置此值。
默认情况下,当在DOM元素上引发事件后调用监听器时,函数的这个值就是DOM元素,这让大多数初学者感到困惑。
jQuery使用jQuery.proxy进行更改变得很简单。
要正确理解“这”,就必须理解上下文和范围以及它们之间的区别。
作用域:在javascript中,作用域与变量的可见性有关,作用域通过使用函数实现。(阅读有关范围的更多信息)
上下文:上下文与对象相关。它指函数所属的对象。当您使用JavaScript“this”关键字时,它指的是函数所属的对象。例如,在函数内部,当你说:“this.accoutNumber”时,你指的是属性“accoutNumber”,该属性属于该函数所属的对象。
如果对象“myObj”有一个名为“getMyName”的方法,当JavaScript关键字“this”在“getMyName”中使用时,它将引用“myObj”。如果函数“getMyName”是在全局范围内执行的,那么“this”是指窗口对象(严格模式除外)。
现在让我们看看一些示例:
<script>
console.log('What is this: '+this);
console.log(this);
</script>
在浏览器输出中运行abobve代码将:
根据窗口对象上下文中的输出,也可以看到窗口原型引用了对象。
现在让我们尝试函数内部:
<script>
function myFunc(){
console.log('What is this: '+this);
console.log(this);
}
myFunc();
</script>
输出:
输出是相同的,因为我们将“this”变量记录在全局范围中,并将其记录在函数范围中,我们没有更改上下文。在这两种情况下,上下文都是相同的,与寡妇对象相关。
现在让我们创建自己的对象。在javascript中,可以通过多种方式创建对象。
<script>
var firstName = "Nora";
var lastName = "Zaman";
var myObj = {
firstName:"Lord",
lastName:'Baron',
printNameGetContext:function(){
console.log(firstName + " "+lastName);
console.log(this.firstName +" "+this.lastName);
return this;
}
}
var context = myObj.printNameGetContext();
console.log(context);
</script>
输出:
因此,从上面的示例中,我们发现“this”关键字引用的是与myObj相关的新上下文,而myObject也具有到Object的原型链。
让我们再举一个例子:
<body>
<button class="btn">Click Me</button>
<script>
function printMe(){
//Terminal2: this function declared inside window context so this function belongs to the window object.
console.log(this);
}
document.querySelector('.btn').addEventListener('click', function(){
//Terminal1: button context, this callback function belongs to DOM element
console.log(this);
printMe();
})
</script>
</body>
输出:有道理吧?(阅读评论)
如果您无法理解上面的示例,让我们尝试使用自己的回调;
<script>
var myObj = {
firstName:"Lord",
lastName:'Baron',
printName:function(callback1, callback2){
//Attaching callback1 with this myObj context
this.callback1 = callback1;
this.callback1(this.firstName +" "+this.lastName)
//We did not attached callback2 with myObj so, it's reamin with window context by default
callback2();
/*
//test bellow codes
this.callback2 = callback2;
this.callback2();
*/
}
}
var callback2 = function (){
console.log(this);
}
myObj.printName(function(data){
console.log(data);
console.log(this);
}, callback2);
</script>
输出:
现在,让我们了解范围、自我、IIFE和THIS的行为
var color = 'red'; // property of window
var obj = {
color:'blue', // property of window
printColor: function(){ // property of obj, attached with obj
var self = this;
console.log('In printColor -- this.color: '+this.color);
console.log('In printColor -- self.color: '+self.color);
(function(){ // decleard inside of printColor but not property of object, it will executed on window context.
console.log(this)
console.log('In IIFE -- this.color: '+this.color);
console.log('In IIFE -- self.color: '+self.color);
})();
function nestedFunc(){// decleard inside of printColor but not property of object, it will executed on window context.
console.log('nested fun -- this.color: '+this.color);
console.log('nested fun -- self.color: '+self.color);
}
nestedFunc(); // executed on window context
return nestedFunc;
}
};
obj.printColor()(); // returned function executed on window context
</script>
输出非常棒,对吧?
关于此关键字的一些信息
让我们将此关键字记录到全局范围内的控制台中,无需任何代码
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关键字在JavaScript中的行为有所不同。在面向对象语言中,this关键字指的是类的当前实例。在JavaScript中,这个值由函数的调用上下文(context.function())及其调用位置决定。
1.在全局上下文中使用时
当您在全局上下文中使用它时,它将绑定到全局对象(浏览器中的窗口)
document.write(this); //[object Window]
当您在全局上下文中定义的函数中使用此函数时,它仍然绑定到全局对象,因为该函数实际上是全局上下文的方法。
function f1()
{
return this;
}
document.write(f1()); //[object Window]
f1以上是全局对象的方法。因此,我们也可以在window对象上调用它,如下所示:
function f()
{
return this;
}
document.write(window.f()); //[object Window]
2.在对象方法内部使用时
当您在对象方法中使用此关键字时,它将绑定到“立即”封闭对象。
var obj = {
name: "obj",
f: function () {
return this + ":" + this.name;
}
};
document.write(obj.f()); //[object Object]:obj
上面我把单词immediate用双引号括起来。这是为了指出,如果您将对象嵌套在另一个对象中,那么这将绑定到直接父对象。
var obj = {
name: "obj1",
nestedobj: {
name:"nestedobj",
f: function () {
return this + ":" + this.name;
}
}
}
document.write(obj.nestedobj.f()); //[object Object]:nestedobj
即使您将函数作为方法显式添加到对象中,它仍然遵循上述规则,即这仍然指向直接父对象。
var obj1 = {
name: "obj1",
}
function returnName() {
return this + ":" + this.name;
}
obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1
3.调用无上下文函数时
当您使用这个在没有任何上下文的情况下调用的内部函数(即,不在任何对象上)时,它将绑定到全局对象(浏览器中的窗口)(即使函数是在对象内部定义的)。
var context = "global";
var obj = {
context: "object",
method: function () {
function f() {
var context = "function";
return this + ":" +this.context;
};
return f(); //invoked without context
}
};
document.write(obj.method()); //[object Window]:global
使用函数尝试所有功能
我们也可以用函数来尝试以上几点。然而,存在一些差异。
上面我们使用对象文字表示法向对象添加了成员。我们可以通过使用这个向函数添加成员。以指定它们。对象文字表示法创建了一个我们可以立即使用的对象实例。对于函数,我们可能需要首先使用新运算符创建其实例。同样,在对象文字方法中,我们可以使用点运算符将成员显式添加到已定义的对象中。这将仅添加到特定实例。然而,我已经向函数原型添加了变量,以便它在函数的所有实例中得到反映。
下面,我尝试了我们使用Object和上面所做的所有事情,但首先创建了函数,而不是直接编写对象。
/*********************************************************************
1. When you add variable to the function using this keyword, it
gets added to the function prototype, thus allowing all function
instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
this.name = "ObjDefinition";
this.getName = function(){
return this+":"+this.name;
}
}
obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition
/*********************************************************************
2. Members explicitly added to the function protorype also behave
as above: all function instances have their own copy of the
variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
return "v"+this.version; //see how this.version refers to the
//version variable added through
//prototype
}
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
3. Illustrating that the function variables added by both above
ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1
obj2.incrementVersion(); //incrementing version in obj2
//does not affect obj1 version
document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
4. `this` keyword refers to the immediate parent object. If you
nest the object through function prototype, then `this` inside
object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj',
getName1 : function(){
return this+":"+this.name;
}
};
document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj
/*********************************************************************
5. If the method is on an object's prototype chain, `this` refers
to the object the method was called on, as if the method was on
the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
//as its prototype
obj3.a = 999; //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
//calling obj3.fun() makes
//ProtoObj.fun() to access obj3.a as
//if fun() is defined on obj3
4.在构造函数内部使用时。
当函数用作构造函数时(即使用new关键字调用时),此内部函数体指向正在构造的新对象。
var myname = "global context";
function SimpleFun()
{
this.myname = "simple function";
}
var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
// object being constructed thus adding any member
// created inside SimipleFun() using this.membername to the
// object being constructed
//2. And by default `new` makes function to return newly
// constructed object if no explicit return value is specified
document.write(obj1.myname); //simple function
5.在原型链上定义的函数内部使用时
如果该方法位于对象的原型链上,则此类方法中的这个引用该方法所调用的对象,就像该方法是在对象上定义的一样。
var ProtoObj = {
fun: function () {
return this.a;
}
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun()
//to be the method on its prototype chain
var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999
//Notice that fun() is defined on obj3's prototype but
//`this.a` inside fun() retrieves obj3.a
6.内部call()、apply()和bind()函数
所有这些方法都在Function.prototype上定义。这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定在执行函数时使用的this值。当原始函数被调用时,它们还接受传递给它的任何参数。fun.apply(obj1[,argsArray])将obj1设置为fun()内部的值,并调用fun(()传递argsArray元素作为其参数。fun.call(obj1[,arg1[,arg2[,arg3[,…]]])-将obj1设置为fun()内部的值,并调用fun(,传递arg1,arg2,arg3,…)。。。作为其论点。fun.bind(obj1[,arg1[,arg2[,arg3[,…]]])-返回对函数fun的引用,此函数内部fun绑定到obj1,fun的参数绑定到指定的参数arg1,arg2,arg3,。。。。到目前为止,apply、call和bind之间的区别肯定已经很明显了。apply允许将参数指定为类似数组的对象,即具有数字长度属性和相应的非负整数财产的对象。而调用允许直接指定函数的参数。apply和call都会在指定的上下文中使用指定的参数立即调用函数。另一方面,bind只返回绑定到指定this值和参数的函数。我们可以通过将返回的函数分配给变量来获取对该函数的引用,以后我们可以随时调用它。
function add(inc1, inc2)
{
return this.a + inc1 + inc2;
}
var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
//above add.call(o,5,6) sets `this` inside
//add() to `o` and calls add() resulting:
// this.a + inc1 + inc2 =
// `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
// `o.a` i.e. 4 + 5 + 6 = 15
var g = add.bind(o, 5, 6); //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />"); //15
var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
// 4 + 5 + 6 = 15
document.write(h() + "<br />"); //NaN
//no parameter is passed to h()
//thus inc2 inside add() is `undefined`
//4 + 5 + undefined = NaN</code>
7.此内部事件处理程序
当您将函数直接分配给元素的事件处理程序时,直接在事件处理函数内部使用该函数将引用相应的元素。这种直接函数分配可以使用addeventListener方法或通过传统的事件注册方法(如onclick)完成。类似地,当您在元素的事件属性(如<button onclick=“…this…”>)中直接使用它时,它引用的是元素。但是,通过在事件处理函数或事件属性内调用的其他函数间接地使用该函数将解析为全局对象窗口。当我们使用Microsoft的event Registration模型方法attachEvent将函数附加到事件处理程序时,可以实现上述相同的行为。它不是将函数分配给事件处理程序(从而生成元素的函数方法),而是在事件上调用函数(在全局上下文中有效地调用它)。
我建议最好在JSFiddle中尝试一下。
<script>
function clickedMe() {
alert(this + " : " + this.tagName + " : " + this.id);
}
document.getElementById("button1").addEventListener("click", clickedMe, false);
document.getElementById("button2").onclick = clickedMe;
document.getElementById("button5").attachEvent('onclick', clickedMe);
</script>
<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>
<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />
<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />
IE only: <button id="button5">click() "attached" using attachEvent() </button>
8.这在ES6箭头函数中
在箭头函数中,它的行为类似于公共变量:它将从其词法范围继承。函数的this,其中定义了箭头函数,将是箭头函数的this。
因此,这与以下行为相同:
(function(){}).bind(this)
请参见以下代码:
const globalArrowFunction = () => {
return this;
};
console.log(globalArrowFunction()); //window
const contextObject = {
method1: () => {return this},
method2: function(){
return () => {return this};
}
};
console.log(contextObject.method1()); //window
const contextLessFunction = contextObject.method1;
console.log(contextLessFunction()); //window
console.log(contextObject.method2()()) //contextObject
const innerArrowFunction = contextObject.method2();
console.log(innerArrowFunction()); //contextObject