写之前先做个分类,大致就这6种情况:
- function 中的 this //
指向调用者本身
- 箭头函数() => {} 中的 this //
定义时最靠近的function运行时的this
- funtion 在 call, bind, apply等 中的 this //
指向重定向的this
- 箭头函数 在 call, bind, apply等 中的 this //
定义时最靠近的function运行时的this
- funciton 回调函数中的 this //
虚空调用,指向全局
- 箭头函数 回调函数中的 this //
定义时最靠近的function运行时的this
由浅入深分析:
1. funtion 中的 this 指向调用者本身
最基本的this指向问题,哪个对象调用就指向这个对象。
比如dom操作中: document.getElementById('btn').onclick = function() {console.log(this);}// 当btn被click的时候,this指向的就是document.getElementById('btn')
这里onclick 是一个 function ,当被点击的时候 => document.getElementById('btn')调用onclick函数,所以指向了 document.getElementById('btn');
比如在Object中:
const man = {
age: 18,
getAge: function(){console.log(this.age)},
};
window.age = 0;
man.getAge(); // 18
const getAge = man.getAge;
getAge(); // 0
getAge只是一个function,当被man调用的时候,则指向man,当被全局调用的时候则指向了window(严格模式下指向undefined,下一篇关于内存会讲)
2. 箭头函数() => {} 中的 this // 定义时最靠近的function运行时的this
听起来有点绕口,不过原因也比较简单,
箭头函数本身的设计是用于匿名函数的,
没有自身的this和arguments, 两者皆附属于定义时最靠近的function,
而function的this则取决于它运行时候的调用对象,所以合并起来就是被定义时最靠近的function + 运行时的this
举3个例子:1.const a = {cb: function(){setTimeout(() => console.log(this))}};
const fn = () => console.log(this); // 箭头函数,定于于全局
2.const b = {cb: function(){setTimeout(fn)}} // 通过变量名引入到b.cb 中
3.const c = {cb: function(params_fn){setTimeout(params_fn)}} // 实验中会讲fn通过传参引入到c.cb 中
然后一个个运行
a.cb() // this指向a ,因为箭头定义最靠近的function->cb, this附属cb,cb运行时候是a调用的
b.cb() // this指向window,因为箭头定义是虚空定义,指向全局,所以运行时候指向了window
c.cb(fn) // this指向window,理由同b
上面的例子还不足以证明this是附属最靠近的function,再加一个const cb = a.cb;
cb();// this指向window
由此可见箭头函数的this直接附属了定义时候的最靠近的function, this的指向取决于附属function的运行环境
3 + 4. bind ,call ,apply
这三个就简单点说了,因为用过的都知道这个是强行转变this指向的,比如bind('硬气'),相当于一个装饰器, 直接 this = '硬气',但是因为箭头函数没有this和arguments,所以对箭头函数不生效,箭头函数的指向已经不受影响。
5 + 6. 回调函数中的this指向
回调函数中,不顾是function 还是 () => {},运行的时候实际上都属于虚空运行,什么叫虚空运行,就是没有调用对象,而在没有调用对象的时候,function的this一般都指向全局(严格模式下为undefined)
, 箭头函数依旧依赖附属this来确定this指向。
回调函数一般都是定义于一个函数之内的,箭头函数很容易确定this的指向,同时不会改变回调函数内部的this指向,所以这也是回调函数,多用箭头函数的原因。
扩展一下: 防抖函数
防抖函数本身的知识点是闭包,不过里面的一个定时器却是用了this指向的技巧
写一个常规的防抖
function dobounce(fn, time = 1000) {
let timeout = null; // 创建变量
return function() {
if(timeout) clearTimeout(timeout); //如果已有定时器,清除定时器
timeout = setTimeout(() => fn.apply(this, arguments), time); // 重新创建定时器
}
}
这里的setTimeout()中箭头函数直接继承闭包返回值function的this 和 arguments, 然后调用这个防抖a = {name: 4, test: dobounce(function(a, b) {console.log(this.name + a + b)})};
a.test(5, 6) // 15
不过防抖里面传入的fn就不要用箭头函数了,原因参考箭头函数的this指向问题。
总结:
箭头函数因为没有自己的this,所以不会被各种外界因素改变this,只附属于定义时最靠近的function
而 function 一般情况下this都是取决于调用对象,但是可以被bind, call ,apply等改变this指向,如果是虚空运行(回调函数中,则指向全局)