1.前言

call,apply,bind这三个方法都是用来改变函数的this指向,如果有对this不熟悉的朋友,可以先看看笔者的这篇博客。

call & apply

call()语法:

function.call(thisArg, arg1, arg2, ...)
  • thisArg
    可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
  • arg1, arg2, …
    指定的参数列表。
  • 返回值
    使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。
    apply()语法
    apply()方法的语法和 call() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。
var person1 = {first_name : 'Niall',last_name : 'Horan',getFull : function (first = this.first_name,last=this.last_name){console.log(first + ' ' + last)}
}
var person2 = {first_name: 'Sam',last_name: 'Smith'
}person1.getFull.call(person2)
person1.getFull.apply(person2)
person1.getFull.call(person2,'Alan','Walker')
person1.getFull.apply(person2,['Alan','Walker'])

输出:

Sam Smith
Sam Smith
Alan Walker
Alan Walker

那么,callapply可以改变函数内this的指向,那么,可以通过这种方式来达到继承的效果:

function Product(name, price) {this.name = name;this.price = price;
}function Food(name, price) {Product.call(this, name, price); //this.category = food;
}var hotDog = new Food('hotDog', 20);

模拟实现call

当我们使用call()时:

person1.getFull.call(person2,'Alan','Walker')
  • call简单来说就是改变了函数内部this的指向,使person1.getFull中的this指向了person2
  • 执行了person1.getFull()
  • call()传入的参数由两部分组成,新的this,和原函数所需要的参数

Ⅰ. 改变this的指向

如何实现呢?
person1.getFull()中的this是指向person1的,因为getFull()是由person1调用的,使用call方法治好,this指向了person2,换一个思路,如果不是person1调用的getFull()中的this指向变了,而是getFull()由person1调用变成了person2调用?

Function.prototype.myCall = function (obj){//函数调用了myCall,在函数内,this指向该函数,记做“调用函数”//为obj增加一个属性func,func是一个函数,把“调用函数”赋值给funcobj.func = this//obj调用func(),相当于obj调用了“调用函数”,也就是getFull// 注意,我们现在还没有给“调用函数”传递参数obj.func()delete obj.func//删除obj的func属性,还原如初
}
person1.getFull.myCall(person2)

输出:

Sam Smith

2. 传入参数

Function.prototype.myCall = function (obj){//函数调用了myCall,在函数内,this指向该函数,记做“调用函数”//为obj增加一个属性func,func是一个函数,把“调用函数”赋值给funcobj.func = thisvar args = []for(let i = 1; i < arguments.length; i++){args.push('arguments[' + i + ']');}//obj调用func(),相当于obj调用了“调用函数”,也就是getFulleval('obj.func('+args+')')delete obj.func//删除obj的func属性,还原如初
}
person1.getFull.myCall(person2,'Alan','Walker')

输出:

Alan Walker

在给args赋值的时候,应当格外小心,如果这样写,可能会报错:

  for(let i = 1; i < arguments.length; i++){args.push('arguments[' + i + ']');}

当参数为全为数字时,以上代码不会出现问题,但是当参数不全是数字时,就会出现以下问题:

ReferenceError: Alan is not defined

在执行eval时,会将所有参数安装字符串连接再执行,上面两种写法对应的执行代码分别是:

1.obj.func(arguments[1],arguments[2])
2.obj.func(Alan,Walker)

这样,第二种写法就会报错,因为eval()将Alan当成变量名处理了,而不是字符串。

3. 返回值

虽然这里的person1.getFull()没有返回值,但是函数有返回值的情况并不少见。

Function.prototype.myCall = function (obj){//函数调用了myCall,在函数内,this指向该函数,记做“调用函数”//为obj增加一个属性func,func是一个函数,把“调用函数”赋值给funcobj.func = thisvar args = []for(let i = 1; i < arguments.length; i++){args.push(arguments[i]);}//obj调用func(),相当于obj调用了“调用函数”,也就是getFullvar res = eval('obj.func('+args+')')delete obj.func//删除obj的func属性,还原如初return res
}

4. 完善没有设置新的this时的myCall

在调用call()方法时,我们可以不设定新的this指向,此时,函数中this的指向会自动替换为全局对象:

console.log(Math.max.call(null,1,2,3,4))

输出:

4

完善myCall()

Function.prototype.myCall = function (obj){//函数调用了myCall,在函数内,this指向该函数,记做“调用函数”//是否存在obj,如不存在,替换为windowvar obj = obj || window//为obj增加一个属性func,func是一个函数,把“调用函数”赋值给funcobj.func = thisvar args = []for(let i = 1; i < arguments.length; i++){args.push('arguments[' + i + ']');}//obj调用func(),相当于obj调用了“调用函数”,也就是getFullvar res = eval('obj.func('+args+')')delete obj.func//删除obj的func属性,还原如初return res
}

5. bind

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

