JavaScript实现callapply和bind
Posted A_山水子农
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript实现callapply和bind相关的知识,希望对你有一定的参考价值。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。详细内容查看JavaScript中call()、apply()和 bind()方法,
1、call方法的实现
下面看一个使用call方法的实例:
function add(c, d)
return this.a + this.b + c + d
var o = a:1, b:3
add.call(o, 5, 7) // 16
add.call(o, 5, 7)使add函数中的this指向o对象,并且执行了add函数。依据上例把o对象改造成如下:
var o=
a: 1,
b: 3,
add: function(c, d)
return this.a + this.b + c + d
;
o.add(5, 7); // 16
给o对象添加一个add属性,这个时候 this 就指向了 o,o.add(5,7)得到的结果和add.call(o, 5, 6)相同。但是给对象o添加了一个额外的add属性,这个属性我们是不需要的,所以可以使用delete删除它。
所以我们使用js实现call方法的步骤可以分为:
将函数设为对象的属性
执行该函数
删除该函数
以上个例子为例,就是:
// 第一步
o.fn = bar
// 第二步
o.fn()
// 第三步
delete o.fn
使用ES3实现call方法
/**
* @description 使用ES3实现call方法
* @param Object context call方法一个指定的this值
* @returns Object, String, Number, Boolean 返回调用函数的值
*/
Function.prototype.call = function (context)
// context为null的时候,context为window
var context = context || window
// 获取调用call的函数
context.fn = this
// 获取call方法的不定长参数
var args = []
for(var i = 1, len = arguments.length; i < len; i++)
args.push('arguments[' + i + ']')
// 运行fn函数并返回结果,
// eval(string)通过计算string得到的值
var result = eval('context.fn(' + args +')')
// 删除fn属性
delete context.fn
// 返回结果
return result;
/**
* @description 测试call方法
* @param Number c,d 函数的参宿
* @returns Number 返回add函数的计算结果
*/
function add(c, d)
return this.a + this.b + c + d
var o = a:1, b:3
add.call(o, 5, 7) // 16
使用ES6实现call方法
/**
* @description 使用ES6数组的扩展运算符(...)实现call方法
* @param Object context call方法一个指定的this值
* @returns Object, String, Number, Boolean 返回调用函数的值
*/
Function.prototype.call = function(context)
// context为null的时候,context为window
var context = context || window
// 获取调用call的函数
context.fn = this
// 获取call方法的不定长参数
var args = []
for (var i = 1, len = arguments.length; i < len; i++)
args.push(arguments[i])
// 使用ES6扩展运算符(...)执行函数,返回结果
var result = context.fn(...args)
// 删除fn属性
delete context.fn
// 返回结果
return result;
更简单的ES6写法:
/**
* @description 使用ES6函数的rest参数和数组的扩展运算符实现call方法
* @param Object context call方法一个指定的this值
* @param Object, String, Number, Boolean context call方法一个指定的this值
* @returns Object, String, Number, Boolean 返回调用函数的值
*/
Function.prototype.call = function(context, ...args)
// 使用ES6函数的rest参数(形式为...变量名),args是数组
// context为null的时候,context为window
var context = context || window
// 获取调用call的函数
context.fn = this
// 使用ES6扩展运算符(...)执行函数,返回结果
var result = context.fn(...args)
// 删除fn属性
delete context.fn
// 返回结果
return result;
2、apply方法的实现
apply和call方法实现类似,apply和call方法的区别仅在于接收参数的方式不同。对于apply()方法而言,第一个参数是作用域没有变化,变化的只是其余的参数apply使用数组或者arguments对象,call直接传递给函数。
使用ES3实现apply方法
/**
* @description 使用ES3实现apply方法
* @param Object context apply方法一个指定的this值
* @param Array arr apply方法传递给调用函数的参数
* @returns Object, String, Number, Boolean 返回调用函数的值
*/
Function.prototype.apply= function (context, arr)
// context为null的时候,context为window
var context = context || window
// 获取调用apply的函数
context.fn = this
var result
// 判断apply是否只有一个参数
if (!arr)
// 执行函数
result = context.fn();
else
// 获取参数
var args = [];
for (var i = 0, len = arr.length; i < len; i++)
args.push('arr[' + i + ']');
// 执行函数
result = eval('context.fn(' + args + ')')
// 删除fn属性
delete context.fn
// 返回结果
return result;
/**
* @description 测试apply方法
* @param Number c,d 函数的参宿
* @returns Number 返回add函数的计算结果
*/
function add(c, d)
return this.a + this.b + c + d
var o = a:1, b:3
add.apply(o, [5, 7]) // 16
使用ES6实现apply方法
/**
* @description 使用ES6数组的扩展运算符实现apply方法
* @param Object context apply方法一个指定的this值
* @param Array arr apply方法传递给调用函数的参数
* @returns Object, String, Number, Boolean 返回调用函数的值
*/
Function.prototype.call = function(context, arr)
// context为null的时候,context为window
var context = context || window
// 获取调用apply的函数
context.fn = this
// 使用ES6扩展运算符(...)执行函数,返回结果
var result = context.fn(...arr)
// 删除fn属性
delete context.fn
// 返回结果
return result
3、bind方法的实现
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。先看一个使用bind方法的实例:
function foo(c, d)
this.b = 100
console.log(c)
console.log(d)
console.log(this.a)
var func = foo.bind(a: 1, 'cc')
func('dd') //cc dd 1
new func('dd') //cc dd undefined
当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。所以上例中使用func为构造函数时,this不会指向a: 1对象,this.a的值为undefined。
/**
* @description 实现bind方法
* @param Object context bind方法一个指定的this值
* @returns Function 返回一个函数
*/
Function.prototype.bind = function (context)
// 判断绑定bind方法的是不是函数
if (typeof this !== "function")
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
// 绑定函数赋值给self
var self = this;
// 获取bind2函数从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments, 1);
// 调用bind方法返回的函数
var fBound = function ()
// 获取函数的参数
var bindArgs = Array.prototype.slice.call(arguments);
// 返回函数的执行结果
// 判断函数是作为构造函数还是普通函数
// 构造函数this instanceof fNOP返回true,将绑定函数的this指向该实例,可以让实例获得来自绑定函数的值。
// 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs))
// 创建空函数
var fNOP = function () ;
// fNOP函数的prototype为绑定函数的prototype
fNOP.prototype = this.prototype;
// 返回函数的prototype等于fNOP函数的实例实现继承
fBound.prototype = new fNOP();
// 以上三句相当于Object.create(this.prototype)
// 返回函数
return fBound;
使用ES6实现bind方法
if (typeof Function.prototype.bind !== 'function')
Function.prototype.bind = function(context, ...rest)
if (typeof this !== 'function')
throw new TypeError('invalid invoked!')
var self = this
return function F(...args)
if (this instanceof F)
return new self(...rest, ...args)
return self.apply(context, rest.concat(args))
参考链接:
不能使用call,apply,bind,如何用js实现call或者apply的功能?
以上是关于JavaScript实现callapply和bind的主要内容,如果未能解决你的问题,请参考以下文章