V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
ety001
V2EX  ›  问与答

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

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


    以下是某篇博文对于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/43022753201131835653841/
    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   ·   实用小工具   ·   3109 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 10:47 · PVG 18:47 · LAX 02:47 · JFK 05:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.