1、this的五种情况分析(this是执行主题,就是谁把他执行的,和在哪创建在哪执行都没关系)
- 函数执行:看方法执前边是否有点,如果没有点指向window(严格模式下指向undefined)。有点,点前边是谁,this就指向谁。
const fn=function func(){
console.log(this);
}
const obj={
name: 'ling',
fn:fn,
}
fn();// this是window
obj.fn();// this是obj
- 给某个元素的某个事件行为绑定方法,当事件行为触发,方法中的this是当前元素本身,排除attachEvent。
document.body.addEventListener('click', function(){
console.log(this); // this是body
})
-
构造函数体中的this是当前类的实例。
-
箭头函数没有执行主题,所用到的this都是所处上下文的this。
const demo={
name: 'demo',
fn(){
console.log(this); //点前边是demo执向demo
setTimeout(function(){
console.log(this); // 函数执行前边没点,执向window
}, 1000);
setTimeout(()=>{
console.log(this);// 指向上级demo
}), 1000);
}
}
demo.fn();
- 可以基于Function.prpototype上的call/apply/bind去改变this指向。
function func(this, x, y){
console.log(this, x, y);
}
let obj={
name: 'ling',
}
func通过原型链__proto__找到Function.prototype.call。去执行call。call作用就是将func的this改为第一个参数。并且把params实参传给func,让func函数立即执行。
func.call(obj, 1, 2);
apply和call类似,只不过传参是数组。
bind方法则和二者不太一样:
- bind修改this不会立即执行。
- 把传进来信息obj,和参数存储起来,闭包。
- 执行bind返回一个新函数例如,proxy。
document.body.addEventListener('click', fnc.bind(obj, 1, 2, 3));
把proxy绑定给元素的事件,当事件触发执行是返回的proxy,在proxy内部再去执行func,并把this和参数都改为上边存储的值。(把传进来信息obj,和参数存储起来,闭包。)
2、手写call、bind
手写call,改变this,就是函数执行前边有点,点前边有东西。并且不影响原本属性,使用symbol唯一key来处理。原理就是点定this机制。
Function.prototype.call=function call(context, ...params){
const self=this; // 此处this是call前函数
const key=Symbol('key');
let result;
context[key]=self;
result=context[key](...params); // 函数执行,函数前边点是context传进来改变this值。
delete context[key]; // 处理结束不影响原函数,删除该属性。
return result;
}
const obj={name: '123'}
function func(num1, num2){
console.log(this, num1, num2); //{name: 123}, 1, 2
};
func.call(obj, 1,2,3);// call前有func,此时this是func函数。
此时绑定基本数据类型值是存在问题的,做如下优化。
Function.prototype.call=function call(context, ...params){
const self=this; // 此处this是call前函数
const key=Symbol('key');
let result;
context==null?context=window:null;
!/^(object|function)$/i.test(typeof context)?context=Object(context):null;//处理基本数据类型,改为对象
context[key]=self;
result=context[key](...params); // 函数执行,函数前边点是context传进来改变this值。
delete context[key]; // 处理结束不影响原函数,删除该属性。
return result;
}
- 手写bind
Function.ptototype.bind=function bind(context, ...params){
const self=this;
return function proxy(...arg){//arg是proxy执行时候可能存在参数,event
self.apply(context, params.concat(arg));
}
}
- 柯里化函数思想:就是将上下文需要用到的参数,提前存起来,就是柯里化函数思想,比如bind实现时候,需要先将this也即是执行函数存起来。
3、掌握this的好玩应用,像鸭子
就是类数组和数组差不多,但是数组东西无法使用,比如slice,forEach等
[].forEach.call(arguments, item=>{console.log(item)});
//相当于Array.prototype.forEach