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

求解,看了下 js 的闭包,有些地方不太明白

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

    《 javascript 高级程序设计》讲闭包的一章中,有这么一个例子

    var name = "The Window";
    var object = {
    	name: "My Object",
        getName: function() {
        	return function() {
            	console.log(this.name) 
            }
        }
    }
    object.getName()(); // "The Window"
    

    我很好奇,然后我又加上了另外一个函数,测试一下

    function object2() {
    	this.name = "My";
      	return function() {
      		console.log(this.name); 
      }
    }
    object2()(); // "My"
    

    第二个函数正常输出了 My,为什么??书上说是“内部函数搜 this 跟 arguments 是不会访问到外部变量的”,可是第二个例子又作何解释??然后我又把 this.name 赋值去掉:

    function object2() {
      	return function() {
      		console.log(this.name); 
      }
    }
    object2()(); // "The Window"
    

    这个时候跟第一个例子相同了,也就是外部函数的 this.name 没这个值得时候,内部函数的 this 会指向 window。于是我又把第一例子改了一下:

    var name = "The Window";
    var object = {
    	name: "My Object",
        getName: function() {
        	console.log(this.name); 
            //console.log(this);
        	return function() {
            	console.log(this.name); 
            }
        }
    }
    object.getName()(); // "My Object","The Window"
    

    然后我尝试输出了 getName() 里面的 this。发现指向的是 object。。。而其余两个 this 输出都是 Window 的一长串东西。我感觉被 this 跟闭包搞晕了。。。

    12 条回复    2017-05-22 14:39:41 +08:00
    ferrum
        1
    ferrum  
       2017-05-21 23:50:13 +08:00
    唉哟好长,后面的没看,就说第二个函数。

    第二个函数`object2`,里面的`this`就是指向`window`,当你`this.name = "my"`时,你实际上是在`window`变量上定义了一个`name`属性。
    wangjie
        2
    wangjie  
       2017-05-22 00:03:03 +08:00   ❤️ 1
    lijsh
        3
    lijsh  
       2017-05-22 00:18:33 +08:00   ❤️ 1
    第一个例子和第四个例子有可比性,因为都是在 object 上定义属性和方法;正常情况下你在对象的方法中访问 this,是会指向这个对象的(也就是 object ),这也是你第四个例子第一个 console.log(this.name)输出'My Obejct'、第二个 console.log(this)会指向 object 的原因;
    但是 getName 方法里又定义了一个函数(闭包),闭包里的 this 会丢失,自动指向全局,这就是第四个例子里最后一个 console.log(this)输出 window 的原因。

    第二和第三个例子只是单纯的函数,没有绑定为对象的方法,所以 this 就是 window。最搞笑的是第二个例子,你本来赋值 this.name = 'My'本身就是给 window 赋值,所以后面拿回来的自然也是这个值。
    lijsh
        4
    lijsh  
       2017-05-22 00:19:43 +08:00
    更正:“……自动指向全局,这就是第四个例子里最后一个 console.log(this.name)输出‘ The Window ’” 的原因。
    seki
        5
    seki  
       2017-05-22 00:24:41 +08:00
    this 指向的是函数的调用者
    SuperMild
        6
    SuperMild  
       2017-05-22 00:30:42 +08:00 via iPhone   ❤️ 1
    this 和闭包是两套东西,变量受闭包影响,但 this 不受闭包影响。总之,this 和闭包不要混在一起看,会很混乱。
    SuperMild
        7
    SuperMild  
       2017-05-22 00:32:35 +08:00 via iPhone
    单独理解 this 的规则就好,看 you don't know is
    sensui7
        8
    sensui7  
       2017-05-22 01:32:33 +08:00   ❤️ 1
    可以放弃 望远镜那本书了, 虽然经典, 已经落伍了. JS 的发展太快.
    闭包就是一个引用另一个作用域里变量的函数.

    ```js
    var name = "The Window";
    var object = {
    name: "My Object",
    getName: function() {
    return function() { // 这个函数其实不算是闭包, 因为它唯一使用的变量是存在于全局环境的, 讨论闭包是无意义的
    console.log(this.name)
    }
    }
    }
    object.getName()(); // "The Window"
    ```

    再说 this 取值,
    1. 函数中的 this 要么是 window(浏览器)要么是 undefined, 这取决于是否是 strict 模式
    你的前 3 个都是函数中的 this, 所以都是 window

    2. 方法中的 this, 是方法的 recevier, 所以第 4 个例子中的 getName 的 this 是 object, 至于它返回的函数, 那个只是函数, 并不是方法调用.

    this 最让人迷惑的地方, 它不是基于词法的, 是运行时决定的.

    var object = {
    name: "My Object",
    getName: function() {
    console.log(this.name);
    //console.log(this);
    return function foo () {
    console.log(this.name); // 这个 this 看起来在 object 内部, 但是 this 跟你把它写在哪里无关, 要看你在哪里使用
    }
    }
    }

    obect.getName()() // 这行表达式最终相当于执行了一次普通函数调用, 函数调用 this 的值是全局对象
    object.getName().call(object) // 这里我们强制指定 foo 的 this 为 object, 它输出'My Object'
    这就能体现 this 跟你把它写在哪里是无关的, 要看你如何调用.
    另外, es6 的箭头函数的 this 就是基于词法的, 只跟你把函数定义在哪有关, 不用担心调用时 this 的取值问题.
    Biwood
        9
    Biwood  
       2017-05-22 09:23:52 +08:00 via Android   ❤️ 1
    楼上几个没说到点上,关键词:JavaScript Context

    你在讨论 this 的时候跟闭包无关,但是跟函数的**执行环境**有关

    一般而言
    对于:

    A.foo()

    A.foo 引用的函数的 Context 是 A,所以函数里的 this 指向 A。

    对于:

    foo()

    等价于

    window.foo()

    所以 foo 里面的 this 指向 wondow。

    那么:

    object.getName()()

    等价于

    window.foo = object.getName() // Context 是 object
    window.foo() // Context 是 window
    wensonsmith
        10
    wensonsmith  
       2017-05-22 10:31:28 +08:00   ❤️ 1
    论讲闭包的文章, 我只服这一个系列:深入理解 javascript 原型和闭包(完结) http://www.cnblogs.com/wangfupeng1988/p/3977924.html


    这个是我看过讲的最透彻的了
    sensui7
        11
    sensui7  
       2017-05-22 14:00:26 +08:00
    @Biwood 可以引入'context'这个概念解释 this, 但是你就必须解释 context 是什么, 否则就更让人迷惑, 但是这么做的话, 你又需要记住 context 的不同情况( 函数的 context, 方法的 context, 构造函数的 context), 这样与直接记住 this 的取值有什么不同呢?

    所以, 像 You don't know JS 里的解释 this 方法虽然有启发性, 但对实践中对 this 的使用其实并无多大帮助, 你还是需要记住不同情况下 this 的取值.
    我的看法是 You don't know JS 这本书适合有一定经验的人翻翻, 当作甜点, 对工程来讲, 它偏理论, 偏学术. 对理论来讲, 又太浅了, 又不够系统.
    Biwood
        12
    Biwood  
       2017-05-22 14:39:41 +08:00
    @sensui7 我没看过你说的那本书,纯粹基于自己的理解写的。Context 和执行环境,这些词语的意思如果都理解不了那么根本不用学什么 JavaScript 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1028 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 79ms · UTC 20:26 · PVG 04:26 · LAX 12:26 · JFK 15:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.