先从一道小小的面试题说起
call函数做了什么?
于是乎翻开我们自己的面试宝典
- 改变了函数内部的this指向
- 执行该函数
如何做到以上两点
示例1 :
let country = {
leader : "大魏吴王",
}
function slogan(){
console.log(this.leader)
}
slogan.call(country)
现在我想改变slogan函数内部的this指向并且执行该函数该怎么做?
示例2 :
let country = {
leader : "大魏吴王",
slogan : function(){
console.log(this.leader)
}
}
country.slogan()
没毛病啊!
思考与写入过程
如此看我们的问题就变成了如何将示例1的代码变成示例2这种形式
这个问题跟把大象放冰箱需要多少步是一样的
- 赋值函数到对象中
- 执行对象中的函数
按照我们思考的过程一步步来....
- 首先重写call方法要写在哪里?
这个问题我们要明确谁掉用了call的方法,含无疑问是函数,用示例1中的例子说就是slogan。所以我们要把它写在函数的原型上
示例3:
Function.prototype.call2 = function(context) {
// context就是传入的对象
}
- 怎么取到函数,并把它复制到对象中?
来看示例3中call2中的this指向:
我们知道this是函数运行时的环境:
结合示例1和示例3我们可以大致的画出他的内存图:
从图中我们看到当前的环境依旧是slogan所以说示例3中call2中的this依旧是函数本身
由此我们要完成第二部赋值函数到对象中
Function.prototype.call2 = function(context) {
// context就是传入的对象
context.fn = this;
context.fn();
delete context.fn;// 还没完执行过后我们要删除fn
}
关于为什么要删除fn我们看一下这个例子
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
如果不删除它就会影响原有的对象,这与引用类型在内存中的存储方式有关在这里就不细说了
- 最后我们需要考虑this的特性
- 能接受很多参数
- 传入null时this指向window
- 函数可以有有返回值 最后就是这样的
Function.prototype.call2 = function (context,...args) {.
var context = context || window;
context.fn = this;
let result = context.fn(...args)
delete context.fn
return result;
}