手撕JS call、apply、bind方法
在js中call、apply、bind方法都是用来改变this指针的指向的,也是前端面试手撕代码的一个考点,拼多多笔试就考了bind的源码实现,现在给大家列出这三种方法JS实现
1.call方法
call方法的第一个参数也是this的指向,后面传入的是一个参数列表,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
call方法JS实现:
Function.prototype.myCall = function (context) {
// 此时这里的this指向person
if (typeof this !== 'function') {
throw new TypeError('not function')
}
// 如果未null或undefined指向windows
context = context || window
// 改变this指向
context.fn = this
// 处理参数
let arg = [...arguments].slice(1)
let res = context.fn(...arg)
delete context.fn
// 返回结果
return res
2.apply方法
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入
改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
apply方法JS实现:
Function.prototype.myApply = function (context) {
// 此时这里的this指向person
if (typeof this !== 'function') {
throw new TypeError('not function')
}
// 如果未null或undefined指向windows
context = context || window
// 改变this指向
context.fn = this
// 处理参数
let res
if (arguments[1]) {
res = context.fn(...arguments[1])
} else {
res = context.fn()
}
delete context.fn
// 返回结果
return res
}
3. bind方法
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)改变this指向后不会立即执行,而是返回一个永久改变this指向的函数
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
const bindFn = fn.bind(obj); // this 也会变成传入的obj ,bind不是立即执行需要执行一次
bindFn(1,2) // this指向obj
fn(1,2) // this指向window
bind方法JS实现
Function.prototype.myBind = function (context) {
// 此时这里的this指向person
if (typeof this !== 'function') {
throw new TypeError('not function')
}
let that = this
// console.log(that)
let args = [...arguments].slice(1)
// console.log(args)
return function F() {
// 调用apply方法
if (this instanceof F) {
return new that(...args, ...arguments)
} else {
return that.apply(context, args.concat(...arguments))
}
}
}