Web学习笔记-JavaScript

  1. 1. JS的调用方式与执行顺序
  2. 2. 变量与运算符
  3. 3. 输入与输出
  4. 4. 判断语句
  5. 5. 循环语句
  6. 6. 对象
  7. 7. 数组
  8. 8. 函数
  9. 9. 类
  10. 10. 事件
  11. 11. 常用库
    1. 11.1 jQuery
    2. 11.2 setTimeout与setInterval
    3. 11.3 requestAnimationFrame
    4. 11.4 Map与Set
    5. 11.5 localStorage
    6. 11.6 JSON
    7. 11.7 日期
    8. 11.8 WebSocket
    9. 11.9 window
    10. 11.10 Canvas

本文记录 JavaScript 的学习过程。
JavaScript 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。目前 JavaScript 的标准是 ECMAScript6,简称 ES6。

1. JS的调用方式与执行顺序

JS 常见使用方式有以下几种:

  • 直接在 HTML 的 <script type="module"></script> 标签内写 JS 代码。
  • 直接引入 .js 文件:<script type="module" src="/static/js/index.js"></script>
  • 将所需的代码通过 import 关键字引入到当前作用域。

例如 /static/js/index.js 文件中的内容为:

1
2
3
4
5
6
7
8
9
10
let name = "acwing";

function print() {
console.log("Hello World!");
}

export {
name,
print
}

<script type="module"></script> 中的内容为:

1
2
3
4
5
6
<script type="module">
import { name, print } from "/static/js/index.js";

console.log(name);
print();
</script>

执行顺序:

  1. 类似于 HTML 与 CSS,按从上到下的顺序执行。
  2. 事件驱动执行。

HTML、CSS、JavaScript 三者之间的关系:

  • CSS 控制 HTML;
  • JavaScript 控制 HTML 与 CSS;
  • 为了方便开发与维护,尽量按照上述顺序写代码。例如:不要在 HTML 中调用 JavaScript 中的函数。

2. 变量与运算符

(1)letconst:用来声明变量,作用范围为当前作用域。

  • let 用来定义变量。
  • const 用来定义常量。

例如:

1
2
3
4
5
6
7
8
9
10
11
const N = 100;

let s = "Hello World!", x = 10;
let dict = {
name: "AsanoSaki",
age: 18
};

console.log(N);
console.log(s + ' ' + x);
console.log(dict.name, dict.age);

(2)变量类型:

  • number:数值变量,例如:1, 2.5
  • string:字符串,例如:"acwing", 'AsanoSaki',单引号与双引号均可。字符串中的每个字符为只读类型。
  • boolean:布尔值,例如:true, false
  • object:对象,类似于 C++ 中的指针,例如:[1, 2, 3]{ name: "AsanoSaki", age: 18 }null
  • undefined:未定义的变量。

类似于 Python,JavaScript 中的变量类型可以动态变化。

(3)运算符:

与 C++、Python、Java 类似,不同点:

  • ** 表示乘方。
  • 等于与不等于用 ===!==

3. 输入与输出

(1)输入

  • 从 HTML 与用户的交互中输入信息,例如通过 inputtextarea 等标签获取用户的键盘输入,通过 clickhover 等事件获取用户的鼠标输入。
  • 通过 AjaxWebSocket 从服务器端获取输入。
  • 标准输入。

(2)输出

  • 调试用 console.log(),会将信息输出到浏览器控制台。
  • 改变当前页面的 HTML 与 CSS。
  • 通过 AjaxWebSocket 将结果返回到服务器。

通过 HTML 输入输出示例:

1
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
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

<link rel="stylesheet" href="/Web Application Lesson/static/css/index.css">
</head>

<body>
输入:
<br>
<textarea class="inputText" name="" id="" cols="30" rows="10"></textarea>
<br>

<button class="run">Run</button>
<br>

输出:
<br>
<pre class="outputText"></pre>

