正确声明 javascript 的变量
目录
var
是 javascript 中用来声明变量的关键字。使用它所声明的变量,与在其它编程语言的声明的变量有不同的表现行为,即作用域表现不同。没有搞清楚它之前,很容易在你的 javascript 中为程序留下 bug。
看完本文之后,你将了解 var
本身的问题,以及如何避免。
有人说,javascript 是一种定义非常糟糕、混乱的编程语言。如果你是其它像 java、C++ 这些语言的开发者,可能会认同这一观点。如果编程的初学者,可能不会有任何体会,反而会觉得这是对 javascript 的无端指责。在软件测试与 debug 过程中,发现往往对变量数据类型、作用域的宽松,反而会为软件本身带来更多潜在的问题。严格清晰的定义反而有助软件程序行为的确定性。
javascript 的作者在设计 javascript 时,并没有想象到 javascript 会成为统治前端开发的第一语言。当时仅仅是用于为浏览器添加扩展能力的脚本语言,如今却服务于前端网面开发、服务后端多种应用场景。因此,在当时看似够用而缺少的语言特性,如今反而变成影响 javascript 更大规模使用。技术发展永远不会停止前进的脚步。javascript本身非常开放,并且js 最主要的运行时环境–各大浏览器js 运行时的同步跟进,使得为它升级语言规范成为可能。它也 有望成为一门最多用户参与规范定制、功能设计、最开放的语言。
说了这么多,有悟想说的是,因为历史原因,一些看似不合理的东西一直被遗留下来,而后来人会不断的改进。
了解了这些背景,以后对 javascript 语言级别的改进就不用惊讶了,因为有些改进在其它的编程语言中已经自然地存在了很长时间。
本文为什么要谈这个 var
关键字?因为这关键字涉及变量定义,是编程中不可缺少的步骤,有悟觉得非常必要(同时,有悟也曾经被困扰过):
- 对于新手,因为没有太多编程经验,会被一些看似相似的东西混淆
- 对于其它语言的经验老手,就是了解 javascript 的关键字特性,然后利用
理清这个关键字与其它相似功能关键字的区别,有助于编写出质量更高的 javascript 程序。
javascript 的简易运行环境 #
- 使用 浏览器(chrome、firefox)的 devtools,使用快捷
F12
打开。 - nodejs 命令行,安装后输入 node 就可以启动一个 REPL 交互式执行环境,或者
node xxx.js
执行 js 脚本。 - deno 命令行 js 执行器,安装后输入 deno 可以启动一个 REPL 交互式执行环境,或者
deno run xxx.js
执行 js 脚本。
变量作用域的概念 #
在定义变量后,通过名称或者引用可以访问到该变量的值,这个可被访问到的范围就是变量的作用域。从这里又可延伸出全局变量(当前的执行上下文)、本地变量(也叫局部变量,被限制在函数体或者语句块中使用)。
变量声明与问题 #
var 的基本作用 #
➜ deno
Deno 1.13.0
exit using ctrl+d or close()
> var y = 1
undefined
> y
1
>
var
可以在编程过程中定义一个变量,这与在其它编程语言中是一致的,本身没什么好讲。
关键是使用 var
所声明出来的变量,会有这些特点:
- 使用
var
在 函数外部 声明的变量是全局变量 - 可随意重复定义
var v // 声明一个变量,但它此时可以不赋值,初始化为 undefined
{
v = 1 // 在一个块中对v 的设置会直接修改全局变量的值
console.log(v)
}
{
var v = "hello" // 重复定义无关系,因为被会覆盖掉
}
console.log(v)
function youwu_today() {
v = 2 // 修改全局变量
var v1 = 123456 // 声明一个作用域为 scope 函数的变量
console.log(v1)
}
youwu_today()
console.log(v)
console.log(v1) // v1 不在作用域中
// ^
// ReferenceError: v1 is not defined
看似非常方便的随时访问、修改全局变量的功能,却是长程序所害怕的,它会让程序变得更不好维护、行为预测。在某个的函数中被修改了会难以察觉,查错变得相当麻烦。
这些特点在某些编程语言中是被限制使用或者甚至不被支持的。因为它在大规模使用时非常容易产生问题。 当然在短程序中,这种用法可以减少很频繁的变量声明。
未声明的变量 #
这个标题怪怪的,未声明哪来的变量。
// 默认情况下,未声明变量的,可在 node 或者浏览器 dev tools 控制台中执行
var v
{
v = 1 // 在一个块中对v 的设置会直接修改全局变量的值
console.log(v)
}
function youwu_today() {
v = 2 // 修改全局变量
var v1 = 123456 // 声明一个 scope 函数范围内的变量
console.log(v1)
uv = "abcd" // strict 模式下会报, ReferenceError。无法通过 deno 执行 。
// ^
// ReferenceError: uv is not defined
}
youwu_today()
console.log(v)
// console.log(v1) // v1 不在作用域内
// ^
// ReferenceError: v1 is not defined
console.log(uv)
uv = "efg" // 在函数youwu_today中定义的变量可以修改
console.log(uv)
上面这段代码在使用
node xxx.js
可以执行,deno 为 strict 模式,无法通过。
没有使用 var
声明变量,更可怕,是一个全局变量。随便修改、随便用。使用这种方式定义的变量,要非常小心。
ES2015(ES6)声明变量的新方式 #
在 javascript 的 ES2015(ES6)规范中,为它新增了两种新的变量定义方式,const
和 let
。它们是对在 js 中声明变量的改进。
const
是声明一个不可变的变量,即常量,了解 java 编程的开发者都非常熟悉。
{
const c = "hello"
// c = "world" // 不能修改
// ^
// TypeError: Assignment to constant variable.
}
console.log(c) // 超出作用域,无法访问
// ^
// c is not defined
let
,它与 var
很接近,非常容易混淆。使用 let
声明的变量作用域为当前语句块,避免了那种莫名其妙冒出来的变量,也避免了被语句块的函数修改造成难以查错的尴尬。
// 作用域差异
{
// 在块中声明变量
var v = 1 // v 是全局变量
console.log(v)
let l = "hello" // let 变量仅在块中可用
console.log(l)
}
console.log(v)
console.log(l) // 超出 l 的作用域
// ^
// ReferenceError: l is not defined
// 重复定义的差异
{
var v = 1
var v = "world"
let l = 1
let l = "world" // 不能重复定义
// ^
// Identifier 'l' has already been declared
}
通过上面两段示例代码可见,let
让 js 更接近其它编程语言,更符合现代软件开发,而非脚本粘贴。
如果你的编程水平暂时理解不了这种差别时,请尽量使用let
来声明变量。
如何更好的声明变量 #
通过本文,至少要学习到以下几点,帮助编写质量更高的 JavaScript 程序。
宽松与严格 #
变量的可变性、可访问范围从宽松到严格的顺序是:
未声明的变量 -> var 变量 -> let 变量 -> const 变量
声明变量的姿势 #
javascript 提供了多种方式来声明变量,它们在某些场合下是可混合使用或者替换的,但为了编写高质量的程序,请尽量按照以下原则:
- 声明变量时,尽量确定变量的用途,即不允许修改的使用 const,限制作用域的使用 let
- 在不清楚该使用哪种方式来声明变量或者 无声明、var 声明、let 声明这三种方式都可以的情况下,直接采用 let 来声明变量。
即使全局变量能为你编程达到非常大的便利,也要节制的使用
编写本文时,参考了: var 描述 let JavaScript 中的 Var,Let 和 Const 有什么区别