V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
cheroky
V2EX  ›  JavaScript

现在我把闭包跟 this 分开来看了,又遇到了些问题,望各位解答

  •  
  •   cheroky · 2017-05-22 12:47:47 +08:00 · 3414 次点击
    这是一个创建于 2530 天前的主题,其中的信息可能已经有所发展或是发生改变。

    谢谢上一个帖子各位大牛细心解答,是应该把 this 分开看。然后我看 you dont know js,先把 this 搞懂,里面说的 this 的会突然指向 window 的变量,结合楼内大神的答案,其实可以用 window 解释,然后我遇到了回调函数 this 的下面问题:

    var name = "global";
    var obj = {
    	name: "Yes",
    	getName: function() {
    		console.log(this.name);
    	}
    }
    
    function doo(f) {
    	f(); 
    }
    
    doo(obj.getName) //"global"
    

    至少这个例子我可以理解为 obj.getName 的调用点再 doo 函数里,所以 this 应该指向这个,而调用这个函数可以解释为 window.doo(obj.getName),doo 的 this 指向 window,所以最终的 this 指向 window。那如果我用一个有 this 的对象来调用呢。代码如下:

    var name = "global";
    var obj = {
    	name: "Yes",
    	getName: function() {
    		console.log(this.name);
    	}
    }
    
    var obj2 = {
    	name: "Yes2",
    	getName: function(f) {
    		f();
    	}
    }
    
    obj2.getName(obj.getName) //"global"
    

    发现这个 this 还是指向 window,有点想不通。。。

    26 条回复    2017-05-26 16:10:56 +08:00
    UncleRiver
        1
    UncleRiver  
       2017-05-22 12:57:03 +08:00
    参考一下这个:
    http://stackoverflow.com/questions/7043509/this-inside-object

    > Javascript has no block scope, only function scope
    whimsySun
        2
    whimsySun  
       2017-05-22 13:08:43 +08:00
    函数被传递时没有通过 bind 方法指定 this,this 指向其调用方的上下文,调用方你可以理解为 ${invoke}.${function}. 如果 invoke 没有指定,就是 this 就指向 global。
    上面的例子:
    调用 obj2.getName 时候 getName 的调用方就是 obj2,this 既 obj2. obj.getName 本传递后,仅做为一个函数传递,使用的时候也没有指定调用方,既 this 为 global

    如果你需要 obj2.getName(obj.getName) 输出 “ Yes ”, 通过 obj2.getName(obj.getName.bind(obj))

    如果你需要 obj2.getName(obj.getName) 输出 ‘ Yes2 ’, 修改 obj2,`obj2.getName = function(f) { f.apply(this) }`
    whimsySun
        3
    whimsySun  
       2017-05-22 13:10:53 +08:00
    @UncleRiver 和楼主的例子是两种情况
    ericls
        4
    ericls  
       2017-05-22 13:11:18 +08:00
    @UncleRiver `let` and `const` are block scoped
    otakustay
        5
    otakustay  
       2017-05-22 13:27:09 +08:00
    只看调用不看怎么定义和怎么传参,你只要看到 doo 里是 f()(函数调用),那么这个 f 只要没 bind,无论它怎么定义的怎么传进来的,它的 this 都是 global,和 doo 的 this 是啥以及这个 f 怎么来都没关系
    cheroky
        6
    cheroky  
    OP
       2017-05-22 13:29:57 +08:00
    @whimsySun 感谢热心回答。但是我还有问题是这个函数作为对象的方法被调用,不是应该指向该对象么?而且这个函数既不是全局函数。而且用 obj2 调用,为什么不指向 obj2?
    lijsh
        7
    lijsh  
       2017-05-22 13:33:08 +08:00   ❤️ 1
    @cheroky #6 你只是传 obj.getName 这个方法进去,等价于传 function() {
    console.log(this.name);
    },传进去就变闭包了,调用的时候就指向全局。
    必须 obj.getName() 才是 obj 调用
    wodewone
        8
    wodewone  
       2017-05-22 13:42:24 +08:00
    js 执行要看它的运行环境而不是它的初始环境
    xilixjd
        9
    xilixjd  
       2017-05-22 13:43:58 +08:00
    你确定你看完了 u don't know 吗。。
    this 的指向是看在哪调用的,好像有 4 种调用形式
    你这个例子跟第一种是一样的
    sensui7
        10
    sensui7  
       2017-05-22 14:11:06 +08:00   ❤️ 1
    > 至少这个例子我可以理解为 obj.getName 的调用点再 doo 函数里...

    你理解错了, 这里不存在 obj.getName 的调用, 你只不过是把  obj.getName 赋值给了形式参数f,你调用的是形参f. 形参f是什么, 是函数调用阿.
    ChefIsAwesome
        11
    ChefIsAwesome  
       2017-05-22 14:13:56 +08:00
    别想了,你不用 this 就完事了。
    cheroky
        12
    cheroky  
    OP
       2017-05-22 14:51:31 +08:00
    @lijsh 试了一下,不知道是不是等同于下面的代码:
    var name = "global";

    var obj2 = {
    name: "Yes2",
    getName: function() {
    (function() {
    console.log(this.name);
    })();
    }
    }
    obj2.getName() //"global"
    结果是一样的不知道是不是同个原理
    bramblex
        13
    bramblex  
       2017-05-22 15:16:48 +08:00
    const f = function () {console.log(this)}

    f() // => window / global

    const aaa = {}

    aaa.f = f

    aaa.f() // => aaa

    这个你能理解嘛?

    f() 等效于 f.apply(window/global)
    bramblex
        14
    bramblex  
       2017-05-22 15:18:10 +08:00
    上面的要是你都理解,把他反过来你就不会了?

    const aaa = {f: function() {console.log(this)}}

    const f = aaa.f

    f()
    cheroky
        15
    cheroky  
    OP
       2017-05-22 15:32:37 +08:00
    @bramblex 这个我能理解,我的理解是
    f=aaa.f 相当于 window.f=aaa.f 是吧,调用 window.f(),this 指向 window。问题是我的例子我把函数直接调用,和作为参数传出去再调用,我输出了 this 之后,发现函数作为参数传过去为什么 this 指向了 window,中间有涉及到 window 吗?
    whimsySun
        16
    whimsySun  
       2017-05-22 15:41:31 +08:00   ❤️ 1
    @cheroky 不知道你之前是不是学过一些面向对象的语言,收到了影响。js 里面,你可以把函数当成一个单一个体,只是在调用的使用会有归属,不知道这个你能不能理解
    Exin
        17
    Exin  
       2017-05-22 15:49:54 +08:00 via iPhone
    建议楼主把 You dont know js 中的相关章节先仔细完整地阅读,讲的还是蛮清楚的
    bramblex
        18
    bramblex  
       2017-05-22 15:53:25 +08:00
    @cheroky

    const aaa = {f: function(){console.log(this)}}

    下面两条有什么区别吗?

    xxx(aaa.f)

    ===
    const f = aaa.f
    xxx(f)

    如果你觉得上面两条没区别吗? f 只在调用的时候才会去找调用它的对象,传参的时候又没调用。

    最后 f = aaa.f 和 window.f = aaa.f 有本质区别的。
    Biwood
        19
    Biwood  
       2017-05-22 15:54:02 +08:00   ❤️ 1
    你这个问题问的挺好的,以后再项目中你会遇到很多次这种情况。

    先说原因,obj2.getName(obj.getName) 等价于 obj2.getName(匿名函数),这个 obj.getName 引用的是一个匿名函数,传参的时候,匿名函数被赋值给了形参 f,这一步是关键,形参 f 默认没有绑定任何 Context,你觉得 f 的 Context 是 obj2 吗? f 不是 obj2 的属性,所以显然不是。对于没有手动绑定 Context 的函数,默认 Context 为全局对象,浏览器里面就是 window 对象,严格模式下面会是 undefined。

    配合 ES5 里面的 Function.prototype.bind() 的用法,理解起来会容易许多。你可以在 Chrome 浏览器调试栏里面执行如下代码:

    var name = "global";
    var obj = {
    name: "Yes",
    getName: function() {
    console.log(this.name);
    }
    }

    var obj2 = {
    name: "Yes2",
    getName: function(f) {
    console.dir(f);
    f();
    }
    }

    obj2.getName(obj.getName.bind(obj2))

    对比一下跟你原来的写法有什么不同,同时看看打印出来的函数对象的具体结构,大概就能理解了
    bramblex
        20
    bramblex  
       2017-05-22 15:56:29 +08:00   ❤️ 1
    @cheroky

    楼主你的问题在于,你根本不理解在 js 函数和其他变量是一样的东西,一个函数里面的 this 是不会绑定一个特定的对象的,而是会在 [函数调用的那一刻] 寻找所调用它的对象。

    所以你上面的例子,只有到 f() 这个执行,才会去找调用 f 的对象。但是现在没人在调用 f 啊,所以就默认是 window 或者 global
    bramblex
        21
    bramblex  
       2017-05-22 16:00:13 +08:00
    这个 this 叫做 “上下文指针”,指向的是这个函数在被调用的那一刻,直接调用这个函数的对象。

    而函数在创建的时候,这个 this 是毫无意义的。
    cheroky
        22
    cheroky  
    OP
       2017-05-22 16:55:35 +08:00
    @Biwood
    @bramblex
    @whimsySun
    大概有点理解了,那就是 function a(){b();} 虽然放在 a()里面,这个 b()并不不不不不不不不不不不不不是 a()调用的!!!!??,所以也不存在 this 指向 a,然后最后 b()所以指向的是 window ?
    IdJoel
        23
    IdJoel  
       2017-05-22 17:17:22 +08:00
    留一下 不忙了看 感谢 LZ 和大牛
    crs0910
        24
    crs0910  
       2017-05-22 22:54:14 +08:00 via iPhone
    js 的 this 不是 self 而是 context
    saga
        25
    saga  
       2017-05-23 17:40:49 +08:00   ❤️ 1
    wedaren
        26
    wedaren  
       2017-05-26 16:10:56 +08:00   ❤️ 1
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3330 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 12:12 · PVG 20:12 · LAX 05:12 · JFK 08:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.