定义
this是js的一个关键字,this指向当前执行上下文中的对象。在不同的上下文中,this的值会有所不同。
不同上下文的this指向
全局上下文
在全局执行上下文中,this 指向全局对象。在浏览器中,这个全局对象是 window
,在Node.js中则是 global
。
console.log(this); // 在浏览器中输出 Window 对象
函数上下文
在普通函数内,this 默认为全局对象(在严格模式下为 undefined
)。但是如果通过对象调用一个方法,this 会指向那个对象。
function showThis() {
console.log(this);
}
showThis();// 在浏览器中输出Window对象或undefined
const obj = {
name: 'Tom',
fn1: function () {
console.log(this.name);
}
};
obj.fn1(); // 输出'Tom'
构造函数上下文
当使用 new
关键字调用构造函数时,this 指向新创建的实例对象。
function Person(name) {
this.name = name;
}
const p = new Person('Alice');
console.log(p.name); // 输出'Alice'
箭头函数的上下文
箭头函数不会创建自己的 this
值,它会从外部上下文中继承 this
。
const obj = {
value: 42,
getValue: function() {
return () => this.value; // 这里的 this 继承自 getValue 的上下文
}
};
const func = obj.getValue();
console.log(func()); // 输出 42
function foo() {
this.value = '111';
(() => {
console.log(this.value); // 此处的this继承自foo的实例
})();
}
new foo(); // 输出:111
DOM元素上下文
当在 DOM 元素的上下文中使用 this
时,this
都指向触发事件的 DOM 元素。
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this); // 输出按钮元素
});
<button onclick="console.log(this)">Click me</button>
类的上下文
在 ES6 类中,构造函数和类方法中的 this
关键字指向类的实例。因此,类的方法在被实例化对象调用时,this
会指向该实例。
class Animal {
constructor(name) {
this.name = name;
}
action() {
console.log(`${this.name} is sleeping.`);
}
}
const cat = new Animal('Cat');
cat.action(); // 输出 'Cat is sleeping.'
bind、apply和call的上下文
bind(), call() 和 apply() 都是 JavaScript 中函数对象的方法,它们用于操作函数并改变其行为。bind() 函数用于创建一个新的函数,这个新函数绑定指定的上下文(this);call() 和 apply() 则分别用于立即调用一个函数,并允许设置特定的this值以及传递给函数的具体参数列表。简而言之,bind() 创建绑定,call() 调用并设置this,apply() 同样调用但接受数组作为参数。
bind
基础用法
function introduce(greeting, punctuation1, punctuation2) {
console.log(greeting + ', ' + this.name + punctuation1 + punctuation2);
}
const person = { name: 'Bob' };
// 使用 bind 预设第一个参数
const boundIntroduce = introduce.bind(person, 'Hi');
// 在调用时再传递剩余参数
boundIntroduce('!', '!!'); // 输出: Hi, Bob!!!
多次bind绑定
// 多重绑定-this值、参数顺序
// 严格模式,防止this被封装到包装对象中
"use strict";
function log(...args) {
console.log(this, ...args);
}
const boundLog = log.bind('new value', 1, 2);
const newBoundLog = boundLog.bind('new new value', 3, 4,5);
newBoundLog(7, 8);
// new value 1 2 3 4 5 7 8
类数组对象转换为真正的数组
// 定义一个示例函数,里面使用 arguments 作为类数组对象
function exampleFunction() {
// 获取类数组对象 arguments
const args = arguments;
// 输出类数组对象
console.log('类数组对象 arguments:', args);
// 使用 Array.prototype.slice.call() 将类数组对象转换为真正的数组
const argsArray = Array.prototype.slice.call(args);
// 输出转换后的数组
console.log('转换后的数组:', argsArray);
}
// 调用示例函数,传入一些参数
exampleFunction(1, 'two', true, { key: 'value' });
以下方法确保了 Array.prototype.slice
的 this
上下文在调用时始终指向 Array.prototype.slice
,使得可以更灵活地使用这个方法。
function exampleFunction() {
const unboundSlice = Array.prototype.slice;
const slice = Function.prototype.call.bind(unboundSlice);
// 获取类数组对象
const args = arguments;
// 使用绑定的 slice 函数将类数组对象转换为数组
const argsArray = slice(args);
console.log('类数组对象 arguments:', args);
console.log('转换后的数组:', argsArray);
}
// 调用示例函数,传入一些参数
exampleFunction(1, 'two', true, { key: 'value' });
apply
基础用法
function greet(greeting, punctuation) {
return greeting + ', ' + this.name + punctuation;
}
const person = { name: 'Alice' };
// 使用 apply 调用 greet 函数
const message = greet.apply(person, ['Hello', '!']);
console.log(message); // 输出: "Hello, Alice!"
数组方法应用于类数组对象
const arrayLike = { 0: 'hello', 1: 'world', length: 2 };
const result = Array.prototype.join.apply(arrayLike, [', ']);
console.log(result); // 输出: "hello, world"
实现替代数组展开语法
一般而言,fn.apply(null, args) 等同于使用参数展开语法的 fn(...args),只是在前者的情况下,args 期望是类数组对象,而在后者的情况下,args 期望是可迭代对象。
//调用
const numbers = [5, 6, 2, 3,7];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
console.log('max, min', max, min);
//参数展开
const array = ["a", "b"];
const elements = [0, 1,2];
array.push.apply(array, elements);
console.info(array);
array.push(...elements);
console.log(array);
//批量添加数组元素
const array = ["a", "b"];
const elements = [0, 1,2];
array.push.apply(array, elements);
console.info(array);
array.push(...elements);
console.log(array);
call
基础用法
function introduce(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Alice' };
// 使用 call 传递所有参数
introduce.call(person, 'Hello', '!'); // 输出: Hello, Alice!
call与bind的区别
1、call立即执行,bind返回一个新函数
2、call一次性传递所有参数,bind不一定一次性传递所有参数,可以第一次传递部分参数,剩余参数传给返回值
call与apply的区别
call使用逗号分隔的参数进行传递,apply则是将所有参数放入一个数组进行传递
异步操作上下文
setTimeout/setInterval 的上下文
setTimeout和setInterval中的回调函数默认指向undefined或全局对象
setTimeout(() => {
console.log(this)
}, 0)
setInterval(() => {
console.log(this)
}, 1000)
Promise的上下文
Promise的this也默认指向undefined或全局对象
const promise = new Promise(function(resolve, reject) {
console.log(this); //undefined (严格模式) 或者是全局对象 (非严格模式)
resolve('完成');
});