call apply bind
用法
call
function.call(thisArg, arg1, arg2, ...)
参数
- thisArg
- arg1, arg2...
返回值
使用调用这提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。
apply
function.apply(thisArg, [argsArray])
参数
- thisArg
- argsArray
返回值
调用有指定 this 值和参数的函数的结果。如果没有返回undefined
bind
function.bind(thisArg, arg1, arg2...)
参数
- thisArg
- arg1, arg2
返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数
bind 是返回对应函数,便于稍后调用
apply,call 则是立即调用
apply、 call
第三方库的许多函数,以及 JavaScript 语言和宿主环境中许多新的内置函数,都提供了可选的参数,通常被称为“上下文”,在 javascript中,call 和 apply 都是为了改变某个函数运行时的上下文而存在的,换句话说,就是为了改变函数体内部 this 的指向。
apply、call 的区别
从 this 绑定的角度来说,call 和 apply 是一样的,它们的区别体现在接收参数的方式不太一样。
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2]);
bind
// 最简单的
function bind (fn, obj) {
return function() {
fn.apply(obj, arguments)
}
}
bind(func, this)(arg1, arg2)
// MDN
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
throw new TypeError(
"Function.prototype.bind - what is trying" +
"to be bound is not callable"
)
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fTobind.apply(
(
this instanceof fNOP &&
oThis ? this : oThis
),
aArgs.concat(
Array.prototype.slice.call(arguments)
)
)
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
}
回想一下bind是怎么调用的
var a = function(name, age) {
this.name = name;
this.age = age;
}
var obj = {};
a.bind(obj, 'tom', '18')();
console.log(obj.name); // 'tom'
console.log(obj.age); // '18'
现在回过头来看看,MDN实现
if (!Function.prototype.bind) { // 判断
Function.prototype.bind = function(oThis) {
// 将更改的 thisArg 赋值给 oThis;
if (typeof this !== 'function') {
// 调用 bind 的必须是函数
throw new TypeError(
"Function.prototype.bind - what is trying" +
"to be bound is not callable"
)
}
var aArgs = Array.prototype.slice.call(arguments, 1), // 提取参数,并且变成数组格式
fToBind = this, // 原this,函数
fNOP = function() {},
fBound = function() {
return fToBind.apply(
(
// this 是否在 fNOP的原型链上 并且 oThis不为空,这里是针对call是否是new
this instanceof fNOP &&
oThis ? this : oThis
),
aArgs.concat(
Array.prototype.slice.call(arguments)
)
)
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
h
return fBound;
}
}
var b = function(){}.call(this);
var cfn = new b();
// 回顾一下 new
// var context = Object.assign(null, b.prototype)
// b 是 fBound, b.prototype = new fNOP();
// b.apply(context, args)
// this instanceof fNOP 是true,
// 所以 如果 用了 new, 那么就会用 context 而不是用 oThis 当上下文
/*
fn.prototype = fNOP.prototype ---> fNOP
| /
| /
__proto__ / new
| /
| /
fBound.prototype ---> fBound
| /
| /
__proto__ / new
| /
| /
cfn
所以 cfn instanceof fn
所以 当 call 返回的函数,用 new 时,this 指向的是新的 空对象
顺便说明一个问题,prototype可以随意更改,instanceof 来测数据类型不准确
*/
知道了原理,来仿写一下
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
// 判断是不是function调用的
if (typeof this !== 'function') {
throw new TypeError('不是函数调用')
}
var args = Array.prototype.slice.call(arguments, 1);
var fToBind = this;
var fTOP = function() {};
var fBound = function () {
// 判断是不是 new , new 的要用 空对象 当this
return fToBind.apply((
this instanceof fTOP && oThis ? this : oThis;
), args.concat(Array.prototype.slice.call(arguments)));
}
fTOP.prototype = this.prototype;
fBound.prototype = new fTOP();
return fBound;
}
}
面试题
- 定义一个log方法,让它可以代理 console.log 方法
function log() {
console.log.apply(console, arguments);
}
- 给消息添加一个“(app)”的前缀
function log() {
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
}
call 和 apply 是立即执行
bind 是返回执行函数