JavaScript中函数原型方法call、apply和bind
call、apply、bind方法主要用来改变函数中this的指向(显式绑定this)
1. call
调用call方法会将函数中的this指向上下文对象,然后传递call中余下的参数给函数,并执行函数。
Function.prototype(context, …rest)。
- call方法的第一个参数为一个上下文对象,也就是绑定this的对象,对于基本类型string、number、boolean等类型,会将其转化为相对应的基本包装类型,而对于undefined或者null会将上下文对象指向全局对象(浏览器中为window,Node中为global);
- 后续的参数是是传递给调用call方法的参数列表;
- 要想函数this指向某个对象,那么直接将函数赋值给那个对象的属性。
function getGlobalThis () {
return this
}
// ES3
Function.prototype.call = function (context) {
context = context == null ? getGlobalThis() : Object(context)
context._fn = this // this为调用call方法的函数实例
var args = []
for (var i = 1; i < arguments.length; i++) {
args.push('arguments['+ i + ']')
}
var res = eval('context._fn(' + args +')')
delete context._fn
return res
}
// ES6
Function.prototype.call = function (context, ...rest) {
context = context == null ? getGlobalThis() : Object(context)
const _fn = Symbol('_fn')
context[fn] = this
const res = context[_fn](...rest)
delete context[_fn]
return res
}
2. apply
apply方法与call方法基本类似,惟一的区别就是,后续参数的不同,apply方法只接受两个参数,第二个为传递给函数的参数列表的数组或者arguments对象。
function getGlobalThis () {
return this
}
// ES3
Function.prototype.apply = function (context, args) {
context = context == null ? getGlobalThis() : Object(context)
context._fn = this
var res
if (args == undefined) {
res = context._fn()
} else if (typeof args !== 'object') { // 支持arguments
throw new TypeError('CreateListFromArrayLike called on none-object')
} else {
args = Array.prototype.slice.call(args) // 将arguments转化为数组
res = eval('context._fn('+ args + ')')
}
delete context._fn
return res
}
// ES6
Function.prototype.apply = function (context, args) {
context = context == null ? getGlobalThis() : Object(context)
const _fn = Symbol('_fn')
var res
if (typeof args !== 'object' && args !== null) {
throw new TypeError('CreateListFromArayLike called on none-object')
} else {
res = context[_fn](...args)
}
delete context[_fn]
return res
}
3. bind
bind方法与前两个方法有所不同;bind方法会将调用它的函数中的this绑定为第一个参数(上下文对象),然后保存接下来的参数列表,最后返回一个新的函数,新的函数name名为bound + 原函数名,并且bound的prototype继承自原函数的prototype。
- 第一个参数与call和apply方法一致;
- 后续参数为参数列表;
- 返回一个新的函数bound,会把传递给bound的参数列表与原来的参数列表进行合并(函数柯里化)
- 调用bound函数后会判断bound是以new操作符调用还是一般调用,如果是new方法调用(即调用bound方法中this在bound的作用域链上),那么将context设置为this。
- bound函数的name属性为[bound + 原函数名],bound的prototype继承自原函数的prototype
function getGlobalThis () {
return this
}
Function.prototype.bind = function (context) {
context = context == null ? getGlobalThis() : Object(context)
var that = this
var args = Array.prototype.slice.call(arguments, 1)
function bound () {
var innerArgs = Array.prototype.slice.call(arguments)
context = this instanceof bound ? this : context
var res = that.apply(context, args.concat(innerArgs))
return res
}
function F () {}
F.prototype = that.prototype
bound.__proto__ = new F()
Object.defineProperties(bound, {
name: {
configurable: true,
enumerable: false,
writable: false,
value: 'bound' + that.name
}
})
return bound
}