var Counter = (function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } })();
1
cyrbuzz 2020-12-26 20:20:10 +08:00
粘贴在控制台没啥报错。
|
2
sodadev OP 很抱歉我不会格式化代码 = = 大家将就看一下吧 这个是 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; } } })(); |
3
des 2020-12-26 20:23:10 +08:00 via iPhone
你这问题不是很怪吗,设计上就是不能直接访问,所以不能直接访问
|
4
sodadev OP @cyrbuzz 是不报错 我在想为什么 console.log(Counter.privateCounter); 返回值会是 undefined
|
6
ck65 2020-12-26 20:35:31 +08:00 3
先抛开闭包不想。
Counter 是一个 object,所以你能 .privateCounter,这在语法上不犯规,所以不报错。 但 Counter 里没有 privateCounter 这个属性,所以读它就是 undefined 。 再回来看为什么 Counter 里没有 privateCounter 这个属性呢?因为这个对象的值是个匿名函数,仅仅是这个匿名函数里有个变量叫 privateCounter 而已。 这个变量不属于 Counter,它被匿名函数包在一个封闭的作用域里了。闭包二字说的就是这个作用域。 |
7
sodadev OP @ck65 谢谢指教! 那我再提问一下, 因为 Counter 这个对象只有一个匿名函数,但因为是匿名的函数 ,所以没法用 "
xx.xx " 这种形式调用是么 |
8
ck65 2020-12-26 21:01:34 +08:00
1 、JS 里函数本身也是 object,不管匿名不匿名
2 、不是没法用 xx.xx ,相反可以,就像你对任意一个 object 去 xx.xx 一样 感觉你的基础概念有点不熟,可以阅读一下《你不知道的 JS 》几本小册子,特别易懂。 |
9
vision1900 2020-12-26 21:03:03 +08:00
避开闭包不谈,请问你能这么搞吗:
var privateCounter = 0; var Counter = { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } // 现在你想 |
10
vision1900 2020-12-26 21:03:41 +08:00 1
// 现在你想访问 Counter.privateCounter? Excuse Me?
|
11
shintendo 2020-12-26 21:33:46 +08:00 1
啊这,虽然函数是对象,但从来没说过函数里面的变量是这个对象的属性吧?你为什么会得出这个结论
|
13
sodadev OP |
14
sodadev OP @shintendo 请问! 那么这样说的话,函数的里的(指花括号里的)变量是不能被外部访问到的对吗,但是,函数可以使用它上一级作用域的变量,这样是对的吗
|
15
Kasumi20 2020-12-26 21:53:16 +08:00 1
找一本书看看吧......................
|
17
johnkiller 2020-12-26 22:19:47 +08:00 via iPhone
Counter 只是一个对象而已,只有最后 return 的三个属性。
|
18
ljpCN 2020-12-26 22:50:20 +08:00 via iPhone
这个函数不是 Counter 的内容,这个函数的返回值才是 Counter 的内容。。函数被立即执行了。。
|
19
oott123 2020-12-27 01:13:48 +08:00 via Android 1
你没有写 Counter.privateCounter = xxx,那么你就访问不了 Counter.privateCounter,就这么简单。
至于函数里面的 var,那和 Counter 无关。 |
20
Zhuzhuchenyan 2020-12-27 03:55:30 +08:00 1
混淆了太多概念,我来尝试指一条明路吧
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 问题混淆,那是另一个大坑 |
21
iceheart 2020-12-27 08:21:54 +08:00 via Android
14 楼的理解有点上路了,问题关键点是楼主对作用域概念理解不够。
多数情况可以理解为花括号里边的变量 /代码不能被外部直接访问。对象算是有点例外。 作用域基本是可见性的另一种说法。函数作用域、文件作用域。其他语言里有类作用域,对应 js 里的对象作用域。相关内容不多,关键词:作用域,可见性 |
22
uselessVisitor 2020-12-27 08:58:56 +08:00 via Android
不太懂 js 但我感觉你可以把有 privateCounter 这个变量的函数抽象出来,那么这样就可以看出来这个产量是局部的,外部没法访问到?
|
23
kiwier 2020-12-27 09:20:12 +08:00
弄明白 js 作用域你就恍然大悟了,函数声明后如果没有执行是无法获知其内部的匿名函数的内部的,最多知道存在一个或者多个匿名函数,但是肯定不会知道自己内部的匿名函数里边的变量的,其内部的匿名函数在被执行的时候会生成自己的 ao 对象,外部函数才能通过匿名函数获取到内部变量
|
24
xiangyuecn 2020-12-27 09:48:02 +08:00
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 对象 |
25
hujun528 2020-12-27 10:54:27 +08:00
@sodadev 新手学闭包却实有点难啃,建议基础学透 http://www.jianxue.mobi/treep/2500/2775
|
26
dartabe 2020-12-27 11:37:20 +08:00
建议把 javascript 的闭包从头开始看 不是什么三言两语说的清楚的
|
30
across 2020-12-27 13:00:51 +08:00
这个就是基础的重要性了。
外部变量不能访问 -> 变量作用域 -> 编译器的处理方式( OOP 语言会封装成类对象) -> 底层堆栈访问控制原则。 但是认真看看 js 的书,前两个也会明确告诉你了。 |
31
px920906 2020-12-27 13:09:47 +08:00
反过来想,如果是这样设计的
```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') ``` 闭包、局部变量不就没什么意义了么,都用全局变量好了 |
32
FaiChou 2020-12-27 17:04:19 +08:00
|
33
v2lf 2020-12-27 17:40:18 +08:00
var 是声明局部变量的,并添加到最近的作用域对象( js,我记得是都是函数作用域和全局)
.运算符是访问对象属性的 |
34
v2lf 2020-12-27 17:40:52 +08:00
建议看下 js 高级程序设计。
|
36
user8341 2020-12-27 19:07:28 +08:00
这个例子很经典的。就是用闭包实现封装,让外界访问不到 counter 的实现细节,只能访问到外部接口。实现细节就是 privateCounter 是匿名函数的内部变量,外部接口是匿名函数的返回值包含的那三个内部函数 inc 、dec 、val 。
counter 是什么?是匿名函数的返回值,Counter = (function() {...}) (); 注意看,它定义了这个函数对象,然后立刻就调用它,返回值赋给 Counter 。 涉及到闭包的概念,三个内部函数之所以能访问到 var privateCounter 是因为它属于他们定义时的上下文环境。 |
37
user8341 2020-12-27 19:08:51 +08:00
在一些语言比如 C++、Java 要实现封装很容易,只要访问控制声明成 private 就行了。但是 js 就是要绕一点。
|
38
ck65 2020-12-27 19:33:04 +08:00
确实如 #18 @ljpCN 所说,Counter 的值是匿名函数返回的对象。我马虎了。但竟然也歪打正着。。总之最终赋给 Counter 的值,也就是 return 的那个对象里面没有 privateCounter 这个 key,访问一个对象里不存在的 key 就得到 undefined 。原因是作用域封闭( closure )这一思路还是有效。
|
39
meepo3927 2020-12-28 09:28:08 +08:00
因为暴露接口更好,对比直接暴露成员变量,可以将代码修改对外部调用的影响降到最低。
假如有一天,因为一些原因,你要改成员变量( privateCounter )的名字,如果没有 value 接口的话,外部调用者岂不是都要改。 有了 value 接口,你就随便改了,甚至可以在 value 接口中写很多逻辑。 |
40
no1xsyzy 2020-12-28 09:29:30 +08:00
那么,为了帮助楼主之类的新手,是否存在可以查看 js 变量作用域的( IDE 插件|独立演示用工具)?
|
41
0x11901 2020-12-28 10:21:51 +08:00
说句题外话,匿名函数和闭包本身就不是同一个概念
|
42
ck65 2020-12-28 11:12:56 +08:00
@no1xsyzy 还真搜到一个差不多齐活的 visualizer https://ui.dev/javascript-visualizer/
@sodadev 右边下方点击「 Complex Closures 」有惊喜 lol |
43
cczeng 2020-12-28 11:26:29 +08:00
作用域、执行上下文
|
44
theohateonion 2020-12-28 16:13:00 +08:00
@no1xsyzy 不在词法作用域的变量是个 ide 都会报错了把
|
45
no1xsyzy 2020-12-28 18:03:57 +08:00
@theohateonion 是直接显示范围,而不是得等有错才会报错
|