了解Function.prototype.call()
手写call
首先我们来看这段代码
let foo = {
value:1
}
function bar(){
console.log(this.value)
}
bar.call(foo) // 控制台输出1
我们来梳理一下call的作用
- call会改变this的指向,谁调用它就会指向谁
- 会执行bar这个函数
相当于执行了 这个对象里面的这个方法
let foo = {
value:1,
bar:()=>{
console.log(this.value)
}
}
foo.bar()
这个时候 this 就指向了 foo,但是这样却给 foo 对象本身添加了一个属性,所以们用 delete 再删除它即可。
所以我们模拟的步骤可以分为:
1. 将函数设为对象的属性;
2. 执行该函数;
3. 删除该函数;
foo.fn = bar
foo.fn()
delete foo.fn
然后我们按照这个步骤模拟写一下call这个方法给他挂在原型上
Function.prototype.call2 = (context)=>{
console.log(this)
context.fn = this //这里的this指的是调用call2的函数对象,比如例子中的bar函数。这一步是将该函数赋值给context对象的一个属性fn,这样在context上就可以通过fn来调用这个函数了。
context.fn();
delete context.fn;
}
这个是没有参数的形式,我们的call方法一般都有参数,而且一般都有多个参数比如说bar.call(foo, 'kevin', 18);如果考虑到有参数的话我们要怎么实现呢,请看下面这段代码
Function.prototype.call2 = function(context,...arguments) {
console.log(...arguments)
context.fn = this
context.fn(...arguments);
delete context.fn;
}
let foo = {
value: 1
}
function bar(name,age) {
console.log(name,age)
console.log(this.value)
}
bar.call2(foo,'kaiwen',123) // kaiwen 123 1
我们以es6的...arguments来代替后面所有的参数,之后再传进执行函数fn里去就可以了,...arguments是一个类似于数组的东西,当然这种写法我们没有考虑到函数有返回值的情况,有返回值的情况请看接下来的代码
Function.prototype.call2 = function (context, ...arguments) {
context.fn = this
let result = context.fn(...arguments);
delete context.fn;
return result
}
let foo = {
value: 1
}
function bar(name, age) {
return {
value: this.value,
name,
age
}
}
console.log(bar.call2(foo, 'kaiwen', 123)) // {value:1,name:'kaiwen',age:123}
这就是手写call的全部了
手写apply
手写apply和手写call类似就是传的参数不一样第二个参数是数组
Function.prototype.apply2 = function (context, arr) {
context.fn = this
if(arr.length){
result = context.fn(arr);
}else {
result = context.fn();
}
delete context.fn;
return result
}
let foo = {
value: 1
}
function bar(arr) {
console.log(arr)
return {
value: this.value,
arr
}
}
console.log(bar.apply2(foo, [1,2,3]))