JavaScript实现call、apply和bind
2017-12-13 17:29
896 查看
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。详细内容查看JavaScript中call()、apply()和 bind()方法,
add.call(o, 5, 7)使add函数中的this指向o对象,并且执行了add函数。依据上例把o对象改造成如下:
给o对象添加一个add属性,这个时候 this 就指向了 o,o.add(5,7)得到的结果和add.call(o, 5, 6)相同。但是给对象o添加了一个额外的add属性,这个属性我们是不需要的,所以可以使用delete删除它。
所以我们使用js实现call方法的步骤可以分为:
将函数设为对象的属性
执行该函数
删除该函数
以上个例子为例,就是:
使用ES3实现call方法
使用ES6实现call方法
更简单的ES6写法:
使用ES3实现apply方法
使用ES6实现apply方法
当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。所以上例中使用func为构造函数时,this不会指向{a: 1}对象,this.a的值为undefined。
使用ES6实现bind方法
参考链接:
JavaScript深入之call和apply的模拟实现
不能使用call,apply,bind,如何用js实现call或者apply的功能?
JavaScript深入之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)) } } }
参考链接:
JavaScript深入之call和apply的模拟实现
不能使用call,apply,bind,如何用js实现call或者apply的功能?
JavaScript深入之bind的模拟实现
相关文章推荐
- JavaScript 之 call和apply,bind 4000 的模拟实现
- 开启Javascript中apply、call、bind的用法之旅模式
- JavaScript中的call、apply、bind方法的区别
- 你不知道的JavaScript--Item9 call(),apply(),bind()与回调
- 深入浅出 妙用Javascript中apply、call、bind
- javascript中的call(),apply()以及bind()的用法
- 【JavaScript】call, apply, bind
- javascript中的作用域 (apply、call、.bind)
- call,apply,bind,jsonp的实现
- 深入浅出 妙用Javascript中apply、call、bind
- Javascript中apply、call、bind
- Javascript中call,apply,bind方法的详解与总结
- 妙用Javascript中apply、call、bind
- Javascript中关于call,apply,bind这三个函数的用法
- JavaScript中的作用域 (apply、call、.bind)
- javaScript中的apply call bind
- 深入浅出妙用 Javascript 中 apply、call、bind
- JavaScript方法call,apply,caller,callee,bind的使用详解及区别
- JavaScript的那些坑之call,apply,bind
- javascript中apply、call和bind的区别