call、apply、bind

刚开始写技术笔记的时候,很浅显的写了一篇this的指向问题,现在看起来不能说错误百出,但也确实是没什么技术水平。今天分享自己对于call、apply、bind新的认识,并手写一个自己的call、apply、bind。

三个方法的语法大体一样:

fnction fn() {}
fn.call(thisArg, arg1, arg2, ...)
fn.apply(thisArg, [arg1,arg2,...])
fn.bind(thisArg, arg1, arg2, ...)

call和bind的参数一样,apply的参数是一个数组(a开头,Array),call和apply返回的是fn执行的结果,bind返回的是fn的拷贝并指定this值和参数(bind不执行,需要调用)。

thisArg是可选的,fn的this指向是thisArg对象。

非严格模式thisArg指定为null、undefined,fn的this指向就是window:

function fn() {
  console.log(this);//window
}
fn.call()
严格模式fn的this指向是undefined:
"use strict"
function fn() {
  console.log(this);//undefined
}
fn.call()

如果thisArg是数字、字符串、布尔值,this指向原始值的对象:

function fn() {
  console.log(this);//String||Number||Boolean
}
fn.call('' || 5 || true)

call、apply和bind是挂在Function对象上的方法,只有函数才能调用。

说真的,这三个方法开发业务的时候并不常用,有用也是用来装一装,面面试。三个方法最主要的就是借助别人的方法,减少重复代码,节省内存。比如fn1方法和fn2方法,fn2需要用到fn1的方法,这时候直接用fn1的方法而不是自己声明一个方法。bind方法比较不一样,bind返回的是一个函数,所以还可以用来做闭包等。

比如求一个数组的最大最小值:

var arr = [5, 6, 2, 8, 1];
console.log(Math.max.apply(Math, arr));//8
console.log(Math.min.apply(Math, arr));//1

实现一个call::

Function.prototype.myCall = function (context) {
  if(context === undefined || context === null){
    context = window;
  }else{
    context = Object(context);
  };
  context.fn = this;
  let result = context.fn([...arguments].slice(1));
  delete context.fn;
  return result;
};

这边需要注意的一个就是context,不能context = context || window;因为这样会把原始数值都绑定到window,但是上面也说了,String是绑定String的。还有就是直接context.fn也是不够严谨,可以使用symbol就绝对不会冲突,只能说fn这个需要用特殊的不会命名冲突的去实现,考虑兼容的话symbol好像也不是非常合适。

实现一个apply:

Function.prototype.myApply = function (context) {
  if(context === undefined || context === null){
    context = window;
  }else{
    context = Object(context);
  };
  context.fn = this;
  let result;
  if(arguments[1] instanceof Array){
    result = context.fn(...arguments[1]);
  }else{
    result = context.fn();
  };
  delete context.fn;
  return result;
};

跟call差不多,就是一个参数的判断不一样,其他的跟call的注意点一样。

实现一个bind:

Function.prototype.mybind = function (context) {
  let that = this;
  if(context === undefined || context === null){
    context = window;
  }else{
    context = Object(context);
  };
  let args = [...arguments].slice(1);
  return function Fn() {
    if(this instanceof Fn){
      return new that(...args, ...arguments)
    }else{
      return that.apply(context, args.concat(...arguments));
    }
  }
}

因为bind返回的是一个函数,所以思路是一样的,不同的是需要判断,bind之后是否是直接new这个函数,如果是new,那么this就是这个构造函数。

function Fn() {
  console.log(this);//Fn
}
new Fn();

(完)

本文分享自微信公众号 - coding个人笔记(gh_2ce38b49dae1),作者:wade

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ES6之函数的扩展

    正常情况下没有问题,但是要是a是0或者是false等会被转换成Boolean类型false的时候就会出错。ES6对参数的默认值允许直接设置:

    wade
  • Flex布局

    一般的网页布局,都是和模型布局,要想把行标签和块标签一排布局,一般都用display: inline-block、或者是浮动float。但是这两...

    wade
  • 自己实现一个eventBus

    昨天使用了vue提供的事件发布订阅,于是今天就自己实现了一个,发现没有太大难度,直接上代码:

    wade
  • 标星72K,确遭GitHub全面封杀!

    YouTube-dl是一款万能下载神器,它能够从全网七百多个网站免费下载视频,包括YouTube、优酷、爱奇艺、B站等热门网站;它还可以把视频内容转成任意的格式...

    Rocky0429
  • iOS8新特性扩展(Extension)应用之三——照片编辑插件

            通过前几篇博客的介绍,我们了解到扩展给app提供的更加强大的交互能力,这种强大的交互能力另一方面体现在照片编辑插件的应用。

    珲少
  • 几行代码轻松实现跨系统传递 traceId,再也不用担心对不上日志了!

    " 新项目查日志太麻烦,多台机器之间查来查去,还不知道是不是同一个请求的。打印日志时使用 MDC 在日志上添加一个 traceId,那这个 traceId 如何...

    程序员小航
  • 数组类型题目解题方法总结

    leetcode explore 初级算法数组章节已经全部写完,今天我们来总结下对于数组类一些简单题目的解题方式

    用户7685359
  • 手把手教你写一个完整的自定义View

    View的内部本身提供了post系列的方法,完全可以替代Handler的作用,使用起来更加方便、直接。

    Carson.Ho
  • Solidus(1)

    solidus,这篇文章写的非常丰富,而且很有创新性。不是研究热门的比特币类型的P2P交易模型,而是聚焦银行中介模型,这种在现实生活中更贴切金融系统的模型。不像...

    安包
  • Nginx高可用的配置-Keepalived

    单个Nginx负载均衡整个系统,如果nginx挂了,那么整个系统都将瘫痪,这时我们就需要保证及时1个Nginx挂了,整个系统也是可用的。

    用户4919348

扫码关注云+社区

领取腾讯云代金券