bind和call相比,它将一个改变了this指向的函数保存了下来,可以多次调用该函数:

let person2_getFull = person1.getFull.bind(person2)
person2_getFull('August','Peter')
person2_getFull('Harry','Dank')

输出:

August Peter
Harry Dank

6. 模拟实现bind

person1.getFull.bind(person2)中,bind做了什么呢?

  • 改变了this的指向;
  • 返回了改变后的函数。
Function.prototype.myBind = function (obj){var func = this //将调用函数赋值给funcvar obj = obj || window//返回一个函数,在该函数中调用了func.apply。//记binded = function.myBind(obj),当我们调用binded()时,在binded内部//触发了func.apply(obj,arguments),这里的arguments就是我们调用binded()时传入的参数//注意,arguments是数组,所以这里使用apply更方便一些return function (){return func.apply(obj,arguments)}
}
let person2_getFull_2 = person1.getFull.myBind(person2)
person2_getFull_2('August','Peter')
person2_getFull_2('Harry','Dank')

输出:

August Peter
Harry Dank

JavaScript call,apply,bind详解及实现相关推荐

  1. JavaScript中的this详解

      前  言  this  JavaScript中的this详解 this详解 This的指向有几种情况?如何人为控制? [谁调用this,this指向谁!!] [this的指向,不关心this写在哪 ...

  2. Javascript常用的设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  3. Javascript this关键字 指向详解

    Javascript this关键字 指向详解 面向对象语言中 this 表示当前对象的一个引用.在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变. 1) 单独使用 ...

  4. Js中apply方法详解说明

    Js apply 方法 详解 我在一开始看到JavaScript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了, ...

  5. 【JavaScript】类数组详解

    [JavaScript]类数组详解 文章目录 [JavaScript]类数组详解 什么是类数组 类数组转换成数组 ES6 的方法转数组 callee属性 箭头函数没有arguments HTMLCol ...

  6. Js apply 方法 详解

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  7. Js apply()使用详解

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  8. Js apply方法详解

    Js apply方法详解 主要解决一下几个问题: apply和call的区别在哪里 什么情况下用apply,什么情况下用call apply的其他巧妙用法(一般在什么情况下可以使用apply) 首先从 ...

  9. 众望所归的《JAVASCRIPT凌厉开发--EXT详解与实践 》终于上市了!

    大家好,我是这本书的策划编辑,经过努力,这本书终于上市了. 从创作开始,我们的目标就是写一本真正适合开发者参考和借鉴的EXT书,写作过程中,经过了无数次讨论和痛苦修订,感谢三位作者付出的艰辛劳动. 这 ...

  10. JavaScript中getBoundingClientRect()方法详解

    JavaScript中getBoundingClientRect()方法详解 getBoundingClientRect() 这个方法返回一个矩形对象,包含四个属性:left.top.right和bo ...

最新文章

  1. 用memcache.php监测memcache的状况
  2. 【蓝桥java】递归基础之振兴中华
  3. 清华团队让 AI 写诗“更上一层楼”,诗歌图灵测试迷惑近半数玩家
  4. 鼠标跟随flash代码_FLASH如何制作密码锁功能(AS3)
  5. 模块计算机类型x64与目标计算机类型X86冲突
  6. UVa 389 Basically Speaking
  7. fz响应无法连接到服务器,fz链接不到服务器
  8. python中赋值语句的作用_python中return可以使用赋值语句吗?
  9. 已知服务器ftp的账号密码,求解数据库表的内容
  10. VTK:图片之ImageEllipsoidSource
  11. 开源项目【zheng】搭建流程
  12. mysql plsql cursor_Oracle--plsql游标创建和使用
  13. 金币(信息学奥赛一本通-T1100)
  14. 15款精美的 WordPress 电子商务网站模板
  15. 博客园上海地区活动——LinkCoder主题社区第二期:淘宝服务化架构的设计和实践...
  16. java中解决request中文乱码问题
  17. Windows azure中公用云服务的两个虚机FTP的设置
  18. C++迭代器 iterator
  19. 50 Most Frequently Used UNIX / Linux Commands (With Examples)
  20. Android Studio新建工程syncing失败;Android studio Connection timed out: connect

热门文章

  1. CCPC-Wannafly Winter Camp Day1 自闭总结
  2. 【数据科学】06 数据转换-数据离散化、编码分类特征(哑变量矩阵、数字编码)
  3. qt linguist使用注意
  4. Baxter手臂控制
  5. 量子计算机淘汰了吗,量子计算机将淘汰目前的所有计算机
  6. mysql导出教程_MySQL导出导入数据实现教程
  7. PWN-COMPETITION-HGAME2022-Week2
  8. 缺点 霍夫圆_基于机器视觉的磁环表面缺陷检测研究
  9. 性格内向的人,是否适合做产品经理 ?
  10. Android 模拟器一键获取root权限 一键安装Google play 服务