Open hzzzzzzzq opened 2 years ago
首先,我们先来了解一下 call,apply,bind 的区别,方便我们去手写实现自己的 call、apply、bind。
call
apply
bind
call、apply、bind
我们可以分为七个步骤:
判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况 判断传入上下文对象是否存在,如果不存在,则设置为 window 处理传入的参数,截取第一个参数后的所有参数 将函数作为上下文对象的一个属性 使用上下文对象来调用这个方法,并保存返回结果 删除刚才新增的属性 返回结果
window
call 在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个参数或方法。
this
let value = 1; const foo = { value: 2, }; function fn() { console.log(this.value); } fn.call(foo); // 2
我们来看看步骤:
Object
Function.prototype.myCall = function (context) { if (typeof this !== 'function') { console.log('只有函数可以调用 myCall'); return; } context.fn = this; context.fn(); delete context.fn; };
这时候我们会发现打印结果对上了,也是 2。
2
接下来就是处理参数了,从区别可以知道,call 是可以有无数参数的,我们在第二步之前添加一个步骤。
先来看看真实 call 的测试。
let value = 1; const foo = { value: 2, }; function fn(name, age) { console.log(this.value); console.log(name); console.log(age); } fn.call(foo, 'hzzzzzzzq', 18); // 2 // hzzzzzzzq // 18
Function.prototype.myCall = function (context) { if (typeof this !== 'function') { console.log('只有函数可以调用 myCall'); return; } let args = [...arguments].slice(1); // 截取参数 context.fn = this; context.fn(...args); // 给函数传入参数 delete context.fn; };
使用我们的 myCall 执行,发现结果是相同的。
myCall
在这里我们需要注意两个问题
null
注意,我将外层的定义 let 改成了 var,因为 var 设置了全局变量,如果设置 let 则会打印 undefined,并没有在 window 对象中定义。
let
var
undefined
var value = 1; const foo = { value: 2, }; function fn(name, age) { console.log(this.value); } fn.call(null); // 1
执行代码,可以知道,传入 null 时,指向了我们的 window 对象。
var value = 1; const foo = { value: 2, }; function fn(name, age) { return { value: this.value, name, age, }; } const result = fn.call(foo, 'hzzzzzzzq', 18); console.log(result); // { // value: 2, // name: 'hzzzzzzzq', // age: 18 // }
首先处理传入 null 的时候,判断传入上下文对象是否存在,如果不存在,则设置为 window。 处理第二个问题则是将方法执行结果进行保留,返回结果。
Function.prototype.myCall = function (context) { if (typeof this !== 'function') { // 判断,在手写过程中 可以不需要 console.log('只有函数可以调用 myCall'); return; } context = context || window; // 判断上下文对象是否存在 context.fn = this; // 将函数作为上下文的一个属性 let args = [...arguments].slice(1); // 获取传入的参数,从第二个参数起的所有参数 const result = context.fn(...args); // 执行该函数,并使用上面的参数 delete context.fn; // 删除新增对象 return result; // 返回结果 };
测试一下我们自己写的 call 代码
var value = 2; let obj = { value: 1, }; function test(name, age) { console.log(this.value); return { value: this.value, name: name, age: age, }; } console.log(test.myCall(obj, 'hzq', 18)); // 1 // { // "value": 1, // "name": "hzq", // "age": 18 // }
我们首先了解了三者的区别,我们发现 apply 与 call 的区别,就在于传入的参数,针对这一区别,我们开始实现 apply,就只需要对 call 的代码进行部分修改即可。
判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况 判断传入上下文对象是否存在,如果不存在,则设置为 window 将函数作为上下文对象的一个属性 判断参数值是否传入 使用上下文对象来调用这个方法,并保存返回结果 删除刚才新增的属性 返回结果
Function.prototype.myApply = function (context, arr) { if (typeof this !== 'function') { console.log('只有函数可以调用 myApply'); return; } context = context || window; context.fn = this; let result = null; if (!arr) { // 数组没有值时,直接调用 result = context.fn(); } else { // 数组有值,传入参数 result = context.fn(...arr); } delete context.fn; return result; };
测试与 call 类似,只是修改了传入参数为数组, 打印结果相同。
console.log(test.myApply(obj, ['hzq', 18]));
call bind apply 的区别
首先,我们先来了解一下
call
,apply
,bind
的区别,方便我们去手写实现自己的call、apply、bind
。call 实现
我们可以分为七个步骤:
第一版
call
在使用一个指定的this
值和若干个指定的参数值的前提下调用某个参数或方法。我们来看看步骤:
Object
对象去调用试一下,是抛出了一个错误的,这里我们用打印来表示。这时候我们会发现打印结果对上了,也是
2
。第二版
接下来就是处理参数了,从区别可以知道,
call
是可以有无数参数的,我们在第二步之前添加一个步骤。先来看看真实
call
的测试。使用我们的
myCall
执行,发现结果是相同的。最后版
在这里我们需要注意两个问题
null
注意,我将外层的定义
let
改成了var
,因为var
设置了全局变量,如果设置let
则会打印undefined
,并没有在window
对象中定义。执行代码,可以知道,传入
null
时,指向了我们的window
对象。首先处理传入 null 的时候,判断传入上下文对象是否存在,如果不存在,则设置为
window
。 处理第二个问题则是将方法执行结果进行保留,返回结果。测试一下我们自己写的
call
代码apply 实现
我们首先了解了三者的区别,我们发现
apply
与call
的区别,就在于传入的参数,针对这一区别,我们开始实现apply
,就只需要对call
的代码进行部分修改即可。测试与
call
类似,只是修改了传入参数为数组, 打印结果相同。