【退役生活】JavaScript 学习笔记
NCC79601
2020-02-08 21:19:04
# JavaScript 笔记
**请注意,JavaScript 和 Java 没有任何关系。**
## 基本语法
### 结构
JavaScript 与 C / C++ 极为相似,甚至可以认为 JavaScript 就是 C++ 的简化版。JavaScript 没有特定的程序结构,因为 JavaScript 一般是被用于 **函数定义** 来辅助 HTML 页面,而不会像 C++ 程序一样被编译执行。
JavaScript 的每句话以分号结尾。
### 插入 JavaScript 代码
在 HTML 中插入 JavaScript 代码时使用 `<script>` 标签。例如:
```html
<script>
alert("hello, world!");
</script>
```
JavaScript 还可以直接向 HTML 文档写入语句,例如:
```javascript
document.write("<h1>标题</h1>");
document.write("<p>段落</p>");
```
如果在文档加载完毕后使用 `document.write()`,则会覆盖整个 HTML 文档。
在 HTML 文档中可以放入无限多个脚本。脚本可以位于 `<head>` 或者 `<body>` 部分,通常把 JavaScript 函数放入 `<head>` 部分或者页面底部。例如:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>
test
</title>
<script>
function myFunction() {
document.getElementById("demo").innerHTML =
"Hello world!";
}
</script>
</head>
<body>
<p id="demo">
JavaScript
</p>
<button type="button" onclick="myFunction()">
点击查看效果
</button>
</body>
</html>
```
在这个例子中,按钮被按下后就会执行 `<head>` 中定义的函数 `myFunction()`,把段落文字变为 `Hello World!`。
除此之外,JavaScript 代码还可以置于外部,在 `<script>` 的属性中引入即可。例如:
```javascript
function myFunction() {
document.getElementById("demo").innerHTML = \
"Hello world!";
}
```
把这段代码保存为 `myScript.js`,那么 HTML 文档中就可以写:
```html
<script src="myScript.js"></script>
```
### 输出
JavaScript 没有特定的输出函数。如果希望 JavaScript 显示数据,则有以下几种方法可选:
|函数名|描述|
|:-|:-|
|`window.alert()`|弹出警告窗|
|`document.write()`|写入 HTML 文档|
|`.innerHTML`|写入 HTML 元素|
|`console.log()`|写入浏览器控制台|
### 注释
JavaScript 注释和 C++ 一模一样。
### 变量
JavaScript 的所有变量统一使用 `var` 声明。变量的类型可以 **动态变化**,也就是说可以实现:
```javascript
var x = 1926;
x = "frog";
```
JavaScript 中可以 **重复声明** 同一个变量,其值不会丢失。如果声明一个变量后不赋初值,则由于变量类型没有定义,所以其值为 `undefined`。
使用 `typeof varName` 可以返回变量的类型。而 `===` 符号(即绝对相等)可以判断两个变量的 **值和类型** 是否都一致。如果希望声明一个变量的类型,则可以用 `new`,例如:
```javascript
var x = new Number;
```
则 `x` 就有了初值 0。但这样声明后,`x` 的类型就不再是 `Number` 而是 `Object`。
如果把值赋给尚未声明的变量,该变量将自动作为 `window` 的一个属性。非严格模式下,该变量可以被删除。
### 局部变量
`let` 关键字可以用于声明局部变量,例如:
```javascript
var x = 10;
// 此处 x = 10
{
let x = 2;
// 此处 x = 2
}
// 此处 x = 10
```
所以在 `for` 循环中的定义就可以使用 `let`,防止出现混乱。
### 常量
`const` 关键字用于声明常量,和 C++ 中的 `const int` 类似。
### 数据类型
JavaScript 中的数据类型非常简洁,常用的只有数字、字符串、数组、对象这四种。
#### 数字
JavaScript 中所有数字都属于同一种数据类型,也就是说 **不用区分整型和浮点型**,实在是太爽了。**所有数字都是 64 位**,等同于 C++ 中的 `long long`。不过既然不搞 OI 了,上限是多少又如何呢?
`num.toString(bit)` 可以把数字转换为 `bit` 位表示下的字符串。例如:
```javascript
var x = 4;
str = x.toString(2);
// str = "100"
```
`NaN` 是代表非数字值的数字值,而 `isNaN()` 函数可以用于判断一个值是否是数字。除此之外,JavaScript 还有 `Infinity` 常量,代表爆 `Number` 的临界值(有正负),不过 `Infinity` 进行运算后依然是 `Infinity`,非常贴心。
数字运算符和 C++ 一模一样,非常容易上手。
`Math` 对象包含很多数学运算函数,可以依需调用。
#### 字符串
字符串可以用单引号 `'...'` 或者双引号 `"..."` 标识。JavaScript 中的字符串还可以和数字直接相加,例如:
```javascript
str = "ncc" + 79601;
```
那么 `str` 就变为了 `"ncc79601"`。
在字符串内还可以用反斜杠 `\` 进行拆行,例如:
```javascript
var str = "today is \
a good day";
```
则 `str` 为 `"today is a good day"`。
和数组类似,`str[i]` 可以访问单个字符;`str.length` 属性可计算字符串长度。
|方法|描述|
|:-|:-|
|`indexOf()`|查找一个子串首次出现的位置。如果未找到,则返回 -1|
|`lastIndexOf()`|查找一个子串最后出现的位置|
|`match()`|查找是否出现字符串。若找到则返回原字符串;否则返回 `null`|
|`toUpperCase()`|全部转换为大写|
|`toLowerCase()`|全部转换为小写|
|`split()`|转换为数组,参数为分隔符|
#### 数组
数组和 C++ 略微不同,使用中括号 `[...]` 标识。例如:
```javascript
a = [1, 2, 3];
```
或者:
```javascript
a = new Array();
a[0] = 1;
a[1] = 2;
a[2] = 3;
```
又或者:
```javascript
a = new Array(1, 2, 3);
```
数组下标默认从 0 开始,而 `indexOf()` 方法可以返回对应元素的下标。注意数组的类型也是 `object`。
遍历数组的方法:
```javascript
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
for (let i in arr) {
console.log(arr[i]);
}
for (let i of arr) {
console.log(i);
// 注意和上一种的区别
}
```
|方法|描述|
|:-|:-|
|`concat()`|合并数组(参数不定)|
|`join()`|用数组元素组合为字符串|
|`pop()`|弹出最后一个元素|
|`push()`|在末尾压入一个元素|
|`reverse()`|翻转数组|
|`shift()`|弹出第一个元素|
|`unshift()`|在开头添加元素|
|`splice()`|在第二位置插入元素|
|`slice()`|截取子数组(左闭右开)|
|`sort()`|排序|
|`toString`|转换到字符串|
### 对象
JavaScript 面向对象,并且 JavaScript 中的所有事物都是对象。
#### 创建对象
JavaScript 创建对象非常简单,利用类似 Python 中字典的描述方法即可。例如:
```javascript
obj = {firstName: "Max", lastName: "Tedder", age: 16};
```
或者使用构造函数,例如:
```javascript
function person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
// 属性
function changeAge(newAge) {
this.age = newAge;
} // 方法
}
// 创建一个对象实例:
var obj = new person("max", "tedder", 16);
```
#### 访问属性
键值对在 JavaScript 中也被称为 **属性**。访问属性有两种方法:
```javascript
obj.firstName = "Max"; // 1
obj["firstName"] = "Max"; // 2
```
`.constructor` 可以返回对象的构造函数。
对象属性的遍历方法:
```javascript
for (let i in obj) {
console.log(i, ":", obj[i]);
}
```
#### 原型对象
所有的 JavaScript 对象都会从一个 `prototype`(原型对象)中继承属性和方法。对于一个已经定义的对象构造器,是 **不能向其中添加新属性** 的,要添加一个新的属性需要在在构造器函数中添加(暴力修改);除此之外,也可以使用 `prototype` 属性。例如:
```javascript
function person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
person.prototype.nationality = "China";
// 所有 person 类的 nationality 属性都为 "China"
person.prototype.changeAge = function (newAge) {
this.age = newAge;
} // 方法
```
## 程序结构
### 循环与判断
与 C++ 完全一致,不做赘述。
### 函数
`function` 关键字用于声明函数。剩余的与 C++ 和 Python 类似,可以使用 `return` 语句结束函数并返回一个值。`arguments.length` 属性可以返回函数调用的参数个数。
对于 `void()` 关键字,将运行括号内的语句,而无返回值。例如 `void(0)` 就是什么也不做。
同时,声明函数时可以不带有函数名(匿名函数)。
#### 参数
ES6 版本支持默认参数,和 Python 相同。
JavaScript 函数有个内置的对象 `arguments`,其包含了函数调用的参数数组。
#### 对象函数
函数可以作为对象方法调用,其中 `this` 关键字指代的是对象自己。例如:
```javascript
var obj = {
firstName: "Max",
lastName: "Tedder",
fullName: function () {
return this.firstName + " " + this.lastName;
}
}
obj.fullName(); // 返回 "Max Tedder"
```
函数也可以用于构造对象,例如:
```javascript
function name(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
var person = new name("Max", "Tedder");
// 创建了一个对象 person
```
#### 预定义函数方法
`call()` 和 `apply()` 是预定义的函数方法,两个方法可用于调用函数,例如:
```javascript
function mul(a, b) {
return a * b;
}
x = mul.call(x, 3, 4);
// 等价于:
x = mul.apply(x, [3, 4]);
// x = 12
```
#### 函数表达式
函数可以通过一个表达式定义(类似 Python 中的 `lambda`,实际上此处也属于匿名函数),相当于 **把函数赋给了一个变量**,这个变量充当函数名。例如:
```javascript
var mul = function(a, b) { return a * b };
var x = mul(3, 4); // x = 12
```
ES6 版本引入了箭头函数,比函数表达式更加简洁,例如:
```javascript
var mul = (a, b) => a * b;
// 相当于 (a, b) => { return a * b };
var x = mul(3, 4); // x = 12
```
#### 自调用函数
自调用函数可以理解为在声明的时候就完成调用的函数。例如:
```javascript
var a = (function f1(x) {
console.log("调用 f1");
return x;
})(6);
console.log(a);
```
则会将 6 作为参数执行 `f1()` 函数,而后作为返回值赋给 `a`。
#### `this` 的使用
`this` 指代对象自己。既可以指代具体的一个对象(作为对象方法调用),又可以指代全体对象(纯粹函数调用)。[参考资料](http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html)
#### 闭包
JavaScript 支持 **嵌套函数**,嵌套函数可以访问上一层的函数变量。由于函数内定义的变量无法在外部调用,因此为了实现这一功能,就有了 **闭包** 这个概念。
简单地说,闭包就是指用函数内部的函数来实现访问函数内变量的程序结构。例如,设 `f2()` 是 `f1()` 内部定义的一个函数,则外部可以通过 `f2()` 来访问 `f1()` 内部变量的值。在这个过程中,由于 `f1()` 内定义的变量被 `f2()` 引用,所以变量 **不会被垃圾回收机制回收**,也就是说这个变量会一直留在内存池中。
一个用函数内部变量实现全局变量功能的计数器:
```javascript
var add = (function (base) {
// 自调用函数
var counter = base;
console.log("defined counter");
console.log(counter);
return function () {
// 匿名函数
counter++;
console.log("add counter");
return counter;
}
})(1);
// 相当于把内层函数赋给了 add
console.log(add());
console.log(add());
console.log(add());
```
输出为:
```
defined counter
1
add counter
2
add counter
3
add counter
4
```
### 异常捕捉
与 Python 类似,JavaScript 也支持对异常的捕捉。格式为:
```javascript
try {
// 异常抛出
} catch(err) {
// 异常处理
} finally {
// 异常清理
}
```
使用 `throw` 可以抛出异常,抛出的异常可以为任意类型。例如:
```javascript
try {
throw "hello!";
} catch(err) {
alert(err);
}
```
### 变量提升
JavaScript 中,函数及变量的声明都将被提升到函数的 **最顶部**。所以变量可以先使用再声明。
需要注意的是,带有初始化的变量声明不会被提升。例如:
```javascript
var x;
var y = 1;
```
则 `x` 会被提升,而 `y` 不会。
### 调试
`debugger` 语句可以用于替代设置断点,方便进行调试。一般在浏览器的“检查元素”处可以进行调试。
### 严格模式
在 JavaScript 头部添加:
```javascript
"use strict";
```
即可进入严格模式。严格模式可以使得 JavaScript 程序更加严谨,为未来的 JavaScript 铺路。
## 面向 HTML
### 表单
在 HTML 中有 `<form>` 标签描述的表单,表单经提交后即可被 JavaScript 处理。
下面这个例子实现了判断 `myForm` 中的 `firstName` 是否合法。
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
test
</title>
<script>
function judge() {
var x, text;
x = document.forms["myForm"]["firstName"].value;
// 从 document 对象读取表单
if (x == null || x == "") {
alert("输入不合法");
return false;
}
alert("提交成功")
return true;
}
</script>
</head>
<body>
<form name="myForm" action="form_action.php" onsubmit="judge()">
<input type="text" name="firstName" value="First Name">
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
</body>
</html>
```
同样,JavaScript 还可以用于判断普通的输入是否合法,例如:
```html
<script>
function judge() {
var x = document.getElementById("inputBox").value;
var text;
if (isNaN(x) || x <= 0) {
text = "输入错误";
} else {
text = "输入正确";
}
document.getElementById("infor").innerHTML = text;
}
</script>
<input id="inputBox" value="输入一个正数">
<button onclick="judge()">check</button>
<p id="infor"></p>
```
## JSON
JSON 是用于存储和传输数据的格式。一般一个 JSON 文件描述了一个 JavaScript 对象,便于作为文本在服务器和客户端之间传输。例如:
```json
{"processor": [
{"name": "8700K", "gen": "8th"},
{"name": "8705G", "gen": "8th"},
{"name": "9700K", "gen": "9th"},
{"name": "1065G7", "gen": "10th"}
]}
```
### 对象转 JSON
```javascript
str = JSON.stringify(obj);
```
### JSON 转对象
```javascript
obj = JSON.parse(str);
```
## HTML DOM
DOM 全名为 **文档对象模型**(Document Object Model),上文出现过的 `document` 对象就是一个 DOM。DOM 按照 HTML 标签的父子关系将整个 HTML 文档划分为一个树形结构。
### 查找 HTML 元素
#### 通过 `id` 查找
使用 `getElementById` 方法,例如:
```javascript
var x = document.getElementById("demo");
```
如果未找到,则会返回 `null`。
#### 通过标签名查找
使用 `getElementsByTagName` 方法,例如:
```javascript
var x = getElementById("demo");
var y = getElementsByTagName("p");
// 查找 <main> 标签中的所有 <p> 标签(DOM Collection)
```
#### 通过类名查找
使用 `getElementByClassName` 方法,例如:
```javascript
var x = getElementByClassName("studentInfor");
```
### 改变 HTML
#### 改变输出流
在上文提到的 `document.write()` 方法就是改变 HTML 输出流的方法,其可以在 HTML 文档中直接写入内容。当然,绝对不能在 HTML 加载完毕之后使用这个方法,不然会使原来的文档被覆盖。
#### 改变内容
查找到元素后,修改 `innerHTML` 属性可以修改 HTML 元素的内容。例如:
```javascript
document.getElementById("demo").innerHTML = "你好";
```
#### 改变元素属性
查找到元素后,修改相应的属性即可。例如:
```javascript
document.getElementById("image").src = "earth.jpg";
```
### 改变 CSS
查找到元素后,修改 `style.property` 即可改变相应的样式,例如:
```javascript
document.getElementById("demo").style.color="#00ff00";
document.getElementById("demo").style.fontSize="larger";
```
### 使用事件
HTML 事件是发生在 HTML 元素上的事情。HTML 元素可以添加事件属性,例如:
```html
<标签 事件名="JavaScript 代码()">
```
常见的 HTML 事件有:
|事件名|描述|
|:-|:-|
|`onchange`|元素改变|
|`onclick`|点击元素|
|`onmouseover`|在元素上移动鼠标|
|`onmouseout`|从元素上移出鼠标|
|`onkeydown`|按下键盘|
|`onload`|完成页面加载|
|`onfocus`|输入框获得焦点|
|`onblur`|输入框失去焦点|
例如:
```javascript
document.getElementById("button").onclick = function () {
document.getElementById("demo").innerHTML = Date();
} // 实用了匿名函数
```
**注意**:传入事件名的应该是函数,如果是调用已定义的函数,则函数名后 **不应该加括号**。
`addEventListener()` 方法可以向元素添加事件句柄,这使得一个元素可以拥有多个事件句柄。新添加的事件句柄不会覆盖已有的事件句柄。可以向 **任何 DOM 对象** 添加事件监听,不仅仅是 HTML 元素。如:`window` 对象。
格式为:
```javascript
element.addEventListener(event, function, useCapture);
// 此处的事件名要去掉 "on"
// useCapture 默认为 false
```
`useCapture` 指的是事件传递方式,它决定了事件触发的顺序。当属性为 `false`,传递方式为 **冒泡**(由内而外);当属性为 `true`,传递方式为 **捕获**(由外而内)。
例如:
```javascript
document.getElementById("button").addEventListener("click", function () {
alert("Hello World!");
});
```
如果需要传递参数,则使用匿名函数调用带参数的函数。
`removeEventListener()` 方法可以用于移除事件句柄,格式与 `addEventListener()` 一致(无 `useCapture` 参数)。
为了使 `EventListener` 稳定工作,可以把 `addEventListener` 写入 `window.onload` 事件,也就是:
```javascript
window.onload = function () {
var x = document.getElementById("button");
x.addEventListener("click", myFunction);
};
```
### 新建元素
要创建新的 HTML 元素(节点)需要先创建一个元素,然后用 `appendChild()` 方法在已存在的元素中添加它。例如:
```html
<div id="div1">
<p>第一段</p>
<!--新的一段会出现在这里-->
</div>
<script>
var newPara = document.createElement("p");
var newNode = document.createTextNode("第二段");
newPara.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(para);
</script>
```
`appendChild()` 方法可以把节点添加到元素末尾,而 `insertBefore()` 方法则可以把节点添加到特定元素之前。在调用次方法时,必须传入参照节点的对象。例如:
```html
<div id="div1">
<!--新的一段会出现在这里-->
<p id="p1">第一段</p>
</div>
<script>
var newPara = document.createElement("p");
var newNode = document.createTextNode("第二段");
newPara.appendChild(node);
var element = document.getElementById("div1");
var child = document.getElementById("p1");
element.insertBefore(newPara, child);
// 注意两者函数参数的区别
</script>
```
`removeChild()` 方法可以移除已存在的元素。其使用方法和 `appendChild()` 相同。需要注意的是,使用 `removeChild()` 方法时 **必须知道目标元素的父元素**。
`replaceChild()` 方法可以替换已存在的元素。例如:
```html
<div id="div1">
<p id="p1">第一段</p>
<p id="p2">第二段</p>
</div>
<script>
var newPara = document.createElement("p");
var newNode = document.createTextNode("第三段");
newPara.appendChild(node);
var element = document.getElementById("div1");
var child = document.getElementById("p2");
element.replaceChild(newPara, child);
</script>
```
### HTML Collection
`getElementsByTagName()` 方法返回 HTML Collection 对象,包含对应标签名的所有元素。HTML Collection 对象虽然支持数组的一些操作,但其 **不是数组**,而是特殊的一种对象。
下面这个例子修改所有段落的背景颜色:
```javascript
var myCollection = document.getElementsByTagName("p");
for (let i = 0; i < myCollection.length; i++) {
myCollection[i].style.backgroundColor = "red";
}
```
### NodeList
NodeList 不同于却类似于 HTML Collection 对象,是一个从文档中获取的节点列表。
例如将上一个例子改为用 NodeList 实现:
```javascript
var myNodeList = document.querySelectorAll("p");
// 获取 <p> 元素的集合
for (let i = 0; i < myNodeList.length; i++) {
myNodeList[i].style.backgroundColor = "red";
}
```
## JavaScript Window
浏览器对象模型(BOM)使 JavaScript 有能力与浏览器交流。`window` 对象就是一个 BOM。HTML DOM 的 `document` 也是 `window` 对象的属性(`window.document`),不过在代码中可以将 `window` 省略。
### Window 尺寸
|属性|描述|
|:-|:-|
|`window.innerHeight`|内部高度(包括滚动条)|
|`window.innerWidth`|内部宽度(包括滚动条)|
|`screen.availHright`|可用屏幕高度|
|`screen.availWidth`|可用屏幕宽度|
### Window 地址
|属性 / 方法|描述|
|:-|:-|
|`location.hostname`|主机域名|
|`location.pathname`|当前页面路径|
|`location.port`|主机端口|
|`location.protocol`|web 协议|
|`location.href`|当前页面 url|
|`location.assign(url)`|加载新文档|
### Window Navigator
`navigator` 包含访问者浏览器的相关信息。
### 弹窗
|方法|描述|
|:-|:-|
|`alert()`|警告框|
|`confirm()`|确认框|
|`prompt()`|提示框|
### 计时事件
|方法|描述|
|:-|:-|
|`setInterval(function, ms)`|间隔指定毫秒数循环执行指定代码|
|`clearInterval()`|停止执行 `setInterval()` 方法|
|`setTimeout(function, ms)`|在指定毫秒数后执行指定代码|
|`clearTimeout()`|停止执行 `setTimeout()` 方法|
### Cookie
Cookie 用于存储 web 页面的用户信息。在连接关闭后,服务端不会记录用户的信息,此时就需要 Cookie 记录客户端的用户信息。例如:
```javascript
document.cookie="username=Max Tedder; userid=114514";
```
删除 Cookie 时,只需要把等号后的内容删除。例如:
```javascript
document.cookie="username=; userid=";
```
不过由于涉及 Cookie 的操作在现阶段用处不大,因此不做赘述。
---
至此,JavaScript 的基本内容就讲解完毕了。