js实现call和bind
call ,apply 和 bind 都可以强制更改函数中的this指向,语法:
call: call(context, param1,param2,…)
apply:apply(context, [param1,param2, …])
bind:bind(context, param1,param2,…)
这三个函数中的第一个参数 context 就是自己决定函数中的this的指向,其余参数就是传给函数的实参
call、apply、bind的区别
call和apply在改变函数中this的执行时候会立即执行函数
function fn(){
console.log(this)
}
let obj = {};
fn.call(obj); // obj
fn.apply(obj); // obj
bind只是先改变了函数中的this指向,并不会立即执行函数,如果需要执行函数,需要另行调用
function fn(){
console.log(this)
}
let obj = {};
fn.bind(obj); // 这里没有输出
fn.bind(obj)(); // obj
call源码及关于call的题目
- call源码
Function.prototype.call = function(context, ...param) {
context = context == null? window: context;
if(typeof context != 'object' || typeof context != 'function'){
context = Object(context);
}
context.$fn = this;
let res = context.$fn(...param);
delete context.$fn;
return res;
- call相关题目
var name = "测试";
function A(x,y) {
var res = x + y;
console.log(res, this.name);
}
function B(x, y) {
var res = x - y;
console.log(res, this.name);
}
B.call(A,40, 30);
// this-> B; context -> A
// context.$fn = this; context.$fn(40,30);
// => A.$fn = B; A.$fn(40,30); 执行B这个函数,只是函数中的this是A
B.call.call.call(A, 20,10);
// this: B.call.call; context: A
// context.$fn = this => A.$fn = B.call.call;
// context.$fn(20,10); => B.call.call(20,10); 只是这个执行,函数中的this是A,而不是B.call
// =>this: A; context: 20
// context.$fn = this; => 20.$fn = A
// context.$fn(10); => 20.$fn(10); 效果等价于 A.call(20,10);
// 执行的是A,函数中的this是20,传递的参数是 【10】
// => NaN undefined
Function.prototype.call(A, 60, 50);
// this: Function.prototype; context: A
// context.$fn = this; => A.$fn = Function.prototype;
// context.$fn(60,50); => Function.prototype(60,50) 这个函数执行,只是函数中的this的A,而不是 Function
// 由于Function.prototype是一个匿名空函数,所以不会有任何输出
// =>没有任何输出
Function.prototype.call.call.call(A, 80,70);
// this: Funtion.prototype.call.call; context: A
// context.$fn = this; => A.$fn = Function.prototype.call.call
// context.$fn(80,70); => Function.prototype.call.call(80,70); 这个函数执行,只是函数中的this是A,而不是 Function.prototype.call
// this: A; context: 80
// context.$fn = this; => 80.$fn = A;
// context.$fn(70); =>A.$fn(70); 效果等价于 A.call(80, 70)
// 执行的是A,函数中的this是80,传递的参数是 【70】
// => NaN undefined
// 变式训练
function fn1(){
console.log(1)
}
function fn2() {
console.log(2)
}
fn1.call(fn2); // 1
fn1.call.call.call(fn2);
// this: fn1.call.call; context: fn2;
// fn2.$fn = this; => fn2.$fn = fn1.call.call
// fn2.$fn(); => fn1.call.call(); 执行这个函数,只是函数中的this是fn2,而不是 fn1.call
// this: fn2; context: window
// context.$fn = this; => window.$fn = fn2
// context.$fn(); => window.$fn(); 等价于 fn2.call()
// => 2
Function.prototype.call(fn2);
// fn2.$fn = Function.prototype;
// fn2.$fn(); => Function.prototype() 匿名空函数执行,没有输出
Function.prototype.call.call.call(fn2);
// fn2.$fn = Function.prototype.call.call
// fn2.$fn(); => Function.prototype.call.call() 函数中的this的fn2
// window.$fn = fn2;
// window.$fn(); => fn2();
// => 2
*/
bind 源码
- 问题引申
var obj = {name: 'obj'};
function fn(){
console.log(this);
}
// 想要实现点击时,触发函数,输出的是obj, 而不是当前元素
// document.body.onclick = fn; // 这样显然不行
// document.body.onclick = function(){ // 这样也不行
// fn(); // window
// }
// document.body.onclick = function(){
// fn.call(obj); // 可行, 由此推测出bind的源码
// }
- bind 源码
(function(){
function bind(context, ...param){
// this: 当前要操作的函数
context = context == null ? window: context;
let that = this;
return function(...arg){
let args = param.concat(arg)
that.call(context, ...args);
}
}
Function.prototype.bind = bind;
})();
document.body.onclick = fn.bind(obj); // obj