写点什么

悟透前端 | 参悟 Javascript 中的 call 和 apply

用户头像
devpoint
关注
发布于: 2021 年 01 月 23 日
悟透前端 | 参悟Javascript中的call和apply

对于前端工程师来说,callapply算是常用的函数方法,允许通过函数和在函数调用中指定this的指向。那么这两个方法到底有什么区别呢?本文将详细介绍这两个方法,顺便加深对其理解。

call

方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。允许为不同的对象分配和调用属于一个对象的函数/方法。提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。


const obj = {    domain: "www.infoq.cn",};function siteurl(protocol, path) {    console.log([protocol, this.domain, path].join(""));}siteurl.call(obj, "https://", "/u/devpoint"); // https://www.infoq.cn/u/devpoint
复制代码

语法

function.call(thisArg, arg1, arg2, ...)
复制代码


  • thisArg:可选的。

在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

  • arg1, arg2, ...:指定的参数列表。

返回值

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

apply


apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

语法

func.apply(thisArg, [argsArray]);
复制代码


  • thisArg:必选的。

在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

  • argsArray:可选的。

一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或  undefined,则表示不需要传入任何参数。从 ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。

返回值

调用有指定this值和参数的函数的结果。

call 和 apply 的区别

它们的作用一模一样,区别仅在于传入参数形式的不同。



apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传递给调用的函数:

const func = function (a, b, c) {    console.log([a, b, c]);};func.apply(null, [1, 2, 3]); // [ 1, 2, 3 ]
复制代码


这段代码中,参数 1、2、3 被放在数组中一起传入 func 函数,它们分别对应 func 参数列表中的 a 、b 、c

call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数:

const func = function (a, b, c) {    console.log([a, b, c]);};func.call(null, 1, 2, 3); // [ 1, 2, 3 ]
复制代码

调用一个函数时,Javascript 的解释器并不会计较形参和实参在数量、类型以及顺序上的区别,Javascript 的参数在内部就是用一个数组来表示的。


从这个意义上说,apply 比 call 的使用率更高,不关心具体多少参数被传入函数,只要用 apply 一股脑地推过去就可以了。


call 是包装在 apply 上的一颗语法糖,如果明确地知道函数接收多少个参数,而且想一目了然地表达形参和实参的对应关系,那么可以用 call 来传送参数。


当使用 call 或者 apply 的时候,如果传入的第一个参数为 null ,函数体内的 this 会指向默认的宿主对象,在浏览器中则是 window


const func = function(a,b,c){    console.log(this === window );}func.apply(null,[1,2,3]);   // true
复制代码


如果是在严格模式下,函数体内的 this 还是为 null :

const func = function (a, b, c) {    console.log(this === window);};func.apply(null, [1, 2, 3]); // true
复制代码

改变 this 指向

const obj = {    site: "https://www.infoq.com",};function func() {    console.log(this.site);}func.call(obj); // https://www.infoq.com
复制代码


call 方法的第一个参数是作为函数上下文的对象,上面的代码把 obj 作为参数传给了 func,此时函数里的 this 便指向了 obj 对象。此处 func 函数相当于下面的方法:

const obj = {    site: "https://www.infoq.com",};function func() {    console.log(obj.site);}
复制代码

apply()方法的一些妙用


Math.max 可以实现得到数组中最大的一项


因为Math.max不支持Math.max([param1,param2])也就是数组,但是它支持Math.max(param1,param2...),所以可以根据apply的特点来解决 var max=Math.max.apply(null,array),这样就轻易的可以得到一个数组中的最大项(apply会将一个数组转换为一个参数接一个参数的方式传递给方法)这块在调用的时候第一个参数给了null,这是因为没有对象去调用这个方法,我只需要用这个方法帮我运算,得到返回的结果就行,所以直接传递了一个null过去。


用这种方法也可以实现得到数组中的最小项:Math.min.apply(null,array)


Array.prototype.push实现两个数组的合并


同样push方法没有提供push一个数组,但是它提供了push(param1,param2...paramN),同样也可以用apply来转换一下这个数组,即:

const target = new Array("1", "2", "3");const source = new Array("4", "5", "6");const result = Array.prototype.push.apply(target, source); // 得到合并后数组的长度,因为push就是返回一个数组的长度console.log(target); // [ '1', '2', '3', '4', '5', '6' ]console.log(source); // [ '4', '5', '6' ]console.log(result); // 6
复制代码


发布于: 2021 年 01 月 23 日阅读数: 699
用户头像

devpoint

关注

细节的追求者 2011.11.12 加入

专注前端开发,用技术创造价值!

评论

发布
暂无评论
悟透前端 | 参悟Javascript中的call和apply