在全局作用域中声明变量加 var 关键字和不加 var ,js 引擎都会将这个变量声明为全局变量,在实际代码运行时,两种声明方式的变量的行为也几乎是一致的。但是在全局作用域下是否声明一个变量的时候加 var 和不加 var,js 引擎具体执行了哪些操作呢,其效果又是否完全一致?

首先我们看在一个函数体内(局部作用域)声明变量,如下:

  1. // 变量声明不加 var
  2. function foo (a) {
  3. console.log(a + b) // b is not defined
  4. b = a
  5. }
  6.  
  7. foo(2)

【分析】执行 foo(2) 的时候,我们具体看 foo 函数,首先打印了 a + b 的值,然后声明了一个 b 变量(没有使用var关键字),并将传入的 a 赋值给 b,因为 js 引擎按照代码顺序编译和执行代码,因此在打印 a + b 的时候,在任何作用域中都是无法找到 b 变量的。

在执行 foo(2) 表达式的时候 js 引擎具体的的操作过程如下:

  1. 在当前作用域中查找名为 foo 的函数(RHS)
  2. 进入 foo函数体,首先 JS 引擎在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定变量的作用域(js引擎读取一段js代码,首先执行预解析,就是逐行读取js代码,寻找全局变量和全局函数,遇到全局变量,把变量的值变为undefind,存在内存中,遇到全局函数,直接存在内存中,这个过程如果发现语法错误,预解析终止)。因此第一步搜集变量,发现在函数作用域中这里只有作为参数的局部变量 a,提升到作用域顶部
  3. 将 2 赋值给参数变量 a(a = 2, LHS)
  4. 查找 console 对象(RHS),发现是内置函数,在 console 对象下查找 log 函数(RHS)
  5. 在当前作用域中查找变量 a,并获取 a 的值为(a = 2, RHS)
  6. 在当前作用域中查找变量 b,未找到该变量
  7. 将 a 和 b 的查找结果传入 console.log() 函数,打印结果( b 未定义,抛出错误: b is not defined)
  8. 继续执行 b = a。首先获取变量 a 的值(a = 2, LHS), 然后在当前作用域中查找变量 b(RHS),未找到,到上一层作用域(全局作用域)中查找(RHS),未找到,
  9. 在全局作用作用域中创建一个名称为 b 的变量,并将其返回给引擎(注意:严格模式下禁止自动或隐式地创建全局变量)
  10. 将 a 的值(2)赋值给全局变量 b( b = a, LHS)

再看第二个例子

  1. // 变量声明加 var
  2. function foo (a) {
  3. console.log(b) // undefined
  4. console.log(a + b) // NaN
  5. var b = a
  6. }
  7.  
  8. foo(2)

【分析】执行 foo(2) 的时候,我们看 js 引擎具体做了哪些操作?

  1. 在当前作用域中查找名为 foo 的函数(RHS)
  2. 进入 foo 函数体,搜集变量,发现声明了局部变量 a 和 b,因此将 a 和 b 提升到函数作用域的顶部(此时 b 的值为 undefined)
  3. 参数赋值,将 2 赋值给变量 a(a = 2, LHS)
  4. 查找 console 对象(RHS),发现是内置函数,在 console 对象下查找 log 函数(RHS)
  5. 在当前作用域中查找变量 b(RHS),发现已经声明,但是值为 undefined ,传给log()函数,执行打印,输出结果(b is not defined)
  6. 重复第4步 RHS 查找 console.log() 函数,查找变量 a 的值(a = 2, RHS);查找变量b的值(b = undefined, RHS),将 a 和 b 的值传入 console.log(),执行运算,输出结果(2 + undefined,结果 NaN)

通过上述在函数体内声明变量的例子,已经可以看出来 js 引擎在处理这两种情况的区别,在全局作用域中的也是如此,而且理解起来更为简单。

看两个例子:

  1. console.log(a) // undefined
  2. a = 3

声明变量 a 的时候没有加 var,因此 js 引擎默认将变量 a 声明为全局变量(值为 undefined)并提升到作用域顶部(为什么在console.log() 中可以访问到 a),但是此时的赋值操作需要等到 console.log() 方法执行完之后才会执行,因此在 console.log(a) 打印的结果会是 undefined

  1. console.log(a) // undefined
  2. var a = 3

这里的输出结果仍旧是 undefined,但是和上面的例子不同的是, js 引擎并没有主动的去创建变量 a,而是直接将变量 a 搜集到全局变量的集合中,并将 a 提升到作用域顶部。

【延伸】全局变量是 window 的属性(浏览器环境下),因此声明全局变量时是否加 var,可以通过 Object 提供的 getOwnPropertyDescriptot(object, propertyName) 来进行比较是否存在不同:

  1. var a = 1
  2. b = 2
  3. console.log(Object.getOwnPropertyDescriptor(window, a))
  4. // { value: 1, writable: true, enumerable: true, configurable: false }
  5.  
  6. console.log(Object.getOwnPropertyDescriptor(window, b))
  7. // { value: 2, writable: true, enumerable: true, configurable: true

通过结果的比较可以发现,未使用 var 声明的全局变量的configurable 属性是 true,也就是说,未通过 var 声明的变量是可以删除的,如下:

  1. delete a
  2. // false
  3.  
  4. delete b
  5. // true

*关于 Object.getOwnPropertyDescriptor(object, propertyName) (propertyName 需要传入字符串形式的属性名)

【参考:深入理解javascript对象系列第三篇——神秘的属性描述符

