THIS5:基于call/apply/bind可以改变函数中this的指向(强行改变)
01.CALL/APPLY
第一个参数就是改变的THIS指向,写谁就是谁(特殊:非严格模式下,传递null/undefined指向的也是window
唯一区别:执行函数,传递的参数方式有区别,call是一个个的传递,apply是把需要传递的参数放到数组中整体传递
func.call([context],10,20)
func.apply([context],[10,20])
02.BIND
call/apply都是改变this的同时直接把函数执行了,而bind不是立即执行函数,属于预先改变this和传递一些内容 =>“柯理化”
Function.prototype={
call
apply
bind
}
重写重写bind
~ function anonymous(proto) {
// Es5 的方法重写bind
function bind(context) {
//context may be null or undefined
if (context == undefined) {
context = window;
}
//获取传递的实参集合
var args = [].slice.call(arguments, 1);
//需要最终执行的函数
var _this = this;
return function anonymous() {
var amArg = [].slice.call(arguments, 0);
_this.apply(context, args.concat(amArg));
};
}
// ES6的方法重写bind
//经过测试:apply的性能不如call
function bind(context = window, ...args) {
return (...amArg) => this.call(context, ...args.concat(amArg));
}
proto.bind = bind;
}(Function.prototype);
重写call
~ function anonymous(proto) {
function call(context = window, ...args) {
//=>必须保证context是引用类型
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}
//详细版
function call(context = window, ...args) {
context === null ? context = window : null;
let type = typeof context;
if (type !== "object" && type !== "function" && type !== "symbol") {
//=>基本类型值
switch (type) {
case 'number':
context = new Number(context);
break;
case 'string':
context = new String(context);
break;
case 'boolean':
context = new Boolean(context);
break;
}
}
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}
proto.call = call;
}(Function.prototype);
重写apply
~ function anonymous(proto) {
// 重写apply
function apply(context = window, args) {
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
}
proto.apply = apply;
}(Function.prototype);
理解
let obj = {
fn(x, y) {
console.log(this, x, y);
}
};
obj.fn.call({}, 10, 20);
obj.fn.apply(window, [10, 20]);
setTimeout(obj.fn.bind(window, 10, 20), 1000);
setTimeout(anonymous, 1000); 1S后先执行bind的返回结果anonymous,在anonymous中再把需要执行的obj.fn执行,把之前存储的context/args传给函数
document.body.onclick = obj.fn.bind(window, 10, 20);
document.body.onclick = anonymous;
练习题
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
拆分理论
function call(context = window, ...args) {
context.$fn = this;
let result = context.$fn(...args);
delete context.$fn;
return result;
} => AAAFFF000
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
fn1.call(fn2); //=>执行的是FN1 =>1
/*
* call执行
* this=>fn1
* context=>fn2
* args=>[]
* fn2.$fn = fn1; fn2.$fn(...[])
*/
fn1.call.call(fn2); //=>执行的是Fn2 =>2
/*
* 先让最后一个CALL执行
* this=>fn1.call=>AAAFFF000
* context=>fn2
* args=>[]
* fn2.$fn=AAAFFF000 fn2.$fn(...[])
*
* 让CALL方法再执行
* this=>fn2
* context=>undefined
* args=>[]
* undefined.$fn=fn2 undefined.$fn()
*
* 让fn2执行
*/
Function.prototype.call(fn1);
/*
* 先让最后一个CALL执行
* this=>Function.prototype(anonymous函数)
* context=>fn1
* args=>[]
* fn1.$fn=Function.prototype fn1.$fn()
* 让Function.prototype执行
*/
Function.prototype.call.call(fn1); //=>1
/*
* 先让最后一个CALL执行
* this=>Function.prototype.call(AAAFFF000)
* context=>fn1
* args=>[]
* fn1.$fn=AAAFFF000 fn1.$fn()
*
* 让CALL执行
* this=>fn1
* context=>undefined
* args=>[]
* undefined.$fn=fn1 undefined.$fn()
* 让fn1执行
*/