<script type="module">
import { runOnclick } from "/Web Application Lesson/static/js/index.js";

runOnclick();
</script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let inputText = document.querySelector(".inputText");
let run = document.querySelector(".run");
let outputText = document.querySelector(".outputText");

function runOnclick() {
console.log(inputText);
run.addEventListener("click", function () {
let s = inputText.value;
outputText.innerHTML = s;
});
}

export {
runOnclick
}

通过标准输入输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let fs = require('fs');
let buf = '';

process.stdin.on('readable', function () {
let chunk = process.stdin.read();
if (chunk) buf += chunk.toString();
});

process.stdin.on('end', function () {
buf.split('\n').forEach(function (line) {
let tokens = line.split(' ').map(function (x) { return parseInt(x); });
if (tokens.length != 2) return;
console.log(tokens.reduce(function (a, b) { return a + b; }));
});
});

(3)格式化字符串

  • 字符串中填入数值:
1
2
let name = 'AsanoSaki', age = 18;
let s = `My name is ${name}, I'm ${age} years old.`;
  • 定义多行字符串:
1
2
3
4
5
let s = 
`<div>
<h2>标题</h2>
<p>段落</p>
/div>`
  • 保留两位小数:
1
2
let x = 1.234567;
let s = `${x.toFixed(2)}`;

4. 判断语句

JavaScript 中的 if-else 语句与 C++、Python、Java 中类似。例如:

1
2
3
4
5
6
7
8
9
10
let score = 90;
if (score >= 85) {
console.log("A");
} else if (score >= 70) {
console.log("B");
} else if (score >= 60) {
console.log("C");
} else {
console.log("D");
}

JavaScript 中的逻辑运算符也与 C++、Java 中类似:&& 表示与、|| 表示或、! 表示非。

5. 循环语句

JavaScript 中的循环语句与 C++ 中类似,也包含 forwhiledo while 循环。

(1)for 循环

1
2
3
for (let i = 0; i < 10; i++) {
console.log(i);
}

枚举对象或数组时可以使用:

  • for-in 循环:可以枚举数组中的下标,以及对象中的 key
  • for-of 循环:可以枚举数组中的值,以及对象中的 value

例如:

1
2
3
4
5
6
7
8
9
let a = [2, 4, 6];

for (let key in a) {
console.log(key);
}

for (let val of a) {
console.log(val);
}

(2)while 循环

1
2
3
4
5
let i = 0;
while (i < 10) {
console.log(i);
i++;
}

(3)do while 循环

do while 语句与 while 语句非常相似。唯一的区别是,do while 语句限制先循环体后检查条件。不管条件的值如何,我们都要至少执行一次循环体。

1
2
3
4
5
let i = 0;
do {
console.log(i);
i++;
} while (i < 10);

6. 对象

英文名称:Object

类似于 C++ 中的 map,由 key:value 对构成。

  • value 可以是变量、数组、对象、函数等。
  • 函数定义中的 this 用来引用该函数的“拥有者”。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
let person = {
name: "John",
age: 20,
money: 0,
friends: ["Tom", "Alice", "Bob"],
clothes: {
color: "blue",
price: 20
},
add_money: function (x) {
this.money += x;
}
}

对象属性与函数的调用方式:

  • (1)用 . 调用:person.nameperson.add_money()
  • (2)用 [] 调用:person["name"]person["add_money"]()

7. 数组

数组是一种特殊的对象,类似于 C++ 中的数组,但是 JavaScript 数组中的元素类型可以不同(数组中的元素可以是变量、数组、对象、函数)。例如:

1
2
3
4
5
6
7
8
9
10
11
let a = [1, 2, "a", "abc"];

let b = [
1, // 变量
"abc", // 变量
['a', 'b', 3], // 数组
function () { // 函数
console.log("Hello World");
},
{ name: "abc", age: 18 } // 对象
];

可以通过下标访问数组元素,例如:

