call()
定义:
call()为函数中的一个方法, 其参数为一个指定的this值,以及一个参数列表
语法:
fn.call(thisArg, a,b,c)
- thisArg:在fu函数运行时指定this的值,如果指定为null或者undefined会指向全局对象,浏览器中就是window对象。
- a,b,c:参数列表,作为参数传给fu函数
也就是说call能使fn函数中的this指向其第一个参数thisArg对象
const obj1 = {
name: 'ahua',
getName(h1,h2) {
console.log(`我叫${this.name},喜欢${h1}和${h2}`);
}
};
const obj2 = {
name: '阿花'
};
obj1.getName.call(obj2,'读书','看报')
// 我叫阿花,喜欢读书和看报
手写call()
- call支持多个参数,可能一个,可能一个也没有
- 多参数时要把thisArg外的参数传给扩展方法
- 结束后要把扩展的自定义函数删除
Function.prototype.mycall = function(context){
// context 就是上面call的参数obj2,因为只有一个形参,所以后面两个参数会被忽略
// 如果没有指定this,this指向window
var context = context || window;
// 调用mycall的是obj1对象的getName函数,所以this指向getName函数
// 这个表达式的意思就是在obj2中定义一个fn去复制getName函数
context.fn = this;
var args = [];
// Javascrip中的函数都有一个Arguments对象实例arguments(类数组),它引用着函数的实参arguments,arguments[num]和其参数一一对应。
// 将mycall中第一个参数thisArg对象(obj2)移除,剩余的('读书','看报')作为fn的参数
for (var i = 1, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
const result = context.fn(...args);
// 删除fn方法
delete context.fn;
return result;
}
obj1.getName.mycall(obj2,'读书','看报')
// 我叫阿花,喜欢读书和看报
apply()
定义:
apply() 为函数的一个方法, 其参数为一个指定的this值,以及一个数组(或类似数组的对象)
语法:
fu.apply(thisArg, [argsArray])
- thisArg:在fu函数运行时指定this的值,如果指定为null或者undefined会指向全局对象,浏览器中就是window对象。
- arrgsArray:一个数组或者类数组对象,作为参数传给fu函数
apply也能使函数中的this指向其第一个参数thisArg对象
const obj1 = {
name: 'ahua',
getName(x,y) {
console.log(`我叫${this.name},喜欢${x}和${y}`);
}
};
const obj2 = {
name: '阿花'
};
obj1.getName.apply(obj2,['吃饭','睡觉'])
// 我叫阿花,喜欢吃饭和睡觉
手写apply
apply不同的就是第二个参数变成了数组
Function.prototype.myapply = function(content) {
// 如果没有传或传的值为空对象 context指向window
if (typeof context === "undefined" || context === null) {
context = window
}
let args = [...arguments].slice(1) //效果同mycall中的for循环
// 给obj2添加一个方法fn(复制this) 因为是getName调用的myapply所以this指向getName
content.fn = this
// arguments引用着myapply的两个参数,第一个是obj2,第二个是['吃饭','睡觉']
const result = content.fn(arguments[1][0],arguments[1][1])//执行fn
delete content.fn //删除方法
return result
} obj1.getName.myapply(obj2,['吃饭','睡觉'])
// 我叫阿花,喜欢吃饭和睡觉
bind()
用法和call类似,不过bind()会创建一个新的函数,需要再次进行调用
function a(x, y, z){
return this.name + '喜欢' + x + '、' + y + '和' + z;
}
var b = {name : '阿花'};
a.getName.bind(b,'读书','看报')('滑板')
// 阿花喜欢读书、看报和滑板
手写bind()
- 函数调用
- 改变this
- 返回一个绑定this的函数
- 接收多个参数
- 支持柯里化形式传参 fn(1)(2)(3)
代码实现
Function.prototype.mybind = function(context){
if (typeof context === "undefined" || context === null) {
context = window
}
// a.mybind(b,参数1,参数2)(参数3) context是b args是通过arguments获取参数1和参数2 return函数中的参数是参数3
var args = [...arguments].slice(1);
// args['读书','看报']
// 保存this,因为如果后面进入return函数后,this的指向会改变
var self = this;
// 调用bind后返回一个this指向第一个参数(b)的函数
return function(){
// mybind(b, '读书', '看报')是调用自定义的mybind方法,
// a.mybind(b, '读书', '看报')('滑板')是继续调用的mybind中return的函数,所以return中的函数参数为('滑板')
// 将类数组转换为数组 innerArgs ['滑板']
var innerArgs = [...arguments];
console.log(arguments)
// 实现函数curry:把接受多个参数的函数变换成接受一个单一参数的函数
// 即:将bind(thisArg,参数1,参数2)(参数3) 中的参数1,参数2,参数3合并为一个参数作为apply的第二个参数
var finalArgs = args.concat(innerArgs);
//finalArgs ['读书','看报' ,'滑板']
// 用apply是因为传入的第二个参数为数组
return self.apply(context, finalArgs);
}
}
// 测试结果
function a(x, y, z){
return this.name + '喜欢' + x + '、' + y + '和' + z;
}
var b = {name : '阿花'};
console.log(a.mybind(b, '读书', '看报')('滑板'));