用于查询一个属性的描述符,并以对象的形式返回,返回结果的属性如下:

  • Configurable:是否可以使用 delete 删除属性,以及是否可以修改属性描述符的特性,默认值为 true
  • Enumerable:是否出现在对象的属性枚举中,比如是否可以通过 for-in 循环返回该属性,默认值为 true
  • Writable:是否可以修改属性的值,默认值为 true
  • Value:属性的数据值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为 undefined

【补充知识点】

LHS 和 RHS

全局变量和局部变量

作用域、作用域链、预解析

更多变量声明是否加 var 的区别

详解变量声明加 var 和不加 var 的区别的更多相关文章

  1. javascript 中加’var‘和不加'var'的区别,你真的懂吗?

    没看之前千万别说我是标题党,这个问题真的有好多淫都不懂!!! 大家都看了很多文章,都说避免隐式声明全局变量,就是说声明变量前必须加'var',那加了'var'和不加'var'到底有啥区别呢? 先来看一 ...

  2. javascript中加var和不加var的区别

    Javascript是遵循ECMAScript标准下的一个产物,自然ECMAScript的标准其要遵循. 先来看下var关键字的定义和用法 var 语句用于声明变量. JavaScript 变量的创建 ...

  3. js中加“var”和不加“var”的区别

    JavaScript 拥有动态类型.这意味着相同的变量可用作不同的类型: var x // x 为 undefined var x = 6; // x 为数字 var x = "Bill&q ...

  4. js中加“var”和不加“var”的区别,看完觉得这么多年js白学了

    Javascript声明变量的时候,虽然用var关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种方式还是有区别的.可以正常运行的代码并不代表是合适的代码. var num = 1: 是在 ...

  5. C++中创建对象的时候加括号和不加括号的区别

    c++创建对象的语法有----- 1 在栈上创建 MyClass a; 2 在堆上创建加括号 MyClass *a= new MyClass(); 3 不加括号 MyClass *a = new My ...

  6. Java中主类中定义方法加static和不加static的区别

     Java中主类中定义方法加static和不加static的区别(前者可以省略类名直接在主方法调用(类名.方法),后者必须先实例化后用实例调用) 知识点:1.Getter and Setter 的应用 ...

  7. onclick时间加return和不加return的区别

    JAVASCRIPT在事件中调用函数时用return返回值实际上是对window.event.returnvalue进行设置. 而该值决定了当前操作是否继续.当返回的是true时,将继续操作.当返回是 ...

  8. C++中创建对象的时候加括号和不加括号的区别(转)

    c++创建对象的语法有----- 1 在栈上创建 MyClass a; 2 在堆上创建加括号 MyClass *a= new MyClass(); 3 不加括号 MyClass *a = new My ...

  9. 【转】new对象时,类名后加括号和不加括号的区别

    请看测试代码: #include <iostream> using namespace std; // 空类 class empty { }; // 一个默认构造函数,一个自定义构造函数 ...

随机推荐

  1. 201521123095 《Java程序设计》第2周学习总结

    1. 本周学习总结 学会了使用码云管理代码,使用eclipse关联jdk源代码: 还学习了Java语言中各种数据类型以及运算符. 2. 书面作业 1.使用Eclipse关联jdk源代码,并查看Stri ...

  2. Java课程设计—学生成绩管理系统(201521123005 杨雪莹)

    一.团队课程设计博客链接 学生成绩管理系统 二.个人负责模块或任务说明 学生成绩录入 显示所有学生信息 显示各科平均成绩 显示学生成绩(按降序排序) 三.自己的代码提交记录截图 四.自己负责模块或任务 ...

  3. 201521123036 《Java程序设计》第14周学习总结

    本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 书面作业 MySQL数据库基本操作 1.1 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) ...

  4. CSS3滤镜(filter--CSS3技术

    filter 属性定义了元素(通常是<img>)的可视效果,例如图片的模糊.饱和度.灰度等……个人感觉功能很强大 1.filter的语法 filter: none | blur() | b ...

  5. editplus的设置

    1, 下载editplus3软件并且进行安装, 我这里是 EditPlus_3.4.1.1123_XiaZaiBa 2, 进行相关设置: 工具-->参数设置-->常规--勾选 (把Edit ...

  6. markdown编辑器的学习

    markdown编辑器的学习 1 标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 2列表 无序列表 1 2 3 4 有序列表 1 2 3 4 3引用 这里是引用,哈哈我也不知道到我引 ...

  7. Android 之JDBC

    JDBC(Java DataBase Connectivity)是使用 Java 存取数据库系统的标准解决方案,它将不同数据库间各自差异API与标准 SQL语句分开看待,实现数据库无关的 Java操作 ...

  8. JS--微信浏览器复制到剪贴板实现

    由于太忙很久没写博客了,如有错误遗漏,请指出,感谢! 首先这里要注意,是微信浏览器下的解决方案,其他浏览器请自行测试. 先说复制到剪贴板主要有什么使用场景: 优惠券优惠码,需要用户复制 淘宝商品,需要 ...

  9. eclipse中搜狗输入法中文状态下输出的全是英文

    在eclipse中搜狗输入法变成了如图这样 在中文状态下,提示的全是中文. 查询到的解决方案: 快捷键ctrl+shift+E关闭搜狗智能英文.然而与eclipse中 Ctrl+shift+E 快捷键 ...

  10. 【黑马18期Java毕业生】黑马程序员Java全套资料+视频+工具

        Java学习路线图引言:        黑马程序员:深知广大爱好Java的人学习是多么困难,没视频没资源,上网花钱还老被骗. 为此我们历时一个月整理这套Java学习路线图,不管你是不懂电脑的小 ...