function foo() {
var x = 10;
return function bar() {
console.log(x);
};
} // "foo"返回的也是一个function
// 并且这个返回的function可以随意使用内部的变量x var returnedFunction = foo(); // 全局变量 "x"
var x = 20; // 支持返回的function
returnedFunction(); // 结果是10而不是20

  EMCAScript使用的是静态作用域/语法作用域[static/lexical scope]。上面的x变量就是在函数bar的语法作用域里搜寻到的是10。如果采用动态作用域[dynamic scope], 那么上述的x应该被解释为20。

// 全局变量 "x"
var x = 10; // 全局function
function foo() {
console.log(x);
} (function (funArg) { // 局部变量 "x"
var x = 20; // 这不会有歧义
// 因为我们使用"foo"函数的[[Scope]]里保存的全局变量"x",
// 并不是caller作用域的"x" funArg(); // 10, 而不是20 })(foo); // 将foo作为一个"funarg"传递下去

同样把外部函数传入内部函数去执行一样存在同样的选择, js使用静态作用域,自由变量是函数创建时候上下文里最近的那个,所以值是10

-------------------------------------------------------------------关于作用域---------------------------------------------------------------------------------
  维基百科上的解释:

  静态作用域又叫做词法作用域,采用词法作用域的变量叫词法变量。词法变量有一个确定的作用域。词法变量的作用域可以是一个函数或一段代码段,该变量在这段代码区域内存在;在这段区域以外该变量不存在(或无法访问)。词法作用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。

大多数现在程序设计语言都是采用静态作用域规则,如C/C++C#PythonJavaJavaScript……

  相反,采用动态作用域的变量叫做动态变量。只要程序正在执行定义了动态变量的代码段,那么在这段时间内,该变量一直存在;代码段执行结束,该变量便消失。这意味着如果有个函数f,里面调用了函数g,那么在执行g的时候,f里的所有局部变量都会被g访问到。在静态作用域的情况下,g不能访问f的变量。动态作用域里,取变量的值时,会由内向外逐层检查函数的调用链,并打印第一次遇到的那个绑定的值。显然,最外层的绑定即是全局状态下的那个值。

  采用动态作用域的语言有Emacs LispCommon Lisp(兼有静态作用域)、Perl(兼有静态作用域)。

  

  自己的理解,首先明白所谓“自由变量”的概念,就是一个函数非本地定义也不是函数参数的变量。这个变量从哪里取,就衍生了这两种规则。

  从实现的角度,所谓静态还是动态作用域,它们对“自由变量”的定位是不一样的。 动态作用域中查找自由变量,是顺着函数调用活动纪录形成的堆栈反向查找(当然这只是一种实现方式,还有别的实现方式),所谓的“动态链”。(就是程序调用顺序会影响变量的定位位置)

  静态作用域中查找自由变量,是在函数定义时的环境中查找。为了让静态作用域更直观,可以把函数实现为一个闭包(即包含代码和定义时的环境的一个二元组),这样查找自由变量就方便多了。

-------------------------------------------------------------------关于作用域---------------------------------------------------------------------------------

  闭包是一系列代码块(在ECMAScript中是函数),并且静态保存所有父级的作用域。通过这些保存的作用域来搜寻到函数中的自由变量。个人理解狭义的闭包是一个写法风格,将自由变量和函数写在一个代码块里,让静态作用域的概念更直观,满足以下两点就可以理解为狭义的闭包:

  • 使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
  • 在代码中引用了自由变量

如果一种语言不采用静态作用域,那闭包也就无从谈起。

  而广义的闭包就是任何函数都是闭包,因为函数都在作用域链之中。这里有一个特殊情况就是用Function函数构造new出来的函数,因为这种方式不论在哪里调用都是直接关联到顶层的全局作用域,不存在调用位置的链结构了。

  “闭包”要实现将局部变量在上下文销毁后仍然保存下来,基于栈的实现显然是不适用的,因为方法调用栈完了之后那些在栈里创建的变量都要清除。因此在这种情况下,上层作用域的闭包数据是通过动态分配内存的方式来实现的(基于“堆”的实现),配合使用垃圾回收器(garbage collector简称GC)和 引用计数(reference counting)。这种实现方式比基于栈的实现性能要低,然而,任何一种实现总是可以优化的: 可以分析函数是否使用了自由变量,函数式参数或者函数式值,然后根据情况来决定 —— 是将数据存放在堆栈中还是堆中。

  一个例子var data = [];

for (var k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
} data[0](); // 3, but not 0
data[1](); // 3, but not 1
data[2](); // 3, but not 2

----------------------------------------------------------------
//换一种写法
var data = []; for (var k = 0; k < 3; k++) {
data[k] = (function (x) {
return function () {
alert(x);
};
})(k); // 将k当做参数传递进去
} // 结果正确
data[0](); //
data[1](); //
data[2](); // ----------------------------------------------------------------
//另外的写法

var data = [];

for (var k = 0; k < 3; k++) {
(data[k] = function () {
alert(arguments.callee.x);
}).x = k; // 将k作为函数的一个属性
}

// 结果也是对的
data[0](); // 0
data[1](); // 1
data[2](); // 2

 

第一种写法得不到想要的结果,是因为三个函数执行的时候都要去找自由变量k,而k在外部最后被赋值为3,所以结果都是3.

第二种写法对外部函数来说k已经不是自由变量了,是入参,所以结果正确。另外涉及到一个关于内存的知识点,这里js函数参数传值的是值传递,也就是将k的值赋值了一份传入内部函数,当然内部函数的栈里会保存这个值,自然也就不会去外面找了。对内部函数来说,x是自由变量,要到作用域链里去找,就找到了外部函数的入参,这里对内部函数来说相当于是一个闭包:父级作用域有变量,有函数。

第三种写法与闭包无关了,因为与自由变量无关,是把值写死在调用函数的属性上了,这种写法知道就行,玩玩而已。

由javascript的闭包引申到程序语言编译上的自由变量作用域的考量的更多相关文章

  1. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  2. JavaScript的闭包和内存泄漏问题

    闭包 http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html JavaScript中必须提到的功能最强大的抽象 ...

  3. 深入理解javascript的闭包

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域 ...

  4. 理解Javascript 的闭包(closure)

    要理解闭包的概念先从变量的作用域说去 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之 ...

  5. JavaScript的“闭包”到底是什么(2)

    我的上篇博客标题不对,造成一些误解.我认为博客的宗旨不是背教科书,而是分享研发心得.我的上篇标题因该改成“JavaScript 闭包的一个议题:它对outer scope 的影响”,因为我没有严格地去 ...

  6. Javascript中闭包问题(转载)

    学习Javascript闭包(Closure)   作者: 阮一峰 日期: 2009年8月30日 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现 ...

  7. JavaScript之闭包

    JavaScript之闭包 在JavaScript中,闭包恐怕是很多人不能理解的一个概念了,甚至很多人也会把闭包和匿名函数混淆. 闭包是有权访问另一个函数作用域中的变量的函数.首先要明白的就是,闭包是 ...

  8. JavaScript的闭包原理

    什么是js(JavaScript)的闭包原理,有什么作用? 一.定义 官方解释:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 个人的理解是 ...

  9. javascript中闭包的概念

    这个是每个前端工程师绕不开的一个问题,网上各种资料很多,整个春节,我仔细研读了红皮经典中关于这一块的注释,加深了对这一块的理解. 有好几个概念需要重申一下.以下都是我的理解: 1. 闭包是javasc ...

随机推荐

  1. linux学习笔记-文件相关知识

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一.文件属性 在当前用户家目录下以ls -al命令输出为例: -rw-r--r--     1          renren  ...

  2. OkHttp3源码详解(二) 整体流程

    1.简单使用 同步: @Override public Response execute() throws IOException { synchronized (this) { if (execut ...

  3. 【Java入门提高篇】Day33 Java容器类详解(十五)PriorityQueue详解

    今天要介绍的是基础容器类(为了与并发容器类区分开来而命名的名字)中的另一个成员——PriorityQueue,它的大名叫做优先级队列,想必即使没有用过也该有所耳闻吧,什么?没..没听过?emmm... ...

  4. python第十六天,昨天来晚了,作业终于完成了

    作业 1: 员工信息表程序,实现增删改查操作 可进行模糊查询,语法至少支持下面3种: select name,age from staff_table where age > 22 select ...

  5. MySQL复制ERROR 1794 (HY000): Slave is not configured or failed to initialize properly.

    ERROR 1794 (HY000): Slave is not configured or failed to initialize properly. You must at least set  ...

  6. 将爬取的数据保存到mysql中

    为了把数据保存到mysql费了很多周折,早上再来折腾,终于折腾好了 安装数据库 1.pip install pymysql(根据版本来装) 2.创建数据 打开终端 键入mysql -u root -p ...

  7. win10怎么查看进程

    1.在任务栏右击-任务管理器 2.

  8. Hbase-2.0.0_01_安装部署

    该文章是基于 Hadoop2.7.6_01_部署 进行的 1. 主机规划 主机名称 IP信息 内网IP 操作系统 安装软件 备注:运行程序 mini01 10.0.0.11 172.16.1.11 C ...

  9. Ubuntu 16.04 LTS 降级安装GCC 4.8

    转载自https://www.linuxidc.com/Linux/2017-03/142299.htm Ubuntu 16.04 LTS 降级安装GCC 4.8 [日期:2017-03-28] 来源 ...

  10. 开启远程Windows系统3389端口

    1.Win7.Win2003.XP系统REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDen ...