THIS:全局上下文中的THIS是WINDOW;块级上下文中没有自己的THIS,它的THIS是继承所在上下文中的THIS的;在函数的私有上下文中,THIS的情况会多种多样;
THIS不是执行上下文(EC才是执行上下文),THIS是执行主体
例如:我拿着加了五个鸡蛋的鸡蛋灌饼去大饭店吃早餐(事情本身是吃早餐,"我"吃早餐,这件事情的主体是"我"【THIS】,在饭店吃,饭店是事情发生所在的上下文【EC】
this的五种情况
- 事件绑定:给元素的某个事件行为绑定方法,当事件行为触发,方法执行,方法中的THIS是当前元素本身(特殊:IE6~8中基于attachEvent方法实现的DOM2事件绑定,事件触发,方法中的THIS是WINDOW而不是元素本身)
-
普通方法(函数)执行(包含自执行函数执行、普通函数执行、对象成员访问调取方法执行等):只需要看函数执行的时候,方法名前面是否有“点”,有“点”,“点”前面是谁THIS就是谁,没有“点”的话THIS就是window(非严格模式)/undefined(严格模式)
自执行函数中的THIS一般都是window/undefined
回调函数中的THIS一般也是window/undefined(除非某个函数内部给回调函数做了特殊的处理,这样回调函数中的THIS有自己的特殊情况)
-
普通方法(函数)执行(包含自执行函数执行、普通函数执行、对象成员访问调取方法执行等):只需要看函数执行的时候,方法名前面是否有“点”,有“点”,“点”前面是谁THIS就是谁,没有“点”的话THIS就是window(非严格模式)/undefined(严格模式)
- 构造函数执行(NEW XXX):构造函数体中的THIS是当前类的实例
- 箭头函数: 箭头函数没有自己的THIS,它的THIS是继承所在上下文中的THIS
- 可以基于CALL/APPLY/BIND等方式,强制手动改变函数中的THIS指向:这三种模式很直接很暴力(前三种情况在使用这三个方法的情况后,都以手动改变的为主)
call/bind/apply
- call:[function].call([context],params1,params2,...) [function]作为Function内置类的一个实例,可以基于__proto __找到Function.prototype的call方法,并且把找到的call方法执行;在call方法执行的时候,会把[function]执行,并且把函数中的THIS指向为[context],并且把params1,params2...等参数值分别传递给函数
- apply:[function].apply([context],[params1,params2,...]) 和call作用一样,只不过传递给函数的参数需要以数组的形式传递给apply
- bind:[function].bind([context],params1,params2,...) 语法上和call类似,但是作用和call/apply都不太一样;call/apply都是把当前函数立即执行,并且改变函数中的this指向的,而bind是一个预处理的思想,基于bind只是预先把函数中的this指向[context],把params这些参数值预先存储起来,但是此时函数并没有被执行
call的重写
// 核心原理:给CONTEXT设置一个属性(属性名尽可能保持唯一,避免我们自己设置的属性修改默认对象中的结构,例如可以基于Symbol实现,也可以创建一个时间戳名字),属性值一定是我们要执行的函数(也就是THIS,CALL中的THIS就是我们要操作的这个函数);接下来基于CONTEXT.XXX()成员访问执行方法,就可以把函数执行,并且改变里面的THIS(还可以把PARAMS中的信息传递给这个函数即可);都处理完了,别忘记把给CONTEXT设置的这个属性删除掉(人家之前没有你自己加,加完了我们需要把它删了)
// 如果CONTEXT是基本类型值,默认是不能设置属性的,此时我们需要把这个基本类型值修改为它对应的引用类型值(也就是构造函数的结果)
Function.prototype.call = function call(context, ...params) {
// this->fn 当前要处理的函数
// context->obj 给函数改变的this
// params-> 给函数传递的参数
// 关键:对于context类型的处理(基本数据类型无法设置键值对)
context == null ? context = window : null;
if(! (typeof context == "function" || typeof context == "object")){
//Object()可以把值类型转换为对象类型
context = Object(context)
}
// 细节点:建立关联的属性保持唯一 Symbol
let key = Symbol('KEY'),
result;
context[key] = this;
result = context[key](...params);
delete context[key];
return result;
};
bind的重写
// 执行BIND(BIND中的THIS是要操作的函数),返回一个匿名函数给事件绑定或者其它的内容,当事件触发的时候,首先执行的是匿名函数(此时匿名函数中的THIS和BIND中的THIS是没有关系的)
// BIND的内部机制就是利用闭包(柯理化函数编程思想)预先把需要执行的函数以及改变的THIS再以及后续需要给函数传递的参数信息等都保存到不释放的上下文中,后续使用的时候直接拿来用,这就是经典的预先存储的思想
Function.prototype.bind = function bind(context = window, ...params) {
//this->func
let _this = this;
return function anonymous(...params) {
_this.call(context, ...params);
};
};