大部分情况可遵循:谁调用的,this就指向谁,没有指定谁调用的就默认是全局对象(浏览器环境是window,在nodejs中指向这个文件的module对象)。
一、默认绑定
概念:函数调用时无任何调用前缀的情景
function fn() {
console.log(this); //window
console.log(this.name);//123
};
function fn1() {
"use strict";
console.log(this); //undefined
console.log(this.name);//报错
};
var name = '123';
fn();
fn1() //TypeError: Cannot read property 'a' of undefined
函数调用时前面没有指定任何对象,这种情况下this指向全局对象window(非严格模式),严格模式指向undefined。
为什么this指向全局对象window(非严格模式)?
在这个上下文(执行环境)中函数并没有绑定任何一个对象,往上找,所以 this 指向 window;
从作用域和调用链方面看就很好理解了,函数 fn 的上一级是全局, 相当于是全局调用的fn,所以fn的 this 指向全局;
为什么严格模式this指向undefined?
在严格模式下,this将保持他进入执行环境时的值,如果 this 没有被执行环境(execution context)定义,那它将保持为 undefined。
二、隐式绑定
概念:如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上,如果函数调用前存在多个对象,this指向距离调用自己最近的对象。
function fn() {
console.log(this.name);
};
let obj = {
func: fn,
};
let obj1 = {
name: '听风是风',
o: obj
};
obj1.o.func() //undefined
为什么?
obj1.o === obj
obj.func === fn
综上:是obj调用的fn,所以this指向obj,this.name即obj.name,obj并没有name属性,所以是undefined。
隐式丢失
在特定情况下会存在隐式绑定丢失的问题,最常见的就是作为参数传递以及变量赋值。
var name = '111';
let obj = {
name: '222',
fn: function () {
console.log(this.name);
}
};
let obj1 = {
name: '333'
}
function fn1(param) {
param();
};
fn1(obj.fn);//111
let fn2 = obj.fn;
fn2(); //111
obj1.fn = obj.fn;
obj1.fn(); //333
分析:
obj.fn作为fn1的参数传入,调用obj.fn的函数就是fn1,而fn1并没有绑定到任意对象上,所以this指向全局对象window,所以输出111。- 将
obj.fn赋值给fn2执行fn2(),fn2默认绑定的是全局对象window,所以this指向window,所以输出111。 - 给
obj1添加fn属性,赋值为obj.fn,执行obj1.fn(),this就指向了调用它的对象obj1,所以输出333。
三、显式绑定
概念:通过call、apply以及bind方法改变this的行为,相比隐式绑定,我们能清楚的感知 this 指向变化过程。
在javascript中调用一个函数时,函数处于一个被动的状态,称之为函数调用。
而call与apply让函数从被动变主动,函数能主动选择自己的上下文,称之为函数应用。
let obj1 = {
name: '111'
};
let obj2 = {
name: '222'
};
let obj3 = {
name: '333'
}
var name = '444';
function fn() {
console.log(this.name);
};
fn(); //444
//call和apply改变this指向的同时还会执行函数
fn.call(obj1); //111
fn(); //444
fn.apply(obj2); //222
fn(); //444
//bind返回的是一个bound func, 所以要加个()执行
fn.bind(obj3)(); //333
let boundFn = fn.bind(obj1);
boundFn.call(obj2);//111
boundFn.apply(obj2);//111
boundFn.bind(obj2)();//111
fn(); //444
注意:
如果在使用call之类的方法改变this指向时,指向参数提供的是null或者undefined,那么 this 将指向全局对象。
call、apply与bind区别:
call、apply与bind都用于改变this绑定。但call、apply在改变this指向的同时还会执行函数,而bind在改变this后是返回一个全新的boundFunction绑定函数。
这也是为什么上方例子中bind后还加了一对括号()的原因。bind属于硬绑定,返回的 boundFunction的this指向无法再次通过bind、apply或call修改;call与apply的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。call与apply功能完全相同,唯一不同的是call方法的形参是散列形式 ,而apply方法的形参是一个数组 。在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。
let obj = {
name: 'aaa'
};
function fn(age,describe) {
console.log(`我是${this.name},我的年龄是${age},${describe}!`);
};
fn.call(obj,'26','ccc');//我是aaa,我的年龄是26,ccc
fn.apply(obj,['26','ccc']);//我是aaa,我的年龄是26,ccc
四、new绑定
new一个函数三大步
- 以构造器的
prototype属性为原型,创建新对象; - 将
this(可以理解为:上句创建的新对象)和调用参数传给构造器,执行; - 如果构造器没有手动返回对象,则返回第一步创建的对象
function Fn(){
this.name = '111';
};
let echo = new Fn();
echo.name//111
构造调用创建了一个新对象echo,而在函数体内,this将指向新对象echo上(可以抽象理解为新对象就是this)。
this绑定优先级
- 显式绑定 > 隐式绑定 > 默认绑定
- new绑定 > 隐式绑定 > 默认绑定
- 显示和new同时出现会报错。
为什么显示和new同时出现会报错?
new Fn()返回的是一个对象,而call是Function的方法,所以报错 call is not a function
function Fn(){
this.name = '111';
};
let obj = {
name:'2222'
}
let echo = new Fn().call(obj);//报错 call is not a function
优先级示例:
let obj = {
name:'111',
fn:function () {
console.log(this.name);
}
};
obj1 = {
name:'222'
};
//显式>隐式,
obj.fn.call(obj1);// 222
//new>隐式
let echo = new obj.fn();
//相当于把obj.fn当做构造器 new出一个对象,新对象的name属性来自原构造方法
echo.name;//111
五、箭头函数绑定
准确来说,箭头函数中没有this,箭头函数的this指向取决于外层作用域中的this,外层作用域或函数的this指向谁,箭头函数中的this便指向谁。
function fn() {
return () => {
console.log(this.name);
};
}
let obj1 = {
name: '111'
};
let obj2 = {
name: '222'
};
fn.call(obj1)(); // 111,fn this指向obj1,箭头函数this也指向obj1
fn.call(obj2)(); // 222,fn this 指向obj2,箭头函数this也指向obj2
let bar = fn.call(obj1); //fn this指向obj1,bar返回的是一个函数
bar.call(obj2); //111
为什么我们第一次绑定this并返回箭头函数后,再次改变this指向没生效呢?
前面说了,箭头函数的this取决于外层作用域的this,fn.call(obj1)执行时this指向了obj1,所以箭头函数的this也指向obj1。除此之外,箭头函数this还有一个特性,那就是一旦箭头函数的this绑定成功,也无法被再次修改。所以再次改变时没有生效。
为什么箭头函数的this绑定成功后无法被再次修改?
箭头函数的this是在词法层面就绑定到了外层作用域,他的this只能是来自外层作用域的this,无论你通过什么方式都不能改变,除非你修改了外层作用域的this。
如何修改箭头函数this的指向?
箭头函数的this就像作用域继承一样从上层作用域找,因此我们可以修改外层函数this指向达到间接修改箭头函数this的目的。
setTimeout中的this指向
setTimeout(超时调用)都是在全局作用域中执行的,所以函数中的this的值在非严格模式下指向window对象,在严格模式下是undefined。
本文深入解析JavaScript中this的五种绑定规则:默认、隐式、显式、new及箭头函数绑定,阐述不同情景下this指向的变化及原因,帮助开发者理解和避免常见错误。
397

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



