apply.call.bind 的作用:都是为了改变函数运行时上下文(this指向)。
三兄弟的区别:
- 三兄弟接收的第一个参数都是 要绑定的this指向.
- apply的第二个参数是一个参数数组,call和bind的第二个及之后的参数作为函数实参按顺序传入。
- bind不会立即调用,其他两个会立即调用。
模拟实现函数的call方法
函数定义:call是可以被所有方法调用的,所以需要定义在 Function的原型上。
函数接收参数:绑定函数被调用时只传入第二个参数及之后的参数。
- 首先context为可选参数,如果不传的话默认上下文是window
- 接下来给content创建一个fn属性,并将值设置为需要调用的函数
- 因为call可以传入多个参数作为调用函数的参数,所有需要将参数剥离出来
- 然后调用函数并将对象上的函数删除
Function.prototype.defineCall = function (context) {
context = context || window; //第一个参数为undefined或null的时候,那么会转变为window
context.fn = this; //给context添加一个方法,指向this
let args = [];
args = [...arguments].slice(1); //首先通过[...xxx]把arguments类数组变成数组,再拿到去除第一个参数的新数组
let result = context.fn(...args);
delete context.fn; //删除该方法,不然会对传入对象造成污染
return result;
}
测试结果:
let sayName = function (age) {
console.log('current name: ' + this.name, "current age: " + age);
}
let obj1 = {
name: "obj's name"
}
sayName.defineCall(obj1, 22); //this指向 sayName函数实例
// current name: obj's name current age: 22
模拟实现函数的apply方法
思路和call是一样的只是传参不同方式而已
// 模拟 apply 方法
Function.prototype.defineApply = function (context, arr) {
context = context || window;
context.fn = this;
let args = [...arguments][1];
let result = args ? context.fn(args) : context.fn();
delete context.fn;
return result;
}
测试结果:
let obj2 = {
name: ['Tom', 'Johy', 'Joe', 'David']
}
sayName.defineApply(obj2, [3, 4, 5, 6, 7]);
// current name: Tom,Johy,Joe,David current age: 3,4,5,6,7
模拟实现函数的bind方法
bind() 方法会创建一个新函数。
//用call、apply模拟实现bind
Function.prototype.bind = function (context) {
let self = this; // 保存函数的引用
return function () { // 返回一个新的函数
// console.log(arguments);
// return self.apply(context, arguments);
return self.call(context, arguments);
}
};
测试结果:
let obj = {
name: 'seven'
}
let func = function () {
console.log(this.name) //seven
}.bind(obj);
func('zhangsan', 20);