深入浅出FE(二)call/apply和bind

目录

一、定义

1、call

2、apply

3、bind

二、 关于call、apply和bind

1、call、apply、bind方法的共同点:

2、call、apply、bind方法的不同点:

三、拓展

1、模拟实现call

2、模拟实现apply,和call类似

3、模拟实现bind

4、应用

四、浏览器兼容性

五、参考资料


一、定义

1、call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

fuction.call(thisArg, arg1, arg2, ...)

参数:

thisArg

可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

arg1, arg2, ...

指定的参数列表。

2、apply

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象,ES5之后支持传类数组,如dom中的一些类数组结果或者arguments)提供的参数。

function.apply(thisArg, [argsArray])

参数:

thisArg

必选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

argsArray

可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或  undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。

返回值

调用有指定this值和参数的函数的结果。

注意:call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

3、bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

function.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

thisArg

调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,执行作用域的 this 将被视为新函数的 thisArg

arg1, arg2, ...

当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

bind是ES5新增的一个方法,不会执行对应的函数(call或apply会自动执行对应的函数),而是返回对绑定函数的引用

二、 关于call、apply和bind

1、callapplybind方法的共同点:

apply 、 call 、bind 三者都是Function原型(即Function.prototype)的方法
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的执行上下文环境;
apply 、 call 、bind 三者都可以利用后续参数传参;

2、callapplybind方法的不同点:

bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

call/apply的用法相同,区别在于传参数方式不同,call接受一个参数列表,apply接受一个参数数组,call是apply的语法糖

三、拓展

1、模拟实现call

Function.prototype.myCall = function (ctx) {
    //获取上下文ctx
    let ctx = ctx || window;
    //获取当前将要执行的函数
    ctx.fn = this;
    //截取参数
    let args = [...arguments].slice(1);
    //执行函数
    let res = ctx.fn(...args);
    //去除副作用
    delete ctx.fn;
    return res;
}

2、模拟实现apply,和call类似

Function.prototype.myApply = (ctx){
    var ctx = ctx || window;
    ctx.fn = this;
    var res;
    if (arguments[1]) {
        re s = ctx.fn(...arguments[1]);
    } else {
        res = ctx.fn();
    }
    delete ctx.fn;
    return res
}

3、模拟实现bind

//bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,
//而其余参数将作为新函数的参数,供调用时使用。
Function.prototype.bind = Function.prototype.bind || function (context) {
    if (typeof this !== "function") {
        throw new Error("Function.prototype.bind is not a function")
    }
    var self = this;
    var args = [...arguments].slice(1);

    return function F() {
        if (self instanceof F) {
            return new self(...args, ...arguments);
        }
        return new self.apply(context, args.concat(...arguments));
    }
};

4、应用

(1)call可以用作检测数据类型

const getVarType = (target) => {
  var classToType = {};
  "Array Date RegExp Object Error".split(" ").forEach(function(item) {
    classToType["[object " + item + "]"] = item.toLowerCase();
  });

  function type(target) {
    if (target === null) {
      return String(target);
    }
    return typeof target === "object"
      ? classToType[object.prototype.toString.call(target)] || "object"
      : typeof target;
  }
  return type(target);
};

2、apply可以用作柯里化

函数柯里化是把接受多个参数的函数转变成接受一个单一参数(最初函数的第一个参数),并且返回接受余下的参数而且返回结果的新函数的技术。

柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。这增加了函数的适用性,但同时也降低了函数的适用范围。

这是以前看《javascript设计模式与开发实践》做的笔记,

var currying = function(fn) {
  var args = [];

  return function() {
    if (arguments.length === 0) {
      return fn.apply(this, args); // 没传参数时,调用这个函数
    } else {
      [].push.apply(args, arguments); // 传入了参数,把参数保存下来
      return arguments.callee; // 返回这个函数的引用
    }
  }
}

var cost = (function() {
  var money = 0;
  return function() {
    for (var i = 0; i < arguments.length; i++) {
      money += arguments[i];
    }
    return money;
  }
})();

var cost = currying(cost);

cost(100); // 传入了参数,不真正求值
cost(200); // 传入了参数,不真正求值
cost(300); // 传入了参数,不真正求值

console.log(cost()); // 求值并且输出600

上面这种情况没有考虑为函数与传参数情况,所以做了一点点修改

var currying = function(fn) {
  var args = [...argumengts].slice(1);

  return function() {
    if (arguments.length === 0) {
      return fn.apply(this, args); // 没传参数时,调用这个函数
    } else {
      [].push.apply(args, arguments); // 传入了参数,把参数保存下来
      return arguments.callee; // 返回这个函数的引用
    }
  }
}

3、apply函数可以用来实现bind(参见上面bind的实现)

四、浏览器兼容性

详见参考资料1、2、3-对应call、apply和bind的MDN文档

五、参考资料

1、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call

2、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

3、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

发布了380 篇原创文章 · 获赞 115 · 访问量 39万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章