this(函数执行的主体)
- 全局上下文的this是window
块级上下文没有自己的this,所用的this是继承上下文的this(箭头函数也是)
事件绑定
普通函数执行
其他情况看点,点前面是谁,this就是谁;没点就是window
call、apply、bind强制改变this指向问题
都是用来改变某一个函数中this关键字指向的
初步了解 call&bind&apply
call改变函数执行的this,并且执行函数
语法: function.call(thisArg, arg1, arg2, …) 参数:第一个this指向 第二个及后面的是实参 返回值:函数执行的返回值
bind改变函数执行的this,但是不执行函数
语法:function.bind(thisArg[, arg1[, arg2[, …]]]) 参数:第一个this指向 第二个及后面的是实参 返回值:改变this指向的函数体
apply改变函数执行的this,并且执行函数
语法 :func.apply(thisArg, [argsArray]) 参数:第一个参数是this指向,第二个参数是数组 返回值:函数执行的返回值
- apply与call的区别在于传参方式不同我们需要把所有参数放在这个数组里,apply会把数组里的每一项一个一个传给函数
示例:
let a = 1;
let obj = {
a: 2,
fn: fn
};
function fn(b, c) {
console.log(this.a);
return b + c;
};
fn(4, 5); //正常执行fn,this指向的就是window,此时a为window里的属性,因为没有,输出undefined
fn.call(obj); //fn.call(obj)执行一次函数,会把this改成obj 不输出
fn.bind(obj);
console.log(fn.bind(obj));//bind改变后会输出函数体,但不执行,返回的是改变后的函数体,看不出来,但已经改变了
fn.apply(obj, [1, 2]); //apply的传参方式不同,必须以一个数组的方式传参,只接收两个参数:第一个参数是this指向,第二个参数值是数组,把所有需要传递的参数都放在数组里;返回值是函数结果,也会执行词义函数
console.log(fn.apply(obj, [1, 2]));
详细了解 call&bind&apply
call
1、[fn].call([this],[param]…)
- fn.call:当前实例(函数fn)通过原型链的查找机制,找到function。prototype上的call方法
- fn.call() : 把找到的call方法执行
- 当call方法执行的时候,内部处理了一些事情
- =>首先把要操作函数中的this关键字变为call方法第一个传递的实参值
- =>把call方法第二个及第二个以后的实参获取到
=>把要操作的函数执行,并且把第二个以后的传递进来的实参传给函数
let sum=function(a,b){
console.log(this,a+b);//=>opt
};
let opt={n:20};
sum.call(opt,20,30)//=>call执行 call中的this是sum 把this(call中的)中的“this关键字”改为opt 把this(call中的)执行,把20,30分别传递给它 //=>sum中this:opt a=20 b=30
sum.call.call(opt)
//1.sum.call 找到Function.prototype上的call方法(也是一个函数,也是函数类的一个实例,也可以继续调用call/apply等方法) =>A(函数)
//2.A.call(opt) 继续找到原型上的call方法,把call方法执行:把A中的this关键字修改为opt,然后把A执行
CALL中的细节
1.非严格模式下,如果参数不传,或者第一个传递的是null/undefined,THIS都指向WINDOW
2.在严格模式下(加 ‘use strict’ 字符串就是严格模式),第一个参数是谁,THIS就指向谁(包括null/undefined),不传THIS就是undefined
fn.call(obj, 10, 20);//=>this:obj a=10 b=20 fn.call(10, 20);//=>this:10 a=20 b=undefined fn.call();//=>this:window a=undefined b=undefined fn.call(null);//=>this:window fn.call(undefined);//=>this:window
阿里call面试题
/* Function.prototype.call = function (context, ...arg) { //=>my_call方法中的this就是我们要处理的那个函数(fn/sum...) this.toString().replace('this', context); this(...arg); }; */ function fn1() { console.log(1); } function fn2() { console.log(2); } fn1.call(fn2);//=>1 /* * 执行CALL方法 * 1.CALL方法中的THIS:FN1 * 2.CALL方法中的CONTEXT:FN2 */ fn1.call.call(fn2);//=>2 /* * 执行的是最后一个CALL方法 * 1.THIS:FN1.CALL * 2.CONTEXT:FN2 */ //=>第一次执行最后面的CALL,代码执行第一步 //FN1.CALL.toString().replace('this', FN2); // FN1.CALL=function (context, ...arg) { // FN2.toString().replace('this', context); // FN2(...arg); // }; //=>第一次执行最后面的CALL,代码执行第二步 //FN1.CALL(); /* * 第二次执行CALL方法 * CONTEXT='UNDEFINED' * FN2.toString().replace('this', undefined); * FN2(); */ //===============另一种思路 fn1.call(fn2); /* * CALL中的THIS是FN1,把FN1中的THIS关键字修改为FN2,然后再把FN1执行 =>"CALL方法中的THIS是谁,最后执行的就是谁" */ fn1.call.call(fn2); /* * 第一次执行最末尾的CALL,CALL中的THIS是FN1.CALL,先把FN1.CALL中的THIS改为FN2,然后让FN1.CALL执行 * 第二次CALL执行,方法中的THIS已经被上一次修改为FN2了,所以参考“THIS是谁就执行谁”的标准,执行的是FN2 */ fn.call.call(1)//报错
手写call
Function.prototype.call = function() { let [thisArg, ...args] = [...arguments]; if (!thisArg) { //context为null或者试undefined thisArg = typeof window === 'undefined' ? global : window; } //this指向试当前函数func thisArg.func = this; //执行函数 let result = thisArg.func(...args); delete thisArg.func; return result; }
bind预处理this,不执行函数
bind:语法和call一模一样,唯一的区别在于立即执行还是等待执行
fn.call(obj,10,20) 改变FN中的THIS,并且把FN立即执行,返回的是fn函数执行的结果
fn.bind(obj,10,20) 改变FN中的THIS,此时的FN并没有执行(不兼容IE6~8),把改变this后的函数体返回
let fn = function (a, b) { console.log(this); }; let obj = {name: "OBJ"}; document.onclick = fn;//=>把FN绑定给点击事件,点击的时候执行FN document.onclick = fn();//=>在绑定的时候,先把FN执行,把执行的返回值(UNDEFINED)绑定给事件,当点击的时候执行的是undefined //=>需求:点击的时候执行FN,让FN中的THIS是OBJ document.onclick = fn;//=>this:document document.onclick = fn.call(obj);//=>虽然this确实改为obj了,但是绑定的时候就把fn执行了(call是立即执行函数),点击的时候执行的是fn的返回值undefined document.onclick = fn.bind(obj);//=>bind属于把fn中的this预处理为obj,此时fn没有执行,当点击的时候才会把fn执行 function fe(){ console.log(this) } fe.call(1)//=> Number(1) 实例化后的1
apply (传参方式不同)
apply:和call基本上一模一样,唯一区别在于传参方式
fn.call(obj,10,20)
- fn.apply(obj,[10,20]) APPLY把需要传递给FN的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给FN一个个的传递