您的位置:新葡亰496net > 新葡亰官网 > 通俗易懂,深刻浅出妙用

通俗易懂,深刻浅出妙用

发布时间:2019-06-23 00:05编辑:新葡亰官网浏览(126)

    深入浅出妙用 Javascript 中 apply、call、bind

    2015/09/24 · JavaScript · 4 评论 · apply, bind, call

    本文作者: 伯乐在线 - chokcoco 。未经作者许可,禁止转载!
    欢迎加入伯乐在线 专栏作者。

    这篇文章实在是很难下笔,因为网上相关文章不胜枚举。

    巧合的是前些天看到阮老师的一篇文章的一句话:

    “对我来说,博客首先是一种知识管理工具,其次才是传播工具。我的技术文章,主要用来整理我还不懂的知识。我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写。炫耀从来不是我的动机,好奇才是。”

    对于这句话,不能赞同更多,也让我下决心好好写这篇,网上文章虽多,大多复制粘贴,且晦涩难懂,我希望能够通过这篇文章,能够清晰的提升对apply、call、bind的认识,并且列出一些它们的妙用加深记忆。

       apply、call

    在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

    JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。

    先来一个栗子:

    JavaScript

    function fruits() {} fruits.prototype = { color: "red", say: function() { console.log("My color is " this.color); } } var apple = new fruits; apple.say(); //My color is red

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function fruits() {}
     
    fruits.prototype = {
        color: "red",
        say: function() {
            console.log("My color is " this.color);
        }
    }
     
    var apple = new fruits;
    apple.say();    //My color is red

    但是如果我们有一个对象banana= {color : “yellow”} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法:

    JavaScript

    banana = { color: "yellow" } apple.say.call(banana); //My color is yellow apple.say.apply(banana); //My color is yellow

    1
    2
    3
    4
    5
    banana = {
        color: "yellow"
    }
    apple.say.call(banana);     //My color is yellow
    apple.say.apply(banana);    //My color is yellow

    所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。

    apply、call 的区别

    对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:

    JavaScript

    var func = function(arg1, arg2) { };

    1
    2
    3
    var func = function(arg1, arg2) {
     
    };

    就可以通过如下方式来调用:

    JavaScript

    func.call(this, arg1, arg2); func.apply(this, [arg1, arg2])

    1
    2
    func.call(this, arg1, arg2);
    func.apply(this, [arg1, arg2])

    其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

    JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call 。

    而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

    为了巩固加深记忆,下面列举一些常用用法:

    1、数组之间追加

    JavaScript

    var array1 = [12 , "foo" , {name "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

    1
    2
    3
    4
    var array1 = [12 , "foo" , {name "Joe"} , -2458];
    var array2 = ["Doe" , 555 , 100];
    Array.prototype.push.apply(array1, array2);
    /* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

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

    JavaScript

    var numbers = [5, 458 , 120 , -215 ]; var maxInNumbers = Math.max.apply(Math, numbers), //458 maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

    1
    2
    3
    var  numbers = [5, 458 , 120 , -215 ];
    var maxInNumbers = Math.max.apply(Math, numbers),   //458
        maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

    number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

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

    JavaScript

    functionisArray(obj){ returnObject.prototype.toString.call(obj) === '[object Array]' ; }

    1
    2
    3
    functionisArray(obj){
        returnObject.prototype.toString.call(obj) === '[object Array]' ;
    }

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

    JavaScript

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

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

    Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。

    但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。

    深入理解运用apply、call

    下面就借用一道面试题,来更深入的去理解下 apply 和 call 。

    定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:

    JavaScript

    function log(msg) { console.log(msg); } log(1); //1 log(1,2); //1

    1
    2
    3
    4
    5
    function log(msg) {
      console.log(msg);
    }
    log(1);    //1
    log(1,2);    //1

    上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的,方法如下:

    JavaScript

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

    1
    2
    3
    4
    5
    function log(){
      console.log.apply(console, arguments);
    };
    log(1);    //1
    log(1,2);    //1 2

    接下来的要求是给每一个 log 消息添加一个”(app)”的前辍,比如:

    JavaScript

    log("hello world"); //(app)hello world

    1
    log("hello world");    //(app)hello world

    该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:

    JavaScript

    function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args); };

    1
    2
    3
    4
    5
    6
    function log(){
      var args = Array.prototype.slice.call(arguments);
      args.unshift('(app)');
     
      console.log.apply(console, args);
    };

    bind

    说完了 apply 和 call ,再来说说bind。bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。

    MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

    直接来看看具体如何使用,在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。 像这样:

    JavaScript

    var foo = { bar : 1, eventBind: function(){ var _this = this; $('.someClass').on('click',function(event) { /* Act on the event */ console.log(_this.bar); //1 }); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var foo = {
        bar : 1,
        eventBind: function(){
            var _this = this;
            $('.someClass').on('click',function(event) {
                /* Act on the event */
                console.log(_this.bar);     //1
            });
        }
    }

    由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $(‘.someClass’).on(‘click’,function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:

    JavaScript

    var foo = { bar : 1, eventBind: function(){ $('.someClass').on('click',function(event) { /* Act on the event */ console.log(this.bar); //1 }.bind(this)); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var foo = {
        bar : 1,
        eventBind: function(){
            $('.someClass').on('click',function(event) {
                /* Act on the event */
                console.log(this.bar);      //1
            }.bind(this));
        }
    }

    在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。再来一个简单的栗子:

    JavaScript

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

    1
    2
    3
    4
    5
    6
    7
    var bar = function(){
        console.log(this.x);
    }
     
    bar(); // undefined
    var func = bar.bind(foo);
    func(); // 3

    这里我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。

    有个有趣的问题,如果连续 bind() 两次,亦或者是连续 bind() 三次那么输出的值是什么呢?像这样:

    JavaScript

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var bar = function(){
        console.log(this.x);
    }
    var foo = {
        x:3
    }
    var sed = {
        x:4
    }
    var func = bar.bind(foo).bind(sed);
    func(); //?
     
    var fiv = {
        x:5
    }
    var func = bar.bind(foo).bind(sed).bind(fiv);
    func(); //?

    答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

    apply、call、bind比较

    那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:

    JavaScript

    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。

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

    再总结一下:

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

    本文实例出现的所有代码,在我的github上可以下载。

    打赏支持我写出更多好文章,谢谢!

    打赏作者

    作者:伯乐在线专栏作者 - chokcoco

    如有好文章投稿,请点击 → 这里了解详情

    如需转载,发送「转载」二字查看说明

    各位观众老爷大家好,欢迎收看内裤总动员之程序猿的IT程序大讲堂,今天给大家分享一个小知识.就是call和apple的区别.

    这篇文章实在是很难下笔,因为网上相关文章不胜枚举。
    巧合的是前些天看到阮老师的一篇文章的一句话:
    “对我来说,博客首先是一种知识管理工具,其次才是传播工具。我的技术文章,主要用来整理我还不懂的知识。我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写。炫耀从来不是我的动机,好奇才是。”
    对于这句话,不能赞同更多,也让我下决心好好写这篇,网上文章虽多,大多复制粘贴,且晦涩难懂,我希望能够通过这篇文章,能够清晰的提升对apply、call、bind的认识,并且列出一些它们的妙用加深记忆。
    apply、call
    在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。
    JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。
    先来一个栗子:

    因为JavaScript 中this 是在运行期进行绑定的,因此JavaScript 中this 关键字具备多重含义。

    打赏支持我写出更多好文章,谢谢!

    任选一种支付方式

    新葡亰496net 1 新葡亰496net 2

    2 赞 16 收藏 4 评论

     

    在javascript中, call和apple都是为了改变某个函数中运行的上下文而存在的,换句话说就是为了改变函数内部的this的指向.

    function fruits() {};
    fruits.prototype = {
        color: "red",
        say: function() {
            console.log("My color is "   this.color);
        }
    }
    
    var apple = new fruits;
    apple.say();    //My color is red
    

    具体在实际应用中,this的指向大致可以分为下面4种。

    关于作者:chokcoco

    新葡亰496net 3

    经不住流年似水,逃不过此间少年。 个人主页 · 我的文章 · 63 ·    

    新葡亰496net 4

    这篇文章实在是很难下笔,因为网上相关文章不胜枚举。

    JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。

    但是如果我们有一个对象banana= {color : “yellow”} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法:

    1. 作为对象的方法调用   obj.a()
    2. 作为普通函数调用    a()
    3. 构造函数调用        var b = new a(); 
    4. function.prototype.call或function.prototype.apply调用

     

    先给大家来一个栗子:

    banana = {
        color: "yellow"
    }
    apple.say.call(banana);     //My color is yellow
    apple.say.apply(banana);    //My color is yellow
    

    作为对象的方法调用 和 作为普通函数调用

    巧合的是前些天看到阮老师的一篇文章的一句话:

    functionfruits() {}

    fruits.prototype = {

    color:"red",

    say:function() {

    console.log("My color is " this.color);

    }

    }

    varapple =newfruits;

    apple.say();//My color is red

    所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。
    apply、call 的区别
    对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:

     1 window.a = 2;
     2 var obj = {
     3     a:1,
     4     getA:function(){
     5         console.log(this.a);
     6     }
     7 }
     8 obj.getA();       //输出1,作为对象的方法调用,this指向当前对象
     9 var x = obj.getA;
    10 x();   //输出2,作为普通函数调用,this全部指向window对象。 
    

    “对我来说,博客首先是一种知识管理工具,其次才是传播工具。我的技术文章,主要用来整理我还不懂的知识。我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写。炫耀从来不是我的动机,好奇才是。"

    但是如果我们有一个对象banana= {color : "yellow"} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法

    var func = function(arg1, arg2) {

    注意,不管x之前是obj.getA,还是其他某个对象的属性,只要最后是以x(),fun()这种方式调用的,均视为普通函数调用,此时this指向window对象

     

    banana = {

    color:"yellow"

    }

    apple.say.call;//My color is yellow

    apple.say.apply;//My color is yellow

    };
    就可以通过如下方式来调用:

    但是,在ECMAScript5的strict模式下,作为函数调用的 this被规定不会指向全局对象

    对于这句话,不能赞同更多,也让我下决心好好写这篇,网上文章虽多,大多复制粘贴,且晦涩难懂,我希望能够通过这篇文章,能够清晰的提升对apply、call、bind的认识,并且列出一些它们的妙用加深记忆。

    所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。

    func.call(this, arg1, arg2);
    func.apply(this, [arg1, arg2])
    其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。
    JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call 。
    而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。
    为了巩固加深记忆,下面列举一些常用用法:
    1、数组之间追加

     1 window.a = 2;
     2 var obj = {
     3     a:1,
     4     getA:function(){
     5         "use strict"
     6         console.log(this.a);
     7     }
     8 }
     9 var x = obj.getA;
    10 x();   //underfined
    

     

    对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:

    var array1 = [12 , "foo" , {name "Joe"} , -2458];
    var array2 = ["Doe" , 555 , 100];
    Array.prototype.push.apply(array1, array2);
    /* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
    

    作为构造函数调用

    apply、call

    varfunc =function(arg1, arg2) {

    };

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

    通常情况下,构造函数里的this指向返回的这个对象,但是如果构造器显示地返回了一个object类型的对象,则this指向这个返回的object对象

     

    就可以通过如下方式来调用:

    var  numbers = [5, 458 , 120 , -215 ];
    var maxInNumbers = Math.max.apply(Math, numbers),   //458
    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
    
     1 var Myclass = function(){
     2     this.name = 'beidan';
     3 }
     4 var obj = new Myclass();  
     5 console.log(obj.name);//beidan
     6  
     7 var Myclass = function(){
     8     this.name = 'beidan';
     9     return{         //显示的返回一个对象,注意!既要是显示,即有return,也要是对象{}
    10         name:'test'
    11     }
    12 }
    13 var obj = new Myclass();
    14 console.log(obj.name);//test
    

    通俗易懂,深刻浅出妙用。在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

    func.call(this, arg1, arg2);

    func.apply(this, [arg1, arg2])

    number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

    作为function.prototype.call或function.prototype.apply调用

     

    其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

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

     

    JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。

    JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call 。

    functionisArray(obj){
    return Object.prototype.toString.call(obj) === '[object Array]' ;
    }
    4、类(伪)数组使用数组方法

    • 理解call,apply

     

    而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

    var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
    Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。
    但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
    深入理解运用apply、call
    下面就借用一道面试题,来更深入的去理解下 apply 和 call 。
    定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:

     

    先来一个栗子:

    为了巩固加深记忆,下面列举一些常用用法:

    function log(msg) {
      console.log(msg);
    }
    log(1);    //1
    log(1,2);    //1
    

    call,apply都是为了改变函数体内部 this 的指向。例如,fun1.call()或者fun1.apply() 都是为了改变fun1函数内部的this指向。

     

    1、数组之间追加

    上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的,方法如下:

    二者的作用完全一样,只是接受参数的方式不太一样。

    function fruits() {}

     

    fruits.prototype = {

    color: "red",

    say: function() {

    console.log("My color is "   this.color);

    }

    }

     

    var apple = new fruits;

    apple.say(); //My color is red

    vararray1 = [12 ,"foo", {name"Joe"} , -2458];

    vararray2 = ["Doe", 555 , 100];

    Array.prototype.push.apply(array1, array2);

    /* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

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

    func1.call(this, arg1, arg2);        //参数列表arg1,arg2

     

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

    接下来的要求是给每一个 log 消息添加一个”(app)”的前辍,比如:
    log("hello world"); //(app)hello world
    该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:

    func1.apply(this, [arg1, arg2]);   //参数数组 [arg1,arg2]

    但是如果我们有一个对象banana= {color : "yellow"} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法:

    varnumbers = [5, 458 , 120 , -215 ];

    varmaxInNumbers = Math.max.apply(Math, numbers),//458

    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215);//458

    function log(){
      var args = Array.prototype.slice.call(arguments);
      args.unshift('(app)');
    
      console.log.apply(console, args);
    };
    

     

     

    number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

    bind
    说完了 apply 和 call ,再来说说bind。bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。
    MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
    直接来看看具体如何使用,在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。 像这样:

    第一个参数指定那个了函数体内this对象的指向,他可以任何一个 JavaScript 对象(JavaScript 中一切皆对象),如果为null,则函数体内的this会指向默认的宿主对象,在浏览器中则是window。

    banana = {

    color: "yellow"

    }

    apple.say.call(banana); //My color is yellow

    apple.say.apply(banana); //My color is yellow

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

    var foo = {
        bar : 1,
        eventBind: function(){
            var _this = this;
            $('.someClass').on('click',function(event) {
                /* Act on the event */
                console.log(_this.bar);     //1
            });
        }
    }
    

     

     

    functionisArray{

    return Object.prototype.toString.call ==='[object Array]';

    }

    由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $(‘.someClass’).on(‘click’,function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:

    第二个参数,call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

    所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。

    4、类数组使用数组方法

    var foo = {
        bar : 1,
        eventBind: function(){
            $('.someClass').on('click',function(event) {
                /* Act on the event */
                console.log(this.bar);      //1
            }.bind(this));
        }
    }
    

    当你的参数不确定数量时用 apply ,然后把参数 push 进数组传递进去。或者也可以通过 arguments来获取所有的参数。这样看来,apply的使用率更高。

     

    vardomNodes = Array.prototype.slice.call(document.getElementsByTagName;

    在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。再来一个简单的栗子:

     

    apply、call 的区别

    Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments

    var bar = function(){
        console.log(this.x);
    }
    
    bar(); // undefined
    var func = bar.bind(foo);
    func(); // 3
    
    • call,apply的用途

     

    对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属

    这里我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。
    apply、call、bind比较
    那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:

     

    对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:

    于伪数组。不能应用 Array下的 push , pop 等方法。

    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
    

    1.修正this的指向

     

    但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。

    三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。
    也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
    再总结一下:
    * apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
    * apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
    * apply 、 call 、bind 三者都可以利用后续参数传参;
    * bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

    比如,在实际开发中,会出现以下这种问题

    var func = function(arg1, arg2) {

     

    };

    下面就借用一道面试题,来更深入的去理解下 apply 和 call 。

    1 document.getElementById('div1').onclick = function(){
    2     console.log(this.id);   //div1
    3     var func = function(){
    4         console.log(this.id);    
    5     }
    6     func();   //通过普通函数调用,this指向window,输出undefined
    7 }
    

     

    定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:

    这时我们可以用call来修正func 函数内this的指向。

    就可以通过如下方式来调用:

    functionlog {

    console.log;

    }

    log;//1

    log;//1

    1 document.getElementById('div1').onclick = function(){
    2     console.log(this.id);   //div1
    3     var func = function(){
    4         console.log(this.id);
    5     }
    6     func.call(this);   //使用call,使func函数内部的this指向当前的函数对象,输出div1
    7 }
    

     

    上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的,方法如下:

    2.模拟继承(借用其他对象的方法)

    func.call(this, arg1, arg2);

    func.apply(this, [arg1, arg2])

    functionlog(){

    console.log.apply(console, arguments);

    };

    log;//1

    log;//1 2

    • 例子一:其他对象(banana)借用apple中的say方法

      1 function fruits() {} 2 fruits.prototype = { 3 color: "red", 4 say: function() { 5 console.log("My color is " this.color); 6 } 7 } 8 var apple = new fruits; 9 apple.say(); //My color is red

     

    接下来的要求是给每一个 log 消息添加一个""的前辍,比如:

    但是,如果我们还有其它 2个对象 banana= {color : "yellow"} ,orange = {color:‘orange’},想使用say方法,但是又不想对它们重新定义say方法。

    其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。  

    log("hello world");//hello world

    那么,我们可以用apply或者call 借用 fruit里面的say方法

     

    该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:

    1 banana = {
    2     color: "yellow"
    3 };
    4 orange = {color:‘orange’};
    5 apple.say.call(banana);     //My color is yellow
    6 apple.say.apply(orange );    //My color is orange
    

    JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call 。

    functionlog(){

    varargs = Array.prototype.slice.call(arguments);

    args.unshift;

    console.log.apply(console, args);

    };

    也就是说,当一个 object 没有某个方法(本例子中banana没有say方法),但是其他的有(本例子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。

     

    说完了 apply 和 call ,再来说说bind。bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。

    再看几个例子巩固记忆

    而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

    MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数

    • 例子二:获取数组中的最大值和最小值

      1 var numbers = [5, 458 , 120 , -215 ]; 2 var maxInNumbers = Math.max.apply(Math, numbers), //458 3 maxNumbers = Math.max.call(Math,5, 458 , 120 , -215, 666); //666

     

    作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

    number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

    为了巩固加深记忆,下面列举一些常用用法:

    直接来看看具体如何使用,在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。 像这样:

     

     

    varfoo = {

    bar : 1,

    eventBind:function(){

    var _this =this;

    $('.someClass').on('click',function {

    /* Act on the event */

    console.log(_this.bar);//1

    });

    }

    以上就是this在JavaScript中不同情况下的指向。

    1、数组之间追加

    }

    如果这篇文章对你有帮助,就给我一点赞赏吧~~~

    通俗易懂,深刻浅出妙用。 

    由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ }

    新葡亰496net 5新葡亰496net 6

    var array1 = [12 , "foo" , {name "Joe"} , -2458];

    var array2 = ["Doe" , 555 , 100];

    Array.prototype.push.apply(array1, array2);

    /* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

    过渡到 $('.someClass').on('click',function { }) 发生了改变,上述使用变量保存

     

     

    this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:

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

    varfoo = {

    bar : 1,

    eventBind:function(){

    $('.someClass').on('click',function {

    /* Act on the event */

    console.log;//1

    }.bind;

    }

    }

     

    在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this

    var numbers = [5, 458 , 120 , -215 ];

    var maxInNumbers = Math.max.apply(Math, numbers), //458

    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

    关键词会被设置成被传入的值(这里指调用bind。因此,这里我们传入想要的上下文 this(其实就是 foo ),到

     

    bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。再来一个简单的栗子:

    number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

    var bar =function(){

    console.log;

    }

    var foo = {

    x:3

    }

    bar();// undefined

    var func = bar.bind;

    func();// 3

     

    这里我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。

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

    有个有趣的问题,如果连续 bind() 两次,亦或者是连续 bind() 三次那么输出的值是什么呢?像这样:

     

    varbar =function(){

    console.log;

    }

    var foo = {

    x:3

    }

    var sed = {

    x:4

    }

    var func = bar.bind.bind;

    func();//?

    var fiv = {

    x:5

    }

    var func = bar.bind.bind.bind;

    func();//?

    functionisArray(obj){

        returnObject.prototype.toString.call(obj) === '[object Array]' ;

    }

    答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind()

     

    是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind()

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

    相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

     

    那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:

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

    var obj = {

    x: 81,

    };

    var foo = {

    getX:function() {

    return this.x;

    }

    }

    console.log(foo.getX.bind;//81

    console.log(foo.getX.call;//81

    console.log(foo.getX.apply;//81

     

    三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。

    Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。

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

     

    再总结一下:

    但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。

    apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;

     

    apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;

    深入理解运用apply、call

    apply 、 call 、bind 三者都可以利用后续参数传参;

     

    bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

    下面就借用一道面试题,来更深入的去理解下 apply 和 call 。

     

    定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:

     

    function log(msg) {

        console.log(msg);

    }

    log(1); //1

    log(1,2); //1

     

    上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的,方法如下:

     

    function log(){

        console.log.apply(console, arguments);

    };

    log(1); //1

    log(1,2); //1 2

     

    接下来的要求是给每一个 log 消息添加一个"(app)"的前辍,比如:

     

    log("hello world"); //(app)hello world

     

    该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:

     

    function log(){

        var args = Array.prototype.slice.call(arguments);

        args.unshift('(app)');

     

        console.log.apply(console, args);

    };

     

    bind

     

    说完了 apply 和 call ,再来说说bind。bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。

     

    MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

     

    直接来看看具体如何使用,在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。 像这样:

     

    var foo = {

    bar : 1,

    eventBind: function(){

    新葡亰496net,var _this = this;

    $('.someClass').on('click',function(event) {

    /* Act on the event */

    console.log(_this.bar); //1

    });

    }

    }

     

    由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $('.someClass').on('click',function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:

     

    var foo = {

    bar : 1,

    eventBind: function(){

    $('.someClass').on('click',function(event) {

    /* Act on the event */

    console.log(this.bar); //1

    }.bind(this));

    }

    }

     

    在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。再来一个简单的栗子:

     

    var bar = function(){

    console.log(this.x);

    }

     

    bar(); // undefined

    var func = bar.bind(foo);

    func(); // 3

     

    这里我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。

     

    有个有趣的问题,如果连续 bind() 两次,亦或者是连续 bind() 三次那么输出的值是什么呢?像这样:

     

    var bar = function(){

    console.log(this.x);

    }

    var foo = {

    x:3

    }

    var sed = {

    x:4

    }

    var func = bar.bind(foo).bind(sed);

    func(); //?

     

    var fiv = {

    x:5

    }

    var func = bar.bind(foo).bind(sed).bind(fiv);

    func(); //?

     

    答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

     

    apply、call、bind比较

     

    那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:

     

    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

     

    三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。

     

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

     

    再总结一下:

     

    • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;

    • apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;

    • apply 、 call 、bind 三者都可以利用后续参数传参;

    • bind是返回对应函数,便于稍后调用;apply、call则是立即调用 。

     

    本文实例出现的所有代码,在我的github上可以下载。

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:通俗易懂,深刻浅出妙用

    关键词:

上一篇:没有了

下一篇:没有了