一、this 指向
在 JavaScript 中,this 的指向是一个重要且有时令人困惑的概念。它的值在不同的上下文中可能不同,主要取决于函数的调用方式。下面详细介绍普通函数和箭头函数中 this 的指向。
1. 普通函数中的 this
普通函数中的 this 指向取决于函数的调用方式:
-
全局上下文:
-
在浏览器中,如果你在全局作用域中调用一个函数,
this指向window对象(在严格模式下,this为undefined)。 -
例如:
function show() { console.log(this); // 浏览器中输出 window } show();
-
-
对象方法:
-
当函数作为对象的方法被调用时,
this指向调用该方法的对象。 -
例如:
const person = { name: 'Alice', greet: function() { console.log(this.name); // 输出 "Alice" } }; person.greet();
-
-
构造函数:
-
当函数被用作构造函数(即通过
new关键字调用时),this指向新创建的对象实例。 -
例如:
function Person(name) { this.name = name; } const alice = new Person('Alice'); console.log(alice.name); // 输出 "Alice"
-
-
事件处理函数:
-
在事件处理函数中,
this指向触发事件的 DOM 元素。 -
例如:
document.getElementById('myButton').addEventListener('click', function() { console.log(this); // 输出触发事件的 DOM 元素 });
-
2. 箭头函数中的 this
箭头函数的 this 行为与普通函数不同:
-
词法绑定:
-
箭头函数不会创建自己的
this,它的this绑定来自于它定义时的上下文(词法作用域)。因此,箭头函数中的this值由外部上下文决定。 -
例如:
const person = { name: 'Alice', greet: function() { setTimeout(() => { console.log(this.name); // 输出 "Alice",因为箭头函数中的 `this` 继承自 `greet` 方法的 `this` }, 1000); } }; person.greet();
-
【备注:箭头函数的其他特性】
箭头函数没有constructor,是匿名函数,不能作为构造函数,不能通过 new 调用;没有new.target 属性。在通过 new 运算符被初始化的函数或构造方法中,new.target 返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target 的值是 undefined箭头函数不绑定Arguments 对象。取而代之用 rest 参数...解决。由于 箭头函数没有自己的 this 指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定 this),他们的第一个参数会被忽略。(这种现象对于 bind 方法同样成立)- 箭头函数没有原型属性 Fn.prototype 值为 undefined
- 箭头函数不能当做 Generator 函数,不能使用 yield 关键字
注意:箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
参考:箭头函数与普通函数的区别
二、call、apply 和 bind 的作用和区别
call、apply 和 bind 是 Function 对象上的方法,用于改变函数的 this 指向。
1. call
-
作用:
-
call方法调用函数时,明确指定this的值,并且可以立即执行该函数。 -
语法:
function func(arg1, arg2, ...) { // 函数体 } func.call(thisArg, arg1, arg2, ...); -
示例:
function greet(greeting) { console.log(`${greeting}, ${this.name}`); } const person = { name: 'Alice' }; greet.call(person, 'Hello'); // 输出 "Hello, Alice"
-
2. apply
-
作用:
-
apply方法调用函数时,明确指定this的值,并且可以传入一个数组(或类数组对象)作为参数。 -
语法:
function func(arg1, arg2, ...) { // 函数体 } func.apply(thisArg, [arg1, arg2, ...]); -
示例:
function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } const person = { name: 'Alice' }; greet.apply(person, ['Hello', '!']); // 输出 "Hello, Alice!"
-
3. bind
-
作用:
-
bind方法创建一个新的函数,调用时this被绑定到指定的thisArg,并且可以预设参数。 -
语法:
function func(arg1, arg2, ...) { // 函数体 } const boundFunc = func.bind(thisArg, arg1, arg2, ...); -
示例:
function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } const person = { name: 'Alice' }; const boundGreet = greet.bind(person, 'Hello'); boundGreet('!'); // 输出 "Hello, Alice!"
-
区别总结
相同:
- 都是用来改变函数的 this 对象的指向的。
- 第一个参数都是 this 要指向的对象。
- 都可以利用后续参数传参。
不同:
- apply 和 call 传入的参数列表形式不同。apply 接收 arguments,call 接收一串参数列表
- bind:语法和 call 一模一样,区别在于立即执行还是等待执行,bind 不兼容 IE6~8
- bind 主要就是将函数绑定到某个对象,bind()会创建一个函数,返回对应函数便于稍后调用;而 apply、call 则是立即调用。
基于 Function.prototype 上的 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表, bind方法通过传入一个对象,返回一个this 绑定了传入对象的新函数。这个函数的 this指向除了使用new 时会被改变,其他情况下都不会改变。若为空默认是指向全局对象 window。