土匪:天王盖地虎。
杨子荣:宝塔镇河妖。
电脑:
var computerWord = '你是二百五'
console.log(computerWord)
我(开发者):
computerWord = '电脑才是二百五'
console.log(computerWord)
土匪和杨子荣的对话,是土匪之间特有的黑话,没接触过的人很难理解其实际含义。而对于普通人而言,我(开发者)和电脑之间的JavaScript脚本代码也是一种黑话,一种开发者和电脑之间独有的对话,不理解的人也很难理解其含义。
入行不久的土匪只能听懂一部分黑话,而入行不久的前端工程师也只接触过一部分的JavaScript语法;就算是入行很久的土匪也有可能会听不出来一些黑话,当然也有可能忘记了,而入行久的前端工程师也可能不认识一些前端 “黑话” ,也可能是忘记了。
编程语言是专业的编程人员与机器对话的一种方式,也是一种“黑话”,虽然JavaScript中绝大多数的“黑话”对于前端工程师而言是轻车熟路,但是也不免会有一些遗漏,从这篇文章开始我将逐字逐句地为大家重新认识JavaScript中的"黑话"(系列文章持续更新中)。
从var开始复习黑话
var是绝大多数JavaScript开发者(从此处开始不再以前端工程师定义JavaScript的使用人员)最开始常用的定义变量的保留关键字,现在可能let和const更被开发者青睐。
其实,从上面的一句话就可以总结出var的2个特点:
1. var是一个保留关键字;
2. var 被用来定义变量;
首先作为关键字,那么意味着一般情况下我们不能拿它作为变量名、函数名等等,最好不要把它用在除了定义变量之外的用途上;其次它是被用来定义变量,那么它一定有特殊的用法。
提前总结:JavaScript中的保留关键字都是有着特殊用法的,他们都有着特殊的语法,同时不能被用作对应语法之外的用途(简单来说,不要乱用保留关键字)。
接着上面的说,既然var被用来定义变量,同时又有着自己的特定语法,那么我们先来看看它的语法:
var varname1 [= value1] [, varname2 [= value2] ... [, varnameN [= valueN]]];
上面的语法来自于权威的MDN。我来解释下这句语法的意思,其实这段伪代码指出了通过var定义变量的几种形式:
1. var varname1 // 定义变量不赋值,默认会用一个初始值,值为undefined
2. var varname1 = 'value1' // 定义变量的同时赋予初始值
3. var varname1, varname2 // 定义多个变量不赋予初始值
4. var varname1 = 'value1', varname2 = 'value2' // 定义多个(1-N个)变量,赋予初始值
出了语法之外,var还有一些值得开发者注意的点,来看下面的例子:
// example 1
function test() {
var beefPrice = 35
porkPrice = 25
}
test()
console.log(porkPrice)
console.log(beefPrice)
请读者先在心里默默想一下上面的输出,再看下面我给出的结果. 结果是:
25
VM328:7 Uncaught ReferenceError: beefPrice is not defined at <anonymous>:7:
结果输出了猪肉的价格(porkPrice)25,牛肉的价格(beefPrice)却报错。因为在这里,porkPrice定义为了一个全局变量(实际上是全局对象的一个属性),而通过关键字var定义的beefPrice是一个局部变量,无法被打印出来。
再来看一个例子:
// example 2
var porkPrice = 25
console.log(porkPrice) // 25
console.log(beefPrice) // undefined
var beefPrice = 35
可能很多读者都看出来了,这里体现了var具有提升变量的特性,实际上上面的代码可能等同于这样:
var porkPrice = 25
var beefPrice
console.log(porkPrice) // 25
console.log(beefPrice) // undefined
beefPrice = 35
前面说道,变量未被赋予初始值的时候,默认被赋值为undefined,所以这里牛肉的价格(beefPrice)是没有报错,但是还没给定价格。那我们用关键词let或者const来替代var,又会是怎样呢?
// example 3
var porkPrice = 25
console.log(porkPrice) // 25
console.log(beefPrice) // VM525:3 Uncaught ReferenceError: beefPrice is not defined
let beefPrice = 35
很显然这个时候变量没有得到提升,关键词let和const不会提升变量。
我们再回过头来看第一个例子:
// example 1
function test() {
var beefPrice = 35
porkPrice = 25
}
test()
console.log(porkPrice)
console.log(beefPrice)
上面我解释说,porkPrice被定义为了一个全局变量(实际上是全局对象的一个属性)。其实如果把这段代码放到浏览器控制台中运行,就能看到,porkPrice也被定义为了全局对象window的一个属性,window.porkPrice的值同样为25。
这里有两个概念,全局变量和全局对象的属性。实际上,在全局环境中通过var定义的变量,会自动成为全局对象window的属性,这个时候两者是等价的,当然不通过关键词直接定义的变量在局部环境也会成为全局变和全局对象window的属性。
但是,关键字let和const不具有这一特性。
var myname = 'zhangsan'
let yourname = 'lisi'
const hisname = 'wangwu'
console.log(window.myname) // zhangsan
console.log(window.yourname) // undefined
console.log(window.hisname) // undefined
很显然window对象上没有属性yourname和hisname,通过let和const定义的变量不会被挂载到全局对象上。
上面既然提到了let和const,那我也姑且说一说。let和const在严格环境下都是保留关键字,其中let只在严格环境被当成保留关键字。const和let都可以用来定义变量,语法与var相同,但是const定义的变量值不能被改变(变量为引用类型时,保存的是变量的引用地址,所以这个时候值是可以改变的,但是引用地址不能改变)。
let和const都不能使变量变量提升,通过let和const定义的变量具有局部作用域(在全局使用的时候,局部作用域指的就是全局作用域)。通过下面的例子可以看到:
function varTest() {
var x = 1;
{
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}
可以看到,通过var定义的变量x,在整个函数作用域内是同一个变量,但是通过let定义的变量x,在函数中不是同一个变量。
写作不易,尤其是工作繁忙的时候。下一期,我将介绍关键字new和关键字function,带领大家认识函数和对象中的种种迷惑。