# 值类型和引用类型
# 值类型
值类型又叫基本数据类型,在 JavaScript 中值类型有以下五种:
- 数值类型
- 布尔类型
- undefined
- null
- 字符串
值类型存储在栈(stack)中,它们的值直接存储在变量访问的位置。比如:
var num = 18;
var flag = true;
var un = undefined;
var nu = null;
var str = "zhangsan";
2
3
4
5
上面定义的这些值类型的数据在内存中的存储如下图所示:
# 引用类型
引用类型又叫复合数据类型,在 JavaScript 中引用类型有以下三种:
- 对象
- 数组
- 函数
引用类型存储在堆中,也就是说存储在变量处的值是一个指针,指向存储对象的内存处。比如:
var arr = [1, 2, 3];
var p = { name: "张三", age: 18 };
2
上面定义的这些引用类型的数据在内存中的存储如下图所示:
# 值类型和引用类型的特征
# 值类型的特征
- 值类型的值是不可变的,不可变是指值类型指向的空间不可变。比如:
var a = 2;
a = a + 2;
console.log(a); // 打印结果为 4。
2
3
在上述例子中,a 变量指向的值变了,但是 2 的内存没有变。
- 按值传递的变量之间互不影响。比如:
var a = 1;
var b = a;
a = a + 2;
console.log(a, b); // 打印结果为 3, 1
2
3
4
- 值类型赋值,直接将值赋值一份。比如:
var num1 = 10;
var num2 = num1;
2
上述代码在内存中的体现为:
- 当参数为值类型的时候,函数内和函数外的两个变量完全不同,仅仅只是存的值一样而已,修改时互不影响。比如:
function foo(num) {
num = num + 1;
}
var a = 1;
foo(a);
console.log(a); // 打印结果为 1
2
3
4
5
6
7
# 引用类型的特征
- 引用类型赋值,是将地址复制一份。比如:
var p = { name: "zhangsan", age: 18 };
var p1 = p;
2
上述代码在内存中的体现为:
再来看一段代码:
var p = { name: "张三", age: 18 };
var p1 = p;
console.log(p.name); // 打印结果为张三
console.log(p1.name); // 打印结果为张三
p.name = "李四";
console.log(p.name); // 打印结果为李四
console.log(p1.name); // 打印结果为李四
2
3
4
5
6
7
- 当参数为引用类型的时候,函数内和函数外的两个变量不同,但是共同指向同一个对象,在函数内修改对象数据时会影响外部。比如:
function foo(o) {
o.age = o.age + 1;
}
var p = { name: "zhangsan", age: 18 };
foo(p);
console.log(p.age); // 打印结果为 19。
2
3
4
5
6
7
8
9
注:其实我们可以这样理解。引用类型中的地址是一把钥匙,钥匙指向的是宝藏,复制一把钥匙后,两把钥匙能打开的是同一个宝藏。
# 调试工具的使用
在编写 JavaScript 代码时,如果遇见错误,首先在浏览器中运行我们的代码,然后 F12,查看错误信息。比如运行以下代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<script>
alerttt("hello");
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
首先查看控制台信息,在控制台中会有报错提示,一般看这个就能知道问题是什么了。
在 Sources 中能够清楚的看到哪一行的代码出问题了,会有很明显地提醒。
如果我们想知道变量的值,在调试的时候,可以加一句 console.log(变量)
语句来打印出来,然后在控制台中看。console.log()
语句也是我们编程中经常需要使用的,因为有时候,我们也不能直观的一下就知道传递进来的值到底是什么,可能需要看半天的逻辑然后计算半天。直接打印出来的方式也有利于帮助我们判断,不过一般情况下大家记得调试完,要把这行语句注释掉或者删掉。
# 设置断点,逐步执行
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<script>
for (var i = 1; i <= 100; i++) {
for (var j = 1; j <= 10; i++) {
j = i * j;
console.log(j);
}
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
首先运行上述代码,点击 F12 进行调试界面,点击 Sources,设置断点的方法很简单,直接左键点击代码旁边的数字行数。如下所示:
将变量添加到 Watch 窗口,实时查看它的值的变化,如下所示:
准备工作做好之后我们还需要点击一下刷新,如下所示,红色箭头所指的地方点击一下:
然后就可以通过点击运行按钮,逐行运行我们设置断点时的代码,并在 Watch 中查看变量值的变化,如下所示:
# 异常处理
在我们实际编程过程中,经常会遇到各种各样的错误,有可能是语法错误,也可能是拼写错误,也可能是浏览器的兼容性问题或者其它莫名其妙的问题。当出现错误时,JavaScript 引擎通常会停止,并生成一个错误信息。那我们应该怎么来调试我们的代码呢?
# 异常捕获
我们使用 try-catch
语句开捕获异常,语法为:
try {
// 这里写可能出现异常的代码
} catch (err) {
// 在这里写,出现异常后的处理代码
}
2
3
4
5
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<input type="button" value="点击一下" onclick="message()" />
<script>
var txt = "";
function message() {
try {
alertt("我爱学习,身体好好"); // 故意写错 alert
} catch (err) {
txt = "There was an error on this page.\n\n";
txt += "Error description: " + err.message + "\n\n";
txt += "Click OK to continue.\n\n";
alert(txt);
}
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
需要注意以下几点:
- 语句
try
和catch
是成对出现的。 - 如果在
try
中出现了错误,try
里面出现错误的语句后面的代码都不再执行,直接跳转到catch
中,catch
处理错误信息,然后再执行后面的代码。 - 如果
try
中没有出现错误,则不会执行catch
中的代码,执行完try
中的代码后直接执行后面的代码。 - 通过
try-catch
语句进行异常捕获之后,代码将会继续执行,而不会中断。
# throw 语句
通过 throw
语句,我们可以创建自定义错误。throw
语句常常和 try catch
语句一起使用。
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<p>请输入 0 到 100 之间的数字:</p>
<input id="demo" type="text" />
<button type="button" onclick="myFunction()">测试输入值</button>
<p id="throwText"></p>
<script>
function myFunction() {
try {
var x = document.getElementById("demo").value;
if (x == "") throw "您输入的值为空";
if (isNaN(x)) throw "您输入的不是数字";
if (x > 100) throw "您输入的值太大";
if (x < 0) throw "您输入的值太小";
} catch (err) {
var y = document.getElementById("throwText");
y.innerHTML = "错误提示:" + err + "。";
}
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28