1
2
a[0] = 1;  // 访问数组a[]的第0个元素
console.log(a[0]);

数组的常用属性和函数:

  • 属性 length:返回数组长度。注意 length 是属性,不是函数,因此调用的时候不要加 ()
  • 函数 push():向数组末尾添加元素。
  • 函数 pop():删除数组末尾的元素。
  • 函数 splice(a, b):删除从下标 a 开始的 b 个元素。
  • 函数 sort():将整个数组从小到大排序。
    • 自定义比较函数:array.sort(cmp),函数 cmp 输入两个需要比较的元素,返回一个实数,负数表示第一个参数排在第二个参数前面,零表示相等,正数表示第一个参数排在第二个参数后面。因此如果要实现从大到小排序只需要令函数为:function(a, b) { return b - a; }

8. 函数

JavaScript 中的函数是用对象来实现的,定义完函数后是允许再对这个对象进行修改的。函数的定义方式如下:

1
2
3
4
5
6
7
8
9
10
11
function add(a, b) {
return a + b;
}

let add = function (a, b) {
return a + b;
}

let add = (a, b) => {
return a + b;
}

函数返回值:如果未定义返回值,则返回 undefined

9. 类

与 C++ 中的 Class 类似,但是不存在私有成员,this 指向类的实例。

(1)定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
this.init();
}

init() {
this.sum = this.x + this.y;
}

toString() {
return `Point: (${this.x}, ${this.y}), Sum: ${this.sum}`;
}
}

let p = new Point(3, 4);
console.log(p.toString());

(2)继承

1
2
3
4
5
6
7
8
9
10
class ColorPoint extends Point {
constructor(x, y, c) {
super(x, y);
this.color = c;
}

toString() {
return `${super.toString()}, Color: ${this.color}`;
}
}

注意:

  • super 这个关键字,既可以当作函数使用,也可以当作对象使用。
    • 作为函数调用时,代表父类的构造函数,且只能用在子类的构造函数之中。
    • 作为对象时,指向父类的原型对象。
  • 在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字。
  • 成员重名时,子类的成员会覆盖父类的成员,类似于 C++ 中的多态。

(3)静态方法

在成员函数前添加 static 关键字即可。静态方法可以被子类继承,但是不会被类的实例继承,只能通过类名来调用。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}

toString() {
return `(${this.x}, ${this.y})`;
}

static print_class_name() {
console.log("Point");
}
}

let p = new Point(3, 4);
Point.print_class_name();
p.print_class_name(); // 报错

(4)静态变量

在 ES6 中,只能通过 class.propname 定义和访问,子类可以继承父类的静态变量,即可以通过子类名访问静态变量。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
Point.cnt++;
}

toString() {
return `(${this.x}, ${this.y})`;
}
}

Point.cnt = 0;

let p = [];
for (let i = 0; i < 5; i++) {
p[i] = new Point(3, 4);
}
console.log(Point.cnt);

10. 事件

JavaScript 的代码一般通过事件触发。可以通过 addEventListener 函数为元素绑定事件的触发函数。

常见的触发函数如下:

(1)鼠标

  • click:鼠标左键点击。
  • dblclick:鼠标左键双击。
  • contextmenu:鼠标右键点击。
  • mousedown:鼠标按下,包括左键、滚轮、右键。
    • event.button:0表示左键,1表示中键,2表示右键。
  • mouseup:鼠标弹起,包括左键、滚轮、右键。
    • event.button:0表示左键,1表示中键,2表示右键。

例如:

1
2
3
4
5
6
7
8
9
10
11
let div = document.querySelector('div');

function main() {
div.addEventListener('click', function (event) {
console.log(event.type, event.button);
});
}

export {
main
}

