今天在学习js的时候想到个有趣的问题, 自己实现一个call方法?.
改变一个函数的this指向这种事听起来真的很原生态.
第一步:
知己知彼,先从这个方法本身下手, 看看他到底有什么神通.
// 1:定义个举例函数;
function fn1(n){
console.log('我是fn1',n,this)
}
function fn2(n){
console.log('我是fn2',n,this)
}
let call = fn1.call
// 2: 打印一下 他的call方法
// 既然是function, 本身也就肯定也有call方法了,继续
console.log(call) // ƒ call() { [native code] }
console.log(call.call) // ƒ call() { [native code] }
console.log(call.call.call) // ƒ call() { [native code] }
// 3: 实际试试输出
fn1.call.call(fn2,1) // 我是fn2 undefined Number {1}
fn1.call.call.call(fn2,1) // 我是fn2 undefined Number {1}
fn1.call.call.call.call(fn2,1) // 我是fn2 undefined Number {1}
// 4: 看到这个结果的时候我蒙了, 这怎么执行fn2了, n是未定义, this指向了1
// 感觉整体往后移了一位, ?呆滞.
第二步: 查资料,逐步实现.
真的很好奇,难道里面有什么判断规则?, 我找了几篇文章, 也没找到答案, 最后在曾经看过的视频里面找到了答案.
// 1: 当然是定义在函数上了
Function.prototype.myCall = function(context) {
// 2: 如果传进来的第一个参数为空,就把它当做window, 所以我们平时想要指向window的时候才会穿个undefined,''之类的, Object()一下这个数, 是为了让他本身可以有对象的特性.
context = context ? Object(context) : window;
// 3: 这个this就是调用当前call方法的'.'前面的对象
// 相当于模拟了一个对象
// context === fn2 === { fn:fn1 } // 所以里面的fn1的this指向也就是父级fn2了.
context.fn = this;
// 4: 参数要处理一下, 以便传入到函数里面.
let as = [];
// 要从1开始, 因为第一个要作为this
for (let i = 1; i < arguments.length; i++) {
as.push(arguments[i]);
}
// 5: 执行context的fn方法, 也就是执行, 调用call的函数
// 数组的加法, 比如 [1,2,3]+'' 结果是 1,2,3 这样就符合参数的样子了
let r = eval('context.fn(' + as + ')');
// 6: 做完这些, 当然要好好善后啦
delete context.fn;
return r
};
// 实验结果
fn1.call(fn2,1) // 我是fn1 1 fn2
第三步: 让我们来理解一下那个多次调用的问题.
首先 :不管写几个call 都是call方法本身去调了一下call(fn2), 执行的时候,也就是相当于, 把call它的this指向变成 fn2, 并执行call这个方法, 而call这个方法一执行就会默再去认执行一下相当于fn2.call(后面的参数),因为fn2是他的this, 就相当于把后面的 1 当做要指定的this传进去, 也就造成了往后窜了一位.
第四步: 总结
不管写多少个call, 执行第一个参数, 第二个参数为第一个参数this的指向, 第三个参数开始是传参...哈哈挺有意思!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。