Javascript基础
1.HelloWorld
1.1<script>
标签
<!DOCTYPE html>
<html>
<body>
<script type="script">
document.body.innerHTML = "<h1>Hello World</h1>";
console.log("Hello World");
</script>
</body>
</html>
1.2.独立js文件
document.body.innerHTML = "<h1>Hello World</h1>";
console.log("Hello World");
<!DOCTYPE html>
<html>
<body>
<!-- script引入外部后,内部无法再写代码,不会被浏览器解析-->
<script type="text/javascript" src="./hello.js"></script>
</body>
</html>
1.3.html标签
<!DOCTYPE html>
<html>
<body>
<button onclick="alert("Hello World")"></button>
<a href="javascript:alert("Hello World")"></a>
</body>
</html>
2.基础语法
2.1语法格式
- 严格区分大小写。
- 语句以
;
·分号结尾,如果不写,浏览器会自动添加。但可能造成错误。 - JS会忽略多个空格和换行。
- 引号、括号闭合。
代码注释:
/* 多行注释 多行注释 */ //单行注释
2.2.变量声明
2.2.1.示例
// 使用 let 声明变量(块级作用域)
let age = 25;
// 使用 const 声明常量(不可重新赋值)
const PI = 3.14159;
// 不推荐使用 var(函数级作用域,易引发问题)
var name = "Alice";
2.2.2.标识符
使用规则:
- 必须以 字母(A-Z/a-z)、下划线(
_
) 或 美元符号($
) 开头。 - 后续字符可以是 字母、数字(0-9)、下划线或美元符号。
- 不区分大小写(但推荐统一风格,如驼峰命名法
myVariable
或下划线分隔my_variable
)。 - 允许使用 Unicode 转义序列(如
\uXXXX
),但需谨慎使用。 命名规范:
- 语义清晰:用名称表达用途(如
userAge
而非a
)。 - 驼峰命名法:多单词变量/函数使用小驼峰(
getUserName
)。 - 常量全大写:常量名用全大写加下划线(
MAX_LENGTH
)。 - 避免缩写:除非是广泛认可的缩写(如
id
、URL
)。
- 语义清晰:用名称表达用途(如
- 禁止使用保留字(见下文)。
// 合法标识符
let userName = "Alice";
const _privateVar = 10;
function calculateTotal() {}
// 非法标识符(会报错)
let 2ndPlace = "Silver"; // 以数字开头
const class = "Math"; // 使用保留字
严格保留字(Strict Mode Reserved Words)
在代码中添加 'use strict';
后,以下关键字禁止使用:
关键字 | 用途 |
---|---|
implements | 接口实现(ECMAScript 6+) |
interface | 接口定义 |
let | 块级作用域变量声明 |
private | 类私有字段(实验性) |
public | 类公共字段(实验性) |
static | 类静态方法/属性 |
yield | 生成器函数关键字 |
await | 异步函数关键字 |
2. 未来保留字(Future Reserved Words)
目前可用,但未来可能成为保留字,建议避免使用:
关键字 | 用途 |
---|---|
enum | 枚举类型(ECMAScript 6+ 提案) |
await | 异步函数关键字(已部分实现) |
implements | 接口实现(ECMAScript 6+) |
3. 其他保留字
以下关键字已在标准中明确定义,不可用作标识符:
关键字 | 用途 |
---|---|
break | 循环/switch 跳出 |
case | switch 分支 |
catch | 异常捕获 |
class | 类定义 |
const | 常量声明 |
continue | 循环继续 |
debugger | 调试断点 |
default | switch 默认分支、模块默认导出 |
delete | 删除对象属性 |
do | do...while 循环 |
else | if 条件分支 |
export | 模块导出 |
extends | 类继承 |
finally | try...catch 最终执行块 |
for | for 循环 |
function | 函数声明 |
if | 条件判断 |
import | 模块导入 |
in | 成员检测、循环遍历 |
instanceof | 类型检测 |
let | 块级变量声明 |
new | 对象实例化 |
return | 函数返回值 |
super | 类父级引用 |
switch | 多分支选择 |
this | 当前上下文对象 |
throw | 抛出异常 |
try | 异常处理 |
typeof | 类型检测 |
var | 函数级变量声明 |
void | 返回无值 |
while | while 循环 |
with | 对象作用域扩展(不推荐使用 |
2.2.3.var、let和const区别
ES5(2009年):引入 var
作为唯一的变量声明关键字。
ES6(2015年):新增 let
和 const
,引入块级作用域,逐步取代 var
。
特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域(Function Scope) | 块级作用域(Block Scope) | 块级作用域(Block Scope) |
变量提升 | 提升到函数或全局顶部 | 不提升(存在暂时性死区) | 不提升(存在暂时性死区) |
重复声明 | 允许 | 禁止 | 禁止 |
重新赋值 | 允许 | 允许 | 禁止(声明时必须初始化) |
内存分配 | 动态分配(可修改) | 动态分配(可修改) | 静态分配(不可修改) |
作用域
var
:作用域为最近的函数或全局范围。function test() { if (true) { var x = 10; } console.log(x); // 输出 10(x 在函数内有效) }
let
/const
:作用域为最近的代码块({}
)。function test() { if (true) { let y = 20; const z = 30; } console.log(y); // 报错(y 未定义) console.log(z); // 报错(z 未定义) }
变量提升(Hoisting)
var
:变量声明会被提升到顶部,但赋值不会。console.log(a); // undefined(声明被提升) var a = 5;
let
/const
:存在“暂时性死区”(Temporal Dead Zone, TDZ),声明前无法访问。console.log(b); // ReferenceError(TDZ) let b = 10;
重复声明
var
:允许重复声明同一个变量。var c = 1; var c = 2; // 合法但危险
let
/const
:禁止重复声明。let d = 1; let d = 2; // SyntaxError
重新赋值
var
/let
:允许重新赋值。let e = 5; e = 10; // 合法
const
:声明时必须初始化,且不可重新赋值。const f = 10; f = 20; // TypeError
3.数据类型
类型 | 示例 | 说明 |
---|---|---|
数字 | let num = 10; | 整数、浮点数均可,不区分类型。 |
字符串 | let str = "Hello"; | 单引号、双引号或反引号包裹。 |
布尔值 | let isTrue = true; | true 或 false 。 |
数组 | let arr = [1, "a", true]; | 用 [] 定义,元素类型可不同。 |
对象 | let obj = { name: "Bob" }; | 键值对集合,用 {} 定义。 |
null/undefined | let n = null; let u; | null 表示空值,undefined 表示未定义。 |
3.1.数值
let num = 10
3.1.1.Number
- 所有数字(包括整数和浮点数)均以 64 位双精度浮点数 存储。
- 格式:
符号位(1位) + 指数(11位) + 尾数(52位)
。 范围:
- 安全整数范围:
-(2^53 - 1)
到2^53 - 1
(即-9007199254740991
到9007199254740991
)。 最大/最小值:
Number.MAX_VALUE = 1.7976931348623157e+308; // 最大正数 Number.MIN_VALUE = 5e-324; // 最小正数 const a = 9007199254740991; // 安全最大值 const b = a + 1; // 可能不精确 console.log(b === a + 1); // false(超出安全范围)
- 安全整数范围:
精度问题
超过安全整数范围时,连续整数可能无法精确表示。
console.log(0.1 + 0.2); // 输出 0.30000000000000004 // 解决方案: console.log((0.1 + 0.2).toFixed(2)); // "0.30"(保留两位小数)
- 浮点数运算可能产生舍入误差(如
0.1 + 0.2 !== 0.3
)。
3.1.2.特殊值
值 | 描述 | 检测方法 |
---|---|---|
NaN | 非数字(Not-a-Number),如 0/0 或 parseInt("abc") 。 | isNaN(value) (全局)或 Number.isNaN(value) (ES6 更精确) |
Infinity | 正无穷大(如 1 / 0 )。 | value === Infinity |
-Infinity | 负无穷大(如 -1 / 0 )。 | value === -Infinity |
-0 | 负零(与 0 在大多数运算中等价,但可通过 Object.is() 区分)。 | Object.is(-0, 0) → false |
Math 对象常用方法:
Math.random(); // 生成 [0, 1) 的随机数
Math.floor(3.7); // 向下取整 → 3
Math.ceil(3.2); // 向上取整 → 4
Math.round(3.5); // 四舍五入 → 4
Math.max(1, 5, 3); // 最大值 → 5
Math.min(-1, -5, 0); // 最小值 → -5
// 数学运算
console.log(Math.sqrt(16)); // 4
console.log(Math.pow(2, 3)); // 8
// 特殊值处理
console.log(isNaN(NaN)); // true(全局 isNaN)
console.log(Number.isNaN(NaN)); // true(ES6 更精确)
// 精度问题
console.log(0.1 + 0.2); // 0.30000000000000004
console.log((0.1 + 0.2).toFixed(2)); // "0.30"
3.1.5.Bigint
值:用
n
后缀表示的大整数(如100n
)。特点:
- 解决
Number
的精度问题(如超过2^53
的整数)。 - 与
Number
类型不兼容,需显式转换。
示例:
const big = 9007199254740991n + 1n; // 9007199254740992n console.log(big + 1); // 报错,需用 big + 1n
- 解决
3.2.字符串
3.2.1.定义
字符串一旦创建,内容不可修改。所有操作会生成新字符串:
const str1 = "Hello World"; // 双引号
const str2 = 'Hello World'; // 单引号
const str3 = `Hello World`; // 反引号(模板字符串)可以跨行
const str4 = `Hello World ${str3}`;//嵌入变量
let s = "abc";
s[0] = "x"; // 无效,严格模式下会报错
console.log(s); // 输出 "abc"
s = "def"; //生成新字符串
3.2.2.常用操作
拼接与重复
const name = "Alice"; const greeting = "Hello, " + name + "!"; // 模板字符串更推荐 console.log(greeting); // 输出 "Hello, Alice!" // 重复字符串 console.log("abc".repeat(3)); // "abcabcabc"
截取与拼接
const text = "JavaScript is awesome!"; console.log(text.slice(0, 10)); // "JavaScript"(左闭右开) console.log(text.substring(0, 10)); // 同 slice console.log(text.split(" ")); // ["JavaScript", "is", "awesome!"]
查找与替换
const str = "Hello, World!"; console.log(str.indexOf("World")); // 7(返回起始索引) console.log(str.includes("World")); // true(ES6+) // 替换 console.log(str.replace("World", "JS")); // "Hello, JS!" console.log(str.replaceAll("l", "L")); // "HeLLo, WorLD!"(ES2021+)
大小写转换
const str = "Hello World!"; console.log(str.toUpperCase()); // "HELLO WORLD!" console.log(str.toLowerCase()); // "hello world!"
字符串遍历
const str = "abc"; for (let char of str) { // 使用 for...of 遍历字符 console.log(char); }
字符串填充
console.log("5".padStart(3, "0")); // "005"(左侧填充) console.log("5".padEnd(3, "0")); // "500"(右侧填充)
字符串截取进阶
const text = "Hello World!"; console.log(text.startsWith("Hello")); // true console.log(text.endsWith("!")); // true console.log(text.includes("World")); // true
3.2.3字符串转义
转义序列 | 作用 | 示例 |
---|---|---|
\" | 双引号(" ) | "他说:\"你好!\"" |
\' | 单引号(' ) | '他说:\'你好!\'' |
\\ | 反斜杠(\ ) | "路径:C:\\Users\\" |
` | 换行符 | `"第一行 |
第二行"` | ||
\t | 制表符(Tab) | "列1\t列2" |
\r | 回车符(Carriage Return) | `"Windows 换行: |
\r"` | ||
\b | 退格符 | "删除前一个字符\b" |
\f | 换页符 | "分页符\f" |
3.3.布尔值
以下值在逻辑判断中会被视为 false
:
值 | 类型 | 说明 |
---|---|---|
false | 布尔值 | 逻辑假 |
0 | 数字 | 零 |
"" | 字符串 | 空字符串 |
null | 对象 | 主动置空 |
undefined | undefined | 未定义 |
NaN | 数字 | 非数字 |
//`0`、`-0`、`NaN`、`""`(空字符串)、`null`、`undefined` 转换为 `false`
console.log(Boolean(0)); // false(Falsy 值)
console.log(Boolean("")); // false
console.log(Boolean([])); // true(对象始终为 Truthy)
console.log(Boolean({})); // true
if (0) { /* 不会执行 */ }
if ("") { /* 不会执行 */ }
3.4.空值
3.4.1.undefined
- 含义:表示变量已声明但未赋值,或函数未返回值。
常见场景:
let name; // 声明未赋值 → name 的值为 undefined function foo() {} // 无返回值 → foo() 返回 undefined
检测方法:
console.log(typeof name); // "undefined" console.log(name === undefined); // true
3.4.2.null
- 含义:表示主动赋值的“空”或“无”,通常用于明确清空值。
常见场景:
let user = { name: "Alice" }; user = null; // 主动置空
检测方法:
console.log(user === null); // true console.log(typeof null); // "object"(历史遗留问题,需注意)
3.4.3.对比
型 | 检测方法 | 典型场景 |
---|---|---|
undefined | value === undefined | 变量未声明或函数无返回值 |
null | value === null | 主动置空或明确表示“无” |
空字符串 | str.length === 0 | 输入框未填写 |
空数组 | arr.length === 0 | 数据列表初始状态 |
空对象 | Object.keys(obj).length === 0 | 配置对象未初始化 |
3.4.4.空值处理
检查
null
或undefined
const value = null; if (value == null) { // 等价于 (value === null || value === undefined) console.log("值为 null 或 undefined"); }
默认值设置
const name = inputName || "匿名"; // 如果 inputName 是假值,则用 "匿名" const age = parseInt(inputAge) ?? 0; // 空值或 NaN 时用 0(ES2020+)
安全访问深层对象
const user = { profile: { address: null } }; // 避免报错:user.profile.address.city → 可能抛出错误 const city = user?.profile?.address?.city ?? "未知"; // 使用可选链和空值合并
3.5.Symbol
3.5.1.定义
用于创建 唯一且不可变的标识符。
基础创建
const sym1 = Symbol(); // 无描述的 Symbol const sym2 = Symbol("description"); // 带描述的 Symbol
全局 Symbol 注册表
通过
Symbol.for()
创建全局共享的 Symbol,可通过Symbol.keyFor()
查找:const globalSym = Symbol.for("sharedKey"); console.log(Symbol.keyFor(globalSym)); // "sharedKey"
特性:
唯一性
每个Symbol
值都是全局唯一的,即使描述相同,两个Symbol
也不相等:const sym1 = Symbol("key"); const sym2 = Symbol("key"); console.log(sym1 === sym2); // false
- 不可变性
Symbol 的描述(description)不可修改,且一旦创建无法销毁。 原始类型
Symbol 是原始类型,不能通过new
关键字实例化:const sym = Symbol(); // 正确 // const sym = new Symbol(); // 报错
3.5.2.内置常量
JavaScript 提供了一些内置的 Symbol 常量,用于定义语言行为:
Symbol 常量 | 用途 |
---|---|
Symbol.iterator | 定义对象的默认迭代器 |
Symbol.toStringTag | 自定义对象的 toString() 结果(如 "[object MyType]" ) |
Symbol.hasInstance | 定义 instanceof 操作符的行为 |
Symbol.unscopables | 控制 with 语句中哪些属性不可访问 |
Symbol.species | 指定构造函数创建实例时的替代构造函数 |
3.5.3.应用场景
对象属性的唯一键
避免对象属性名冲突(尤其适用于第三方库或多人协作项目):
const user = { [Symbol("id")]: 123, name: "Alice" }; console.log(user[Symbol("id")]); // undefined(不同 Symbol 实例) const PLUGIN_SYM = Symbol("plugin");
定义私有属性
结合闭包或
WeakMap
实现私有字段:const privateData = new WeakMap(); class User { constructor(name) { privateData.set(this, { name }); } getName() { return privateData.get(this).name; } }
迭代器协议
通过
Symbol.iterator
定义对象的默认迭代行为:const iterable = { [Symbol.iterator]() { let step = 0; return { next: () => ({ value: step++, done: step > 2 }) }; } }; for (const value of iterable) { console.log(value); // 0, 1, 2 }
自定义
toStringTag
通过
Symbol.toStringTag
定制对象的toString()
结果:class MyClass { get [Symbol.toStringTag]() { return "MyClass"; } } console.log(new MyClass().toString()); // "[object MyClass]"
3.5.4.常见问题
Symbol 与字符串的混淆
const sym = Symbol("key"); console.log(typeof sym); // "symbol"(不是字符串) console.log(sym.toString()); // "Symbol(key)"
无法直接序列化
Symbol 无法被 JSON 序列化:
const obj = { [Symbol("id")]: 123 }; console.log(JSON.stringify(obj)); // "{}"
作为对象键的限制
Symbol 作为对象键时,无法通过常规方式枚举:
const obj = { [Symbol("id")]: 123, name: "Alice" }; console.log(Object.keys(obj)); // ["name"](Symbol 键被忽略) console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id)]
3.6.类型转换
3.6.1转换为布尔值
隐式转换:
- Falsy 值(自动转为
false
):false
、0
、""
(空字符串)、null
、undefined
、NaN
。 Truthy 值(自动转为
true
):- 非空字符串(如
"0"
、" "
) - 非零数字(如
1
、-1
) - 数组(如
[]
) - 对象(如
{}
) - 函数(如
function() {}
)
- 非空字符串(如
if ("0") { console.log("Truthy"); } // 输出(字符串非空)
if (0) { console.log("Falsy"); } // 不输出(0 是 falsy)
if ([]) { console.log("Truthy"); } // 输出(数组是对象)
主动调用:
方法 | 说明 | 示例 |
---|---|---|
Boolean(value) | 强制转换为布尔值 | Boolean(0) → false |
!!value | 双重否定转换 | !!0 → false |
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean([])); // true(空数组是对象,对象为 Truthy)
console.log(Boolean({})); // true(空对象也是 Truthy)
console.log(!!0); // false
console.log(!!"hello"); // true
console.log(!![]); // true
console.log(!!{}); // true
3.6.2.转换为字符串
方法 | 说明 | 示例 |
---|---|---|
String(value) | 强制转换为字符串 | String(123) → "123" |
value.toString() | 对象调用 toString() 方法 | (123).toString() → "123" |
${value} | 模板字符串转换 | `${123} → "123"` |
//String()
console.log(String(123)); // "123"
console.log(String(true)); // "true"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"
console.log(String(Symbol("id"))); // "Symbol(id)"
//toString
console.log((123).toString()); // "123"
console.log(true.toString()); // "true"
console.log((NaN).toString()); // "NaN"
console.log({}.toString()); // "[object Object]"
//${}
const num = 456;
console.log(`Value: ${num}`); // "Value: 456"
console.log(String(NaN)); // "NaN"
console.log(String(Infinity)); // "Infinity"
console.log(String(undefined)); // "undefined"
console.log(String(null)); // "null"
隐式转换:
字符串拼接
console.log("Number: " + 123); // "Number: 123" console.log("Boolean: " + true); // "Boolean: true"
模板字符串插值:
const obj = { name: "Alice" }; console.log(`User: ${obj}`); // "User: [object Object]"
alert()
或prompt()
:alert(123); // 显示 "123"
3.6.3.转换为数值
转换为 Number
方法 描述 示例 Number(value)
强制转换(严格转换,非数字返回 NaN
)。Number("123") → 123
parseInt(string)
解析字符串为整数(可指定进制)。 parseInt("10.5") → 10
parseFloat(string)
解析字符串为浮点数。 parseFloat("3.14") → 3.14
+value
一元加号转换(隐式转换)。 +"123" → 123
console.log(Number("")); // 0
console.log(Number(" ")); // 0
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number("123")); // 123
// parseInt
console.log(parseInt("123.45")); // 123(截断小数)
console.log(parseInt("12a3", 10)); // 12(遇到非数字字符停止解析)
//parseFloat
console.log(parseFloat("12.34")); // 12.34
console.log(parseFloat("12.3.4")); // 12.3(截断到第二个小数点)
//一元加号转换(隐式转换)。
console.log(+"123"); // 123
console.log(+""); // 0
console.log(+"abc"); // NaN
console.log(+null); // 0
console.log(+undefined); // NaN
4.运算符
4.1.算术运算符
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 10 - 4 | 6 |
* | 乘法 | 2 * 3.5 | 7 |
/ | 触发 | 10 / 3 | 3.333... (浮点数) |
% | 取模 | 10 % 3 | 1 |
** | 幂运算 | 2 ** 3 | 8 (指数运算) |
console.log(5 + 3); // 8(数字相加)
console.log("5" + 3); // "53"(字符串拼接)
console.log([1, 2] + { a: 3 }); // "1,2[object Object]"(类型转换后拼接)
console.log("5" + 3 + 2); // "532"(字符串拼接优先级高于加法)
console.log(5 + 3 + "2"); // "82"(先数字相加,再拼接)
console.log(9007199254740991 + 1); // 9007199254740992(超过安全整数范围)
console.log(10 - 4); // 6
console.log("10" - 4); // 6(字符串转数字)
console.log("10a" - 4); // NaN(无法转换)
console.log(10 / 0); // Infinity
console.log(-10 / 0); // -Infinity
console.log(3 * 4); // 12
console.log("3" * "4"); // 12
console.log("3" * true); // 3(`true` 转为 1)
console.log(10 / 2); // 5
console.log(10 / 3); // 3.333...
console.log("10" / 3); // 3.333...
console.log("10" / "2"); // 5
console.log(10 % 3); // 1(10 ÷ 3 = 3 余 1)
console.log(-10 % 3); // -1(-10 ÷ 3 = -4 余 -1)
console.log(10 % -3); // 1(余数符号与被除数一致)
console.log(7 % 3); // 1
console.log(-7 % 3); // -1(余数符号与被除数一致)
console.log(7 % -3); // 1(余数符号与被除数一致)
console.log(2 ** 3); // 8(2 的 3 次方)
console.log(10 ** -1); // 0.1(10 的 -1 次方)
console.log(2 ** 0.5); // 1.414...(平方根)
4.2.赋值运算符
运算符 | 示例 | 等价表达式 | 说明 |
---|---|---|---|
+= | x += 5 | x = x + 5 | 加法后赋值 |
-= | x -= 3 | x = x - 3 | 减法后赋值 |
*= | x *= 2 | x = x * 2 | 乘法后赋值 |
/= | x /= 4 | x = x / 4 | 除法后赋值 |
%= | x %= 7 | x = x % 7 | 取余后赋值 |
**= | x **= 3 | x = x ** 3 | 指数运算后赋值(ES7+) |
<<= | x <<= 2 | x = x << 2 | 左移后赋值 |
>>= | x >>= 1 | x = x >> 1 | 算术右移后赋值 |
>>>= | x >>>= 3 | x = x >>> 3 | 逻辑右移后赋值(无符号) |
&= | x &= 0b1100 | x = x & 0b1100 | 按位与后赋值 |
^= | x ^= 0b1010 | x = x ^ 0b1010 | 按位异或后赋值 |
` | = ` | `x | = 0b0011` |
??= | x ??= 10 | x = x ?? 10 | 若 x 为 null/undefined ,则赋值右侧值 |
&&= | x &&= y | x = x && (x = y) | 若 x 为真值,才赋值右侧值(需注意语法) |
let num = 10;
num += 5; // 等价于 num = num + 5 → 15
let num = 10;
num -= 3; // 等价于 num = num - 3 → 7
let num = 5; // 二进制 101
num <<= 1; // 左移一位 → 1010 (10)
4.3.逻辑运算符
运算符 | 作用 | 返回值类型 | 短路行为 | ||
---|---|---|---|---|---|
&& | 两侧都为真时返回右侧,否则左侧 | 原始操作数 | 是 | ||
` | ` | 两侧任意一侧为真,返回真 | ` | 左侧为真时返回左侧,否则右侧 | |
! | 取反布尔值 | 布尔值 (true /false ) | 否 | ||
?? | 左侧为 null/undefined 返回右侧 | 原始操作数 | 否 |
!!
将任意值强制转换为布尔值:
const value = "hello";
console.log(!!value); // true(非空字符串是 truthy)
逻辑运算符的返回值
&&
返回第一个falsy
值或最后一个值:console.log(5 && 3); // 3(均为 truthy) console.log(0 && 3); // 0(0 是 falsy) console.log("" && null);// ""(空字符串是 falsy)
||
返回第一个truthy
值或最后一个值:console.log(5 || 3); // 5(均为 truthy) console.log(0 || 3); // 3(0 是 falsy) console.log("" || null);// null(均为 falsy)
常见用法:
//条件判断
const age = 20;
if (age >= 18 && age < 60) {
console.log("成年且未退休");
}
//默认值设置
const username = inputUsername || "匿名用户";
//安全访问深层对象属性
const city = user?.profile?.address?.city ?? "未知";
//优先级
console.log(true || false && false); // true(`&&` 优先级高于 `||`)
// 等价于 true || (false && false)
4.4.关系运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
> | 大于 | 5 > 3 | true |
< | 小于 | 3 < 5 | true |
>= | 大于等于 | 5 >= 5 | true |
<= | 小于等于 | 3 <= 5 | true |
== | 等于(弱相等,隐式转换) | 5 == "5" | true |
!= | 不等于(弱不等) | 5 != "5" | false |
=== | 全等(严格相等) | 5 === "5" | false |
!== | 不全等(严格不等) | 5 !== "5" | true |
- 数值与非数值比较,会转换数值后再比较
- 字符串比较时,比较字符的Unicode编码
- null和undefined相等比较返回true
- ===比较两个值是否全等,比较两个值的类型不同时直接返回false
- ==会先将比较内容转换为相同类型,通常转换为数值
console.log(5 == "5"); // true(字符串 "5" 转换为数字 5)
console.log(5 === "5"); // false(类型不同)
console.log(null == undefined); // true(特殊规则)
console.log(null === undefined); // false(类型不同)
console.log(0 == ""); // true(0 和空字符串均转为 0)
console.log("" == false); // true(空字符串和 false 均转为 0)
console.log(NaN == NaN); // false(NaN 不等于任何值,包括自身)
console.log(NaN === NaN); // false
条件运算符
条件?表达式1:表达式2
let a = 200;
let b = 100;
a > b ? console.log(a) : console.log(b)
5.if语句
5.1.语法
if (condition) {
// 当 condition 为 true 时执行的代码
} else if {
//当条件不满足的其他另一情况
} else {
//当上述条件都不满足
}
说明:
condition
:布尔表达式,结果为true
或false
。- 若
condition
为true
,执行代码块;否则退出if
语句。 - JavaScript 会将非布尔值隐式转换为布尔值(Truthy/Falsy)。
- 语句块中调用return方法后,退出if语句。其他条件分支则不会执行。
if (age < 18) {
console.log("未成年");
return;
} else if (age >= 18 && age < 60) {
console.log("成年且未退休");
} else {
console.log("退休");
}
6.switch语句
6.1.基础语法
switch (expression) {
case value1:
// 当 expression === value1 时执行
break;
case value2:
// 当 expression === value2 时执行
break;
default:
// 当所有 case 都不匹配时执行
}
说明:
expression
:任意表达式(变量、函数调用、计算式等)。case
:匹配expression
的值,若匹配则执行对应代码块。break
:跳出switch
,防止代码继续执行后续case
(必须显式添加)。default
:可选分支,当所有case
不匹配时执行。- 严格相等(
===
):case
的值必须与expression
严格相等。 - 若不写
break
,代码会继续执行后续case
const fruit = "apple";
switch (fruit) {
case "apple":
console.log("Red or Green?");
break;
case "banana":
console.log("Yellow");
break;
default:
console.log("Unknown fruit");
}
6.2.ES6新特性
直接返回值,无需 break
:
const day = 3;
const result = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Other day";
};
console.log(result); // "Wednesday"
多个 case
合并
const status = 200;
switch (true) {
case (status >= 200 && status < 300):
console.log("Success");
break;
case (status === 404):
console.log("Not Found");
break;
default:
console.log("Error");
}
7.while语句
7.1.基础语法
while (condition) {
// 当 condition 为 true 时执行的代码块
}
说明:
condition
:布尔表达式,每次循环开始时检查。若为true
,执行循环体;否则退出循环。- 循环体:缩进的代码块,可包含任意语句。
执行流程:
1.检查条件 → 2.条件为
true
则执行循环体 → 3.重复步骤 1,直到条件为false
。- 死循环:
while(true){}
7.2.do...while
会先执行一次循环体,再检查条件,至少执行一次:
let count = 0;
do {
console.log("至少执行一次:", count);
count++;
} while (count < 3);
// 输出:0,1,2
7.3.循环控制
continue:跳过当前循环的剩余代码,进入下一次循环:
let i = 0; while (i < 5) { i++; if (i === 3) continue; // 跳过 i=3 if (i === 5) break; // 当 i=5 时退出 console.log("Skipped:", i); } // 输出:1,2,4
break:立即跳出当前循环。
let num = 0; while (num < 10) { num++; if (num === 5) break; // 当 num=5 时退出循环 if (num === 8) break; // 当 num=8 时退出循环 console.log("Current value:", num); } // 输出:1,2,3,4
嵌套循环的控制
outer: while (true) {
inner: while (true) {
break outer; // 直接退出外层循环
}
}
8.for循环
8.1.基础语法
for (initialization; condition; iteration) {
// 循环体
}
说明:
initialization
:循环变量的初始化(仅在循环开始前执行一次)。condition
:布尔表达式,每次循环开始前检查。若为true
,执行循环体;否则退出循环。iteration
:迭代表达式,每次循环结束后执行(通常用于更新变量)。- 三个表达式都可以省略。
执行流程:1.初始化变量 → 2. 检查条件 → 3. 条件为 true
则执行循环体 → 4. 执行迭代表达式 → 重复步骤 2。
for (let i = 0; i < 5; i++) {
console.log(i); // 输出 0, 1, 2, 3, 4
}
//死循环
for (;;) {}
嵌套:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 2; j++) {
console.log(`i=${i}, j=${j}`);
}
}
// 输出:i=0,j=0 → i=0,j=1 → i=1,j=0 → ...
8.2.ES6新特性
for...of
遍历可迭代对象(如数组、字符串、Map 等):
const arr = [1, 2, 3]; for (const value of arr) { console.log(value); // 输出 1, 2, 3 } const str = "hello"; for (const char of str) { console.log(char); // 输出 h, e, l, l, o }
for...in
遍历对象的可枚举属性(包括原型链属性):
const obj = { a: 1, b: 2 }; for (const key in obj) { console.log(key); // 输出 "a", "b" }
循环控制同while
9.对象
9.1.定义
对象字面量
const person = { name: "Alice", age: 25, greet() { console.log(`Hi, I'm ${this.name}`); } };
构造函数
function Person(name, age) { this.name = name; this.age = age; this.greet = function() { console.log(`Hi, I'm ${this.name}`); }; } const alice = new Person("Alice", 25);
Object.create()
const proto = { greet() { console.log("Hello!"); } }; const obj = Object.create(proto); obj.greet(); // 输出 "Hello!"
ES6 Class 语法
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hi, I'm ${this.name}`); } } const bob = new Person("Bob", 30);
9.2.常用操作
属性访问与修改
console.log(person.name); // "Alice" person.age = 26; // 修改属性 person["address"] = "New York"; // 动态添加属性
计算属性名(ES6)
const key = "email"; const user = { [key]: "alice@example.com" }; console.log(user.email); // "alice@example.com"
删除属性
delete person.age; // 移除 age 属性
对象遍历
for...in
循环for (const key in person) { if (person.hasOwnProperty(key)) { // 过滤原型链属性 console.log(`${key}: ${person[key]}`); } }
Object.keys()
/values()
/entries()
console.log(Object.keys(person)); // ["name", "greet"] console.log(Object.values(person)); // ["Alice", greet 函数] console.log(Object.entries(person)); // [["name", "Alice"], ["greet", ƒ]]
for...of
与迭代器const obj = { [Symbol.iterator]() { let step = 0; return { next: () => ({ value: step++, done: step > 2 }) }; } }; for (const value of obj) { console.log(value); // 0, 1, 2 }
9.3.Window对象
Window
对象是浏览器中的全局对象,代表当前浏览器窗口或标签页。它是 JavaScript 运行环境的核心,所有全局变量、函数和对象均属于 Window
对象的属性和方法。
9.3.1.属性
属性 | 描述 |
---|---|
window.navigator | 提供浏览器信息(如名称、版本、操作系统等) |
window.location | 获取或设置当前 URL,支持跳转、刷新等操作 |
window.history | 管理浏览历史记录(前进、后退、跳转) |
window.screen | 获取屏幕信息(分辨率、颜色深度等) |
window.document | 代表当前页面的文档对象(DOM 树的入口) |
window.frames | 获取所有子框架(<iframe> )的集合 |
window.parent | 获取当前窗口的父窗口(若为 <iframe> ) |
console.log(window.navigator.userAgent); // 浏览器用户代理字符串
console.log(window.location.href); // 当前页面 URL
const element = window.document.getElementById("myElement");
9.3.2.方法
方法 | 描述 |
---|---|
setTimeout(fn, ms) | 延迟执行函数 fn ,超时时间为 ms 毫秒 |
setInterval(fn, ms) | 每隔 ms 毫秒重复执行函数 fn |
clearTimeout(id) | 清除由 setTimeout 设置的定时器 |
clearInterval(id) | 清除由 setInterval 设置的定时器 |
const timer = setTimeout(() => {
console.log("延迟 2 秒执行");
}, 2000);
clearTimeout(timer); // 取消定时器
方法 | 描述 |
---|---|
window.open(url) | 打开新窗口或标签页 |
window.close() | 关闭当前窗口(仅限通过 open() 打开的窗口) |
window.alert(msg) | 显示警告对话框 |
window.confirm(msg) | 显示确认对话框,返回布尔值 |
window.prompt(msg) | 显示输入对话框,返回用户输入值 |
window.open("https://www.example.com");s
9.4.原型对象
9.4.1.原型概念
- 每个函数(构造函数)都有一个
prototype
属性,指向一个原型对象。 - 通过
new
创建的实例对象的[[Prototype]]
(即__proto__
)会指向构造函数的prototype
对象。 - 原型对象本身也是一个普通对象,可以包含属性和方法。
static
方法属于类本身,不挂载到原型。- 普通方法挂载到原型对象。
function Person(name) {
this.name = name;
}
const p1= new Person('1');
// 原型对象
console.log(Person.prototype); // Person {}
console.log(Object.getPrototypeOf(p1))
console.log(p1.__proto__);
9.4.2.原型作用
将一个该类实例中所有的公共属性(方法)统一存储到原型,这样只需创建一个属性,即可被所有实例访问
通过修改原型链实现继承:
class Student extends Person {
constructor(name, grade) {
super(name);
this.grade = grade;
}
}
const bob = new Student("Bob", 12);
bob.sayHi(); // 继承自 Person 的方法
将方法定义在原型上,避免每个实例重复创建:
function Person(name) {
this.name = name;
}
Person.prototype.sharedMethod = function() {
console.log("Shared method");
};
const alice = new Person("Alice");
const bob = new Person("Bob");
console.log(alice.sharedMethod === bob.sharedMethod); // true(同一函数引用)
9.4.3.原型属性查找规则
当访问对象的属性时,JavaScript 引擎会:
- 在对象自身查找。
- 若未找到,沿
[[Prototype]]
链向上查找,直到原型链末端(null
)。
示例:
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
const alice = new Person("Alice");
alice.sayHi(); // "Hi, I'm Alice"(通过原型链找到方法)
原型链的层级
- 所有对象最终通过原型链继承自
Object.prototype
。 Object.prototype
的[[Prototype]]
为null
,形成链的终点。
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
9.4.4.操作原型
修改原型对象
- 通过构造函数的
prototype
属性添加方法或属性,所有实例共享这些属性。 - 注意:直接修改
Object.prototype
会污染全局对象,应避免。
// 添加方法到原型
Person.prototype.age = 30;
alice.age; // 30(所有实例共享)
使用 Object.create()
通过 Object.create(proto)
创建一个新对象,并指定其原型:
const bob = Object.create(Person.prototype);
bob.name = "Bob";
bob.sayHi(); // "Hi, I'm Bob"
10.函数
10.1.定义
函数(Function)也是一个对象
创建方式:
// 函数声明(函数名可复用)
function greet(name) {
return `Hello, ${name}!`;
}
// 函数表达式(匿名函数赋值给变量)
const sayGoodbye = function(name) {
return `Goodbye, ${name}!`;
};
// 箭头函数(ES6+)
const add = () => console.log(111);
函数调用:
function(name){
return 'Hello' + name;
}
console.log(greet("Alice")); // "Hello, Alice!"
console.log(sayGoodbye("Bob")); // "Goodbye, Bob!"
console.log(add(2, 3)); // 5
10.2.参数定义
默认参数:如果调用方未传入参数。
function greet(name = "Guest") { return `Hello, ${name}!`; } console.log(greet()); // "Hello, Guest!"
剩余参数(Rest Parameters):接收可变参数
function sum(...numbers) { return numbers.reduce((acc, num) => acc + num, 0); } console.log(sum(1, 2, 3, 4)); // 10
解构赋值参数:
function printUser({ name, age }) { console.log(`Name: ${name}, Age: ${age}`); } printUser({ name: "Alice", age: 25 }); // "Name: Alice, Age: 25"
函数参数:
function executeTwice(fn) { fn(); fn(); } executeTwice(() => console.log("Hello!")); // 输出两次 "Hello!"
注意事项:
- JS不会检查参数类型,可以传递任意类型。
- 实参多余形参,多余的实参不会使用。
- 形参多余实参,多余的形参为undefined。
- 箭头函数只有一个参数时,括号可以省略。
function hello(name, content){
alert(`hello,${name},${content}`);
}
hello('sam');//hello,sam,undefined
const read = content => {}
10.3.返回值
- 显式返回:使用
return
返回值。 - 隐式返回:箭头函数若无
return
,默认返回undefined
。 - 无返回值:未使用
return
或返回undefined
。
function multiply(a, b) {
return a * b; // 显式返回
}
const divide = (a, b) => a / b; // 隐式返回
特殊情况:
返回函数:
function createAdder(a) { return function(b) { return a + b; }; } const add5 = createAdder(5); console.log(add5(3)); // 8
箭头函数返回值:可以直接写在箭头后
const sum = (a, b) => a + b //直接返回对象时,需要用()包裹 const fn = () => ({name:"sam"})
10.4.作用域
10.4.1.词法作用域(Lexical Scope)
函数内部可以访问外部作用域的变量,但外部无法访问内部变量。
const outerVar = "I am outer";
function innerFunction() {
const innerVar = "I am inner";
console.log(outerVar); // 输出 "I am outer"
}
innerFunction();
console.log(innerVar); // 报错(innerVar 未定义)
10.4.2.闭包(Closure)
闭包是函数与其词法环境的组合,允许函数访问外部变量。
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
JS会优先选择当前作用域的变量进行使用
let a = 1; let b = 1; { let a = 2; let sum = a + b; }
10.5.立即执行函数
格式:(function)()
//立即执行,且只会调用一次
(function(){
let a = 10;
console.log(111);
}());
10.6.this关键字
10.6.1.普通函数
默认绑定:
在非严格模式下,this
默认指向全局对象(浏览器中是window
,Node.js 中是global
)。
在严格模式下,this
为undefined
。function foo() { console.log(this); } foo(); // 非严格模式:window;严格模式:undefined
隐式绑定:
当函数作为对象的方法调用时,this
指向调用该方法的对象。const obj = { name: "Alice", sayHi() { console.log(this.name); } }; obj.sayHi(); // "Alice"
显式绑定:
使用call
、apply
或bind
显式指定this
。function greet(greeting) { console.log(`${greeting}, ${this.name}`); } const user = { name: "Bob" }; greet.call(user, "Hello"); // "Hello, Bob"
new
绑定:
使用new
调用构造函数时,this
指向新创建的对象。function Person(name) { this.name = name; } const alice = new Person("Alice"); console.log(alice.name); // "Alice"
10.6.2.箭头函数
箭头函数没有自己的 this
,继承自外层作用域的 this
。
const obj = {
value: 42,
getValue: function() {
console.log(this.value);
},
getValueArrow: () => {
console.log(this.value); // undefined(箭头函数继承全局 this)
}
};
obj.getValue(); // 42
obj.getValueArrow(); // undefined
11.类
11.1.类的定义
class Person {
name = "";
age = 1;
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
console.log(`Hi, I'm ${this.name}`);
}
}
// 创建实例
const alice = new Person("Alice", 25);
alice.greet(); // "Hi, I'm Alice"
11.2.属性
实例属性:只能通过实例进行访问
class Person { name = "lisi"; } const lisi = new Person(); lisi.name;
静态属性:
static
关键字定义类属性,通过类名.获取class Person { static name = "lisi"; } Person.name
私有属性:外部无法直接访问的属性
class Person { #name; constructor(name){ this.#name = name; } getName(){ return this.#name } }
11.3.方法
实例方法:通过实例对象访问
class Person { read(){ console.log('read'); } } new Person().read();
静态方法:通过类名访问
class Person { static read(){ console.log('read'); } } Person.read();
11.4.构造函数
- 作用:初始化对象的属性。
- 特点:每个类只能有一个构造函数,若未显式定义,会自动生成一个空构造函数。
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
}
11.5.封装、继承、多态
- 封装:将属性、方法等封装为一个对象,并将他们私有化,以保护该对象数据。
继承:通过
extends
关键字实现继承:class Student extends Person { constructor(name, age, grade) { super(name, age); // 调用父类构造函数 this.grade = grade; } study() { console.log(`${this.name} is studying in grade ${this.grade}`); } } const bob = new Student("Bob", 18, 12); bob.greet(); // "Hi, I'm Bob"(继承自 Person) bob.study(); // "Bob is studying in grade 12"
- 多态:JS不会检查参数类型,调用某个函数,无需指定的类型,只需满足某些条件。
11.6.InstanceOf&hasOwnProperty
instanceOf
检查一个对象是否是某个构造函数的实例(即是否存在于其原型链中)。
instanceof
会沿着对象的原型链([[Prototype]]
)向上查找,直到找到匹配的构造函数的 prototype
属性或到达原型链末端(null
)。
//
function Person() {}
const alice = new Person();
console.log(alice instanceof Person); // true
console.log(alice instanceof Object); // true(所有对象最终继承自 Object)
function Animal() {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true(原型链继承)
检查对象自身(不含原型链)是否具有某个属性。
const obj = { a: 1 };
console.log(obj.hasOwnProperty("a")); // true
console.log(obj.hasOwnProperty("toString")); // false(继承自 Object.prototype)
const parent = { inherited: "I am inherited" };
const child = Object.create(parent);
child.own = "I am own";
console.log("inherited" in child); // true(原型链上的属性)
console.log(child.hasOwnProperty("inherited")); // false
12.数组
12.1.定义
// 定义数组
const arr1 = [1, 2, 3]; // 字面量
const arr2 = new Array(3); // 构造函数(长度为3的空数组)
const arr3 = Array.of(1, "a"); // ES6 方法(支持混合类型)
console.log(typeof arr1); // "object"(数组本质是对象)
console.log(Array.isArray(arr1)); // true(判断是否为数组)
特性:
- 有序性:元素按索引(
0
开始)排列。 - 灵活性:允许混合类型元素(数字、字符串、对象等)。
- 动态长度:可随时添加或删除元素。
12.2.常用操作
增删改查
方法 描述 示例 push(...items)
末尾添加元素,返回新长度 arr.push(4)
→[1,2,3,4]
pop()
删除末尾元素,返回被删元素 arr.pop()
→4
shift()
删除头部元素,返回被删元素 arr.shift()
→1
unshift(...items)
头部添加元素,返回新长度 arr.unshift(-1)
→[-1,1,2]
splice(start, deleteCount, ...items)
插入/删除元素(修改原数组) arr.splice(1, 1, "a")
→ 替换索引1的元素截取与合并
方法 描述 示例 slice(start, end)
截取子数组(不修改原数组) arr.slice(1, 3)
→[2,3]
concat(...arrays)
合并多个数组,返回新数组 arr.concat([4,5])
→[1,2,3,4,5]
遍历与映射
方法 描述 示例 forEach(callback)
遍历数组,执行回调函数 arr.forEach((item) => console.log(item))
map(callback)
生成新数组(元素经回调处理) arr.map(x => x * 2)
→[2,4,6]
filter(callback)
筛选符合条件的元素,生成新数组 arr.filter(x => x > 1)
→[2,3]
reduce(callback)
累计计算(如求和、求积) arr.reduce((sum, x) => sum + x, 0)
→6
扩展运算符(Spread Operator)
const arr1 = [1, 2]; const arr2 = [...arr1, 3]; // [1,2,3] const combined = [...arr1, ...arr2]; // [1,2,1,2,3]
解构赋值
const [first, second] = [1, 2]; console.log(first); // 1 console.log(second); // 2
Array.from()
:将类数组对象如arguments
转为数组const listItems = document.querySelectorAll("li"); const arr = Array.from(listItems); // 转为数组
Array.find()
:查找第一个符合条件的元素。const user = users.find(u => u.id === 1);
Array.some()
/every()
:检测数组是否满足条件。arr.some(x => x > 3); // 是否存在大于3的元素 arr.every(x => x > 0); // 是否所有元素都大于0
12.3.实战示例
去重
const arr = [1, 2, 2, 3]; const uniqueArr = [...new Set(arr)]; // [1,2,3]
扁平化嵌套数组
const nested = [1, [2, [3]]]; const flat = nested.flat(Infinity); // [1,2,3]
统计元素频率
const arr = ["apple", "banana", "apple"]; const frequency = arr.reduce((acc, item) => { acc[item] = (acc[item] || 0) + 1; return acc; }, {}); // { apple: 2, banana: 1 }