文章目录
- 一、前言
- 二、基本语法
-
- [2.1 注释](#2.1 注释)
- [2.2 变量](#2.2 变量)
- 三、数据类型
-
- [3.1 基础数据类型](#3.1 基础数据类型)
- [3.2 引用数据类型](#3.2 引用数据类型)
- [3.3 运算符&表达式](#3.3 运算符&表达式)
- [3.4 更多常用类型](#3.4 更多常用类型)
- 四、流程控制
-
- [4.1 条件语句](#4.1 条件语句)
- [4.2 异常处理](#4.2 异常处理)
- 五、循环迭代
-
- [5.1 常用语句](#5.1 常用语句)
- [5.2 其他用法](#5.2 其他用法)
- 六、函数
-
- [6.1 函数定义](#6.1 函数定义)
- [6.2 函数参数](#6.2 函数参数)
- [6.3 作用域&函数栈](#6.3 作用域&函数栈)
- 七、对象
-
- [7.1 概述](#7.1 概述)
- [7.2 this](#7.2 this)
- [7.3 原型](#7.3 原型)
- [7.4 更多用法](#7.4 更多用法)
- 八、类
- 九、DOM
-
- [9.1 概述](#9.1 概述)
- [9.2 事件](#9.2 事件)
- 十、BOM
-
- [10.1 概述](#10.1 概述)
- [10.2 核心对象](#10.2 核心对象)
- [10.3 本地存储](#10.3 本地存储)
- 十一、异步编程
-
- [11.1 回调](#11.1 回调)
- [11.2 异步](#11.2 异步)
一、前言
参考文档:https://developer.mozilla.org/zh-TW/docs/Web/JavaScript
https://www.w3schools.com/js/js_date_methods.asp
二、基本语法
2.1 注释
注释与其他许多语法都相似,分为单行注释和多行注释:
javascript
// 单行注释
/*
多行注释
*/
|----------------------------------------------------------------------------------------------------------|
| 备注:在一些 JavaScript 脚本中有像#!/usr/bin/env node 的第三种注释。 这种注释称为hashbang注释,被用来指定执行 JaveScript 脚本的引擎的路径。 |
为避免出错,可使用严格模式来严格规范语法:
javascript
'use strict';
2.2 变量
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰变量的声明▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
JavaScript有三种变量声明方式:不建议直接赋值,优先使用const
- var:可用于声明局部变量或者全局变量
- let:声明块级作用域的局部变量
- const:声明块级作用域的只读常量,不能改变值
也可以使用解构语法来声明变量:
javascript
let a, b, rest;
[a, b] = [10, 20];
console.log(a);
// 期望输出:10
console.log(b);
// 期望输出:20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// 期望输出:Array [30, 40, 50]
变量声明时如果没有初始化,会被赋值为undefined
;但是使用const来声明时需要初始化,否则会报SyntaxError错误。
变量有多种作用域:
- 全局作用域:当前文档
- 函数作用域:函数内部
- 块级作用域:花括号里
变量提升:使用var声明的变量可以先使用后声明,但是它的值不会提升
javascript
console.log(x === undefined); // true
var x = 3;
(function () {
console.log(x); // undefined
var x = "局部值";
})();
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰变量的命名▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
变量的命名规范:以字母、下划线或者美元符号开头,后续字符可以为数字;区分大小写,并使用Unicode字符集,以及不能与保留关键字同名:
- 驼峰命名规范:第一个单词首字母小写,其余单词首字母大写
- ruby风格的命名规范:单词之间使用
_
连接,每个单词都是小写
保留关键字:

三、数据类型
3.1 基础数据类型
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰概述▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
最新的 ECMAScript 标准定义了 8 种数据类型:
数据类型 | 描述 |
---|---|
number | 整数或浮点数 |
boolean | true和false |
null | 表示空值 |
undefined | 未定义值 |
bigint | 任意精度的整数 |
string | 字符序列 |
symbol | 实例是唯一且不可变的数据类型 |
可使用typeof
来判断变量类型:
javascript
var a = "string"
console.log(typeof a) //string
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰类型转换▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
使用+
运算符的表达式中有数字和字符串,会把数字转换为字符串,举个栗子:
javascript
x = "答案:" + 43 ; // 答案:43
使用其他运算符时,不会将数字转换为字符串:
javascript
"37" - 7; // 30
除此之外,还有一些方法也可以进行类型转换:
方法 | 描述 |
---|---|
parseInt(string,radix) |
默认将字符串解析为十进制整数,参数radix 可选,表示进制的基数 |
String(x) |
将x转换为字符串 |
Boolean(x) 或!!x |
将x转换为布尔型 |
Number(x) |
将x转换为Number类型,不能转换则返回NaN |
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰Number类型▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
数字均为双精度浮点类型,数字精度为53位;除了表示浮点数,还能表示三种符号值:+Infinity
正无穷、-Infinity
负无穷、NaN
非数字。
常用属性:
属性 | 描述 |
---|---|
Number.MAX_VALUE |
最大值 |
Number.MIN_VALUE |
最小值 |
Number.NaN |
非数字 |
常用方法:
方法 | 描述 |
---|---|
Number.isNaN(value) |
value是否为NaN |
Number.isInteger(value) |
value是否为整数 |
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰String类型▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
可以使用以下方式创建字符串:
javascript
const string1 = new String("A String object");
const string2 = "A String object";
有两种方式访问字符:索引从0开始
javascript
"cat".charAt(1); // "a"
"cat"[1]; // "a"
字符串的比较:区分大小写
javascript
// 大于和小于
const a = "a";
const b = "b";
if (a < b) {
// true
console.log(`${a} 小于 ${b}`);
}
字符串常用方法,更多方法参见https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String
方法 | 描述 |
---|---|
indexOf(searchString[, position]) |
返回查询的字符第一次出现的位置索引,参数position 指定搜索范围 |
replace(pattern, replacement) |
将多个匹配的pattern 被替换为replacement |
substring(indexStart[, indexEnd]) |
返回起始索引到结束索引(没有则字符串末尾)的部分,左开右闭 |
match(regexp) |
使用正则检索字符串 |
模板字符串:ES6引入的一种新的字符串语法,使用反引号包裹内容,可以嵌入变量和表达式
javascript
const name = "Alice";
const age = 25;
const message = `Name: ${name}, Age: ${age}`; // "Name: Alice, Age: 25"
const sum = `1 + 2 = ${1 + 2}`; // "1 + 2 = 3"
3.2 引用数据类型
|----------------|
| 引用数据类型保存的是内存地址 |
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰数组类型▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
数组创建:可以使用以下方法来创建一个数组:
javascript
// 构造函数
const arr1 = new Array(1,2,3); //[1,2,3]
// 字面量
const arr2 = [1,2,3];//[1,2,3]
// ES6新增方法
const arr3 = Array.from("hello")// ["h", "e", "l", "l", "o"](类数组转数组)
const arr4 = Array.of(3)// [3](避免单个数值的歧义)
如果要创建长度不为0,但又没有任何元素的数组:arrayLength
需要为整数
javascript
const arr1 = new Array(arrayLength);
// 或者
const arr3 = [];
arr3.length = arrayLength;
元素访问与修改:索引从0开始
javascript
// 索引访问
let arr =[1,2,3];
console.log(arr[0]); // 1
// 展开运算符
let sparseArr = [1, ,3];
console.log([...sparseArr]); // [ 1, undefined, 3 ]
// 元素修改
arr[1]=20
arr[5]=4; // 动态扩展,如果中间索引未定义则为空值
console.log(arr) // [ 1, 20, 3, <2 empty items>, 4 ]
数组属性: length
统计数组元素数量,如果给length赋值,会截断数组:
javascript
const cats = ["Dusty", "Misty", "Twiggy"];
console.log(cats.length); // 3
cats.length = 2;
console.log(cats); // [ 'Dusty', 'Misty' ] - Twiggy 已经被移除了
cats.length = 0;
console.log(cats); // 输出 [],猫名称的数组现在已经空了
cats.length = 3;
console.log(cats); // 输出 [ <3 empty items> ]
数组遍历 :可使用循环、forEach
和map
函数;forEach
和map
函数的区别在于map
函数会返回一个新数组。
javascript
const colors = ["red", "green", "blue"];
// 普通循环
for (let i = 0; i < colors.length; i++) {
console.log(colors[i]);
}
// for... of
for (let color of colors){
console.log(color)
}
// map方法
colors.map((color) => console.log(color));
// forEach方法
colors.forEach((color) => console.log(color));
forEach函数 :格式为forEach(callbackFn[,thisArg])
;参数callbackFn
为数组中每个元素执行的函数,并会丢弃它的返回值,该函数被调用时会传入以下参数:
element
:数组中正在处理的当前元素index
:数组中正在处理的当前元素的索引array
:调用了forEach
的数组
thisArg
:执行callbackFn
时用作this
的值
forEach返回值为undefined
,具体参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
数组常见方法一览:
常见方法 | 描述 |
---|---|
concat([value...]) |
连接两个数组或多个数组并返回一个新的数组 |
join([separator]) |
将数组中的所有元素连接成一个字符串,separator指定分隔数组元素的字符串 |
push([element...]) |
末尾添加元素,并返回length属性 |
pop() |
移除最后一个元素,并返回该元素 |
shift() |
移除第一个元素,并返回该元素 |
sort([compareFn]) |
对元素进行排序,并返回对数组的引用;compareFn定义排序顺序的函数 |
indexof(searchElement[,fromIndex]) |
搜索searchElement并返回第一个匹配的索引,fromIndex 为开始搜索的索引,默认从0开始 |
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰Map映射类型▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
map与object相似,但也有一些区别:map的key可以为任何类型;map没有默认的键
常用方法 | 描述 |
---|---|
new Map() |
创建map对象 |
set(key,value) |
存储数据 |
get(key) |
获取指定键的元素 |
size() |
返回键值对数量 |
clear() |
移除所有键值对 |
delete(key) |
删除指定键的元素,如果元素存在且被移除返回true |
has(key) |
判断指定键的元素是否存在 |
map的迭代可以使用for...of
或者forEach()
javascript
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
for (const key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// zero
// one
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
myMap.forEach((value,key)=>{
console.log(`${key} = ${value}`);
})
map的复制或合并:数据本身未被克隆
javascript
const original = new Map([[1, "one"]]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false. 浅比较 不为同一个对象的引用
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰Set集合▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
集合的值是唯一的,只能出现一次,值可以是任何类型。集合只有一个属性length
;
集合的基本方法:
方法 | 描述 |
---|---|
new Set() |
创建新集合 |
add() |
添加新元素 |
clear() |
删除所有元素 |
delete() |
删除一个元素 |
entries() |
返回一个迭代器,每次迭代会返回[value,value] 的键值对 |
has() |
判断值是否存在 |
keys() |
与values() 一样 |
values() |
可以遍历集合中所有元素 |
集合还可以像数学集合一样进行运算:

3.3 运算符&表达式
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰概述▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
以下是一些常用的运算符,更多见https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_operators
运算符 | 描述 |
---|---|
赋值运算符 | = 、+= 、-= 、*= 、/= 、%= 、**= 等 |
比较运算符 | == 、!= 、=== 、!=== 、> 、< 、>= 、<= |
算数运算符 | % 、++ 、-- 、- 、+ 、** |
位运算符 | & 、` |
逻辑运算符 | && 、` |
字符串运算符 | 连接运算符+ |
条件运算符 | 条件 ? 值1:值2 |
逗号运算符 | , ,从左到右求值,并返回最后一个操作数的值 |
一元运算符 | delete 、typeof 、in |
关系运算符 | in 、instanceof |
运算符优先级,从高到低:

▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰更多细节▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
比较运算符中的 ==
与 ===
: ==
是值相等,而===
是值相等且类型也相同。
算数运算符中的自增与自减 :如果++
在操作数前(++x
),则返回+1后的值;如果是在后面,则返回操作数原值再+1;自减也同理。
短路求值:逻辑表达式进行求值是从左到右,使用以下规则:
javascript
false && anything // 被短路求值为 false
true || anything // 被短路求值为 true
in
与instanceof
:in
操作符指如果指定属性在指定对象里会返回true;insatnceof
则是判断对象是否是指定的类型,是则返回true
javascript
// in 的使用
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees; // true
3 in trees; // true
6 in trees; // false
// instanceof 的使用
var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
console.log("yes") // yes
}
3.4 更多常用类型
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰数学对象▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
数学对象Math
没有构造函数,他是静态的。Math
有8个数学属性:
javascript
Math.E // 欧拉数
Math.PI // 圆周率
Math.SQRT2 //2的平方根
Math.SQRT1_2 // 1/2的平方根
Math.LN2 // 2的自然对数
Math.LN10 // 10的自然对数
Math.LOG2E // 2为底E的对数
Math.LOG10E // 10为底E的对数
常用方法一览:

随机数的常用方法:
javascript
// 返回随机整数 0-9
Math.floor(Math.random() * 10)
// 返回最小值和最大值之间的随机数,不包括最大值
function getRndInteger(min,max){
return Math.floor(Math.random() * (max - min) ) + min
}
// 返回最小值和最大值之间的随机数
function getRndInteger(min,max){
return Math.floor(Math.random() * (max - min + 1) ) + min
}
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰日期对象▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
Date 对象的范围是相对距离 UTC 1970 年 1 月 1 日 的前后 100,000,000 天
javascript
// 创建日期对象
var dateObjectName = new Date([parameters]);
不使用关键词new
会返回当前时间和日期的字符串,参数parameters
有多种选择:

Date对象方法可以分为以下几种:
方法 | 描述 |
---|---|
set | 设置日期和时间 |
get | 获取日期和时间 |
to | 返回字符串格式 |
parse和UTC | 解析Date字符串 |
获取日期的方法:

时间戳获取:
javascript
// 使用getTime方法
const date = new Date()
console.log(date.getTime())
// +new Date()
console.log(+new Date())
// Date.now():只能获取当前时间戳
console.log(Date.now())
四、流程控制
4.1 条件语句
JavaScript支持两种条件语句:if...else
和switch
condition
可以是任何能求值为true
或false
的表达式,false
值包括:false
、undefined
、null
、0
、NaN
、空字符串;其他则为true
javascript
if (condition1)
{
statement1;
}
else if (condition2)
{
statement2;
}
else {
statement3;
}
switch
语句:
javascript
switch (expression) {
case label1:
statements1;
break;
case label2:
statements2;
break;
// ...
default:
statementsDefault;
}
4.2 异常处理
通常使用throw
抛出异常,并用try...catch
来处理异常
throw
可以抛出任意表达式
javascript
throw "错误 2"; // 字符串类型
throw 42; // 数字类型
throw true; // 布尔类型
throw {
toString() {
return "我是一个对象";
},
};
try...catch
语句:使用try
来执行可能会有异常的操作,catch
来捕获异常,无论是否出错都要执行finally
,finally
语句可不写。
javascript
openMyFile();
try {
writeMyFile(theData); // 这可能会抛出错误
} catch (e) {
handleError(e); // 如果错误出现了,处理它
} finally {
closeMyFile(); // 总是关闭资源
}
可以使用name
和message
属性来获取error对象详细信息:
javascript
function doSomethingErrorProne() {
if (ourCodeMakesAMistake()) {
throw new Error("消息");
} else {
doSomethingToGetAJavaScriptError();
}
}
try {
doSomethingErrorProne();
} catch (e) {
// 现在,实际使用 `console.error()`
console.error(e.name); // 'Error'
console.error(e.message); // "消息",或者一个 JavaScript 错误消息
}
有六种错误类型:
错误 | 描述 |
---|---|
EvalError | eval() 函数发生错误 |
RangeError | 数字超出范围 |
ReferenceError | 非法引用(未声明的变量或对象属性/方法) |
SyntaxError | 语法错误 |
TypeError | 类型错误 |
URIError | encodeURI() 发生错误 |
五、循环迭代
5.1 常用语句
💫for语句
:基本格式如下所示,与C的for循环很相似:
javascript
for ([initialExpression]; [condition]; [incrementExpression])
statement
💫while语句
:每次执行前会检查是否满足条件
javascript
while (condition)
statement
💫do while
:在检查条件时会先执行一次,基本格式如下所示:
javascript
do
statement
while (condition);
💫label语句
:可以标记语句(块)
javascript
label :
statement
label
的值可以是任何非保留字的标识符,举个栗子:
javascript
var num = 0;
outPoint: for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
break outPoint; // 在 i = 5,j = 5 时,跳出所有循环,
// 返回到整个 outPoint 下方,继续执行
}
num++;
}
}
alert(num); // 输出 55
💫break
:终止循环、switch语句或者链接label语句,
- 如果不带label,会终止当前循环
- 如果带label,会终止指定的带标记的语句
javascript
break [label];
💫continue
:继续执行下一个循环、switch语句或者label语句,
- 如果不带label,会跳过当前循环
- 如果带label,会执行带标记的语句
javascript
continue [label];
5.2 其他用法
💫for...in
:循环一个指定的对象来循环一个对象所有可枚举的属性
javascript
for (variable in object) {
statements
}
💫for...of
:循环一个可迭代对象,例如数组
javascript
for (variable of object) {
statement
}
用代码来展示两者之间的区别:
javascript
let arr = [3, 5, 7];
arr.foo = "hello";
for (let i in arr) {
console.log(i); // 输出 "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // 输出 "3", "5", "7"
}
// 注意 for...of 的输出没有出现 "hello"
六、函数
6.1 函数定义
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰函数定义▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
一般函数 :通过function
关键词来声明函数;name
函数名、param
参数、statements
声明语句。如果函数没有return,则默认返回undefined
。
javascript
function name([param[, param[, ... param]]]) {
statements
}
函数表达式 :与函数声明非常相似,区别在于name
可以省略,省略后就成了匿名函数;
|-----------------------------------------------|
| 一些注意点:不能在函数定义之前调用;如果函数命名了,堆栈跟踪时会显示函数名,容易找到错误。 |
javascript
var myFunction = function name([param[, param[, ... param]]]) {
statements
}
IIFE:函数只使用一次
javascript
(function () {
statements;
})();
函数生成器:创建一个绑定到给定名称的新生成器函数,生成器函数可以退出,并在稍后重新进入,其上下文(变量绑定)会在重新进入时保存。
javascript
function* name([param[, param[, ...param]]]) {
statements
}
举个栗子:
javascript
function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value);
// 10
console.log(gen.next().value);
// 20
箭头函数表达式 :比传统的函数表达式更简洁,但不能用作构造函数,也不能使用yield,且没有this、arguments等,具体参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
javascript
([param] [, param]) => { statements }
一些注意点:
javascript
// 只有当函数只有一个简单参数时,才能省略括号
const bob1 = (a) => a + 100;
console.log(bob1(12)); // 输出: 112
const bob2 = a => a + 100;
console.log(bob2(12)); // 输出: 112
// 只有当函数直接返回表达式时,才可以省略大括号;
const bob3 = (a, b) => a + b + 100;
console.log(bob3(1, 2)); // 输出: 103
const bob4 = (a, b) => {
const chuck = 42;
return a + b + chuck;
};
console.log(bob4(1, 2)); // 输出: 45
// 只需指定一个表达式,将成为隐式返回值
const func = (x) => x * x;
const func2 = (x, y) => {
return x + y;
}; // 块体语法,需要明确返回值
// 返回字面量表达式
const fun1 = (uname) => ({ uname: uname });
console.log(fun1('Jack')); // 输出: { uname: 'Jack' }
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰函数构造▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
可使用new操作符创建function对象:arg1, arg2, ... argN
参数;functionBody
函数体
javascript
new Function (arg1, arg2, ... argN, functionBody)
生成器构造函数:
javascript
new GeneratorFunction (arg1, arg2, ... argN, functionBody)
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰内置函数▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
常用的有eval
、parseInt
等
6.2 函数参数
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰默认参数▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
函数默认参数为undefined
,也可以自定义。
javascript
function [name]([param1[ = defaultValue1 ][, ..., paramN[ = defaultValueN ]]]) {
statements
}
注意,不能将非默认参数位于默认参数之后,可能会导致未在预期范围的输出:
javascript
function f(x = 1, y) {
return [x, y];
}
f(); // [1, undefined]
f(2); // [2, undefined]
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰剩余参数▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
如果函数的最后一个命名参数以...
为前缀,则它将成为一个由剩余参数组成的真数组,其中从0
(包括)到theArgs.length
(排除)的元素由传递给函数的实际参数提供。即剩余参数只包含那些没有对应形参的实参。
javascript
function(a, b, ...theArgs) {
// ...
}
举个栗子来更好的理解他:
javascript
function fun1(...theArgs) {
alert(theArgs.length);
}
fun1(); // 弹出 "0", 因为 theArgs 没有元素
fun1(5); // 弹出 "1", 因为 theArgs 只有一个元素
fun1(5, 6, 7); // 弹出 "3", 因为 theArgs 有三个元素
function f(...[a, b, c]) {
return a + b + c;
}
f(1); // NaN (缺少两个参数b和c)
f(1, 2, 3); // 6
f(1, 2, 3, 4); // 6 (没有第四个元素)
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰arguments对象▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
对应于传递给函数的参数的类数组对象,除了length
属性和索引元素之外没有其他数组属性。
javascript
function func1(a, b, c) {
console.log(arguments[0]);
// 1
console.log(arguments[1]);
// 2
console.log(arguments[2]);
// 3
}
func1(1, 2, 3);
也可以通过索引赋值:
javascript
arguments[1]=4
6.3 作用域&函数栈
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰递归▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
递归就是函数自己调用自己,有三种方式:
javascript
// 函数名
function loop(x) {
if (x >= 10) {
return;
}
loop(x + 1);
}
// arguments.callee :不推荐使用
function create() {
return function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n - 1);
};
}
const result = create()(5); // 返回 120(5 * 4 * 3 * 2 * 1)
// 作用域内一个指向该函数的变量名
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰闭包▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
内部函数对外部函数是私有的,内部函数只可以在外部函数中访问,内部函数可以访问外部函数的参数和变量,但是外部函数不能使用内部函数的参数和变量。
javascript
function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
console.log(addSquares(2, 3)); // 13
console.log(addSquares(3, 4)); // 25
如果内部函数定义了一个和外部的某个变量名称相同的变量,那么这个闭包将无法引用外部作用域中的这个变量。以下程序就是错误的
javascript
const createPet = function (name) {
// 外部函数定义了一个名为"name"的变量。
return {
setName(name) {
// 闭包函数还定义了一个名为"name"的变量。
name = name;
},
};
};
七、对象
7.1 概述
对象是以key-value键值对的形式存在的集合,与python字典类型相似。key必须是字符串类型或者symbol类型,值可以是任意类型。
对象分为实例成员和静态成员,实例对象是通过构造函数创建的对象,他的属性和方法称为实例成员;静态成员是构造函数的属性和方法。
对象的创建方式:
javascript
// 字面量
const person1 = {
name: "Alice",
age: 23,
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
// 构造函数
const person2 = new Object();
person2.name = "Alice";
person2.age=23;
person2.greet = function() { console.log(`Hello, I'm ${this.name}`};
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car("Eagle", "Talon TSi", 1993);
// var mycar.nationality = "English" 报错,不能向现有构造函数添加新属性
// ES6语法
class User{
constructor(name){
this.name=name;
}
greet(){
console.log(`Hello, I'm ${this.name}`
}
}
const person3 = new User("Alice")
对象属性常用操作:
javascript
const person = {
name: "Alice",
age: 23,
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
// 访问属性
person.name
person["name"]
// 修改属性
person.job = "Engineer" //添加属性
person.age = 26; //修改属性
// 删除属性
delete person.job; //成功则返回true
// 检查属性是否存在
"name" in person; //true(包括原型链)
person.hasOwnProperty("name"); //true(仅自身属性)
// 遍历属性
for (const property in person){
console.log(person[property])
}
console.log(Object.getOwnPropertyNames(person))
常用静态方法,只有Object可以调用
方法 | 描述 |
---|---|
keys(obj) |
获取对象中所有属性(键),返回的是数组 |
values(obj) |
获取对象中的所有属性值 |
assign(target,source) |
常用于对象拷贝 |
entries(obj) |
返回键值对数组 |
fromEntries(obj) |
根据键值对列表创建一个对象 |
7.2 this
默认情况下this指向全局对象,如window;
javascript
let x = this;
在函数中this引用全局对象;但是在严格模式下,this是undefined
javascript
function myFunction() {
return this;
}
在对象方法中,this引用该对象;
javascript
const person = {
firstName : "John",
lastName : "Doe",
id : 5566,
myFunction : function() {
return this;
}
};
在事件中,this指接收事件的元素
javascript
<button onclick="this.style.display='none'">
Click to Remove Me!
</button>
除此之外,可以通过call
、apply
、bind
指定this
call
方法是预定义的JavaScript方法,可以用于对象作为参数来调用方法
javascript
const person = {
fullName: function(city, country) {
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
const person1 = {
firstName:"John",
lastName: "Doe"
}
person.fullName.call(person1, "Oslo", "Norway"); //
apply
方法与call方法类似,区别在于apply以数组的形式接收参数
javascript
const person = {
fullName: function(city, country) {
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
const person1 = {
firstName:"John",
lastName: "Doe"
}
person.fullName.apply(person1, ["Oslo", "Norway"]);
bind
方法可以让一个对象从另一个对象中借用方法
javascript
const person = {
firstName:"John",
lastName: "Doe",
fullName: function () {
return this.firstName + " " + this.lastName;
}
}
const member = {
firstName:"Hege",
lastName: "Nilsen",
}
let fullName = person.fullName.bind(member);
7.3 原型
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰原型▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
所有JavaScript对象都从原型继承属性和方法,例如Data对象继承自Date.prototype
、Array对象继承自Array.prototype
;而 Object.prototype
位于原型继承链的顶端。
可以使用prototype
属性来向对象构造函数添加新属性或者新方法:
javascript
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}
Person.prototype.nationality = "English";
Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
};
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰原型链▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
访问一个对象的属性时会先在自身属性中查找,如果没有找到会沿着__proto__
这条链向上查找,还是没找到则返回undefined
,这就是原型链,又称隐式原型链,它的作用就是查找对象的属性(方法)。
javascript
const parent = { a: 1 };
const child = Object.create(parent);
child.b = 2;
console.log(child.a); // 1(通过原型链访问)
7.4 更多用法
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰属性描述符▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
属性描述符可以用来控制行为
控制属性行为有:writable
可写、enumerable
可枚举、configurable
可配置
javascript
const obj = {};
Object.defineProperty(obj, "id", {
value: 123,
writable: false, // 不可修改
enumerable: true, // 可枚举(出现在 for-in 循环中)
configurable: false // 不可删除或重新配置
});
// 获取描述符
const descriptor = Object.getOwnPropertyDescriptor(obj, "id");
访问器属性可通过get
和set
定义属性的读写逻辑
javascript
const obj = {counter : 0};
Object.defineProperty(obj, "reset", {
get : function () {this.counter = 0;}
});
Object.defineProperty(obj, "increment", {
get : function () {this.counter++;}
});
Object.defineProperty(obj, "decrement", {
get : function () {this.counter--;}
});
Object.defineProperty(obj, "add", {
set : function (value) {this.counter += value;}
});
Object.defineProperty(obj, "subtract", {
set : function (value) {this.counter -= value;}
});
obj.reset;
obj.add = 5;
obj.subtract = 1;
obj.increment;
obj.decrement;
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰访问器▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
JavaScript使用Getters
和Setters
来定义对象访问器,它们可以确保更好的数据质量
javascript
const person = {
firstName: "John",
lastName: "Doe",
language: "en",
get lang() {
return this.language;
}
set lang(lang) {
this.language = lang;
}
};
person.lang = "en";
document.getElementById("demo").innerHTML = person.lang;
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰对象保护▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
Object.preventExtensions()
方法可防止向对象添加属性,可使用Object.isExtensible()
来检查对象是否可扩展。
javascript
const person = {firstName:"John", lastName:"Doe"};
Object.preventExtensions(person);
person.nationality = "English"; // 会报错
let answer = Object.isExtensible(person); // false
object.seal()
方法可防止添加或删除属性,可使用Object.isSealed()
来检查对象是否被密封。
javascript
"use strict"
const person = {
firstName: "John",
lastName: "Doe",
age: 50,
eyeColor: "blue"
};
Object.seal(person)
delete person.age; // 报错
let answer = Object.isSealed(person) // true
object.freeze()
方法可防止对对象进行修改,可使用Object.isFrozen()
来检查对象是否被冻结。
javascript
"use strict"
const person = {
firstName: "John",
lastName: "Doe",
age: 50,
eyeColor: "blue"
};
Object.freeze(person)
person.age = 51; // 报错
let answer = Object.isFrozen(person) //true
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰对象拷贝▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
对象的拷贝分为深浅拷贝
浅拷贝:只复制对象内存地址,类似指针,例如obj2=obj1
深拷贝:完全克隆,生成一个新对象obj3=JSON.parse(JSON.stringify(obj))
八、类
|------------------------|
| 类不是对象,是JavaScript对象的模板 |
创建类:总是添加constructor
方法,类似于python中的__init__
方法
javascript
class Car {
constructor(name, year) {
this.name = name;
this.year = year;
}
}
类继承:使用extends
关键词,会继承父类的所有方法,super
方法引用父类
javascript
class Car {
constructor(brand) {
this.carname = brand;
}
present() {
return 'I have a ' + this.carname;
}
}
class Model extends Car {
constructor(brand, mod) {
super(brand);
this.model = mod;
}
show() {
return this.present() + ', it is a ' + this.model;
}
}
let myCar = new Model("Ford", "Mustang");
document.getElementById("demo").innerHTML = myCar.show();
类也可以使用getters
和setters
,注意getter
/setter
方法的名称不能与属性的名称相同,可以在属性名前使用下划线
javascript
class Car {
constructor(brand) {
this.carname = brand;
}
get cnam() {
return this.carname;
}
set cnam(x) {
this.carname = x;
}
}
const myCar = new Car("Ford");
document.getElementById("demo").innerHTML = myCar.cnam;
静态类方法属于类本身,而不是类的实例,必须通过类名来调用,而不能通过实例来调用。
javascript
class Car {
constructor(name) {
this.name = name;
}
static hello() {
return "Hello!!";
}
}
const myCar = new Car("Ford");
document.getElementById("demo").innerHTML = Car.hello();
// document.getElementById("demo").innerHTML = myCar.hello();
// 报错
九、DOM
9.1 概述
DOM是用来呈现以及与任意 HTML 或 XML文档交互的API。当网页加载时,浏览器会创建页面的DOM树:

基本对象类型:
数据类型 | 描述 |
---|---|
document | 任何在浏览器中载入的网页,并作为网页内容的入口 |
node | 文档中的每个对象 |
element | 一个元素或由dom api成员返回的element类型的节点 |
nodelist | 元素组成的节点列表 |
attr | 元素的属性 |
namednodemap |
document
对象常用方法:https://developer.mozilla.org/zh-CN/docs/Web/API/Document#方法
方法 | 描述 |
---|---|
querySelector(selectors) |
返回文档中与指定选择器或选择器组匹配的第一个element对象 |
querySelectorAll(selectors) |
返回与指定的选择器组匹配的文档中的元素列表对象NodeList |
createElement(tagName[,options]) |
创建一个由标签名称tagName指定的HTML元素;options参数可选,是包含一个属性名为is的对象 |
getElementById(id) |
返回匹配id属性的element对象 |
element
常用属性:
属性 | 描述 |
---|---|
innerText |
文字内容,不解析标签 |
innerHTML |
文本内容,解析标签 |
href |
链接 |
title |
标题 |
src |
图片链接 |
style.样式属性 |
CSS样式 |
className |
类名,修改类名时会覆盖先前的值 |
classList |
类名,修改类名时不会覆盖先前的值 |
在html5中有专门的data-自定义属性
,在DOM对象上一律以dataset
对象方式获取:
javascript
<body>
<div class="box" data-id="10">1</div>
<script>
const box=document.querySelector('.box')
console.log(box.dataset.id)
</script>
</body>
9.2 事件
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰事件监听▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
事件监听三要素:事件源(谁)、事件类型(方式)、事件调用的函数(做什么);
语法:元素对象.addEventListener('事件类型',要执行的函数)
事件监听版本:区别在于on方式会被覆盖,而addEventListener
可绑定多次,推荐使用。
- DOM L0:
事件源.on事件=function(){}
- DOM L2:
事件源.addEventListener(事件,事件处理函数)
事件类型:
鼠标事件:click、mouseenter、mouseleave、mouseover、mouseout
💡mouseover、mouseout有冒泡效果;而mouseenter、mouseleave没有
焦点事件:focus、blur
键盘事件:keydown、keyup
剪切板事件:copy、cut、paste
页面加载事件:load、DOMContentLoaded
💡load:页面所有资源(外部资源,比如图片、视频)加载完毕;DOMContentLoaded:页面DOM加载完毕
事件对象:事件触发时的相关信息;一般命名为event
、ev
、e
;
示例:元素.addEventListener('click',function(e){})
常用属性 | 描述 |
---|---|
type |
事件类型 |
clientX/clientY |
获取光标相对于浏览器可见窗口左上角的位置 |
offsetX/offsetY |
获取光标相对于当前DOM元素左上角的位置 |
key |
用户按下的键盘键的值,不推荐使用keyCode |
target |
触发事件的元素 |
currentTarget |
绑定事件的元素 |
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰事件流▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
事件流是指事件完整执行过程中的流动路径,有两个阶段:捕获阶段、冒泡阶段(实际工作都是使用事件冒泡为主)

1️⃣事件捕获:从DOM根元素开始执行对应的事件,较少使用捕获机制;L0事件监听则只有冒泡阶段;
示例:DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
2️⃣事件冒泡:依次向上调用所有父级元素;默认存在
3️⃣阻止冒泡:把 事件限制在当前元素内,就需要阻止事件冒泡
javascript
事件对象.stopPropagation(),此方法对捕获阶段也有效
事件对象.preventDefault(),阻止元素默认行为,比如阻止链接的跳转
4️⃣解绑事件:
javascript
// on事件方式直接使用null覆盖
事件对象.onclick=null
// addEventListener方式
// 注意:匿名函数无法被解绑
removeEventListener(事件类型,事件处理函数,[获取捕获或者冒泡阶段])
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰事件委托▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
事件委托是利用事件流的特征解决一些开发需求的知识技巧,利用事件冒泡的特点给父元素注册事件。
语法:事件对象.target.tagName
javascript
const ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
if (e.target.tagName === 'LI'){
this.style.color = 'pink'
}
})
十、BOM
10.1 概述
浏览器对象模型BOM是浏览器提供的与当前窗口或标签页交互的对象集合,用于操作浏览器窗口本身的行为(如导航、窗口尺寸、历史记录等)。
10.2 核心对象
window对象是BOM的顶层对象,所有全局变量和函数都是window的属性和方法
窗口控制:
javascript
window.open("https://example.com"); // 打开新窗口
window.close(); // 关闭当前窗口(需用户允许)
window.resizeTo(800, 600); // 调整窗口尺寸
弹出框:
javascript
window.alert("sometext") // 警告框
window.confirm("sometext") // 确认框
window.prompt("sometext","defaultText") // 提示框
延时器: setTimeout
可以用来让代码延迟执行,仅执行一次;格式:setTimeout(回调函数,等待的毫秒数)
定时器 :能够重复执行代码;开启定时器let 变量名 = setInterval(函数,间隔的时间)
,单位是毫秒;关闭定时器clearInterval(变量名)
,满足一定时间后停止
javascript
const timeoutId = setTimeout(() => {}, 1000); // 单次定时
const intervalId = setInterval(() => {}, 1000); // 循环定时
clearTimeout(timeoutId); // 清除定时器
location对象拆分并保存了URL地址的各个组成部分
属性/方法 | 描述 |
---|---|
href |
用于地址的跳转 |
search |
获取地址中携带的参数,? 后的参数 |
hash |
获取地址中的哈希值,# 后的参数 |
reload() |
刷新当前页面,传入true 表示强制刷新 |
navigator对象记录了浏览器自身的相关信息
属性/方法 | 描述 |
---|---|
userAgent |
浏览器版本及平台 |
platform |
操作系统 |
language |
浏览器语言 |
cookieEnabled |
是否启用cookie |
screen对象记录了浏览器屏幕信息
属性/方法 | 描述 |
---|---|
width |
屏幕宽度(像素) |
height |
屏幕高度 |
availWidth |
可用宽度(扣除任务栏等) |
history对象记录了浏览器历史记录操作
属性/方法 | 描述 |
---|---|
back(step) |
后退step步 |
forward(step) |
前进step步 |
go(参数) |
前进后退,参数为正前进;参数为负后退 |
10.3 本地存储
特点:
- 数据存储在用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage和localStorage约5M左右
分类:本地存储可以分为localStorage
和sessionStorage
💫localStorage:可以将数据永久存储在本地(用户的电脑),除非手动删除;可以多页面共享,以键值对的形式使用
方法 | 描述 |
---|---|
setItem(key,value) |
存储数据 |
getItem(key) |
获取数据 |
removeItem(key) |
删除数据 |
💫sessionStorage:生命周期为关闭浏览器窗口;同一界面下可以共享;键值对存储,用法跟localStorage基本相同。
补充:由于本地只能存储字符串,就引出了存储复杂数据类型的问题
解决:
JSON.stringify(复杂数据类型)
,但是又会有一个问题,无法直接修改对应的数据
解决:
JSON.parse(JSON字符串)
十一、异步编程
11.1 回调
回调是作为参数传递给另一个函数的函数,myDisplayer
就是回调函数,函数作为参数传递时,不要使用括号。
javascript
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2, myCallback) {
let sum = num1 + num2;
myCallback(sum);
}
myCalculator(5, 5, myDisplayer);
11.2 异步
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰概述▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
回调通常与异步一起使用;通过回调函数实现异步,异步任务会添加到任务队列中;异步任务有三种类型:
任务 | 描述 |
---|---|
普通事件 | 如click、resize |
资源加载 | 如load、error |
定时器 | 如setInterval、setTimeout |
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰Promise对象▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
可以用Promise对象来实现异步编程,Promise对象有两种属性:state和result,注意它们是无法访问的,必须使用Promise方法来处理promise

Promise使用示例:异步操作成功时会传回value,异步操作失败时会传回error
javascript
let myPromise = new Promise(function(myResolve, myReject) {
myResolve(); // 操作成功时执行
myReject(); // 操作失败时执行
});
myPromise.then(
function(value) { /* 状态为fulfilled时执行 */ },
function(error) { /* 状态为rejected时执行 */ }
);
▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰async▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
async通常与await结合使用,async使函数返回一个Promise,await使函数等待Promise
javascript
async function myFunction() {
return "Hello";
}
myFunction().then(
function(value) {myDisplayer(value);}
);
await关键字只能在async函数内部使用,他会让函数暂停执行,直到资源准备完毕。
javascript
async function myDisplay() {
let myPromise = new Promise(function(resolve) {
setTimeout(function() {resolve("I love You !!");}, 3000);
});
document.getElementById("demo").innerHTML = await myPromise;
}
myDisplay();