这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战。
call、apply、bind 的共同点都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。
不同点:
call() 和 apply() 是立即执行的,而 bind() 是返回一个函数。
call() 可以传递多个参数,第一个参数和 apply() 一样,是用来替换的对象,后面是参数列表。
apply() 最多只能有两个参数 —— 新this 对象和一个参数数组 argArray
bind() 和其他两个方法的作用也是一致的,只是该方法会返回一个函数,并且可以通过bind() 实现柯里化。
手写实现call()、apply()、bind() 函数 对于实现以下三个函数,可以从这几个方面进行考虑:
不传入第一个参数,那么默认为 window 。 改变了 this 指向,让新的对象可以执行该函数。那么思路就可以变成给新的对象添加一个函数,然后在执行完以后删除。
Function.prototype.myCall = function( context ){
// 1. 判断有没有传入要绑定的对象,没有默认为window;如果是基本类型的话通过Object()方法进行转换
var context = Object(context) || window;
// 2. 给context添加一个fn属性,值为this,也就是fn()
context.fn = this;
// 3. 保存返回值
let result = '';
// 4. 取出传递的参数,第一个参数是this
// 截取除第一个参数外剩余参数的方法
const args = [...arguments].slice(1);
// const args = Array.prototype.slice.call(arguments , 1);
// const args = Array.from(arguments).slice(1);
// 5. 执行方法,传入参数
// ... 是es6的展开数组
result = context.fn(...args);
// 6. 删除该属性
delete context.fn;
// 7. 返回结果
return result;
}
// 测试用例
const obj = {
value :'hello'
}
function fn(name , age){
return {
value : this.value ,
name ,
age
}
}
let res = fn.myCall(obj , 'LL' , 25);
console.log(res) // { value: 'hello', name: 'LL', age: 25 }
手写实现 apply()
Function.prototype.myApply = function( context , args ){
var context = Object(context) || window;
context.fn = this;
let result = '';
// 4. 判断有没有传入第二个参数 args,如果传入就将第二个参数展开
if(!args){
// 没有传入,直接返回该函数
result = context.fn();
}else{
// 传入了,将参数展开
result = context.fn(...args);
}
delete context.fn;
return result;
}
// 测试用例
const obj = {
value :'hello'
}
function fn(name , age){
return {
value :this.value ,
name ,
age
}
}
let res = fn.myApply(obj ,[ 'LL' , 25]);
console.log(res) // { value: 'hello', name: 'LL', age: 25 }
- 手写实现 bind()
Function.prototype.myBind = function( context ){
// 1. 判断this是不是一个函数
if(typeof this !== 'function'){
// 不是函数,抛出错误
throw new Error('不是一个函数');
}
// 2. 暂存this
const self = this;
// 3. 获取传入的参数
// 拿到第一组参数,如果没传,是一个空数组
const args1 = [...arguments].slice(1);
// 第二次调用bindFn
const bindFn = function(){
// 获取第二个参数
const args2 = [...arguments];
// 将第一部分参数和第二部分参数合并到一起,进行返回
return self.apply(context , args1.concat(args2));
}
return bindFn
}
// 测试用例
const obj = {
value :'hello'
}
function fn(name , age){
return {
value :this.value ,
name ,
age
}
}
let res = fn.myBind(obj)('HH' , 30);
console.log(res)