技术解析

关于 js 闭包的联想,为什么匿名函数内的变量不能直接访问呢 具体看代码
0
2021-08-11 04:00:06
idczone

var Counter = (function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } })();


粘贴在控制台没啥报错。

很抱歉我不会格式化代码 = = 大家将就看一下吧 这个是 MDN 上的原版示例
var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() { changeBy(1); },
decrement: function() { changeBy(-1); },
value: function() { return privateCounter; }
}
})();

你这问题不是很怪吗,设计上就是不能直接访问,所以不能直接访问

是不报错 我在想为什么 console.log(Counter.privateCounter); 返回值会是 undefined

我就是这个意思,。。。为什么设计成不能访问。。。

先抛开闭包不想。
Counter 是一个 object,所以你能 .privateCounter,这在语法上不犯规,所以不报错。
但 Counter 里没有 privateCounter 这个属性,所以读它就是 undefined 。
再回来看为什么 Counter 里没有 privateCounter 这个属性呢?因为这个对象的值是个匿名函数,仅仅是这个匿名函数里有个变量叫 privateCounter 而已。
这个变量不属于 Counter,它被匿名函数包在一个封闭的作用域里了。闭包二字说的就是这个作用域。

谢谢指教! 那我再提问一下, 因为 Counter 这个对象只有一个匿名函数,但因为是匿名的函数 ,所以没法用 "
xx.xx " 这种形式调用是么

1 、JS 里函数本身也是 object,不管匿名不匿名
2 、不是没法用 xx.xx ,相反可以,就像你对任意一个 object 去 xx.xx 一样
感觉你的基础概念有点不熟,可以阅读一下《你不知道的 JS 》几本小册子,特别易懂。

避开闭包不谈,请问你能这么搞吗:
var privateCounter = 0;
var Counter = {
increment: function() { changeBy(1); },
decrement: function() { changeBy(-1); },
value: function() { return privateCounter; }
}
// 现在你想

// 现在你想访问 Counter.privateCounter? Excuse Me?

啊这,虽然函数是对象,但从来没说过函数里面的变量是这个对象的属性吧?你为什么会得出这个结论

谢谢指教!已经找到资源了!

按照我目前的理解来说, 应该是不可以的。。
我的理解是
privateCounter 和 Counter 是同级的(同一个作用域)
所以不可以去读取
请问这样的理解对吗

请问! 那么这样说的话,函数的里的(指花括号里的)变量是不能被外部访问到的对吗,但是,函数可以使用它上一级作用域的变量,这样是对的吗

找一本书看看吧......................

有没那个语言是把函数内变量,作为函数这个对象的属性的呢

Counter 只是一个对象而已,只有最后 return 的三个属性。

这个函数不是 Counter 的内容,这个函数的返回值才是 Counter 的内容。。函数被立即执行了。。

你没有写 Counter.privateCounter = xxx,那么你就访问不了 Counter.privateCounter,就这么简单。
至于函数里面的 var,那和 Counter 无关。

混淆了太多概念,我来尝试指一条明路吧
1. 理解 Js 中对象和属性访问器的概念,参考阅读
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Property_Accessors
此处重点是要理解 JS 中一切皆是对象,并且对象中的属性和方法可以被属性访问器访问到
2. 理解什么是闭包,参开阅读
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
此处需要重点理解词法作用域的概念,进而理解闭包的本质就是你持有的的某个可以被调用的函数会保持着自己作用域链(又称词法环境)的引用。
3. 理解 IIFE,参考阅读
https://developer.mozilla.org/zh-CN/docs/Glossary/立即执行函数表达式
4. 理解 var,参考阅读
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var
此处重点其实是这句话,“用 var 声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,或者对于声明在任何函数外的变量来说是全局。”
5. 在 1,2,3,4 的基础上就可以推导出,privateCounter 只是一个在匿名函数词法作用域中的一个变量,并且和对象没有任何关系
6. 你可能还会一知半解,延伸阅读推荐,you don't know js,有中文版,开源地址为
https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/scope-closures
这一章对词法作用域,作用域链,全局作用域,对象生命周期,闭包有详实的讲解
7. 千万别和 this 问题混淆,那是另一个大坑

14 楼的理解有点上路了,问题关键点是楼主对作用域概念理解不够。
多数情况可以理解为花括号里边的变量 /代码不能被外部直接访问。对象算是有点例外。
作用域基本是可见性的另一种说法。函数作用域、文件作用域。其他语言里有类作用域,对应 js 里的对象作用域。相关内容不多,关键词:作用域,可见性

