在开发过程中往往被this弄得晕头转向,这篇文章让我们一起把this彻底搞懂
THIS:全局上下文中的THIS是window;块级上下文中没有自己的THIS,它的THIS是继承所在上下文中的THIS的;在函数的私有上下文中,THIS的情况会多种多样,也是接下来我们重点研究的;
THIS不是执行上下文(EC才是执行上下文),THIS是执行主体
- 例如:奂康拿着加了五个鸡蛋的鸡蛋灌饼去北京大饭店吃早餐(事情本身是吃早餐,奂康吃早餐,这件事情的主体是奂康【THIS】,在北京饭店吃,北京饭店是事情发生所在的上下文【EC】)
如何区分执行主体:
-
-
事件绑定:给元素的某个事件行为绑定方法,当事件行为触发,方法执行,方法中的THIS是当前元素本身(特殊:IE6~8中基于attachEvent方法实现的DOM2事件绑定,事件触发,方法中的THIS是WINDOW而不是元素本身)
// 事件绑定 DOM0 DOM2 let body = document.body; body.onclick = function () { // 事件触发,方法执行,方法中的THIS是BODY console.log(this); }; body.addEventListener('click', function () { console.log(this); //=>BODY }); // IE6~8中的DOM2事件绑定 // box.attachEvent('onclick', function () { // console.log(this); //=>WINDOW // });
-
-
-
普通方法执行(包含自执行函数执行、普通函数执行、对象成员访问调取方法执行等):只需要看函数执行的时候,方法名前面是否有“点”,有“点”,“点”前面是谁THIS就是谁,没有“点”THIS就是WINDOW[非严格模式]/UNDEFINED[严格模式]
(function () { console.log(this); //=>window })(); let obj = { fn: (function () { console.log(this); //=>window return function () {} })() //把自执行函数执行的返回值赋值给OBJ.FN }; function func() { console.log(this); } let obj = { func: func }; func(); //=>方法中的THIS:WINDOW obj.func(); //=>方法中的THIS:OBJ [].slice(); //=>数组实例基于原型链机制,找到ARRAY原型上的SLICE方法([].slice),然后再把SLICE方法执行,此时SLICE方法中的THIS是当前的空数组 Array.prototype.slice(); //=>SLICE方法执行中的THIS:Array.prototype [].__proto__.slice(); //=>SLICE方法执行中的THIS:[].__proto__===Array.prototype function func() { // THIS => WINDOW console.log(this); } document.body.onclick = function () { // THIS => BODY func(); };
-
-
-
构造函数执行(NEW XXX):构造函数体中的THIS是当前类的实例
function Func() { this.name = "F"; console.log(this); //=>构造函数体中的THIS在“构造函数执行”的模式下,是当前类的一个实例,并且THIS.XXX=XXX是给当前实例设置的私有属性 } Func.prototype.getNum = function getNum() { // 而原型上的方法中的THIS不一定都是实例,主要看执行的时候,“点”前面的内容 console.log(this); }; let f = new Func; f.getNum();//this->f f.__proto__.getNum();//this->f.__proto__ Func.prototype.getNum(); //this->Func.prototype
-
-
-
ES6中提供了ARROW FUNCTION(箭头函数): 箭头函数没有自己的THIS,它的THIS是继承所在上下文中的THIS
let obj = { func: function () { console.log(this); }, sum: () => { console.log(this); } }; obj.func(); //=>THIS:OBJ obj.sum(); //=>箭头函数THIS是所在上下文(EC(G))中的THIS:WINDOW obj.sum.call(obj); //=>箭头函数是没有THIS,所以哪怕强制改也没用 THIS:WINDOW 不建议乱用箭头函数(部分需求用箭头函数还是很方法便的) let obj = { i: 0, // func:function(){} func() { // THIS:OBJ let _this = this; setTimeout(function () { // THIS:WINDOW 回调函数中的THIS一般都是WINDOW(但是有特殊情况) _this.i++; console.log(_this); }, 1000); } }; obj.func();
-
-
-
可以基于CALL/APPLY/BIND等方式,强制手动改变函数中的THIS指向:这三种模式是和直接很暴力的(前三种情况在使用这三个方法的情况后,都以手动改变的为主)
let obj = { i: 0, func() { setTimeout(function () { // 基于BIND把函数中的THIS预先处理为OBJ,也就是把this转换为外层的this this.i++; console.log(this); }.bind(this), 1000); } }; obj.func(); let obj = { i: 0, func() { setTimeout(() => { // 箭头函数中没有自己的THIS,用的THIS是上下文中的THIS,也就是OBJ this.i++; console.log(this); }, 1000); } }; obj.func();
-