对于 Javascript 中的 apply 和 call 方法定义中的一个对象替换另一个对象不明白 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ety001
V2EX    问与答

对于 Javascript 中的 apply 和 call 方法定义中的一个对象替换另一个对象不明白

  •  
  •   ety001 2014-06-01 09:02:50 +08:00 4108 次点击
    这是一个创建于 4152 天前的主题,其中的信息可能已经有所发展或是发生改变。
    先看代码:


    以下是某篇博文对于call和apply的定义:
    call方法:
    语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
    定义:调用一个对象的一个方法,以另一个对象替换当前对象。
    说明:
    call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
    如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

    apply方法:
    语法:apply([thisObj[,argArray]])
    定义:应用某一对象的一个方法,用另一个对象替换当前对象。
    说明:
    如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。
    如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递


    [不懂的地方]
    以另一个对象替换当前对象。
    如果是替换的话,为何我写的代码中的20行打印出来的this会有name,age,school和grade成员?因为第一个apply会把当前function的this替换掉,第二个apply又把当前的this替换掉了。
    我看网上很多文章都说的是替换。是我的代码验证的不严谨吗?

    [我的理解]
    Obj.apply(thisObj,[])
    如果把apply和call分别翻译成“应用”和“调用”,那么我的理解是,thisObj应用(调用)Obj,并把[]作为Obj的传入参数,而不是以[]作为Obj的参数并替换thisObj。
    第 1 条附言    2014-06-01 12:24:52 +08:00
    结贴。
    7楼 @serenader 的回答我觉得更好,应该称为上下文的切换。
    第 2 条附言    2014-06-01 12:31:44 +08:00
    再补充一句,其实apply就是把你想要进行继承操作的构造方法(比如我例子中的Student2)的上下文交给你想要集成的构造方法(比如我例子中的Person),实际上,自始至终都是在操作最初的构造方法的上下文。
    第 3 条附言    2014-06-01 15:08:23 +08:00
    感谢各位大过节的还跑来码这么多字,感谢都已送出,祝儿童节快乐~~~
    16 条回复    2014-06-01 18:15:06 +08:00
    hitsmaxft
        1
    hitsmaxft  
       2014-06-01 09:47:27 +08:00   1
    call 和 apply 只是传参方式不同, 作用都一样的, 执行一个函数,并替换作用域里的 this 指向的实例

    这段代码里面带 this 的 函数, 可以当成构造器, 那么用了 apply 相当于把当前的 this 重新初始化了一遍, 所以属性修改了.

    你的不理解跟 apply 的工作原理没啥关系, 是不熟悉 Javascript 的构造器写法导致的..
    ety001
        2
    ety001  
    OP
       2014-06-01 11:06:24 +08:00
    @hitsmaxft 貌似你没有看懂我要表达的问题。我的问题其实是质疑用“替换”这个词来表述,因为我写的那个代码的执行结果并不是“替换”,而是“补充”,如果是“替换”的话,第20行打印出来的this应该是Print的内容才对,而实际打印的内容是Person,Print,Student2的合并后的内容。

    因为看到很多人都是用“替换”这个词,所以我在想是不是我哪个地方没有理解正确。

    就像你所说的,使用apply相当于是把当前的this重新初始化了一遍,问题就出在这个重新初始化,到底应该是表述为用一个类去替换当前类,还是用一个类去补充当前类。实际的操作来看,这是一个补充的过程。
    SoloCompany
        3
    SoloCompany  
       2014-06-01 11:09:08 +08:00   1
    把 this 看成一个显示参数而不是隐式参数,那么 call 和 apply 的左右就容易理解了,这两个方法用途是完全一样的,使用哪个取决于你的参数是怎么取得,call 和你直接调用一个方法完全一样,只是第一个参数变成了 this,其余参数位置自动加一,apply 就完全不同,所有参数作为数组放到第二个参数中传递
    ispinfx
        4
    ispinfx  
       2014-06-01 11:42:59 +08:00   1
    输出的东西有你说合并的东西没啥问题啊,我觉得是LZ没弄清楚被替换的是当前对象这里.
    Mutoo
        5
    Mutoo  
       2014-06-01 11:45:50 +08:00   1
    > [我的理解]
    > Obj.apply(thisObj,[])

    很明显,你这里误解了一个东西, Obj. 应该是 Function. 才对

    Obj是构造函数,或者一般化为普通函数。

    函数执行时有上下文(context),js通过这个上下文来查找变量。

    function.apply(thisObj, [arguments..]) 的意思是
    以 thisObj 作为 this 的引用变量,执行 function(arguments...)
    ispinfx
        6
    ispinfx  
       2014-06-01 11:51:05 +08:00
    作为JS小白,我表示我是这样理解的.

    https://gist.github.com/isolet/1dc64eb6e9be3bfe1842
    serenader
        7
    serenader  
       2014-06-01 12:10:45 +08:00   1
    在执行 var s = new Student2('Jim',22,'Harvard',2); 这个表达式时其实 Student2 函数内部的 this 指向的都是新实例,也就是 s 。

    而所谓的替换过程其实是发生在调用 apply 或者 call 的这个函数内部的。在你这个例子中,

    var Student2 = function(name,age,school,grade){
    Person.apply(this,arguments);
    Print.apply(this,arguments);
    console.log(this);
    this.school = school;
    this.grade = grade;
    }

    Person.apply(this,arguments) 的意思是,调用 Person 这个函数,然后将 Person 内部的 this 替换为(或者说切换为)apply 方法传递进去的第一个参数,也就是 this 。

    不知道你理解不。

    “Obj.apply(thisObj,[])
    如果把apply和call分别翻译成“应用”和“调用”,那么我的理解是,thisObj应用(调用)Obj,并把[]作为Obj的传入参数,而不是以[]作为Obj的参数并替换thisObj。”

    理解有些不当。应该是, 调用 Obj 这个函数,并且将 Obj 的上下文切换为传递进 apply 方法的第一个参数。然后 [] 是作为 Obj 的参数传递的。而这整个过程中,Student2 的 this 并没有变。当构造新实例时是始终指向新实例的。

    所以你给出的例子就可以很好的理解了。

    附上 jsfiddle 。我只是写了一些注释。 http://jsfiddle.net/Jx8r7/

    另外楼主可以看看这两个链接,有帮助你理解 this 和 call/apply

    阮一峰:Javascript的this用法

    http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_Javascript.html

    郭培:Javascript中call的使用

    http://hszy00232.blog.163.com/blog/static/4302253201131835653841/
    emric
        8
    emric  
       2014-06-01 12:39:20 +08:00   1
    lijsh
        9
    lijsh  
       2014-06-01 12:44:39 +08:00   1
    楼主,console的一行是不会有school和grade属性输出的,因为还没到这两个属性的赋值部分。

    替换这个说法是没错的,对于apply和call,第一个参数就是代表上下文的替换(当然切换更好)。

    这里当以new调用Student2的时候,Student2里的this都会指向实例后的对象,这里就是s;既然内部的this都是指向同一个引用,两个apply所谓的替换看起来更像补充作用。
    lijsh
        10
    lijsh  
       2014-06-01 12:47:34 +08:00
    楼主的第二条补充有点不严谨,应该是在Student2里把Person的上下文切换到Student2中的this。
    zzNucker
        11
    zzNucker  
       2014-06-01 12:53:24 +08:00
    我觉得lz的补充说明他还是不懂。。。。。
    andy12530
        12
    andy12530  
       2014-06-01 13:04:26 +08:00   1
    我是不能理解为毛20行不能 打印出name,age,school和grade,都说是替换了,又不是把Student2里面的this删掉,后面又对this添加了school等属性。

    顺便说一句,浏览器里面的console.log是异步的。
    andy12530
        13
    andy12530  
       2014-06-01 13:13:15 +08:00
    『其实apply就是把你想要进行继承操作的构造方法(比如我例子中的Student2)的上下文交给你想要集成的构造方法』

    LZ 你还是没理解。

    apply和call方法的功能,只是 "调用一个函数", 把这个 函数里面的 this 变成你传递的第一个参数。

    例子:Array.prototype.slice.call(ListCollection),本来slice这个方法只能在array上调用,通过call方法,我们可以slice方法在非数组元素上。
    ety001
        14
    ety001  
    OP
       2014-06-01 15:06:43 +08:00
    @lijsh
    以18行的代码为例,就是把当前Student2的this传递给了Person,apply会把Person作为实施目标,用传入的Student2的this替换原有的this,所谓的替换,应该就是这一步,之前理解不了,就是因为把这个替换的方向搞反了,一直认为是Person的this替换了Student2的this。
    说白了,就是把Student2的this告诉了Person,让Person可以直接操作这个this,通过这个方式让Student2拥有Person的一些属性。


    @andy12530 浏览器里面的console.log是异步的,这个之前还真是不清楚。不过上午做这个试验的时候,看到这个结果,有想到这个console.log可能会是异步。另外我觉得你说的“apply和call方法的功能,只是 "调用一个函数", 把这个 函数里面的 this 变成你传递的第一个参数”跟我理解的意思是一致的。
    jakwings
        15
    jakwings  
       2014-06-01 16:17:23 +08:00
    相信大家已经解释清楚了。我补一个相关的细节问题:经过.bind(this) 产生的新函数,无法再替换this,即是说:
    function a() { console.log(this); }
    a.call({x: 1}); //=> {x: 1}
    b = a.bind({y: 2});
    b.call({x: 1}); //=> {y: 2}
    lijsh
        16
    lijsh  
       2014-06-01 18:15:06 +08:00 via Android
    @andy12530 老版的webkit中确实是这样,现在基本不会了,我在新版chrome中测了,console没表现出异步,没输出后面赋值的属性。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     958 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 19:52 PVG 03:52 LAX 12:52 JFK 15:52
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86