初探JavaScript——JS另类的作用域和声明提前

简介:

最近恰逢毕业季,千千万万的学生党开始步入社会,告别象牙塔似的学校生活。往往在人生的各个拐点的时候,情感丰富,感触颇深,各种对过去的美好的总 结,对未来的展望。与此同时,也让诸多的老“园”工看完这些小年轻的文章后感触良多,不禁也要写上几笔,所以就出来了很多类似“毕业两年小记”、“毕业五 年有感”……

可能就是某篇博文的一句话,某碗心灵鸡汤就拨动了你心里的那根尘封已久的弦,让你情不自禁的点了个赞,还忍不住的要在下面评论区留下自己此刻心潮澎湃的印记。

我今天不是来送鸡汤的,鸡汤虽好,可不要贪杯哦。

截止上一篇关于Javascript的博文《 初探JavaScript(三)——JS带我"碰壁"带我飞 》已经写了三篇。前三篇主要是从一名纯小白的角度,结合《Javascript DOM编程艺术》这本书记录下自己的一些总结和感悟。

大致的翻完《Javascript DOM编程艺术》后,感觉自己了解肯定还是太浅了,因为当时对于” 原型 ”、” 闭包 ”等这些完全没概念,我知道我缺的还很多。于是,我又伸出双手,接过另外一部经典之作《Javascript权威指南》,继续Javascript之行。

今天首先介绍下Javascript的函数作用域的概念,然后了解下什么是作用域和声明提前,最后通过一个例子剖析Javascript的作用域链。

1.变量的作用域

稍微有些编程背景的都知道,变量的作用域分为两种: 全局变量 和 局部变量 。

Javascript是一门 弱类型语言 。所有的变量声明都是通过var来接收,如

 
  1. var num = 1; 
  2. var str = “string”; 
  3. var flag = true

