【js】this问题
demo1:
var obj = { a: 10, b: () => { console.log(this.a); // undefined console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …} }, c: function () { console.log(this.a); // 10 console.log(this); // {a: 10, b: ƒ, c: ƒ} } } obj.b(); obj.c();
箭头函数中的this 指的是上下文 ,c中的this值的是调用它的对象
demo2:
var length = 10; function fn(){ console.log(this.length); } var obj = { length:5, method:function(fn){ fn(); arguments[0](); } } obj.method(fn,1);
// 10 2
demo3:
var a = 10; var obj = { a:5, b:{ fn:function(){ console.log(this.a); } } } obj.b.fn(); // undefined
this 指的是调用它的对象b
this指向问题
- 全局环境、普通函数(非严格模式)指向 window
- 普通函数(严格模式)指向 undefined
- 函数作为对象方法及原型链指向的就是上一级的对象
- 构造函数指向构造的对象
- DOM 事件中指向触发事件的元素
- 箭头函数...
1、全局环境
全局环境下,this 始终指向全局对象(window),无论是否严格模式;
// 在浏览器中,全局对象为 window 对象: console.log(this === window); // true this.a = 37; console.log(window.a); // 37
2、函数上下文调用
2.1 普通函数
普通函数内部的 this 分两种情况,严格模式和非严格模式。
(1)非严格模式下,没有被上一级的对象所调用, this 默认指向全局对象 window。
function f1() { return this; } f1() === window; // true
(2)严格模式下,this 指向 undefined。
function f2() { "use strict"; // 这里是严格模式 return this; } f2() === undefined; // true
2.2 函数作为对象的方法
(1)函数有被上一级的对象所调用,那么 this 指向的就是上一级的对象。
(2)多层嵌套的对象,内部方法的 this 指向离被调用函数最近的对象(window 也是对象,其内部对象调用方法的 this 指向内部对象, 而非 window)。
//方式1 var o = { prop: 37, f: function() { return this.prop; } }; //当 o.f()被调用时,函数内的this将绑定到o对象。 console.log(o.f()); // logs 37 //方式2 var o = { prop: 37 }; function independent() { return this.prop; } //函数f作为o的成员方法调用 o.f = independent; console.log(o.f()); // logs 37 //方式3 //this 的绑定只受最靠近的成员引用的影响 o.b = { g: independent, prop: 42 }; console.log(o.b.g()); // 42
特殊例子
// 例子1 var o = { a: 10, b: { // a:12, fn: function() { console.log(this.a); //undefined console.log(this); //{fn: ƒ} } } }; o.b.fn(); // 例子2 var o = { a: 10, b: { a: 12, fn: function() { console.log(this.a); //undefined console.log(this); //window } } }; var j = o.b.fn; j(); // this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子2中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子1是不一样的,例子1是直接执行了fn
2.3 原型链中的 this
(1)如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法在对象上一样。
var o = { f: function() { return this.a + this.b; } }; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5
上述例子中,对象 p 没有属于它自己的 f 属性,它的 f 属性继承自它的原型。当执行 p.f()时,会查找 p 的原型链,找到 f 函数并执行。因为 f 是作为 p 的方法调用的,所以函数中的 this 指向 p。
(2)相同的概念也适用于当函数在一个 getter 或者 setter 中被调用。用作 getter 或 setter 的函数都会把 this 绑定到设置或获取属性的对象。
(3)call()和 apply()方法:当函数通过 Function 对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的 this 值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript 内部会尝试将其转换成对象然后指向它。
function add(c, d) { return this.a + this.b + c + d; } var o = { a: 1, b: 3 }; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 function tt() { console.log(this); } // 第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。 tt.call(5); // 内部转成 Number {[[PrimitiveValue]]: 5} tt.call("asd"); // 内部转成 String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}
(4)bind()方法:由 ES5 引入, 在 Function 的原型链上, Function.prototype.bind。通过 bind 方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。
function f() { return this.a; } var g = f.bind({ a: "azerty" }); console.log(g()); // azerty var o = { a: 37, f: f, g: g }; console.log(o.f(), o.g()); // 37, azerty
2.4 构造函数中的 this
当一个函数用作构造函数时(使用 new 关键字),它的 this 被绑定到正在构造的新对象。
构造器返回的默认值是 this 所指的那个对象,也可以手动返回其他的对象。
function C() { this.a = 37; } var o = new C(); console.log(o.a); // 37
// 为什么this会指向o?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。
function C2() { this.a = 37; return { a: 38 }; // 手动设置返回{a:38}对象 } o = new C2(); console.log(o.a); // 38
特殊例子
当 this 碰到 return 时
// 例子1 function fn() { this.user = "追梦子"; return {}; } var a = new fn(); console.log(a.user); //undefined // 例子2 function fn() { this.user = "追梦子"; return function() {}; } var a = new fn(); console.log(a.user); //undefined // 例子3 function fn() { this.user = "追梦子"; return 1; } var a = new fn(); console.log(a.user); //追梦子 // 例子4 function fn() { this.user = "追梦子"; return undefined; } var a = new fn(); console.log(a.user); //追梦子 // 例子5 function fn() { this.user = "追梦子"; return undefined; } var a = new fn(); console.log(a); //fn {user: "追梦子"} // 例子6 // 虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊 function fn() { this.user = "追梦子"; return null; } var a = new fn(); console.log(a.user); //追梦子 // 总结:如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
2.5 setTimeout & setInterval
(1)对于延时函数内部的回调函数的 this 指向全局对象 window;
(2)可以通过 bind()方法改变内部函数 this 指向。
//默认情况下代码 function Person() { this.age = 0; setTimeout(function() { console.log(this); }, 3000); } var p = new Person(); //3秒后返回 window 对象 //通过bind绑定 function Person() { this.age = 0; setTimeout( function() { console.log(this); }.bind(this), 3000 ); } var p = new Person(); //3秒后返回构造函数新生成的对象 Person{...}
3、在 DOM 事件中
3.1 作为一个 DOM 事件处理函数
当函数被用作事件处理函数时,它的 this 指向触发事件的元素(针对 addEventListener 事件)。
// 被调用时,将关联的元素变成蓝色 function bluify(e) { //this指向所点击元素 console.log("this === e.currentTarget", this === e.currentTarget); // 总是 true // 当 currentTarget 和 target 是同一个对象时为 true console.log("this === e.target", this === e.target); this.style.backgroundColor = "#A5D9F3"; } // 获取文档中的所有元素的列表 var elements = document.getElementsByTagName("*"); // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色 for (var i = 0; i < elements.length; i++) { elements[i].addEventListener("click", bluify, false); }
3.2 作为一个内联事件处理函数
(1)当代码被内联处理函数调用时,它的 this 指向监听器所在的 DOM 元素;
(2)当代码被包括在函数内部执行时,其 this 指向等同于 普通函数直接调用的情况,即在非严格模式指向全局对象 window,在严格模式指向 undefined:
<button onclick="console.log(this)">show me</button> <button onclick="(function () {console.log(this)})()">show inner this</button> <button onclick="(function () {'use strict'; console.log(this)})()"> use strict </button> // 控制台打印 <button onclick="console.log(this)">show me</button> Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …} undefined
4、箭头函数
4.1 全局环境中
在全局代码中,箭头函数被设置为全局对象:
var globalObject = this; var foo = () => this; console.log(foo() === globalObject); // true
4.2 this 捕获上下文
箭头函数没有自己的 this,而是使用箭头函数所在的作用域的 this,即指向箭头函数定义时(而不是运行时)所在的作用域。
//1、箭头函数在函数内部,以非方法的方法使用 function Person() { this.age = 0; setInterval(() => { this.age++; }, 3000); } var p = new Person(); //Person{age: 0} //普通函数作为内部函数 function Person() { this.age = 0; setInterval(function() { console.log(this); this.age++; }, 3000); } var p = new Person(); //Window{...}
4.2 this 捕获上下文
箭头函数没有自己的 this,而是使用箭头函数所在的作用域的 this,即指向箭头函数定义时(而不是运行时)所在的作用域。
//1、箭头函数在函数内部,以非方法的方法使用 function Person() { this.age = 0; setInterval(() => { console.log(this); this.age++; }, 3000); } var p = new Person(); //Person{age: 0} //普通函数作为内部函数 function Person() { this.age = 0; setInterval(function() { console.log(this); this.age++; }, 3000); } var p = new Person(); //Window{...}
在 setTimeout 中的 this 指向了构造函数新生成的对象,而普通函数指向了全局 window 对象。
4.3 箭头函数作为对象的方法使用
箭头函数作为对象的方法使用,指向全局 window 对象;而普通函数作为对象的方法使用,则指向调用的对象。
var obj = { i: 10, b: () => console.log(this.i, this), c: function() { console.log(this.i, this); } }; obj.b(); // undefined window{...} obj.c(); // 10 Object {...}
4.4 箭头函数中,call()、apply()、bind()方法无效
var adder = { base: 1, //对象的方法内部定义箭头函数,this是箭头函数所在的作用域的this, //而方法add的this指向adder对象,所以箭头函数的this也指向adder对象。 add: function(a) { var f = v => v + this.base; return f(a); }, //普通函数f1的this指向window add1: function() { var f1 = function() { console.log(this); }; return f1(); }, addThruCall: function inFun(a) { var f = v => v + this.base; var b = { base: 2 }; return f.call(b, a); } }; console.log(adder.add(1)); // 输出 2 adder.add1(); //输出全局对象 window{...} console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3,其内部的this并没有因为call() 而改变,其this值仍然为函数inFun的this值,指向对象adder
4.5 this 指向固定化
箭头函数可以让 this 指向固定化,这种特性很有利于封装回调函数
var handler = { id: "123456", init: function() { document.addEventListener( "click", event => this.doSomething(event.type), false ); }, doSomething: function(type) { console.log("Handling " + type + " for " + this.id); } };
上面代码的 init 方法中,使用了箭头函数,这导致这个箭头函数里面的 this,总是指向 handler 对象。如果不使用箭头函数则指向全局 document 对象。
4.6 箭头函是不适用场景
(1)箭头函数不适合定义对象的方法(方法内有 this),因为此时指向 window;
(2)需要动态 this 的时候,也不应使用箭头函数。
//例1,this指向定义箭头函数所在的作用域,它位于对象cat内,但cat不能构成一个作用域,所以指向全局window,改成普通函数后this指向cat对象。 const cat = { lives: 9, jumps: () => { this.lives--; } }; //例2,此时this也是指向window,不能动态监听button,改成普通函数后this指向按钮对象。 var button = document.getElementById("press"); button.addEventListener("click", () => { this.classList.toggle("on"); });
欢迎关注我,一起进步!扫描下方二维码即可加我QQ

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通