js之call、apply、bind模拟实现

call和apply方法主要完成两项工作

  1. 改变函数中this指向
  2. 执行函数,并返回函数的执行结果
call方法模拟,传入参数是散列的形式
Function.prototype.call = function (context) {//context是指this新指向的函数对象
  // 如果传入的函数对象为null,让this指向window对象
  var context = context || window
  // 修改this指向 : 将调用call的函数做为context的属性,再执行函数
  context.fn = this;
  
  //传入参数方法1:arguments获取不定参数列表
  var args = [];
  for (var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
  }
  var result = eval('context.fn(' + args + ')');//eval函数会默认识别字符串里的数组元素,逐个传递参数

  //传入参数方法2:
  // let arr = [...arguments]
  // arr.shift()
  // var result = context.fn(...arr)
  //函数执行完后删除函数属性
  delete context.fn;
  return result
}

// 测试一下
var value = 2;
var obj = {
  value: 1
}
function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age
  }
}

bar.call(null); // 2
console.log(bar.call(obj, 'kevin', 18));
// 1
// Object {
//    value: 1,
//    name: 'kevin',
//    age: 18
// }

apply方法模拟,传入参数是数组的形式
Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        result = context.fn(...arr)
    }

    delete context.fn
    return result;
}
bind方法模拟

bind()方法会创建一个新函数,当这个新函数被调用时,bind()的第一个参数作为它运行时的this,之后的一序列参数将会在传递的实参传入前作为它的参数
bind函数的特点 :

  1. 返回一个函数
  2. 返回的函数在执行时可以传入参数

例子 :

var foo = {
    value: 1
};
function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
}
var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');//1 daisy 18

模拟实现

Function.prototype.bind = function (context) {
    var self = this;//调用bind方法的函数
    // 获取bind函数从第二个参数到最后一个参数
    var args = Array.prototype.slice.call(arguments, 1);
    return function () {
        // 这个时候的arguments是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(context, args.concat(bindArgs));//函数可能有返回值
    }
}
bind()返回值作为构造函数

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

bind()方法最终版本模拟实现 :

Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        //当作为构造函数时,this指向实例对象,此时结果为true,将绑定函数的this指向改实例,可以让实例获得来自绑定函数的值
        //当作为普通函数时,this指向window,此时结果为false,将绑定函数的this指向context
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值