在JavaScript最初的学习中,搞清楚变量的声明与赋值对未来的学习是很有帮助的,如果将二者混淆将对后面知识的学习产生困扰。
变量的声明
变量
在了解变量声明之前,我们先认识一下变量。这里的变量和数学中的x,y代表的含义是相同的,它们同时具有以下几个相同的功能。
- 假装一个未知的数
- 存储一个值
- 参与运算和编辑
所以,将具有这些功能的一个未知数称为变量。
关键字与保留字
对于数学中的变量来说,是我们人为定义的,因此我们知道它的上述三个功能,而计算机只能认识我们人为编写进入的基础单词,这些单词就像计算机的系统一样被计算机所认可,它们被称作关键字和保留字。
声明的目的
关键字和保留字之所以可以在计算机上被识别是因为开发人员在开发语言时进行了声明。而我们所定义的变量却没有,因此当我们在写入代码出现未声明的变量时就会报错,is not defined,可以理解为计算机在说我不认识它,而我们之所以要声明一个变量,就是要让计算机认识它。
如何声明变量
了解完为什么声明变量后,就开始声明一个变量,声明一个普通变量在JavaScript中只有一种方式,那就是使用var进行声明:后面声明函数这个复杂变量时不会用到var关键字。
var xxx; //var是声明要用的关键字,xxx是我自己随意起的变量名
console.log(xxx); //undefined
我们未声明变量时计算机会说我不认识他而报错显示 is not defined,当我们声明过后,在控制台打印这个值就不会报错,而是显示undefined。undefined是JavaScript的一种基本数据类型,简单的来说它所表达的意思有两步:
- 这里有一个计算机认识的变量(声明过)
- 在这个变量中本应该存储有一个值,但是现在啥没有。
变量的赋值
当计算机输出undefined时他就在像你传达以上两个信息,而让计算机拿到变量中的值,就称为变量的赋值,又称为变量的初始化。
赋值符号 =
变量的赋值是通过赋值符号 “ = ” 来进行的,它表示将右侧的东西赋值给左侧。这里要与数学上的等号进行区分,数学上的等号表示左右两边的数据或变量在数值上是相等的。而JavaScript中的 = 是赋值的意思,是将等号右边的内容完全覆盖写入等号左边。
可以理解为左边有一个盒子,右边是一个东西,赋值就是将右边这个东西完完整整的塞到左边的盒子中,这样左边的空盒子就有了右边数据的值。
var num ; //声明一个num变量让计算机认识
console.log(num); //输出为undefined
num = 10; // 将右边的10赋值给左边的num,此时num就变为了10
console.log(num); //此时输出的就是num盒子里装的10
在赋值过程中有一个容易被忽略的点在于,完全覆盖式的写入包括了数据的类型。例如当左边变量中存储的是一个number数据类型时,将一个布尔值赋值给它,则这个变量的类型就会变为布尔值而不在是number数据类型。如果你赋值给这个变量一个函数,效果也是一样的,这个变量就会变成函数。
var num = 10;
console.log(num); //10
console.log(typeof num); //number数据类型
function fn(){};
num = fn; //将函数fn赋值给num
console.log(typeof num); //此时num的类型就变为函数,输出function
console.log(num); //f fn(){}
总结:
变量的声明使用var关键字进行声明;变量的赋值使用=进行赋值;但是大部分时候我们都会声明和赋值同步进行将他们直接写到一起,这样节省空间和代码量,而且在代码中我们最好是给变量一个初始值,以避免它的值是undefined影响后面的运算。
var num = 10; // 既声明了num这个变量,又将10赋值给它
计算机认识变量的方式——预解析
前面提到了声明一个变量就是让计算机认识它,但是计算机在认识变量时并不遵循正常的从上而下的顺序,而是在执行所有代码之前,就会将代码中所有声明的变量都解析一遍,也可以理解为在执行代码之前计算机要先看看你写的这一堆代码里有哪些是我不认识的东西,这个过程就叫做预解析。
预解析要注意以下几个点:
-
是在所有代码开始执行前
-
是所有代码中的变量,例如
if(0){var num = 10}; //如果条件允许,定义一个赋值为10的num变量
console.log(num); //undefined
上面这个例子中,按照正常的理解0是false所以不会执行后面的代码,因此不会声明num这个变量也不会赋值这个变量,在接下来打印num时应该报错,但事实上并没有报错而是输出了undefined,这表明num已经被声明但是没有被赋值。
因此类似于这些按照正常文本流不会被执行的代码中所声明的变量在预解析中也会被声明,类似的还有函数 return 后面的声明以及循环 break 后面的声明。
在预解析过程中一定要理解预解析的目的:是计算机为了查看有哪些我不认识的东西,因此它只查找它不认识的变量,和它在什么位置,将要执行什么代码没有任何的关系。
- 预解析过的声明代码在执行时会跳过,不会在重复执行。
- 声明函数的优先级要高于普通变量
对于初学者来说,起变量名时往往不能语义化变量,前面起的变量没记住,造成变量名和函数名冲突,由于在声明中函数的名称优先级要高于变量,因此当变量名冲突时会造成代码混乱,看下面的例子。
console.log(num); //f num(){return 10;}
var num = 20;
console.log(num); //20
function num(){
return 10;
};
console.log(num()); //报错:num is not a function
这个代码的意思很明显,想先输出一个变量20,在输出一个函数的返回值10。但是最后报错了,分析一下这段代码的执行过程。
- 先预解析,var num; 得到一个num变量。
- 再预解析,function num(),得到一个num函数。
- 开始执行代码,由于函数此时未return,所以输出undefined。
- 下一步var num已经执行过了,此处不重复执行。
- 但是 num = 10这个赋值操作要执行,所以现在num的数值是10,数据类型变为了number。
- 声明函数执行过,不再执行。
- 给这个函数一个结果,return10。
- 打印num()这个函数,此时由第5步可知num是个number类型,不是函数,所以会报错num不是个函数(function)。
因此在写代码过程中,一定要注意变量名不能与函数名重名,如果像上面一样明明定义了函数,却说不是一个函数,就要注意上面是否有重名的变量了。
- 注意函数声明与赋值的区别
function fn(){
num = 10;
return num;
}
fn();
console.log(num); //报错:10
想要理解这个例子就要优先理解作用域这里先说,下一节会详细讲。就是当赋值给一个变量时先从自己作用域找,找到就用自己的,找不到就一直向上找到全局,如果全局没有的话就自己声明一个全局变量。
而这个例子就是错误的把num = 10这个赋值操作当成了声明操作,认为自己的函数作用域里有num这个变量,所以不会生成全局变量,从而输出num时会报错。
然而num = 10只是一个赋值操作,它代表不了声明,没有var,所以会生成全局变量输出结果10.这里一定要分清赋值和声明的区别。