一个网站做多少个关键词比较好,纯flash网站欣赏,北京网站建设价格,酒店 深圳 网站制作一. 作用域
对于多数编程语言#xff0c;最基本的功能就是能够存储变量当中的值、并且允许我们对这个变量的值进行访问和修改。那么有了变量之后#xff0c;应该把它放在哪里、程序如何找到它们#xff1f;是否需要提前约定好一套存储变量、访问变量的规则#xff1f;答案… 一. 作用域
对于多数编程语言最基本的功能就是能够存储变量当中的值、并且允许我们对这个变量的值进行访问和修改。那么有了变量之后应该把它放在哪里、程序如何找到它们是否需要提前约定好一套存储变量、访问变量的规则答案是肯定的这套规则就是作用域。
说到作用域那就不得不先说一说编译原理由于编译原理是一个比较底层的内容这里只简单介绍后面会有一篇文章专门介绍 JS 编译原理。
JavaScript 引擎进行编译的步骤和传统的编译语言非常相似在传统的编译语言中程序中的代码在执行之后会经历三个步骤词法分析、语法分析、代码生成 词法分析这个阶段会将源代码拆成最小的、不可再分的词法单元token。比如代码 var name hello通常会被分解成 var 、name、、hello、; 这五个词法单元。代码中的空格在 JavaScript 中是被直接忽略的。 语法分析这个过程是将上一步生成的 token 数据根据语法规则转为 AST。如果源码符合语法规则这一步就会顺利完成。如果源码存在语法错误这一步就会终止并抛出一个“语法错误”。 代码生成这一步就是将 AST 转化为可执行代码简单来说就是将 var name hello; 的 AST 转化为一组机器指令用来创建一个 name 变量需要给 name 分配内存并将一个值储存在 name 中。
比起那些编译过程只有三个步骤的语言的编译器JavaScript 引擎要复杂的多这里不再细说。总之任何 JavaScript 代码片段在执行前都要进行编译因此在 JS 引擎眼里var name hello; 语句包含了两个声明 var name 编译时处理 name hello 运行时处理
你可能会问JS 不是不存在编译阶段的“动态语言”吗事实上JS 也是有编译阶段的它和传统语言的区别在于JS 不会早早地把编译工作做完而是一边编译一边执行。简单来说所有的 JS 代码片段在执行之前都会被编译只是这个编译的过程非常短暂可能就只有几微妙、或者更短的时间紧接着这段代码就会被执行。
在编译阶段和执行阶段阶段的过程如下 编译阶段 编译器会找遍当前作用域看看是不是已经有一个叫 name 的变量。如果有那么就忽略 var name 这个声明继续编译下去如果没有则在当前作用域里新增一个 name。然后编译器会为引擎生成运行时所需要的代码程序就进入了执行阶段。 执行阶段 JS 引擎在执行代码的时候仍然会查找当前作用域看看是不是有一个叫 name 的变量。如果能找到就给它赋值。如果找不到就会从当前作用域里向上层作用域逐级查找。如果最终仍然找不到 name 变量引擎就会抛出一个异常。
这里JS 引擎的查找过程就是作用域链作用域指的是变量能够被访问到的范围。 在 JavaScript 中作用域也分为好几种ES6 之前只有全局作用域和函数作用域两种。ES6 出现之后又新增了块级作用域下面这来看看这几个概念。
二. 全局作用域
在编程语言中变量一般会分为全局变量和局部变量。在 JavaScript 中全局变量是挂载在 window 对象下的变量所以在网页中的任何位置都可以使用并且访问到这个全局变量。下面来看一下全局作用域
var globalName global;
function getName() { console.log(globalName) // globalvar name innerconsole.log(name) // inner
}
getName();
console.log(name);
console.log(globalName); //global可以看到globalName 变量在任何地方都是可以被访问到的所以它就是全局变量。而在 getName 函数中作为局部变量的 name 变量是不具备这种能力的。
在 JavaScript 中所有没有经过定义而直接被赋值的变量默认就是一个全局变量比如下面代码中 setName 函数里面的 vName
function setName(){ vName setName;
}
setName();
console.log(vName); // setName
console.log(window.vName) // setName可以发现全局变量是拥有全局的作用域无论在何处都可以使用它在浏览器控制台输入 window.vName 时就可以访问到 window 上的全局变量。当然全局作用域有相应的缺点当定义很多全局变量时可能会引起变量命名的冲突所以在定义变量时应该注意作用域的问题。
三. 函数作用域
在 JavaScript 中函数中定义的变量叫作函数变量这种变量只能在函数内部才能访问到所以它的作用域也就是函数的内部称为函数作用域
function getName () {var name inner;console.log(name); //inner
}
getName();
console.log(name);可以看到name 变量是在 getName 函数中进行定义的所以 name 是一个局部的变量它的作用域就在 getName 函数里也称作函数作用域。
除了这个函数内部其他地方都是不能访问到它的。同时当这个函数被执行完之后这个局部变量也相应会被销毁。所以会看到在 getName 函数外面的 name 是访问不到的。
四. 块级作用域
ES6 中新增了块级作用域最直接的表现就是新增的 let 和 const 关键词使用这两个关键词定义的变量只能在块级作用域中被访问有“暂时性死区”的特点也就是说这个变量在定义之前是不能被使用的。
说到暂时性死区还要从“变量提升”说起来看下面代码
function foo() { console.log(bar) var bar 3
}
foo()上面代码会输出undefined原因是变量 bar 在函数内进行了提升。相当于
function foo() { var bar console.log(bar) bar 3
}
foo()但在使用 let 声明时会报错
function foo() { console.log(bar) let bar 3
}
foo() // Uncaught ReferenceError: bar is not defined。使用 let 或 const 声明变量会针对这个变量形成一个封闭的块级作用域在这个块级作用域当中如果在声明变量前访问该变量就会报 referenceError 错误如果在声明变量后访问则可以正常获取变量值
function foo() { let bar 3 console.log(bar)
}
foo()这段代码正常输出 3。因此在相应花括号形成的作用域中存在一个“死区”起始于函数开头终止于相关变量声明的一行。在这个范围内无法访问 let 或 const 声明的变量。
说完暂时性死区下面来看看块级作用域。在 JavaScript 编码过程中 if 语句及 for 语句后面 {} 这里面所包括的就是块级作用域
console.log(a) //a is not defined
if(true){let a 123console.log(a) // 123
}
console.log(a) //a is not defined可以看到变量 a 是在 if 语句{} 中由 let 关键词进行定义的变量所以它的作用域是 if 语句括号中的那部分而在外面进行访问 a 变量是会报错的因为这里不是它的作用域。所以在 if 代码块的前后输出 a 这个变量的结果控制台会显示 a 并没有定义。