关于call(),apply(),bind()函数的理解

来源:互联网 发布:易语言3d游戏源码 编辑:程序博客网 时间:2024/05/29 08:27

在Javascript中,我们比较难以理解的东西就是this,它表示在当前作用域中的上下文。有时候我们需要在使用过程中改变this的指向,那么就需要通过这三种方法:call(),apply(),bind()。

具体什么意思呢?说的太官方可能还是不懂,大致意思就是,有两个对象,其中一个对象想要使用另一个对象中的方法,这个时候就要用到这个三个方法。

举个例子:有两个人张三和李四,张三会说话并说的很好,李四是个哑巴,说不出来,这时候李四特别的想说话,憋得满脸通红说不出来,这时候他知道张三会说话,所以李四就把张三拉过来,用手比划给张三,让张三替他说话。

说了这么一大堆,还是想说一下概念,call(),apply(),bind()我为了改变某个函数的上下文来使用的。

函数其实就是对象,当每个函数在创建时,我们都知道,在函数内部会自动创建arguments和this两个对象,用来代表函数所接收的参数和一个运行环境的上下文this.
但与此同时,函数上还自带几个属性和方法如:
这里写图片描述
从图中我们可以看到,比较熟悉的如arguments,name.length,hasOwnProperty等属性都在,而且还有apply,call,bind这三个,他们都是方法,都是自带的,所以使用起来也是没问题的。

下面就举例子来看看这三个函数是怎么用的。

    var person1 = {        name:'张三',        say:function(city){            console.log(this.name + '说你好,非常欢迎您来到' + city + '!');        }    };    var person2 = {        name:'李四'    };

上面列出了两个人,张三和李四,张三有个say()的功能,李四却没有。

    person1.say('北京');//张三说你好,非常欢迎您来到北京!    person2.say('北京');//person2.say is not a function

可以看到,person2.say()提示了错误,但是我们现在想说话,而且又不想加新的属性该怎么办呢?这个时候我们的call,apply,bind就可以派上用场了。

    person1.say.call(person2,'北京');//李四说你好,非常欢迎您来到北京!    person1.say.apply(person2,['北京']);//李四说你好,非常欢迎您来到北京!    person1.say.bind(person2)('北京');//李四说你好,非常欢迎您来到北京!

可以看到,通过上面三句代码,都可以让我们的李四开口说话,并使用张三的方法。
这三个函数第一个参数都代表原函数的上下文应该更改成这个对象。
很显然从上面代码可以看出来三个方法的用法是不同的。
1 call()和apply()方法都是直接对方法的调用,而bind()执行过后返回一个函数。
2 call()方法从第二个参数开始依次向函数中传参,而apply()方法第二个参数是一个参数数组,不过他也是按照在数组中的顺序依次向函数中传参的。
3 bind()函数倒是没有规范传参的方式,在bind()函数中第二个参数也行,这里放一个数组也行,在外面调用的时候传参同样可以,不过要注意的是bind()函数返回的是一个函数,需要调用这个函数才会执行,而且,bind()方法只能使用一次,连续多个bind()函数是不管用的

    var foo = {        foo:2,        sum:function(x,y){            return this.foo + x + y;        }    };    var bar = {        foo:5    };     var aa = {        foo:12    };    console.log(foo.sum(5,6));//13    console.log(foo.sum.call(bar,5,6));//16    console.log(foo.sum.apply(bar,[5,6]));//16    console.log(foo.sum.bind(bar)(5,6));//16    console.log(foo.sum.bind(bar,5,6)());//16    console.log(foo.sum.bind(bar,5)(6));//16    console.log(foo.sum.bind(bar,5).bind(aa)(6));//16

从上面可以看到,bind()函数的传参方式可以在bind函数体内,也可以在调用函数括号内,但是不管怎么样,他都是从bind()函数第二个参数开始算起的,bind()函数的传参方式和call()方法是一样的,不能传参数数组。最后一句话我们可以看到,在bind()了一个bar对象之后,又进行了一次bind(),这次bind()到了aa,但是打印出来的结果仍然是16,看来,bind()函数只能使用一次,多的是不管用的。

如果第一个参数是null,那么上下文就会指向全局,将会是在全局作用域中使用这个函数。

    var color = 'white';    var obj = {        color:'yellow',        say:function(){            console.log('The color is ' + this.color);        }    };    var green = {        color:'green'    };    obj.say();//The color is yellow    obj.say.call(green);//The color is green    obj.say.call(null);//The color is white

第三句代码中call()方法传入了null,因此他将指向全局作用域,我们在全局作用域中也定义了color,所以他自然打印出了white.

通过这些特性,我们衍生出了很多巧妙的用法:
1 类的继承:

     function Parent(name,age){        this.name = name;        this.age = age;    }    function Child(name,age,height){        Parent.call(this,name,age);        this.height = height;    }

2 数组的合并:

    var arr1 = [1,33,5,3,23];    var arr2 = [55,43,12,56];    var newArr = Array.prototype.push.apply(arr1,arr2);    console.log(arr1);//[1, 33, 5, 3, 23, 55, 43, 12, 56]

我们知道push()方法貌似一次只能想数组中添加一个元素,如果使用apply()方法,apply的参数是一个数组,因此使用apply()之后一次就可以添加整个数组了。

3 数组中的大小值

    var arr1 = [1,33,5,3,23];    var max = Math.max.apply(Math,arr1);    var min = Math.min.apply(Math,arr1);    console.log(max);//33    console.log(min);//1

Math.max(arg1,arg2,arg3,…),使用apply之后就可以将数组参数放入其中,就可以计算出我们想要的值。

总之:call(),apply()和bind()方法就是用来改变函数体内上下文的,这三个方法用的非常的多,作为一名FE,都应该掌握其用法。上面有许多不对的地方还请大家指正,欢迎批评!!

0 0
原创粉丝点击