JavaScript中this关键字的工作原理及使用场景
技术背景
在JavaScript里,this
关键字是一个比较难理解的概念,其行为与其他语言有所不同。在面向对象语言中,this
通常指向类的当前实例;而在JavaScript里,this
的值由函数的调用上下文(context.function()
)和调用位置决定。这一特性导致在不同的调用场景下,this
会指向不同的对象,从而给开发者带来理解上的困扰。
实现步骤
全局执行上下文
在全局作用域中使用this
,它会指向全局对象。在浏览器环境下,全局对象是window
;在Node.js环境下,全局对象是global
。
1 2 3 4
| console.log(this === window);
console.log(this === global);
|
若在全局作用域中定义的函数里使用this
,它同样指向全局对象,因为该函数实际上成为了全局对象的方法。
1 2 3 4
| function f1() { return this; } console.log(f1() === window);
|
对象方法内部
当在对象的方法里使用this
时,this
会指向调用该方法的对象。
1 2 3 4 5 6 7
| var obj = { name: "obj", f: function () { return this + ":" + this.name; } }; console.log(obj.f());
|
构造函数内部
当函数作为构造函数使用(即使用new
关键字调用)时,this
会指向新创建的对象。
1 2 3 4 5
| function SimpleFun() { this.myname = "simple function"; } var obj1 = new SimpleFun(); console.log(obj1.myname);
|
函数的call
、apply
和bind
方法
这三个方法能够改变函数调用时this
的值。
1 2 3 4 5 6 7 8
| function add(inc1, inc2) { return this.a + inc1 + inc2; } var o = { a: 4 }; console.log(add.call(o, 5, 6)); console.log(add.apply(o, [5, 6])); var g = add.bind(o, 5, 6); console.log(g());
|
箭头函数内部
箭头函数不会绑定自己的this
,它会继承外层函数或全局作用域的this
值。
1 2 3 4
| const globalArrowFunction = () => { return this; }; console.log(globalArrowFunction());
|
事件处理函数内部
当直接将函数赋值给元素的事件处理程序时,事件处理函数里的this
会指向对应的元素。
1 2 3
| document.getElementById("button1").addEventListener("click", function () { console.log(this); });
|
核心代码
全局上下文示例
1 2 3 4 5
| console.log(this); function globalFunction() { console.log(this); } globalFunction();
|
对象方法示例
1 2 3 4 5 6 7
| var obj = { name: "example", method: function () { return this.name; } }; console.log(obj.method());
|
构造函数示例
1 2 3 4 5
| function Person(name) { this.name = name; } var person = new Person("John"); console.log(person.name);
|
call
、apply
和bind
示例
1 2 3 4 5 6 7 8
| function greet(message) { console.log(message + ", " + this.name); } var obj = { name: "Alice" }; greet.call(obj, "Hello"); greet.apply(obj, ["Hi"]); var boundGreet = greet.bind(obj); boundGreet("Bonjour");
|
箭头函数示例
1 2 3 4
| const arrowFunction = () => { console.log(this); }; arrowFunction();
|
事件处理函数示例
1 2 3 4 5 6
| <button id="myButton">Click me</button> <script> document.getElementById("myButton").addEventListener("click", function () { console.log(this); }); </script>
|
最佳实践
避免混淆this
的指向
- 当需要在嵌套函数中使用外层函数的
this
时,可以将this
赋值给一个变量(如that
或self
)。
1 2 3 4 5 6 7 8 9
| var obj = { method: function () { var that = this; setTimeout(function () { console.log(that); }, 1000); } }; obj.method();
|
合理使用箭头函数
在需要继承外层this
值的场景下,优先使用箭头函数。
1 2 3 4 5 6 7 8 9 10
| var obj = { name: "Example", method: function () { return () => { console.log(this.name); }; } }; var innerFunction = obj.method(); innerFunction();
|
使用call
、apply
和bind
来明确this
的指向
在需要动态改变this
指向的场景下,使用call
、apply
和bind
方法。
1 2 3 4 5 6 7 8 9
| function printName() { console.log(this.name); } var person1 = { name: "Bob" }; var person2 = { name: "Charlie" }; printName.call(person1); printName.apply(person2); var boundPrintName = printName.bind(person1); boundPrintName();
|
常见问题
this
指向不符合预期
在一些复杂的场景下,如函数作为回调函数传递、方法赋值给变量等,this
的指向可能会不符合预期。解决方法是使用call
、apply
、bind
方法或者箭头函数来明确this
的指向。
1 2 3 4 5 6 7 8 9 10
| var obj = { method: function () { console.log(this); } }; var func = obj.method; func();
var boundFunc = obj.method.bind(obj); boundFunc();
|
箭头函数与普通函数中this
的差异
箭头函数不会绑定自己的this
,它会继承外层的this
值;而普通函数的this
指向由调用方式决定。因此,在需要动态改变this
指向的场景下,不能使用箭头函数。
1 2 3 4 5 6 7 8 9 10 11
| var obj = { name: "Example", arrowMethod: () => { console.log(this.name); }, normalMethod: function () { console.log(this.name); } }; obj.arrowMethod(); obj.normalMethod();
|
严格模式下this
的变化
在严格模式下,全局作用域和未绑定对象的匿名函数中的this
值为undefined
,而不是全局对象。
1 2 3 4 5
| "use strict"; function strictFunction() { console.log(this); } strictFunction();
|