目录
一、定义
1、call
call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
fuction.call(thisArg, arg1, arg2, ...)
参数:
thisArg
可选的。在 function
函数运行时使用的 this
值。请注意,this
可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null
或 undefined
时会自动替换为指向全局对象,原始值会被包装。
arg1, arg2, ...
指定的参数列表。
2、apply
apply()
方法调用一个具有给定this
值的函数,以及作为一个数组(或类似数组对象,ES5之后支持传类数组,如dom中的一些类数组结果或者arguments)提供的参数。
function.apply(thisArg, [argsArray])
参数:
thisArg
必选的。在 func
函数运行时使用的 this
值。请注意,this
可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null
或 undefined
时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func
函数。如果该参数的值为 null
或 undefined
,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。
返回值
调用有指定this
值和参数的函数的结果。
注意:call()方法的作用和 apply() 方法类似,区别就是call()
方法接受的是参数列表,而apply()
方法接受的是一个参数数组。
3、bind
bind()
方法创建一个新的函数,在 bind()
被调用时,这个新函数的 this
被指定为 bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
function.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
thisArg
调用绑定函数时作为 this
参数传递给目标函数的值。 如果使用new
运算符构造绑定函数,则忽略该值。当使用 bind
在 setTimeout
中创建一个函数(作为回调提供)时,作为 thisArg
传递的任何原始值都将转换为 object
。如果 bind
函数的参数列表为空,执行作用域的 this
将被视为新函数的 thisArg
。
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
bind是ES5新增的一个方法,不会执行对应的函数(call或apply会自动执行对应的函数),而是返回对绑定函数的引用。
二、 关于call、apply和bind
1、call
、apply
、bind
方法的共同点:
apply
、 call
、bind
三者都是Function原型(即Function.prototype)的方法apply
、 call
、bind
三者都是用来改变函数的this对象的指向的;apply
、 call
、bind
三者第一个参数都是this要指向的对象,也就是想指定的执行上下文环境;apply
、 call
、bind
三者都可以利用后续参数传参;
2、call
、apply
、bind
方法的不同点:
bind
是返回对应函数,便于稍后调用;apply
、call
则是立即调用 。
call/apply的用法相同,区别在于传参数方式不同,call接受一个参数列表,apply接受一个参数数组,call是apply的语法糖
三、拓展
1、模拟实现call
Function.prototype.myCall = function (ctx) {
//获取上下文ctx
let ctx = ctx || window;
//获取当前将要执行的函数
ctx.fn = this;
//截取参数
let args = [...arguments].slice(1);
//执行函数
let res = ctx.fn(...args);
//去除副作用
delete ctx.fn;
return res;
}
2、模拟实现apply,和call类似
Function.prototype.myApply = (ctx){
var ctx = ctx || window;
ctx.fn = this;
var res;
if (arguments[1]) {
re s = ctx.fn(...arguments[1]);
} else {
res = ctx.fn();
}
delete ctx.fn;
return res
}
3、模拟实现bind
//bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,
//而其余参数将作为新函数的参数,供调用时使用。
Function.prototype.bind = Function.prototype.bind || function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind is not a function")
}
var self = this;
var args = [...arguments].slice(1);
return function F() {
if (self instanceof F) {
return new self(...args, ...arguments);
}
return new self.apply(context, args.concat(...arguments));
}
};
4、应用
(1)call可以用作检测数据类型
const getVarType = (target) => {
var classToType = {};
"Array Date RegExp Object Error".split(" ").forEach(function(item) {
classToType["[object " + item + "]"] = item.toLowerCase();
});
function type(target) {
if (target === null) {
return String(target);
}
return typeof target === "object"
? classToType[object.prototype.toString.call(target)] || "object"
: typeof target;
}
return type(target);
};
2、apply可以用作柯里化
函数柯里化是把接受多个参数的函数转变成接受一个单一参数(最初函数的第一个参数),并且返回接受余下的参数而且返回结果的新函数的技术。
柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。这增加了函数的适用性,但同时也降低了函数的适用范围。
这是以前看《javascript设计模式与开发实践》做的笔记,
var currying = function(fn) {
var args = [];
return function() {
if (arguments.length === 0) {
return fn.apply(this, args); // 没传参数时,调用这个函数
} else {
[].push.apply(args, arguments); // 传入了参数,把参数保存下来
return arguments.callee; // 返回这个函数的引用
}
}
}
var cost = (function() {
var money = 0;
return function() {
for (var i = 0; i < arguments.length; i++) {
money += arguments[i];
}
return money;
}
})();
var cost = currying(cost);
cost(100); // 传入了参数,不真正求值
cost(200); // 传入了参数,不真正求值
cost(300); // 传入了参数,不真正求值
console.log(cost()); // 求值并且输出600
上面这种情况没有考虑为函数与传参数情况,所以做了一点点修改
var currying = function(fn) {
var args = [...argumengts].slice(1);
return function() {
if (arguments.length === 0) {
return fn.apply(this, args); // 没传参数时,调用这个函数
} else {
[].push.apply(args, arguments); // 传入了参数,把参数保存下来
return arguments.callee; // 返回这个函数的引用
}
}
}
3、apply函数可以用来实现bind(参见上面bind的实现)
四、浏览器兼容性
详见参考资料1、2、3-对应call、apply和bind的MDN文档
五、参考资料
1、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
2、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
3、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind