THIS

92 阅读3分钟

THIS:

全局上下文中的THISWINDOW;块级上下文中没有自己的THIS,它的THIS是继承所在上下文中的 THIS 的;在函数的私有上下文中,THIS 的情况会多种多样,也是接下来我们重点研究的;

THIS不是执行上下文(EC才是执行上下文),THIS是执行主体

例如:yzm拿着加了五个鸡蛋的鸡蛋灌饼去北京大饭店吃早餐(事情本身是吃早餐,yzm吃早餐,这件事情的主体是yzm【THIS】,在北京饭店吃,北京饭店是事情发生所在的上下文【EC】)

如何区分执行主体:

  1. 事件绑定:给元素的某个事件行为绑定方法,当事件行为触发,方法执行,方法中的THIS是当前元素本身(特殊:IE6~8中基于attachEvent方法实现的DOM2事件绑定,事件触发,方法中的THIS是WINDOW而不是元素本身)
  2. 普通方法执行(包含自执行函数执行、普通函数执行、对象成员访问调取方法执行等):只需要看函数执行的时候,方法名前面是否有“点”,有“点”,“点”前面是谁THIS就是谁,没有“点”THIS就是WINDOW[非严格模式]/UNDEFINED[严格模式]
  3. 构造函数执行(NEW XXX):构造函数体中的THIS是当前类的实例
  4. ES6中提供了 ARROW FUNCTION (箭头函数): 箭头函数没有自己的THIS,它的THIS是继承所在上下文中的THIS
  5. 可以基于CALL/APPLY/BIND等方式,强制手动改变函数中的THIS指向:这三种模式是和直接很暴力的(前三种情况在使用这三个方法的情况后,都以手动改变的为主)

事件绑定 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”指向谁

(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();
};

构造函数

构造函数体中的THIS在“构造函数执行”的模式下,是当前类的一个实例,并且THIS.XXX=XXX是给当前实例设置的私有属性

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();
f.__proto__.getNum();
Func.prototype.getNum(); 

箭头函数没有自己的THIS

  • THIS是所在上下文(EC(G))中的THIS:WINDOW
  • call/bind/apply 方法也没有用
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();
 
let obj = {
i: 0,
func() {
     setTimeout(function () {
        // 基于BIND把函数中的THIS预先处理为OBJ
        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(); 

练习

var num = 10;
var obj = {
    num: 20
};
obj.fn = (function (num) {
    this.num = num * 3;
    num++;
    return function (n) {
        this.num += n;
        num++;
        console.log(num);
    }
})(obj.num);
var fn = obj.fn;
fn(5);
obj.fn(10);
console.log(num, obj.num);