JavaScript 学习手册
1. JavaScript 基础入门
1.1. JavaScript 注释
// 单行注释
/* 多行注释 */
/**
* 更为美观的多行注释
*/
1.2. 字面量
字面量是一种直接在程序中使用的数据值。比如:
12
1.2
"hello world"
‘Hi‘
true
false
null
1.3. 标识符和保留字
标识符常用于在 JavaScript
代码中命名常量、变量、属性、函数以及类,JavaScript
必须以字母
、下划线(_)
、或美元符号($)
开头。后续字符可以是字母、数字、下划线或美元符号。例如:
i
my_variable_name
_dump
$str
1.4. 保留字
与其他语言一样,JavaScript
为语言自身使用而保留了一些标识符,这些标识符称为保留字,其不能作为常量、变量、函数或类的名字,但是可以作为对象的属性名。这些保留字有:
as const export get null target void
async continue extends if of this while
await debugger false import return throw with
break default finally in set true yield
case delete for instanceof static try arguments
catch do from let super typeof eval
class else function new switch var
1.5. Unicode
JavaScript
程序是使用 Unicode
字符集编写的,因此在字符串和注释中可以使用任意 Unicode
字符。考虑可移植性,建议使用标识符的命名只使用 ASCII
字母和数字。
1.5.1 Unicode 转义序列
某些计算机或硬件无法全部正确的显示 Unicode
全部字符,因此 JavaScript
定义了转义序列,从而可以仅使用 ASCII
字符来表示 Unicode
字符。
Unicode
转义序列以 \u
开头,后面跟 4 位十六进制数字(包括大小写字母),或包含在一对花括号内的 1~ 6
位十六进制数。
JavaScript 早起版本只支持 4 位数字转义序列。ES6 版本新增带花括号的版本,以便更好地支持大于 16 位的 Unicode 码点。比如表情符号:
console.log("\u{1F600}"); // => ??
1.6. 可选的分号
在 JavaScript 中使用分号 ;
分隔语句,这对于保持代码清晰很重要,如果没有分隔符,一条语句的开头和结尾就会很混乱。如果两条语句分别写在两行,在 JavaScript 中是可以省略它们之间的分号的。例如:
var name = "alice";
var a = 10
var b = 20
建议语句结束使用分号,这样的编程规范可以试程序在语义上更清晰。
2. 类型、值和变量
2.1. 概述与定义
JavaScript
类型可以分为两类:原始类型和对象类型。
原始类型包含数值、字符串、和布尔值。JavaScript
中的特殊值 null
和 undefined
也是原始值。它们是 JavaScript
比较唯一的存在。在 ES6 中新增了一种特殊类型叫 Symbol
(符号),后续会讲解。
在 JavaScript
中,任何不是数值、字符串、布尔值、符号、null、undefined 的值都是对象。对象是属性的集合,其中每个属性都有一个名字和一个值。还有一种特殊类型叫数组,它与对象的不同之处是,它是无需集合。
在内存管理方面,JavaScript
解释器会执行自动垃圾回收。当一个值无法以任何方式引用时,解释器就会释放它占用的内存。
JavaScript
支持面向对象编程。在 JavaScript
中,对象是可修改的 (mutable
),而原始类型是不可修改的 (immutable
)。
2.2. 数值
JavaScript 的主要数值类型用于表示整数和近似实数。
2.2.1. 整数字面量
0
10
10000
除了十进制数值,JavaScript 也支持十六进制数值:
0xff // => 255
0XAE6C // => 44652
在 ES6 版本以后,也可以使用二进制和八进制表示整数
0b10101 // => 21
0o377 // => 255
2.2.2. 浮点数
浮点数可以包含小数点,他们对实数使用传统语法。实数值是由数值的整数部分、小数点、以及数值的小数部分组成。例如:
3.14
200.0
.3333
6.23E23
1.42E-28 // => 1.42 x 10的-28次方
2.2.3. Math 数学算术
console.log(Math.pow(2, 4)); // 表示2的4次方
console.log(Math.round(1.5)); // 四舍五入
console.log(Math.ceil(-1.4)); // 向上取整
console.log(Math.floor(1.62)); // 向下取整
console.log(Math.abs(-1)); // 取绝对值
console.log(Math.max(1, 3, 5, 7, 9)); // 最大值
console.log(Math.min(1, 3, 5, 7, 9)); // 最小值
console.log(Math.random()); // 伪随机数,取值范围 [0, 1.0)
console.log(Math.PI); // 圆周率
console.log(Math.E); // 自然对数的底数
console.log(Math.sqrt(2)); // 求2的平方根
console.log(Math.pow(3, 1 / 3)); // 求立方根
console.log(Math.sin(0)); // 三角函数,还有Math.sin、Math.atan等
console.log(Math.log(10)); // 10的自然数对
2.2.4. BigInt数值
// BigInt数值可以表示任意精度的整数
console.log(BigInt(Number.MAX_SAFE_INTEGER));
console.log("1" + "0".repeat(10)); // 1 后边 10 个零
console.log(100n + 200n);
console.log(3000n - 2000n);
console.log(3000n / 997n);
console.log(3000n % 997n);
2.2.5. 时间和日期
let timestamp = Date.now(); // 当前时间戳
console.log(timestamp);
let now = new Date(); // 当前日期对象
console.log(now);
let ms = now.getTime(); // 转换为毫秒时间戳
console.log(ms);
let iso = now.toISOString(); // 转换为标准格式的字符串
let love = "\ud83d\udc99"; //这是一个长度为 2 的 Unicode 字符
console.log(love);
2.3 文本
2.3.1. 字符串字面量
console.log("testing"); // 也可以使用单引号
let str = `曾经沧海难为水,
除却巫山不是云`;
console.log(str);
2.3.2. 转义字符
console.log("\xA9"); // 十六进制的Unicode编码字符,表示版权符号
console.log("\u03c0"); // \u03c0 表示字符π,是4位十六进制的Unicode编码字符
console.log("\u{1f600}"); // \u{1f600} 表示笑口常开,是6位十六进制Unicode编码字符
2.3.3. 字符串
let msg = "你好, " + "JavaScript世界!"; // 使用 + 号进行字符串拼接
console.log(msg);
let s = "hello, World";
// 获取字符串的一部分
console.log(s.length); // 取字符串长度
console.log(s.substring(1, 4)); // 获取子串,字符串的索引是从 0 开始的
console.log(s.slice(1, 4)); // 对字符串进行切片
console.log(s.slice(-3)); // 取最后3个字符
console.log(s.split(",")); // 以逗号进行分割,返回一个数组
// 搜索字符串
console.log(s.indexOf("o")); // 返回第一个搜索到的位置
console.log(s.indexOf("o", 5)); // 指定了从哪个位置开始进行搜索
console.log(s.indexOf("so")); // 没有找到返回-1
console.log(s.lastIndexOf("l")); // 最后一次出现的位置
// ES6 及之后的版本中,布尔值搜索函数
console.log(s.startsWith("hell")); // 以什么开始
console.log(s.endsWith("d")); // 以什么结尾
console.log(s.includes("or")); // 包含子串"or"
// 创建字符串的修改版本
console.log(s.replace("ello", "i")); // 替换字符串
console.log(s.toLowerCase()); // 转换为小写
console.log(s.toUpperCase()); // 转换为大写
console.log(s.normalize()); // Unicode NFC 归一化:ES6新增
// 访问字符串中的个别(16位值)字符
console.log(s.charAt(0));
console.log(s.charAt(s.length - 1));
console.log(s.charCodeAt(0)); // 指定位置的16位数
// ES2017 新增字符串填充
console.log("x".padStart(3)); // 在左侧添加空格,字符串长度变为3
console.log("x".padEnd(3)); // 在右侧添加空格,字符串长度变为3
console.log("x".padStart(3, "*")); // 在左侧添加*号,字符串长度变为3
console.log("x".padEnd(3, "-")); // 在右侧添加-号,字符串长度变为3
// 删除空格函数
console.log(" test ".trim()); // 删除开头和结尾的空格
console.log(" test ".trimStart()); // 删除左侧空格,也叫trimLeft
console.log(" test ".trimEnd()); // 删除右侧空格,也叫trimRight
// 未分类的字符串方法
console.log(s.concat("!!!")); // 字符串拼接
console.log("=".repeat(5)); // 重复5次,ES6新增
2.2.9. 模式匹配
let text = "testing: 1, 2, 3";
let pattern = /\d+/g;
console.log(pattern.test(text)); // true, 存在匹配项
console.log(text.search(pattern)); // 返回匹配的第一个位置
console.log(text.match(pattern)); // ==> [1, 2, 3], 所有匹配结果的数组
console.log(text.replace(pattern, "#")); // ==> testing: #, #, #
console.log(text.split(/\D+/)); // ==> [ ‘‘, ‘1‘, ‘2‘, ‘3‘ ] 以非数字进行拆分
2.4. 布尔值
// 以下值被转换为布尔值时,转换结果都是false
console.log(Boolean(undefined));
console.log(Boolean(null));
console.log(Boolean(0));
console.log(Boolean(-0));
console.log(Boolean(NaN));
console.log(Boolean(""));
// 其他转换结果都为true
console.log(Boolean([]));
2.5. null 和 undefined
- null 通常表示某个值不存在,他是一种特殊的对象
- undefined 也表示值不存在,它体现在如下情况:
- 变量定义后未初始化
- 查询不存在的对象属性或数组元素值
- 没有明确返回值的函数的返回值
- 没有传值的函数的参数值
2.6. 符号 Symbol
符号是 ES6 新增的一种原始数据类型
let strname = "string name"; // 字符串可以作为属性名
let symname = Symbol("propname"); // 符号也可以作为属性名
console.log(typeof strname);
console.log(typeof symname);
let o = {};
o[strname] = 1;
o[symname] = 2;
console.log(o[strname]);
console.log(o[symname]);
// 符号的 toString 方法
let sym1 = Symbol("symO");
console.log(sym1.toString());
// Symbol.for 方法
let sym2 = Symbol.for("shared");
let sym3 = Symbol.for("shared");
console.log(sym2 === sym3);
console.log(sym2.toString());
console.log(Symbol.keyFor(sym3)); // 反查字符串
2.7. 全局对象
JavaScript程序在浏览器加载时都会有一个全局对象并对其添加一组初始化属性。
这些属性有全局常量、全局函数、构造函数
全局常量:undefined、Infinity、NaN
全局函数:isNaN()、parseInt()、eval()
全局构造函数:Date()、RegExp()、String()、Object()、Array()
不同场景中的js全局对象
1、在 Node 程序中,通过 global 全局对象属性来引用全局对象
2、在浏览器中,Window对象是全局对象,通过 window 全局对象属性来引用全局对象
3、ES2020 规定,globalThis 为任何上下文的全局对象引用
2.8. 不可修改的原始值与可以修改的对象引用
js中的
原始值
是不可修改
的,比如:undefined、null、布尔值、数值、字符串
其中,字符串看似可以修改某个索引位置的值,但js中不允许这么做,所以对字符串的
操作其实都返回了新的字符串。
对象是可以修改的,而且对象不是按值进行比较的,即使有时候,两个对象有相同的属
和值。只有当两个对象引用同一个底层对象时,两个对象才是相等的。
如果想创建对象的副本,必须显示复制对象的属性或者数组的元素。
如果要比较两个不同对象或者数组,必须比较他们的属性或元素。
// 创建数组副本
let a = ["a", "b", "c"];
let b = [];
for (let i = 0; i < a.length; i++) {
b[i] = a[i];
}
let c = Array.from(b); // ES6中,可以使用Array.from()复制数组
console.log(a, b, c);
console.log(a === b); // => false
console.log(a === c); // => false
console.log(b === c); // => false
function equalArray(a, b) {
if (a === b) return true;
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
// 比较数组的值是否相等
console.log(equalArray(a, b)); // => true
2.9. 类型转换
转换:严格相等转换 === 判等操作 ==
显示转换:Boolean()、Number()、String()
console.log(Number("3"));
console.log(String(false));
console.log(Boolean([])); // ==> true
// Number 和 String 之间的转换
let n = 10;
let binary = "0b" + n.toString(2); // ==> 0b1010
let octal = "0o" + n.toString(8); // ==> 0o12
let hex = "0x" + n.toString(16); // ==> 0xa
console.log(binary, octal, hex);
// 使用金融或科学数据的数值转换
let num1 = 9882347.237;
console.log(num1.toFixed(0)); // ==> "9882347" 保留到整数位
console.log(num1.toFixed(2)); // ==> "9882347.24" 保留两位小数
console.log(num1.toFixed(5)); // ==> "9882347.23700" 不够保留5位小数补零
console.log(num1.toExponential(1)); // ==> "9.9e+6" 采用指数计数
console.log(num1.toExponential(4)); // ==> "9.8823e+6"
console.log(num1.toPrecision(7)); // ==> "9882347" 按有效数字个数转换
console.log(num1.toPrecision(10)); // ==> "9882347.237"
// 字符串转换为数值:parseInt() parseFloat()
console.log(parseInt(" 3 hello"));
console.log(parseFloat("3.14 metrics"));
console.log(parseInt("1.41"));
console.log(parseInt("0xFF"));
console.log(parseInt(".0xFF"));
console.log(parseFloat(".1"));
console.log(parseInt("0.1"));
console.log(parseInt(".1"));
console.log(parseFloat("$72.1"));
// parseInt 可以接收第二个参数,用于指定数值基底,范围 2 ~ 36
console.log(parseInt("10", 2));
console.log(parseInt("18", 8));
console.log(parseInt("0xff", 16));
2.9.1. 对象到原始的转换
对象到原始值的转换有三种基本算法:偏字符串、偏数值、无偏好
js中,除了Date类型,其他都实现了偏数值算法,Date类实现了偏字符串算法
对象转换成布尔值
- 所有的对象都转换为 true
// toString() 和 valueOf() 方法
// 所有的对象都会继承这两个转换为原始值的方法
// toString() 返回对象的字符串
console.log(["a", "b", "c"].toString()); // => a,b,c
console.log(
(() => {
console.log("你好");
}).toString()
);
// () => {
// console.log("你好");
// }
console.log(Date.now().toString()); // => 1628208654979
console.log(/hi/i.toString()); // => /hi/i
// valueOf 1
let date1 = new Date();
console.log(date1.valueOf()); // => 1628208744158
// 只有一个元素的数组做类型转换时,先转为为该元素对应的字符串,如果数组只有一个数值,则该数值先
// 转换为字符串,再转换回数值。
console.log(Number([])); // => 0
console.log(Number([99])); // => 99
2.10. 变量声明与赋值
ES6 之前使用
var
关键字进行变量声明。
ES6 之后使用let
、const
关键字声明变量,两者的区别是const
必须在声明时初始化为常量。
常量的值是不能改变的,否则会抛出TypeError异常
// 解构赋值 在等号右边的值是数组或对象,左手边通过模拟数组或对象字面量语法定义一个或多个变量
let [x, y] = [1, 2];
console.log(x, y);
[x, y] = [x + 1, y + 1];
console.log(x, y);
// 解构赋值便利对象
let o1 = { x: 1, y: 2 };
for (const [name, value] of Object.entries(o1)) {
console.log(name, value);
}
// 左侧变量个数可与右侧变量个数不一致,左侧变量列表可以换包含额外的逗号,以便跳过某些值:
let [a1, b1] = [1];
console.log(a1, b1);
[x, y] = [1, 2, 3];
console.log(x, y);
[, x, , y] = [10, 20, 30, 40];
console.log(x, y);
// 如果想把所有未使用的或剩余的值收集到一个变量中,可以在左侧最后一个变量名前加上3个点(...)
let [x1, ...y1] = [10, 20, 30, 40, 50];
console.log(x1, y1);
// 数组解构并不要求它一定是数组,右侧可以是任何可迭代对象
let [first, ...rest] = "Hello"; // =>
console.log(first, rest); // => H [ ‘e‘, ‘l‘, ‘l‘, ‘o‘ ]
// 解构赋值右侧可以是对象,此时左侧是一个包含在花括号内逗号分隔的变量名列表
let person = {name: "alice", age: 18, address: ‘hangzhou‘};
let { name, address, age } = person;
console.log(name); // => alice
console.log(age); // => 18