手撕call、apply、bind以及他们之间的区别
为什么要改变this指向?
bind,call,apply三者都是用来改变this的指向,那为何要改变呢
var name="tom";
let obj={
name:"tory",
sayHello:function () {
console.log(this.name);
}
};
obj.sayHello(); //tory,this指向obj对象
setTimeout(obj.sayHello,0); //tom,this指向window对象
可以观察到,正常情况下 sayHello方法中的 this 是指向调用它的 obj 对象的,而定时器 setTimeout 中的 say 方法中的 this 是指向window对象的(在浏览器中),这是因为 say 方法在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的,但我们需要的是 say 方法中 this 指向obj对象,因此我们需要修改 this 的指向。
call apply bind 的作用与区别?
- 相同点:call 和 apply 和 bind 都可以改变this指向,都可以传参
- 不同点call apply自动调用,bind需要再调一次,bind的返回的是对应函数
- call apply 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
1、call()方法
function fn(a, b) {
console.log(this);
console.log(this.name, a, b);
return this.name
}
let obj = {
name: 123
}
Function.prototype.mycall = function (o, ...args) {
o = o || window // 为了防止在第一参数传入null underfind时指向window
o.fn = this
// 第一种方案
// let res = o.fn(...args)
// 第二种方案 采用eval
let newArr = []
for (let i = 1; i < arguments.length; i++) {
newArr.push(`arguments[${i}]`)
}
console.log('arguments',newArr);
// .join(',') 加与不加都行 数组转换成字符串本来就会分割
let res = eval(`o.fn(${newArr.join(',')})`)
delete o.fn
return res
}
let res = fn.mycall(obj, 1, 2)
console.log("####",res);
// fn.mycall(null, 1, 2)
fn.mycall(obj,1,{})
2、bind()方法
function fn(a, b) {
console.log(this.name, a, b);
return this.name
}
let obj = {
name: 123
}
Function.prototype.myapply = function (o, args) {
o = o || window
o.fn = this
let res = null
if (!args) {
res = o.fn()
} else {
// 第一种方案
res = o.fn(...args)
// 第二种方案 采用eval
// let newArr = []
// for (let i = 0; i < args.length; i++) {
// newArr.push(`args[${i}]`)
// }
// console.log('args', newArr);
// // .join(',') 加与不加都行 数组转换成字符串本来就会分割
// res = eval(`o.fn(${newArr.join(',')})`)
}
delete o.fn
return res
}
// fn.myapply(obj, [1, 2])
// console.log("####", res);
fn.myapply(null, [1, 2])
// fn.myapply(obj, 1, {})
3、bind()方法
function fn(a,b,c){
console.log("$$$",this.name,a,b,c);
}
let o ={
name:"你好"
}
Function.prototype.myBind = function(o){
let that = this;
console.log(arguments);
let args1 = Array.prototype.slice.call(arguments,1)
return function(){
let args2 = Array.prototype.slice.call(arguments)
console.log(args2);
// 第一种方案
// that.apply(o,args1.concat(args2))
console.log("$$$",args1.push.apply(args1,args2));
// 第二种方案
that.apply(o,args1.push.apply(args1,args2) && args1)
}
}
fn.myBind(o,1,2)(123)