不太懂 js 但我感觉你可以把有 privateCounter 这个变量的函数抽象出来,那么这样就可以看出来这个产量是局部的,外部没法访问到?

弄明白 js 作用域你就恍然大悟了,函数声明后如果没有执行是无法获知其内部的匿名函数的内部的,最多知道存在一个或者多个匿名函数,但是肯定不会知道自己内部的匿名函数里边的变量的,其内部的匿名函数在被执行的时候会生成自己的 ao 对象,外部函数才能通过匿名函数获取到内部变量

var Counter = (function() {
//var privateCounter = 0;
function changeBy(val) {
Counter.privateCounter = (Counter.privateCounter||0) +val;
}
return {
increment: function() { changeBy(1); },
decrement: function() { changeBy(-1); },
value: function() { return Counter.privateCounter; }
}
})();
变量谁要就给谁,不然函数里面的除了自己能访问谁都访问不了。特例是全局下面的,被划给了 window 对象

新手学闭包却实有点难啃,建议基础学透  http://www.jianxue.mobi/treep/2500/2775

建议把 javascript 的闭包从头开始看 不是什么三言两语说的清楚的

搞懂作用域,很容易理解闭包

我知道是作用域链的问题 但是还是看个来龙去脉比较容易懂

是的,作用域连 原型链 啥的

这个就是基础的重要性了。
外部变量不能访问 -> 变量作用域 -> 编译器的处理方式( OOP 语言会封装成类对象) -> 底层堆栈访问控制原则。
但是认真看看 js 的书,前两个也会明确告诉你了。

反过来想,如果是这样设计的
```js
function sayHello(name) {
var hello = 'Hello'
console.log(hello + ' ' + name)
}
sayHello.hello = 'Fxck you'
sayHello('Jack')
```
就完全可以写成
```js
var hello = 'Hello'
function sayHello(name) {
console.log(hello + ' ' + name)
}
hello = 'Fxck you'
sayHello('Jack')
```
闭包、局部变量不就没什么意义了么,都用全局变量好了

http://www.yinwang.org/blog-cn/2012/08/01/interpreter

var 是声明局部变量的,并添加到最近的作用域对象( js,我记得是都是函数作用域和全局)
.运算符是访问对象属性的

建议看下 js 高级程序设计。

建议看下 js 高级程序设计。
看下作用域链,还有原型链

这个例子很经典的。就是用闭包实现封装,让外界访问不到 counter 的实现细节,只能访问到外部接口。实现细节就是 privateCounter 是匿名函数的内部变量,外部接口是匿名函数的返回值包含的那三个内部函数 inc 、dec 、val 。
counter 是什么?是匿名函数的返回值,Counter = (function() {...}) (); 注意看,它定义了这个函数对象,然后立刻就调用它,返回值赋给 Counter 。
涉及到闭包的概念,三个内部函数之所以能访问到 var privateCounter 是因为它属于他们定义时的上下文环境。

在一些语言比如 C++、Java 要实现封装很容易,只要访问控制声明成 private 就行了。但是 js 就是要绕一点。

确实如 所说,Counter 的值是匿名函数返回的对象。我马虎了。但竟然也歪打正着。。总之最终赋给 Counter 的值,也就是 return 的那个对象里面没有 privateCounter 这个 key,访问一个对象里不存在的 key 就得到 undefined 。原因是作用域封闭( closure )这一思路还是有效。

因为暴露接口更好,对比直接暴露成员变量,可以将代码修改对外部调用的影响降到最低。
假如有一天,因为一些原因,你要改成员变量( privateCounter )的名字,如果没有 value 接口的话,外部调用者岂不是都要改。
有了 value 接口,你就随便改了,甚至可以在 value 接口中写很多逻辑。

那么,为了帮助楼主之类的新手,是否存在可以查看 js 变量作用域的( IDE 插件|独立演示用工具)?

说句题外话,匿名函数和闭包本身就不是同一个概念

还真搜到一个差不多齐活的 visualizer https://ui.dev/javascript-visualizer/
右边下方点击「 Complex Closures 」有惊喜 lol

作用域、执行上下文

不在词法作用域的变量是个 ide 都会报错了把

是直接显示范围,而不是得等有错才会报错

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服