专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
码农翻身  ·  漫画 | 为什么大家都愿意进入外企? ·  2 天前  
程序员小灰  ·  清华大学《DeepSeek学习手册》(全5册) ·  3 天前  
OSC开源社区  ·  2024: 大模型背景下知识图谱的理性回归 ·  5 天前  
程序猿  ·  “未来 3 年内,Python 在 AI ... ·  5 天前  
51好读  ›  专栏  ›  SegmentFault思否

call, apply, bind 函数能干啥?

SegmentFault思否  · 公众号  · 程序员  · 2017-10-15 08:00

正文

call(),apply(),bind() 函数大家可能都有所了解,但是在平时搬砖过程中 很可能或者基本没用过 ,学过但都淡忘了。

但是在大量第三方的框架(库),甚至js自己都在 源码中大量使用 call,apply 函数。所以今天和大家仔细讨论下它们在 开发中的应用场景

1.它们是啥意思

1.1 作用
  1. 他们的作用都是改变函数内部的 this

  2. 这三个函数都是 函数对象 的方法,也就是说只有函数才可以直接调用这些方法。

ps:call,apply,bing属于this显示绑定,还有好几种其他的this绑定方式,感兴趣的可以点这里。

1.2 三者区别

参数

三个函数的第一个参数都是需要绑定的 this

  • call : 可以有n个参数,从第二个参数开始的所有参数都是原函数的参数。

  • apply :只有两个参数,并且第二个参数必须为数组,数组中的所有元素一一对应原函数的参数。

  • bind : 只有一个参数,即要绑定的this。

call 语法:

  1. foo.call(this, arg1,arg2, ... ,argn );

apply 语法:

  1. foo.apply(this, [ arg1,arg2, ... ,argn ] );

bind 语法:

  1. foo.bind(this);

调用

  • call,apply :调用后立即执行原函数。

  • bind :调用后返回已经绑定好this的函数。

小例子一枚:

  1.    function foo(a,b){

  2.        console.log(a+b);

  3.    }

  4.    foo.call(null,'海洋','饼干');         // 海洋饼干  这里this指向不重要就写null了

  5.    foo.apply(null, ['海洋','饼干'] );   // 海洋饼干

  6.    var fun = foo.bind(null);

  7.    fun('海洋','饼干');                  // 海洋饼干

2 .它们能干啥事

这是我们今天讨论的主题,这三个函数如何应用?什么情况下使用? 能改变 this 指向又能咋滴?

2 .1 处理伪数组 (最常用)

先考虑一个问题,如果你使用 vararr=document.getElementsByTagName('li') 获取了5个 li 元素,你现在需要获取其中的第2,3,4三个元素,你会怎么做?

这样 arr.slice(1,4); ? 啊哦, TypeError--arr.sliceisnotafunction(slice不是函数) ,数组操作在日常搬砖中非常常见,我见过最傻的解决这个问题的方式是使用循环,将需要的元素一个个添加到一个新数组里0.0, 下面我介绍的方法完全可以在实战中使用,可以给你的代码加分哦,非常方便简洁 (中高级前端程序员中,算是基本操作了)。

先要介绍一个概念( 伪数组 ),这也是为什么我们刚刚slice切割数组时出错的原因: (对新手来说算是干货了,知道的可以跳过)

什么是伪数组?( 字面的意思已经呼之欲出了 )

  1. 有length属性

  2. 能按索引存储数据

  3. 能像遍历数组一样来遍历

  4. 不能使用数组的 push()、slice() 等方法

简单来说就是可以像数组一样操作的对象,但是没有数组的方法。

js中存在大量伪数组,如 : 1. function的arguments对象。 2. getElementsByName(),getElementsByTagName(),childNodes/children 等方法的返回值。 3. 还有比较常见的jquery,使用它获取的元素也是伪数组。

回到原来的问题,如何截取伪数组中的元素: 伪数组没有这些方法,我们'借用'Array的slice不就行了

  1.    [].slice.call(arr,1,4);  // 推荐写法

不想借用你可以直接给伪数组添加一个slice函数,如

  1. arr.slice = [].slice;

  2. arr.slice(1,4);

当然,'借用' 更方便,直接添加会导致伪数组对象'污染'。

如果可以随意改变原对象,可以 直接将其转成真正的数组对象

  1.    [].slice.call(arr);

2 .2 继承

继承方式多种多样,我们现在讨论的这种是其中很重要的一种实现方式,用 call 实现 js 构造函数继承 。

单继承

  1. function person(name){

  2.    this.name = name

  3. }

  4. function man(name){

  5.    this.age = '男';

  6.    person.call(this,name);              // 继承 man

  7. }

  8. var me = new man('海洋饼干');

  9. console.log(me.name,me.age);             // '海洋饼干' '男'

多继承

  1. function person(name){

  2.    this.name = name

  3. }

  4. function man(name){

  5.    this.age = '男';

  6. }

  7. function manProgrammer(name){

  8.     this.girlfriend = null;

  9.    person.call(this,name);  // 继承 person

  10.    man.call(this,name);     // 继承 man

  11. }

  12. var me = new manProgrammer('海洋饼干');

  13. console.log(me.name,me.age,me.girlfriend);   // '海洋饼干' '男' null

2 .3 this 硬绑定 --- bind

将一个对象强制且永久性绑定到函数的this上,使用call,apply或者其他的绑定方式都无法改变(除了 new 绑定,当然,可以手动撸一个 new 都无法改变的硬绑定)

直接看例子:

  1. var fun ;

  2. var obj = {

  3.    a : 1,

  4.    foo : function(){

  5.         var _this = this;            //平时有没有过这种写法? 为了防止this指向问题

  6.                                     //将this赋值给一个变量,间接维持了this的安全性

  7.       fun = function(){

  8.            console.log(_this.a);

  9.        }

  10.    }

  11. }

  12. obj.foo();

  13. fun();                 // 1

  14. var obj1 = { a : 2}

  15. obj.foo.call(obj1);    // 直接修改_this所绑定的值,boom了

  16. fun();                 // 2

但是这种方法感觉上是在逃避问题,直接不使用this了 ? 这真的不是什么好的解决问题的态度。下面使用我们的bind来优化一下:

  1. var fun ;

  2. var obj = {

  3.    a : 1,

  4.    foo : function(){            







请到「今天看啥」查看全文