看似是一个非常省事的机制,但是也有让人头疼的时候,一些隐式的类型转换经常会把搞晕,这里不做展开,后面有时间可以单独开一篇详谈。先看看全局变量和局部变量:

 
  1. var g = "global"function f(){ var l = "local"

注意 : 1. 如果在函数f()中将去掉var声明,则变量l就会从局部变量升级为全局变量。

2. 局部变量的优先级高于同名的全局变量 。如果在函数f()中声明一个局部变量也为g,则全局变量就会被局部变量覆盖

2.作用域和声明提前

看到Javascript作用域这块,可以说颠覆了以前我对作用域的认识。类似Java和C等编程语言,在花括号“{}”内的代码都是有各自的作用域的,并且在这个范围以外,这些变量是不可见的,我们称这种作用域为 块级作用域

但是这完全不适用于Javascript,因为Javascript没有块级作用域,但是Javascript有 函数作用域 。函数作用域简言之就是:变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

对于“ 变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的 ”这句话的延伸理解:变量在声明之前就已经可用。我们称这种特性为声明提前,也就是函数里的所有变量都被“提前”至函数体的顶部。

下面我们看一个经典的陷阱案例:

 
  1. var v = "yoyo"; (function(){ console.log(v); var v = "check now"
  2.     console.log(v); 
  3. })(); 

对于第二次执行结果“check now”没有什么特别的,为什么第一次输出的不是“yoyo”而是“undefined”。

对于这个问题的解释就用到上面的那句话, 局部变量在整个函数体始终是有定义的 ,即在函数体内局部变量覆盖了同名全局变量,而且,程序只有在执行到var语句时,局部变量才会被真正赋值。所以,这时你大概会明白为什么是undefined了,因为此时还没有遇到var,即没有定义,等价于下面的形式:

 
  1. var v = "yoyo"; (function(){ var scope; console.log(v); var v = "check now"
  2.     console.log(v); 
  3. })(); 
  4.  
  5. 疑问 ? ? ? 
  6.  
  7. 将上面的代码稍稍修改为: 
  8.  
  9. var v = "yoyo"; (function(){ 
  10.     console.log(v); 
  11. })(); 

运行结果为:

相比于上面的代码只是少了一行添加一个局部变量v并赋值的语句,但是结果却是“yoyo”。

这里之所以输出“yoyo”,不能按照上面的定式思维。上面有句话叫“局部变量在整个函数体始终是有定义的”,但是这里没有局部变量的定义,所以按照下面要提到的作用域链会逐层向上寻找变量,最后找到了全局变量v,从而最后的输出是“yoyo”。

以上是我的个人理解,如果你对这两种情况有自己的理解,请在下方给出,望不吝指教。

3.作用域链

全局变量在程序中始终是有定义的,局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的。

每一段Javascript代码(全局代码或函数)都有一个与之相关联的作用域链,这个作用域链就是一个对象列表或链表。比如当 Javascript需要查找变量x的值时,它会从链中的第一个对象开始,如果该对象有一个名为x的属性,则直接使用,如果不存在名为x的属性,则会继续 向链上的下一个对象查找,如此递归下去直到找到。如果整个链上都找不到,则认为不存在x这个属性。举例:

 
  1. name="lwy"function t(){ var name="tlwy"function s(){ var name="slwy"; console.log(name); } function ss(){ console.log(name); } s();   ss();  
  2. }  
  3. t(); 



来源:51CTO

相关文章
|
15天前
|
存储 JavaScript 前端开发
解释 JavaScript 中的作用域和作用域链的概念。
【4月更文挑战第4天】JavaScript作用域定义了变量和函数的可见范围,静态决定于编码时。每个函数作为对象拥有`scope`属性,关联运行期上下文集合。执行上下文在函数执行时创建,定义执行环境,每次调用函数都会生成独特上下文。作用域链是按层级组织的作用域集合,自内向外查找变量。变量查找遵循从当前执行上下文到全局上下文的顺序,若找不到则抛出异常。
20 6
|
19天前
|
机器学习/深度学习 人工智能 JavaScript
js和JavaScript
js和JavaScript
21 4
|
1月前
|
前端开发 JavaScript 区块链
连接区块链节点的 JavaScript 库 web3.js
连接区块链节点的 JavaScript 库 web3.js
27 2
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JS的执行上下文、词法作用域和闭包(中)
深入理解JS的执行上下文、词法作用域和闭包(中)
|
18天前
|
JavaScript 前端开发
JavaScript生成的随机数随机字符串JS生成的随机数随机字符串
JavaScript生成的随机数随机字符串JS生成的随机数随机字符串
14 1
|
27天前
|
JavaScript 前端开发
js开发:请解释什么是模块化(modularization),并说明如何在JavaScript中实现模块化。
模块化将复杂系统拆分为松散耦合的模块,提高代码可读性、可维护性、可复用性和可扩展性。JavaScript模块化历经CommonJS(Node.js中常见,使用`require()`和`module.exports`)、AMD(RequireJS,异步加载,`define()`和`require()`)和ES6 Modules(官方标准,`import`和`export`)三个阶段。打包工具如Webpack、Rollup处理兼容性问题,使模块化代码能在各种环境中运行。
|
27天前
|
JavaScript 前端开发
js开发:请解释什么是作用域(scope),并说明全局作用域、局部作用域和块级作用域的区别。
JavaScript中的作用域规定了变量和函数的可见性与生命周期。全局作用域适用于整个脚本,变量可通过全局对象访问,可能导致命名冲突和内存占用。局部作用域限于函数内部,每次调用创建新作用域,执行完毕后销毁。ES6引入的块级作用域通过`let`和`const`实现,变量仅在其代码块内有效,并有暂时性死区。作用域机制有助于代码组织和变量管理。
23 1
|
28天前
|
JavaScript 前端开发
js开发:请解释this关键字在JavaScript中的用法。
JavaScript中的`this`关键字根据执行上下文指向不同对象:全局作用域中指向全局对象(如`window`),普通函数中默认指向全局对象,但作为对象方法时指向该对象。在构造函数中,`this`指向新实例。箭头函数不绑定`this`,而是继承上下文的`this`值。可通过`call`、`apply`、`bind`方法显式改变`this`指向。
10 2
|
1月前
|
JavaScript 前端开发
JS作用域与作用域链
JS作用域与作用域链
|
1月前
|
自然语言处理 JavaScript 前端开发
深入探索 JS 的提升机制、函数与块作用域以及函数表达式和声明(下)
深入探索 JS 的提升机制、函数与块作用域以及函数表达式和声明(下)