如何使用50行javaScript代码实现简单版的call,apply,bind


Posted in Javascript onAugust 14, 2019

在实现自己的call,apply,bind前,需要复习一下this.

所谓的this其实可以理解成一根指针:

其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象,这就是精髓。最关键所在

this的四种指向:

当this所在的函数被普通调用时,指向window,如果当前是严格模式,则指向undefined

function test() {
 console.log(this);
};

test();
指向window 输出下面的代码:
// Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
严格模式
'use strict';
function test() {
 console.log(this);
};
test();
// undefined

当this所在当函数被以obj.fn()形式调用时,指向obj

var obj = {
 name: 'segmentFault',
 foo: function() {
  console.log(this.name);
 }
}
obj.foo();
// 'segmentFault'

还可以这么做

function test() {
 console.log(this.name);
}
var obj = {
 name: 'qiutc',
 foo: test
}
obj.foo();
// 'qiutc'

当call,apply加入后,this的指向被改变了

function a(a,b,c) {
    console.log(this.name);
    console.log(a,b,c)
  }
  const b = {
    name: "segmentFault"
  }
  a.call(b,1,2,3)    
  //输出 segmentFault和 1,2,3
  function a(a,b,c) {
    console.log(this.name);
    console.log(a,b,c)
  }
  a.apply(b,[1,2,3])
  //输出segmentFault和1,2,3

遇到bind后 :

function a() {
    console.log(this.name);
  }
  const b = {
    name: "segmentFault"
  }
  a.bind(b, 1, 2, 3)

此时控制台并没有代码输出,因为bind会重新生成并且返回一个函数,这个函数的this指向第一个参数

function a() {
    console.log(this.name);
  }
  const b = {
    name: "segmentFault"
  }
  const c = a.bind(b, 1, 2, 3)
  c()
  //此时输出segmentFault

正式开始自己实现call :

在函数原型上定义自己的myCall方法:

Function.prototype.myCall = function (context, ...arg) {
    const fn = Symbol('临时属性')
    context[fn] = this
    context[fn](...arg)
    delete context[fn]
  }

四行代码实现了简单的call,思路如下:

  • 通过对象属性的方式调用函数,这个函数里面的this指向这个对象
  • 每次调用新增一个symbol属性,调用完毕删除
  • 这个symbol属性就是调用mycall方法的函数
  • 函数形参中使用...arg是将多个形参都塞到一个数组里,在函数内部使用arg这个变量时,就是包含所有形参的数组
  • 在调用 context[fn](...arg)时候,...arg是为了展开数组,依次传入参数调用函数

为了简化,今天都不做类型判断和错误边际处理,只把原理讲清楚。

自己实现apply

在函数原型上定义自己的myApply方法:

//实现自己的myApply
  Function.prototype.myApply = function (context, arg) {
    const fn = Symbol('临时属性')
    context[fn] = this
    context[fn](...arg)
    delete context[fn]
  }
  const obj2 = {
    a: 1
  }
  test.myApply(obj2, [2, 3, 4])

同理,只是apply传递的第二个参数是数组,这里我们只需要在调用时,将参数用...把数组展开即可

自己实现bind:

bind跟apply,call的本质区别,bind不会改变原函数的this指向,只会返回一个新的函数(我们想要的那个this指向),并且不会调用。但是apply和bind会改变原函数的this指向并且直接调用

bind在编写框架源码,例如koa等中用得特别多:

//实现自己的myBind
  Function.prototype.myBind = function (context, ...firstarg) {
    const that = this
    const bindFn = function (...secoundarg) {
      return that.myCall(context, ...firstarg, ...secoundarg)
    }
    bindFn.prototype = Object.create(that.prototype)
    return bindFn
  }

  var fnbind = test.myBind(obj, 2)
  fnbind(3)

同理 自己定义好原型上的myBind方法

this劫持 保留最初的调用mybind方法的那个对象

返回一个新的函数 这个新的函数内部this指向已经确定,使用的是我们的mycall方法

学习需要循序渐进,建议根据本文顺序去封装一遍,是比较轻松的,当然bind还需要判断是否是new调用.

完整版本bind

