资讯专栏INFORMATION COLUMN

前端基础:call,apply,bind的的理解

netmou / 1891人阅读

摘要:和区别其实他们的作用是一样的,只是传递的参数不一样而已。接受个参数,第一个参数指定了函数体内对象的指向,第二个参数为数组或者一个类数组。看个栗子一个有意思的事在中,多次是无效的。而则会立即执行函数。

背景

前两天在做小程序的需求的时候用到bind的时候才想起自己对这三的东西的了解比较浅薄,这个时候用的时候就有点怕。时候还是要好好学习下,理解下怎么玩。

正文

先说callapply吧:
ECMAScript3给Function的原型定义了两个方法,他们是Function.prototype.callFunction.prototype.apply. 在实际开发中,特别是在一些函数式风格的代码编写中,call和apply方法尤为有用。

1、call和apply区别

其实他们的作用是一样的,只是传递的参数不一样而已。
apply: 接受2个参数,第一个参数指定了函数体内this对象的指向,第二个参数为数组或者一个类数组。apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
举个栗子:

let obj1 = {
    name: "copyes",
    getName: function(){
        return this.name;
    }
}
let obj2 = {
    name: "Fanchao"
}

console.log(obj1.getName());  // "copyes"
console.log(obj1.getName.call(obj2));  // "Fanchao"
console.log(obj1.getName.apply(obj2));  // "Fanchao"
function showArgs(a, b, c){
    console.log(a,b,c);
}

showArgs.call(this, 3,4,5);
showArgs.apply(this, [5,6,7]);

2、常见的call 和 apply 用法

数组之间追加

let arr1 = [12, "foo", {name: "fanchao"}, -1024];
let arr2 = ["copyes", "22", 1024];

Array.prototype.push.apply(arr1, arr2);
console.log(arr1);
// [ 12, "foo", { name: "fanchao" }, -1024, "copyes", "22", 1024 ]

获取数组中的最大值和最小值

let numbers = [5,665,32,773,77,3,996];
let maxNum = Math.max.apply(Math, numbers);
let maxNum2 = Math.min.call(Math, 5,665,32,773,77,3,996);

console.log(maxNum);
console.log(maxNum2);

验证是否是数组(前提是toString()方法没有被重写过)

function isArray(obj){
    return Object.prototype.toString.call(obj)  === "[object Array]";
}

console.log(isArray(1));
console.log(isArray([1,2]));

类(伪)数组使用数组方法

var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

3、通过一个面试题去深入理解下apply 和 call

定义一个 log 方法,让它可以代理 console.log 方法:

// 常规做法
function log(msg){
    console.log(msg);
}
log(12);
log(1,2)

上面能够基本解决打印输出问题,但是下面多参数的时候就gg了。换个更好的方式吧。

function log(){
    console.log.apply(console, arguments);
}
log(12);
log(1,2)

接下来是要在每一条打印信息前面都要加上一个特定字符串"fanchao`s"又怎么说呢?

function log(){

    let args = Array.prototype.slice.call(arguments);
    
    args.unshift("(fanchao`s)");

    console.log.apply(console, args);
}
log(12);
log(1,2)

4、说完了call和apply,接下来说说bind

bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。
MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
看个栗子:

var func = function(){
    console.log(this.x);
    console.log(arguments);
}

 
func();  // undefined, {}
var obj = {
    x: 2
}
var bar = func.bind(obj,1);

bar(); // 2 , {"0":1}

一个有意思的事:

var bar = function() {
    console.log(this.x);
}
var foo = {
    x: 3
}
var sed = {
    x: 4
}
var func = bar.bind(foo).bind(sed);
func(); //3

var fiv = {
    x: 5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3

在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

5、这三个方法的异同点是什么呢?

还是先看个栗子:

var obj = {
x: 81,
};

var foo = {
getX: function() {
return this.x;
}
}

console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81

看到bind后面对了一对括号。区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

总结

apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind是返回对应函数,便于稍后调用;apply、call则是立即调用 。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83332.html

相关文章

  • 《javascript高级程序设计》函数调用模式 & this深度理解

    在上一篇文章(《javascript高级程序设计》笔记:Function类型)中稍微提及了一下函数对象的属性—this,在这篇文章中有深入的说明: 函数的三种简单调用模式 1 函数模式 定义的函数,如果单独调用,不将其与任何对象关联,那么就是函数调用模式 function fn(num1, num2) { console.log(this); } // 直接在全局调用 fn();// w...

    wyk1184 评论0 收藏0
  • Javascript中this与闭包学习笔记

    摘要:但是在调用函数值执行之后并没有达到我们想要的效果。解析在这里我们为每一个的事件绑定了一个匿名函数,这个匿名函数就形成了一个闭包。这样我们就为每个的事件的匿名函数,都保存下了自己闭包变量。 博客原址 理解 Javascript中的this 基于不同的调用方式this的指向也会有所不同,调用方式大致有如下几种: 调用方式 表达式 构造函数调用 new Foo(); 对象方法...

    pinecone 评论0 收藏0
  • 从一道面试题,到“我可能看了假源码”

    摘要:返回的绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器。同时,将第一个参数以外的其他参数,作为提供给原函数的预设参数,这也是基本的颗粒化基础。 今天想谈谈一道前端面试题,我做面试官的时候经常喜欢用它来考察面试者的基础是否扎实,以及逻辑、思维能力和临场表现,题目是:模拟实现ES5中原生bind函数。也许这道题目已经不再新鲜,部分读者也会有思路来解答。社区上关于原生bind的研...

    Carson 评论0 收藏0
  • 从一道面试题,到“我可能看了假源码”

    摘要:返回的绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器。同时,将第一个参数以外的其他参数,作为提供给原函数的预设参数,这也是基本的颗粒化基础。 今天想谈谈一道前端面试题,我做面试官的时候经常喜欢用它来考察面试者的基础是否扎实,以及逻辑、思维能力和临场表现,题目是:模拟实现ES5中原生bind函数。也许这道题目已经不再新鲜,部分读者也会有思路来解答。社区上关于原生bind的研...

    rockswang 评论0 收藏0
  • 从一道面试题,到“我可能看了假源码”

    摘要:返回的绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器。同时,将第一个参数以外的其他参数,作为提供给原函数的预设参数,这也是基本的颗粒化基础。 今天想谈谈一道前端面试题,我做面试官的时候经常喜欢用它来考察面试者的基础是否扎实,以及逻辑、思维能力和临场表现,题目是:模拟实现ES5中原生bind函数。也许这道题目已经不再新鲜,部分读者也会有思路来解答。社区上关于原生bind的研...

    jlanglang 评论0 收藏0

发表评论

0条评论

netmou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<