做用域首先是变量或者函数的存储规则,很重要,重要性无异于孩子他妈得知道孩子他爸在哪。javascript
不是存储区域的名称,是根据变量名称存储和查找变量的一套规则,能够说是一个画圈的算法,把相同做用域得变量画一个圈,父子级的画个内圈。java
举例 var a = 2 涉及过程:es6
1)词法分析阶段:编译器碰到var a这类的声明会向做用域询问是否已存在该变量,而后生 成执行代码。算法
2)代码执行过程:引擎运行代码,进行赋值操做,赋值前会从当前做用域开始执行LHS查询(见查找规则)是否又a这个变量,若是没有抛出异常 Uncaught ReferenceError: a is not defined,若是有则进行赋值操做。浏览器
本身想到缘由是,解释型语言方便别人查看和复用。安全
表面上的意思是词法分析阶段定义得做用域,具体过程是词法分析阶段进行的是有状态的解析过程,给单词赋予语义,此时根据语义和做用域规则获得了词法做用域。闭包
百度了下,好像真有语法做用域,不过和词法做用域是一个东西,他大舅他二舅都是他就,咱们这里暂且称为词法做用域吧。ide
暂且忽略块级做用域,能够认为做用域是根据函数来画圈的,切记这是词法做用域,不是你调用时候画圈的,而是根据函数代码位置来画圈的,千万别掉坑里。函数
除了词法做用域还有动态做用域,大部分状况下是词法做用域,那么什么是动态做用域,动态做用域是相对于词法做用域这种静态做用域的,就是在代码执行阶段改变做用域。性能
// eval, 特性:动态修改所处做用域 function foo(str, a){ eval(str); console.log(a, b); //1 3 } var b = 2; foo("var b = 3;", 1)
释: 根据词法做用域,词法分析时并无b = 3的赋值,它只是一个字符串,那么打印的3确定是执行代码时赋值的,那么作颇有趣,不用听词法分析的指挥了,咱们能够随时修改做用域,可是这样作性能损失大,而且在严格模式下并不这样。
// with, 特性:根据参数对象建立一个特殊做用域,该做用域包含参数对象的属性同样的变量标识符,可是该做用域中var声明的变量不限制在当前做用域,而是添加到with所处做用域。 function foo(obj) { console.log(age); // undefined !!!被认为当前做用域的变量提高 with(obj){ name = "world"; sex= "男" var age = '18'; } } var per = { name: "hello" }; foo(per); console.log(per); // {name: "world"} console.log(sex); // 男 !!!,和普通变量同样。未声明直接赋值被添加到全局变量中。
这么有趣的东西为何使用频率那么少呢,缺点:
一、在严格模式下表现不一。
二、致使词法分析阶段的优化失效或者编译时碰了欺骗语法干脆不进行优化了。
三、大量的eval和with会致使代码阻塞。
函数做用域和块级做用域都是做用域,二者有相同点,也有区别,ES6中开始有块级做用域。
包含当前函数所有变量的范围。
首先要弄清为何要有函数,函数的目的是隐藏,定义私有变量,最小限度的暴露必要内容,这样作的好处是一方面私有变量更安全,不容易被修改,另外一方面是不容易出现命名冲突,这也就是函数做用域的目的。
1、函数是产生“气泡”(即做用域)的一种方式,另外还有块级做用域。
一些关键词{}之间的内容做为了块级做用域的范围,目前来讲JavaScript没有真正的块级做用域,不对?es6是有块级做用域的概念了,但不是严格意义的做用域,看下面。
// 支持es6的浏览器下运行 console.log(foo); // undefined if(true){ console.log(foo); // foo ... 我是老大 function foo(){ console.log('我是老大') } } else { function foo(){ console.log('我是老二?'); } } console.log(foo); // foo ... 我是老大
第一处输出undefined就很奇怪,这里的意思是有这个声明了可是没赋值,块级做用域的话不该该在这声明,不合理啊。
第二处块级做用域的代码执行完了可是变量并无销毁。
下面看一下ES5环境下的输出
// es5环境(https://pan.baidu.com/s/1piMP-0E5c-FT24Dy8iNzcQ) function bar(){ console.log(foo) if(true){ function foo(){ console.log(1) } } else { function foo(){ console.log(2) } } } undefined > bar() [Function: foo] // 总结一下:就是ES6有了做用域的概念但并非真的做用域,得配合let 或者 const声明。 undefined >
块级做用域作为函数做用域的扩展,使变量在更近的做用域内使用,增长了代码的可读性。
提高有变量提高和函数提高。变量提高(var 声明),是在变量声明位置的前面打印变量是undefined而不是not defined,就是该变量还没有初始化可是已经声明了;而函数提高(非函数表达式的具名函数)是在函数声明的位置以前就能够调用。
由于JavaScript中的做用域是词法分析是肯定的因此变量和函数执行代码以前已经声明好了,因此看上去就像是提高了。
同一个做用域中变量和函数声明同时出现了提高,那么函数声明的提高优先级更高,若是有多个变量提高那么后面声明的优先级更高,函数也是如此。
代码块(if、for)中的函数声明的提高并不带函数体,一样是undefined。
这个过重要了,我关注这个概念一年多,今天感受明白了,以前百度“JavaScript 闭包”,结果给的概念大部分是"经过闭包能够在函数外部能访问到函数内部的变量”,是这个吗?很明显不是,由于这个显然说的是闭包的用途,今天我想从根上刨一下,为何叫闭包?后来发现数学中的拓扑分支有闭包这个概念,难道。。。。拓扑中的闭包是这样定义的:集合和这个集合的导集的并集,集合都知道是啥,导集的意思是凝聚点构成的集合,凝聚点是啥呢,我理解是集合的边界点,也就是拓扑中闭包的概念是:集合和集合的边界点集合的并集。看MDN上关于闭包的定义:函数和函数所在词法环境的组合,若是把函数当成一个集合,把词法环境当成边界点,那么这不正是拓扑的闭包吗。因此说某个函数是闭包并不许确,闭包还有函数所处的词法做用域,只要建立一个函数均可以说是建立了闭包,尽管它不能被外部访问,能被外部访问的确定是闭包,由于这个利用了闭包特性。
从定义上分析闭包是函数和函数所处的词法做用域的组合,那么若是咱们在某个地调用了函数,那么也就是在这个地访问了对应的做用域,这个地能够是返回值是函数的地方,能够是你能调用这个函数的任意地方。