Function.prototype.myBind = function (objThis, ...params) {
  const thisFn = this; // 存储源函数以及上方的params(函数参数)
  // 对返回的函数 secondParams 二次传参
  let fToBind = function (...secondParams) {
    console.log('secondParams',secondParams,...secondParams)
    const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
    const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
    return thisFn.call(context, ...params, ...secondParams); // 用call调用源函数绑定this的指向并传递参数,返回执行结果
  };
  fToBind.prototype = Object.create(thisFn.prototype); // 复制源函数的prototype给fToBind
  return fToBind; // 返回拷贝的函数
};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Js 中debug方式
Feb 07 Javascript
jquery组件使用中遇到的问题整理及解决
Feb 21 Javascript
ext前台接收action传过来的json数据示例
Jun 17 Javascript
关闭页面window.location事件未执行的原因及解决方法
Sep 01 Javascript
jquery validate.js表单验证入门实例(附源码)
Nov 10 Javascript
mvc 、bootstrap 结合分布式图简单实现分页
Oct 10 Javascript
jQuery轻松实现无缝轮播效果
Mar 22 jQuery
JS组件系列之MVVM组件 vue 30分钟搞定前端增删改查
Apr 28 Javascript
nuxt.js 缓存实践
Jun 25 Javascript
对layui中表单元素的使用详解
Aug 15 Javascript
angular2 ng2-file-upload上传示例代码
Aug 23 Javascript
react ant Design手动设置表单的值操作
Oct 31 Javascript
微信小程序之数据绑定原理解析
Aug 14 #Javascript
微信公众号平台接口开发 菜单管理的实现
Aug 14 #Javascript
vue.js中ref和$refs的使用及示例讲解
Aug 14 #Javascript
微信公众号平台接口开发 获取微信服务器IP地址方法解析
Aug 14 #Javascript
vue filter 完美时间日期格式的代码
Aug 14 #Javascript
如何对react hooks进行单元测试的方法
Aug 14 #Javascript
vue 中 命名视图的用法实例详解
Aug 14 #Javascript
You might like
thinkphp中memcache的用法实例
2014/11/29 PHP
PHP中使用socket方式GET、POST数据实例
2015/04/02 PHP
php+ajax登录跳转登录实现思路
2016/07/31 PHP
理解Javascript_01_理解内存分配原理分析
2010/10/11 Javascript
Dom与浏览器兼容性说明
2010/10/25 Javascript
修改jquery.lazyload.js实现页面延迟载入
2010/12/22 Javascript
按下Enter焦点移至下一个控件的实现js代码
2013/12/11 Javascript
jQuery实现图片轮播特效代码分享
2015/09/15 Javascript
js 判断各种数据类型的简单方法(推荐)
2016/08/29 Javascript
node.js路径处理方法以及绝对路径详解
2021/03/04 Javascript
JavaScript SHA1加密算法实现详细代码
2016/10/06 Javascript
js仿搜狐视频记录片列表展示效果
2020/05/30 Javascript
基于JavaScript实现飘落星星特效
2017/08/10 Javascript
bootstrap轮播模板使用方法详解
2017/11/17 Javascript
Node.js连接Sql Server 2008及数据层封装详解
2018/08/27 Javascript
angular中如何绑定iframe中src的方法
2019/02/01 Javascript
vue实现微信二次分享以及自定义分享的示例
2019/03/20 Javascript
vue中的面包屑导航组件实例代码
2019/07/01 Javascript
Vue中Table组件行内右键菜单实现方法(基于 vue + AntDesign)
2019/11/21 Javascript
jQuery操作选中select下拉框的值代码实例
2020/02/07 jQuery
vue 二维码长按保存和复制内容操作
2020/09/22 Javascript
python使用正则筛选信用卡
2019/01/27 Python
django mysql数据库及图片上传接口详解
2019/07/18 Python
python 创建一维的0向量实例
2019/12/02 Python
HTML5使用DOM进行自定义控制示例代码
2013/06/08 HTML / CSS
Spotahome意大利:公寓和房间出租
2020/02/21 全球购物
俄罗斯家居用品购物网站:Евродом
2020/11/21 全球购物
德国BA保镖药房中文网:Bodyguard Apotheke
2021/03/09 全球购物
一篇.NET面试题
2014/09/29 面试题
网络公司美工设计工作个人的自我评价
2013/11/03 职场文书
效能监察建议书
2014/05/19 职场文书
班子个人四风问题整改措施
2014/10/04 职场文书
党员个人自我剖析材料
2014/10/08 职场文书
班主任经验交流心得体会
2015/11/02 职场文书
导游词之新疆-喀纳斯
2019/10/10 职场文书
python代码实现备忘录案例讲解
2021/07/26 Python