(2)键盘

  • keydown:某个键是否被按住,事件会连续触发。
    • event.code:返回按的是哪个键。
    • event.altKeyevent.ctrlKeyevent.shiftKey 分别表示是否同时按下了 altctrlshift 键。
  • keyup:某个按键是否被释放。
    • event 常用属性同上。
  • keypress:紧跟在 keydown 事件后触发,只有按下字符键时触发,适用于判定用户输入的字符。
    • event 常用属性同上。

keydownkeyupkeypress 的关系类似于鼠标的 mousedownmouseupclick

(3)表单

  • focus:聚焦某个元素。
  • blur:取消聚焦某个元素。
  • change:某个元素的内容发生了改变。

(4)窗口

需要作用到 window 元素上。

  • resize:当窗口大小放生变化。
  • scroll:滚动指定的元素。
  • load:当元素全部被加载完成。

11. 常用库

11.1 jQuery

jQuery 能够让我们更加方便地去获取前端的某一个标签、绑定某个事件、改变前端的某个标签的 CSS 属性。

(1)下载地址:jQuery 官网

(2)使用方式:在 <head> 元素中添加:<script src="/Web Application Lesson/static/js/jquery-3.6.1.min.js"></script>

(3)选择器

$(selector)selector 类似于 CSS 的选择器。例如:

1
2
3
$('div');
$('.big-div');
$('div > p');

(4)事件

$(selector).on(event, func) 绑定事件,例如:

1
2
3
$('div').on('click', function (e) {
console.log("click div");
})

$(selector).off(event, func) 删除事件,例如:

1
2
3
4
5
$('div').on('click', function (e) {
console.log("click div");

$('div').off('click');
});

当存在多个相同类型的事件触发函数时,可以通过 click.name 来区分,例如:

1
2
3
4
5
$('div').on('click.first', function (e) {
console.log("click div");

$('div').off('click.first');
});

在事件触发的函数中的 return false 等价于同时执行:

  • e.stopPropagation():阻止事件向上传递。例如 adiv 的子标签,当点击 a 时同样会触发 divclick 事件,当在 a 的事件触发函数中加上该语句时点击 a 就不会触发 divclick 事件。
  • e.preventDefault():阻止事件的默认行为。例如点击 a 时不打开链接,并向上传递触发 divclick 事件。

(5)元素的隐藏、展现

  • $A.hide():隐藏,可以添加参数,表示消失时间(毫秒)。
  • $A.show():展现,可以添加参数,表示出现时间。
  • $A.fadeOut():颜色淡退至消失,可以添加参数,表示消失时间。
  • $A.fadeIn():颜色淡增至出现,可以添加参数,表示出现时间。

(6)元素的添加、删除

  • $('<div class="mydiv"><span>Hello World</span></div>'):构造一个 jQuery 对象。
  • $A.append($B):将 $B 添加到 $A 的末尾。
  • $A.prepend($B):将 $B 添加到 $A 的开头。
  • $A.remove():删除元素 $A
  • $A.empty():清空元素 $A 的所有儿子。

