js深入之call/apply/bind的模拟实现
前言
call,apply,bind这三个方法都是用来改变函数的this指向
call
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
模拟实现
基本思想是把fn.call(obj,args)中的fn赋值为obj的属性
然后调用obj.fn即可实现fn中this指向的改变
// call和apply实现方式类似,只是传参的区别
//myCall函数的参数,没有传参默认是指向window
Function.prototype.myCall = function (context = window) {
context.fn = this //为对象添加方法(this指向调用myCall的函数)
let args = [...arguments].slice(1) // 剩余的参数
let res = context.fn(...args) // 调用该方法,该方法this指向context
delete context.fn //删除添加的方法
return res
}
测试
var foo = {
value: 'bind'
};
function demo(...rest) {
console.log(this.value);
console.log('hello')
console.log(rest);
}
demo.myCall(foo, 'bind1')
apply
call和apply的功能相同,区别是传递函数参数的方式不同,call是一个一个传入,apply是通过一个数组传递
模拟实现
Function.prototype.myApply = function (context = window) {
context.fn = this;
let args = arguments[1] // 剩余的参数
let res = context.fn(...args);
delete context.fn;
return res;
}
测试
var foo = {
value: 'bind'
};
function demo(...rest) {
console.log(this.value);
console.log('hello')
console.log(rest);
}
demo.myApply(foo, ['bind1'])
bind
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
bind的几个特性如下 :
- 返回一个新的函数
- 分段接收参数
- 改变this指向
- 返回的新函数可以使用new操作符
arguments
想要实现模拟的话,我们还需要了解一下arguments
对象。
arguments
对象不是一个Array
。它类似于Array
,但除了length
属性和索引元素之外没有任何Array
属性。可将arguments
转换为:
// es5
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);
// es6
const args = Array.from(arguments);
const args = [...arguments];
模拟实现
Function.prototype.myBind = function (context) {
var self = this; // this 指向调用者
var args = Array.prototype.slice.call(arguments, 1); //第二个参数
var fNOP = function () { };
var fBound = function () { //返回一个函数
var bindArgs = Array.prototype.slice.call(arguments);//接受的参数
// console.log(bindArgs);
return self.apply(context, args.concat(bindArgs));
}
//当 bind 返回的函数作为构造函数的时候
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
测试
var foo = {
value: 'bind'
};
function demo(...rest) {
console.log(this.value);
console.log('hello')
console.log(rest);
}
demo.prototype.friend = 'prop';
let demos = demo.myBind(foo, 'bind1')
let bar = new demos(35);
console.log(bar.friend);