本文是JavaScript基础教程中的第二章,在这一章中,你将学习JavaScript中的变量,学习如何定义变量、改变变量的值以及使用变量提高你的代码质量。
什么是变量?
通过上一章的学习我们知道,我们可以使用编程语言来命令浏览器做一些事情,比如显示文字。 但是你有没有想过,我们能否用编程语言来记录一些信息呢? 比如我们想要记录你的名字,或者你最喜欢的颜色,或者你最想去的地方,我们该怎么做呢?
为了实现这个目的,我们需要学习一个新的概念:变量。
变量就像是一个盒子,可以放任何东西进去,比如数字、文字、图片等等。
我们可以给每个盒子起一个名字,比如 name
、color
、place
等等。
这样我们就可以用这个名字来找到这个盒子里面放的东西了。
比如说,在你的书架里有很多书。你想要把你最喜欢的书放到一个盒子里,并且给这个盒子起一个名字叫“favorite”。你可以这样做:
var favorite = "《共产党宣言》"; // 把《共产党宣言》放到一个叫做 favorite 的盒子里
这样你就定义了一个叫做 favorite
的变量,并且给它指定了一个值:"共产党宣言"。
而这个过程就叫做“定义变量”或者“声明变量”。
需要注意的是,变量名必须以字母或下划线开头,并且不能有空格或特殊符号。
特殊符号
特殊符号是指那些不是字母或数字的符号,比如
!
、@
、#
、$
、%
、^
、&
、*
、(
、)
、-
、+
、=
、[
、]
、{
、}
、\
、|
、;
、'
、"
、,
、.
、/
、<
、>
、 ?
、 ~
等等。
下划线 _
不是特殊符号,所以可以用在变量名中。
现在你可以用 console.log()
来打印这个变量的值了。比如你可以这样写:
console.log(favorite); // 打印 《共产党宣言》
你看,电脑会自动把变量名换成它们指向的数据,然后打印出来。这样你就可以用代码来记录和使用一些信息了。
如何改变变量的值?
有时候,你可能会想要改变变量的值。比如说,你找到了一本更喜欢读的书,叫做《剩余价值理论》。你想要把“favorite”的盒子里面的书换成《剩余价值理论》。你可以这样做:
favorite = "《剩余价值理论》"; // 把 favorite 这个盒子里面的书换成《剩余价值理论》
这样你就改变了 favorite
这个变量的值。注意,改变变量的值时不需要再写 var
了,因为我们已经定义过这个变量了。
现在如果我们再打印这个变量的值,会发现它已经变成了《剩余价值理论》:
console.log(favorite); // 打印 《剩余价值理论》
怎么样,改变一个变量的值很简单吧?只要把新的数据放到原来的盒子里就行了!
但要注意,这个时候,旧的值就会被扔掉!所以,如果你想要保留旧的值,你需要把它放到另一个盒子里。
变量有什么用处?
为什么我们要用变量呢?直接用数据不就行了吗?
其实,使用变量有很多好处。例如,它可以让代码更简洁和清晰。
如果我们想要打印一句话:“我最喜欢的书是《剩余价值理论》”,我们可以直接写:
console.log("我最喜欢的书是《剩余价值理论》");
但是如果我们用了 favorite
这个变量,我们就可以写成:
console.log("我最喜欢的书是" + favorite);
看,这样写更简单明了。而且如果我们想要改变最喜欢的书,我们只需要改变一次 favorite
的值就行了,不需要改动其他地方。
另外,使用变量可以让代码更灵活和可扩展。
如果我们想要让电脑根据用户输入来打印不同的消息,我们可以调用一个叫做 input
的函数来获取用户输入,
并且用一个叫做 message
的变量来存储要打印的消息。
我们可以这样实现:
var message = "我最喜欢的书是" + input(); // 获取用户输入并存储在 message 中
console.log(message); // 打印 message
这样无论用户输入什么书名,电脑都能根据输入来打印相应的消息。
使用变量可以让代码更易于调试和维护。 比如说,如果我们发现代码中某些地方有错误或者需要修改,我们只需要找到相关的变量就行了,不需要在整个代码中搜索和替换数据。
总之,使用变量是编程中非常重要和常见的一种技巧。通过使用变量,我们可以让代码更简洁、清晰、灵活、可扩展、易于调试和维护。
变量类型
我们已经知道,变量可以指向任何东西,比如数字、文字、图片等等。
但是这些东西在电脑里面是有不同的类型的。比如数字是一种类型,文字就是另一种类型,图片又是另一种类型。
我们可以用一个叫做 typeof
的 运算符 来查看一个变量指向的数据是什么类型的。比如我们可以这样写:
var number = 10; // 定义一个叫做 number 的变量,指向数字 10
var string = "hello"; // 定义一个叫做 string 的变量,指向文字 "hello"
var image = document.getElementById("myImage"); // 定义一个叫做 image 的变量,指向页面上的一张图片
console.log(typeof number); // 打印 "number"
console.log(typeof string); // 打印 "string"
console.log(typeof image); // 打印 "object"
你看,电脑会告诉我们每个变量指向的数据是什么类型的。在javascript中,有以下几种基本的数据类型:
number
:数字,比如1, 2, 3.14等等。string
:文字(字符串),比如"hello", "world", "你好"等等。boolean
:布尔类型,表示真假,只有两个值:true
和false
。null
:空值,即没有任何数据。undefined
:未定义,表示没有给变量指定任何数据。symbol
:表示唯一的标识符,用于创建对象(object)的属性名(我们暂时还用不上)。
除了这些基本的数据类型外,还有一种特殊的数据类型:
object
:表示复杂的数据结构,可以包含多个属性和多个方法。比如数组、函数、日期、正则表达式等等都是对象。
我们可以用不同类型的数据来表示不同的信息和功能。在后面的章节中,我们将学习如何对不同类型的数据进行操作和运算。
* 声明变量的三种方式
我们已经知道,我们可以用 var
来定义一个变量,并且给它指定一个值。而在 JavaScript 中还有另外两种方式来定义变量:
let
和 const
。它们和 var
有什么区别呢?让我们来看看。
var
我们已经知道,我们可以用 var
来定义一个变量,并且给它指定一个值。比如:
var x = 10;
var y = "hello";
var z = true;
这样,我们就创造了三个变量,分别叫做 x
,y
和 z
,并且分别把数字 10,文字 "hello" 和布尔值 true 放进去。
但是,我们创造变量的时候,要注意一件事情:我们要记住在哪里可以看到我们创造的变量,也就是说,我们要注意变量的作用范围(scope)。
有一种变量是全世界都可以看到的,叫做 全局变量(global variable)。比如:
var x = 10;
这样,我们就在最外面的地方创造了一个变量 x
,并且把数字 10 放进去。这个变量是全世界都可以看到的,所以叫做 全局变量(global variable)。
如果我们在任何地方打印出 x
这个变量里面的东西,就会得到 10。
有一种变量是只有在函数里面才能看到的,叫做 函数级别的变量(function-level variable)。比如:
function foo() {
var x = 20;
}
这样,我们就在函数 foo 里面创造了一个变量 x
,并且把数字 20 放进去。这个变量只有在这个函数里面才能看到,所以叫做 函数级别的变量(function-level variable)。
如果我们在函数里面打印出 x
这个变量里面的东西,就会得到 20。
你看,虽然两个变量都叫做 x
,但是它们其实是不一样的。一个变量只有在函数里面才能看到,而另一个变量在任何地方都能看到。
这就是变量的作用范围(scope)的意思。如果我们不注意变量的作用范围,就可能会造成一些混乱和错误。
比如,如果我们想要在函数外面拿到函数里面的变量 y
:
function bar() {
var y = 30;
}
bar();
console.log(y);
那么电脑就会报错:y is not defined。因为我们不在函数里面了,所以电脑找不到这个变量 y
。
自然地,我们就无法获取到函数里面的变量 y
里面的东西。
所以,我们在使用变量的时候,要注意它们的作用范围,避免重复创造或者误用。
还有一种情况,就是当我们在一个代码块(也就是用花括号 {}
包起来的一段代码,比如if语句或者for循环)
里面创造了一个变量,它就会跑出来给外面看。比如:
if (true) {
var x = 10;
}
console.log(x);
这样,我们就在 if 语句里面创造了一个变量 x
,并且把数字 10 放进去。
这时,如果我们在外面打印出 x
这个变量里面的东西,就会得到 10!
但我们明明希望这个变量只在这个代码块里面才能看到(就像我们定义的函数里面的变量一样),而它却跑出来给外面看了。
当我们在代码块里面创造了一个变量时,它并不会被限制在这个代码块里面,而是可以被外面看到。 这样可能会造成一些混乱和冲突。
为了解决这些问题,ECMAScript 6(简称ES6)中引入了两种新的方式来创造变量:let
和 const
。它们可以让我们更好地控制变量的作用范围,避免一些错误和混乱。我们下一节会学习它们的用法和区别。
热知识
ECMAScript是一种标准化的脚本语言,由欧洲计算机制造商协会(ECMA)制定和维护。 它是一种通用的语言规范,可以被不同的平台和环境实现和执行。 而最常见的ECMAScript实现就是我们正在学习的JavaScript。
ECMAScript6(也称为ES6或ES2015)是ECMAScript的第六个版本,于2015年正式发布。
它是ECMAScript历史上最重要的一次更新,引入了许多新的特性和语法,使得ECMAScript更加现代化、简洁、灵活和强大。其中一些主要的新特性包括:
let
和 const
、箭头函数、模板字符串、解构赋值、类、模块等等,我们在以后的章节中会逐一介绍。
另外,在使用 var
声明变量时,你也许会发现:在声明一个变量之前你就可以使用它,而不会报错!这是由于 JavaScript 的变量提升机制导致的。
变量提升(hoisting)是指 JavaScript 引擎在执行代码之前,会把所有用 var
声明的变量名提升到当前作用域的最顶部,但不会提升它们的赋值语句。
这意味着,你可以在声明一个变量之前就使用它,而且还不会报错!但是它的值会是 undefined
(因为赋值还没有执行)。
例如,下面的代码:
console.log(x); // undefined
var x = 10;
console.log(x); // 10
实际上相当于:
var x; // 变量名被提升
console.log(x); // undefined
x = 10; // 赋值没有被提升
console.log(x); // 10
由于变量提升机制,我们不仅可以在声明一个变量之前就使用它,还可以在声明一个变量之后再声明它:
var x = 10;
var x = 20;
console.log(x); // 20
可以看到,第二次声明变量 x
并没有报错,而是把第一次声明的变量 x
覆盖掉了。
但变量提升会导致一些问题,比如使得变量的值被意外覆盖,或者让变量的作用域不清晰。
所以我们建议在使用变量之前声明变量,并且尽量避免重复声明变量。或者,直接使用我们下面介绍的 let
和 const
来声明变量。
let
我们刚才学习了 var
这个关键字,它可以让我们创造一个全世界都可以看到的变量,也就是全局变量。但是有时候,我们不想让全世界都看到我们的变量,我们只想让一部分人看到,我们怎么做呢?
我们可以用 let
来告诉电脑,我们要创造一个只有一部分人可以看到的变量,也就是块级别的变量(block-level variable)。也就是说,这个变量只能在当前代码块(用花括号包围起来的部分)中被看到和拿出来用。
比如我们可以这样写:
let x = 10;
这样,我们就用 let 创造了一个变量 x
,并且把数字 10 放进去。这个变量是在最外面的代码块里面的,所以叫做 外层代码块(outer block)。
如果我们在最外面的代码块里面打印出 x
这个变量里面的东西,就会得到 10。
然后我们可以定义一个函数 foo:
function foo() {
let y = 20;
console.log(x);
console.log(y);
}
这样,我们就在函数里面用 let 又创造了一个变量 y
,并且把数字 20 放进去。
这个变量是在函数里面的代码块里面的,所以叫做 当前代码块(current block)。
如果我们在函数里面打印出 x
和 y
这两个变量里面的东西,就会得到 10 和 20。因为函数里面可以看到外层代码块和当前代码块里面的变量。
接下来我们可以执行函数 foo
:
foo();
这样,电脑就会按照函数 foo
的定义来执行,并且打印出 10 和 20。
最后,我们可以在函数外面再次打印出 x
和 y
这两个变量里面的东西:
console.log(x);
console.log(y);
这时候,你会发现电脑只能打印出 x
这个变量里面的东西,也就是 10。当电脑试图打印出 y
这个变量里面的东西时,就会报错:y is not defined。
这是因为我们不在函数里面了,也就不在函数的代码块里面了,所以电脑找不到这个变量 y
。
由此可见,当我们用 let
来创造一个变量时,它就只能在当前代码块或者嵌套在当前代码块里面的代码块中被看到。
如果我们试图在其他地方找它,就会报错。
使用块级别的变量可以避免一些问题。比如,在上面的例子中,如果我们试图在函数里面再次用 let
来创造一个和外层代码块同名的变量 x
:
function foo() {
let x = 20;
}
那么电脑就会报错!这样的机制可以帮助我们避免一些潜在的bug,例如,我们想要使用外层代码块里面的变量 x
,但是却不小心在函数里面又创造了一个同名的变量 x
,
这样就会导致我们使用的是函数里面的变量 x
,而不是外层代码块里面的变量 x
,bug就这么产生了。
另外,如果我们在一个代码块里面创造了一个变量,它就不会跑出来给外面看。比如,如果我们在if语句里面用 let
来创造了一个变量 x
:
if (true) {
let x = 10;
}
那么这个变量 x
就只能在这个 if 语句里面被看到。如果我们试图在外面寻找它,就会报错!同样地,这样也可以帮助我们避免一些潜在的bug。
综上所述,在ES6中推荐使用 let
来代替 var
来创造变量。
const
当我们用 const
来定义一个变量时,我们实际上创建了一个常量。
也就是说,这个变量的值一旦被指定,就不能再被改变了:
const PI = 3.14; // 定义一个常量PI,指向数字3.14
PI = 3.15; // 报错:不能改变常量的值
在上面的代码中,当我们用 const
来定义一个变量 PI
时,它就成了一个常量,
我们就不能再修改它的值了。如果我们试图修改它的值,就会报如下图所示的错误:
使用常量可以让代码更清晰和易读。在上面的例子中,我们使用 PI
而不是用一个数字 3.14 来表示圆周率。这样可以让代码更具可读性。
另外,使用常量还可以避免一些不必要的错误。对于一些运行过程中不应该改变的值,如果不小心改了它,我们就会得到具体的报错而不是各种意料之外的BUG。
因此,在ES6中推荐使用 const
来定义一些不会改变的值,比如数学常数、配置参数、固定的字符串等等。
总结
在这一章中,我们学习了什么是变量,以及如何用变量来记录和使用一些信息。我们知道:
- 变量就像是一个标签,可以贴在任何东西上。
- 我们可以给每个标签起一个名字,并且给它指定一个值。
- 我们可以改变一个变量的值。
- 使用变量有很多好处,比如可以让代码更清晰和易读,可以避免一些错误,可以让代码更灵活等等。
- 数据有不同的类型,比如number, string, boolean, null, undefined, symbol, object等等。
- 我们可以用typeof来查看一个变量指向的数据是什么类型的。
- 我们可以使用var来定义一个全局或者函数级别的变量,使用let来定义一个块级别的变量,使用const来定义一个常量。
小试牛刀
变量名
请写出以下变量名中哪些是合法的,哪些是不合法的,并且解释为什么。
myName
1stPlace
var
_age
color!
赋值
请写出以下代码的输出结果,并且解释为什么。
var x = 10;
var y = x;
x = 20;
console.log(x);
console.log(y);
类型
请写出以下代码的输出结果,并且解释为什么。
var a = "10";
var b = 10;
var c = a + b;
var d = a - b;
console.log(c);
console.log(d);
交换变量
请写出以下代码的输出结果,并且解释为什么。
var a = 10;
var b = 20;
var c = a;
a = b;
b = c;
console.log(a);
console.log(b);