一、为什么js需要this
在js中,this 是个特殊的关键字,通常用于指向当前执行上下文中的对象。
也就是说,this 的结果只有可能是某个object对象或者undefined,不可能是function。
this能够提供对当前对象的引用,并且支持动态上下文,是面向对象思想的体现之一。
二、普通函数的this的指向和赋值时机
1、全局上下文
在全局作用域中(不在任何函数或对象中),this 指向全局对象。在浏览器中,this 指向window;在Node.js中,this 指向global。
在严格模式中,this 在全局上下文中指向 undefined。
赋值时机:代码在全局作用域中执行时,this 被赋值为全局对象。
2、对象的方法
当函数作为对象的方法调用时,this 指向调用该方法的对象。
其中,全局作用域下的函数也符合这种情况,其内的 this 指向window或global。
赋值时机:函数被调用时。
function func1() {
console.log(this);
}
func1(); // 在浏览器中输出: Window {...}(非严格模式)
const obj = {
name: 'Alice',
func2: function() {
console.log(this.name);
}
};
obj.func2(); // 输出: Alice
3、构造函数调用
当函数作为构造函数(使用 new 关键字)调用时,this 指向新创建的对象。
赋值时机:方法被调用时,this 被赋值为调用该方法的对象。
function Person(name) {
this.name = name; // this 指向新创建的对象
}
const person = new Person('Alice');
console.log(person.name); // 输出: Alice
4、DOM 事件处理函数
当函数作为 DOM 事件处理函数时,this 指向触发事件的 DOM 元素。
赋值时机:事件触发时,this 被赋值为触发事件的 DOM 元素。
document.querySelector('button').addEventListener('click', function() {
console.log(this); // this 指向被点击的按钮元素
});
三、箭头函数的this的指向和赋值时机
1、箭头函数没有自己的 this ,它会继承外层作用域的 this 值
因此可以认为,箭头函数中的 this 在定义时就已经确定,而不是在调用时确定。
const obj = {
name: 'Alice',
greet: function() {
setTimeout(() => {
console.log(this.name); // this 继承自外层作用域
}, 100);
}
};
obj.greet(); // 输出: Alice
上面代码中的情况容易判断,此时箭头函数的 this 指向对象 obj 本身。
2、如果箭头函数写在普通函数中,甚至多层箭头函数调用呢?
结果是,箭头函数会逐层继承,直到非箭头函数。
const obj = {
name: 'Alice',
greet: function() {
const outerFunc = () => {
const innerFunc = () => {
console.log(this.name); // this 继承自 greet 函数的 this
};
innerFunc();
};
outerFunc();
}
};
obj.greet(); // 输出: Alice
3、如何理解【箭头函数中的 this 在定义时就已经确定】?
outerFunc是在运行时才能确定 this 指向,那么innerFunc如何在定义时就能确定 this 的值呢?
function outerFunc() {
const innerFunc = () => {
console.log(this); // this 继承自 outerFunc 的 this
};
innerFunc();
}
outerFunc(); // 输出: Window {...}(非严格模式)或 undefined(严格模式)
事实是:innerFunc的 this 静态绑定了 outerFunc的 this 的引用!
在onterFunc定义时,innerFunc的 this 并不是一个独立的值,而是直接指向 onterFunc的 this。等待onterFunc被调用时,其 this 被赋值,innerFunc的 this 也就获得了相同的指向。
4、为何要强调箭头函数继承的是外层【作用域】的 this 值?
因为普通函数是动态绑定的,只与调用时的上下文有关;而箭头函数是定义时静态绑定的,this 绑定到哪里需要看所在作用域。
而作用域和上下文是两个不同概念,作用域指的是变量的可见性和生命周期,是静态的,在代码编写时就已经确定。
由于对象字面量没有自己的作用域,因此将 func2 从普通函数改为箭头函数后,输出会从 Alice 改为 Bob。
var name = 'Bob';
const obj = {
name: 'Alice',
func2: () => {
console.log(this.name);
}
};
obj.func2(); // 输出: Bob
综上,记忆【箭头函数没有自己的 this ,它会继承外层作用域的 this 值】比记忆【箭头函数中的 this 在定义时就已经确定】更加重要。
四、回调函数的this的指向和赋值时机
首先看一个例子:
var name = "lucy";
var obj = {
name: "martin",
say: function () {
console.log(this.name);
}
};
obj.say(); // 输出: martin
setTimeout(obj.say, 0); // 输出: lucy
可以看到,回调函数中的 this 指向了全局对象。
原因是:当obj.say作为回调传递时,它不再是作为obj的方法调用,而是作为一个独立的函数调用。setTimeout 在内部会直接调用传入的回调函数,而不是通过对象调用。
这种情况本质上是 this 动态绑定的对象丢失了,可以通过修改为箭头函数来避免这种情况。
五、apply、call、bind 改变函数运行时的 this 指向
1、apply
作用:临时改变 this 指向一次,并立即执行
参数:① this 的目标指向对象;②函数参数数组
fn.apply(obj,[1,2]);
2、call (与 apply 基本相同)
作用:临时改变 this 指向一次,并立即执行
参数:① this 的目标指向对象;②函数参数数组
fn.call(obj,[1,2]);
3、bind
作用:返回一个永久改变 this 指向的函数,不会立即执行
参数:① this 的目标指向对象;②函数参数数组(可分多次传入)
const bindFn = fn.bind(obj);
bindFn(1,2)
2375

被折叠的 条评论
为什么被折叠?