(7)对类的操作(此处 class_name 无需加 .

  • $A.addClass(class_name):添加某个类。
  • $A.removeClass(class_name):删除某个类。
  • $A.hasClass(class_name):判断某个类是否存在。

(8)对 CSS 的操作

  • $("div").css("background-color"):获取某个 CSS 的属性。
  • $("div").css("background-color", "yellow"):设置某个 CSS 的属性。
  • 同时设置多个 CSS 的属性(注意 JS 中带 - 的标签必须加引号,如果不加会被当做减号):
1
2
3
4
5
$('div').css({
width: "200px",
height: "200px",
"background-color": "orange"
});

(9)对标签属性的操作(除 classid 外可以随意创造新的属性,例如:<div abc="abc"></div>

  • $('div').attr('id'):获取属性。
  • $('div').attr('id', 'ID'):设置属性。

(10)对 HTML 内容、文本的操作

不需要背每个标签该用哪种,用到的时候 Google 或者 Bing 即可。

  • $A.html():获取、修改 HTML 内容(加参数即可修改),例如:<div><span>span content</span></div> 输出为 <span>span content</span>
  • $A.text():获取、修改文本信息,例如:<div><span>span content</span></div> 输出为 span content
  • $A.val():获取、修改文本的值,一般用在 inputtextarea 中。

(11)查找

  • $(selector).parent(filter):查找父元素。
  • $(selector).parents(filter):查找所有祖先元素。
  • $(selector).children(filter):在所有子元素中查找。
  • $(selector).find(filter):在所有后代元素中查找。

(12)ajax

ajax 可以让我们在不刷新页面的情况下只从服务器端获取某些数据,一般是获取一个 json 数据。

GET 方法(从服务器端获取内容):

1
2
3
4
5
6
7
8
9
10
$.ajax({
url: url,
type: "GET",
data: {
},
dataType: "json",
success: function(resp) { // 请求成功的回调函数,resp为服务器端返回的内容
console.log(resp);
},
});

POST 方法(把表单内容提交给服务器):

1
2
3
4
5
6
7
8
9
10
$.ajax({
url: url,
type: "POST",
data: {
},
dataType: "json",
success: function(resp) {
console.log(resp);
},
});

11.2 setTimeout与setInterval

(1)setTimeout(func, delay)

经过 delay 毫秒后,执行函数 func(),可以使用 clearTimeout() 关闭定时器。例如:

1
2
3
4
5
let timeout_id = setTimeout(() => {
console.log("Hello World!");
}, 2000); // 2秒后在控制台输出"Hello World!"

clearTimeout(timeout_id); // 清除定时器

(2)setInterval(func, delay)

每隔 delay 毫秒,执行一次函数 func(),第一次在第 delay 毫秒后执行,可以使用 clearInterval() 关闭周期执行的函数。例如:

1
2
3
4
5
let interval_id = setInterval(() => {
console.log("Hello World!");
}, 2000); // 每隔2秒,输出一次"Hello World!"

clearInterval(interval_id); // 清除周期执行的函数

11.3 requestAnimationFrame

requestAnimationFrame(func) 函数会在下次浏览器刷新页面之前执行一次,一般浏览器每秒刷新60次,因此通常会用递归写法使其每秒执行60次 func() 函数。调用时会向 func() 传入一个参数,表示函数执行的时间戳,单位为毫秒。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
let $div = $('div');
let $window = $(window);

function main() {
function step(timestamp) {
console.log(timestamp);
$div.width($div.width() + 1);
if ($div.width() >= $window.width()) return false;
requestAnimationFrame(step);
}

requestAnimationFrame(step);
}

使用 setTimeoutsetInterval 实现以上效果的代码如下:

1
2
3
4
5
6
7
let timeout_func = () => {
$div.width($div.width() + 1);
if ($div.width() === $window.width()) clearTimeout(timeout_id);
else timeout_id = setTimeout(timeout_func, 16.67);
}

let timeout_id = setTimeout(timeout_func, 16.67);
1
2
3
4
let interval_id = setInterval(function () {
$div.width($div.width() + 1);
if ($div.width() === $window.width()) clearInterval(interval_id);
}, 16.67);

setTimeoutsetInterval 的区别:

  • requestAnimationFrame 渲染动画的效果更好,性能更佳。该函数可以保证每两次调用之间的时间间隔相同,但 setTimeoutsetInterval 不能保证这点。setTmeout 两次调用之间的间隔包含回调函数的执行时间;setInterval 只能保证按固定时间间隔将回调函数压入栈中,但具体的执行时间间隔仍然受回调函数的执行时间影响。
  • 当页面在后台时,因为页面不再渲染,因此 requestAnimationFrame 不再执行。但 setTimeoutsetInterval 函数会继续执行。

11.4 Map与Set

(1)Map

Map 对象保存键值对。

  • for...of 或者 forEach 可以按插入顺序遍历。
  • 键值可以为任意值,包括函数、对象或任意基本类型。

常用 API:

  • set(key, value):插入键值对,如果 key 已存在,则会覆盖原有的 value
  • get(key):查找关键字,如果不存在,返回 undefined
  • size:返回键值对数量。
  • has(key):返回是否包含关键字 key
  • delete(key):删除关键字 key
  • clear():删除所有元素。

例如:

1
2
3
4
5
6
7
let mp = new Map();
mp.set('name', 'AsanoSaki');
mp.set('phone', '123456');
console.log(mp, mp.get('name'), mp.has('name'), mp.size);
for (let [k, v] of mp) {
console.log(k, v);
}

(2)Set

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

  • for...of 或者 forEach 可以按插入顺序遍历。

常用 API:

  • add():添加元素。
  • has():返回是否包含某个元素。
  • size:返回元素数量。
  • delete():删除某个元素。
  • clear():删除所有元素

例如:

1
2
3
4
5
6
7
let st = new Set();
st.add('AsanoSaki');
st.add(20);
console.log(st, st.has(20), st.size);
st.forEach((v) => {
console.log(v);
});

11.5 localStorage

localStorage 可以在用户的浏览器上存储键值对。常用 API:

  • setItem(key, value):插入。
  • getItem(key):查找。
  • removeItem(key):删除。
  • clear():清空。

例如:

1
2
3
4
localStorage.setItem('name', 'AsanoSaki');
localStorage.setItem('age', 18);
console.log(localStorage.getItem('name'));
localStorage.clear();

11.6 JSON

JSON 对象用于序列化对象、数组、数值、字符串、布尔值和 null。常用 API:

  • JSON.parse():将字符串解析成对象。
  • JSON.stringify():将对象转化为字符串。

例如:

1
2
3
4
5
6
7
8
9
10
let obj = {
name: 'AsanoSaki',
age: 18
};

let str = JSON.stringify(obj);
console.log(str);

let new_obj = JSON.parse(str);
console.log(new_obj);

11.7 日期

(1)返回值为整数的 API,数值为 1970-1-1 00:00:00 UTC(世界标准时间)到某个时刻所经过的毫秒数:

  • Date.now():返回现在时刻。
  • Date.parse("2022-04-15T15:30:00.000+08:00"):返回北京时间2022年4月15日15:30:00的时刻。

(2)与 Date 对象的实例相关的 API:

  • new Date():返回现在时刻。
  • new Date("2022-04-15T15:30:00.000+08:00"):返回北京时间2022年4月15日15:30:00的时刻。
  • 两个 Date 对象实例的差值为毫秒数。
  • getDay():返回星期,0表示星期日,1-6表示星期一至星期六。
  • getDate():返回日,数值为1-31。
  • getMonth():返回月,数值为0-11。
  • getFullYear():返回年份。
  • getHours():返回小时。
  • getMinutes():返回分钟。
  • getSeconds():返回秒。
  • getMilliseconds():返回毫秒。

例如:

1
2
3
4
5
let time = new Date();
console.log(time);
console.log(time.getDay());
console.log(time.getDate());
console.log(time.getMonth());

11.8 WebSocket

与服务器建立全双工连接。常用 API:

  • new WebSocket('ws://localhost:8080');:建立 WS 连接。
  • send():向服务器端发送一个字符串。一般用 JSON 将传入的对象序列化为字符串。
  • onopen:类似于 onclick,当连接建立时触发。
  • onmessage:当从服务器端接收到消息时触发。
  • close():关闭连接。
  • onclose:当连接关闭后触发。

11.9 window

  • window.open("https://www.acwing.com"):在新标签栏中打开页面。
  • location.reload():刷新页面。
  • location.href = "https://www.acwing.com":在当前标签栏中打开页面。

11.10 Canvas

Canvas 教程参考:Canvas Tutorial (English Version)Canvas 教程(中文)

上一章:Web学习笔记-CSS

下一章:Web学习笔记-React(配置环境、ES6语法补充、Components)