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)。
    • 避免缩写:除非是广泛认可的缩写(如 idURL)。
  • 禁止使用保留字(见下文)。
// 合法标识符
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 跳出
caseswitch 分支
catch异常捕获
class类定义
const常量声明
continue循环继续
debugger调试断点
defaultswitch 默认分支、模块默认导出
delete删除对象属性
dodo...while 循环
elseif 条件分支
export模块导出
extends类继承
finallytry...catch 最终执行块
forfor 循环
function函数声明
if条件判断
import模块导入
in成员检测、循环遍历
instanceof类型检测
let块级变量声明
new对象实例化
return函数返回值
super类父级引用
switch多分支选择
this当前上下文对象
throw抛出异常
try异常处理
typeof类型检测
var函数级变量声明
void返回无值
whilewhile 循环
with对象作用域扩展(不推荐使用
2.2.3.var、let和const区别

ES5(2009年):引入 var 作为唯一的变量声明关键字。

ES6(2015年):新增 letconst,引入块级作用域,逐步取代 var

特性varletconst
作用域函数作用域(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;truefalse
数组let arr = [1, "a", true];[] 定义,元素类型可不同。
对象let obj = { name: "Bob" };键值对集合,用 {} 定义。
null/undefinedlet 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(即 -90071992547409919007199254740991)。
    • 最大/最小值:

      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/0parseInt("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对象主动置空
undefinedundefined未定义
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.对比
检测方法典型场景
undefinedvalue === undefined变量未声明或函数无返回值
nullvalue === null主动置空或明确表示“无”
空字符串str.length === 0输入框未填写
空数组arr.length === 0数据列表初始状态
空对象Object.keys(obj).length === 0配置对象未初始化
3.4.4.空值处理
  • 检查 nullundefined

    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):
    false0""(空字符串)、nullundefinedNaN
  • 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 + 38
-减法10 - 46
*乘法2 * 3.57
/触发10 / 33.333...(浮点数)
%取模10 % 31
**幂运算2 ** 38(指数运算)
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 += 5x = x + 5加法后赋值
-=x -= 3x = x - 3减法后赋值
*=x *= 2x = x * 2乘法后赋值
/=x /= 4x = x / 4除法后赋值
%=x %= 7x = x % 7取余后赋值
**=x **= 3x = x ** 3指数运算后赋值(ES7+)
<<=x <<= 2x = x << 2左移后赋值
>>=x >>= 1x = x >> 1算术右移后赋值
>>>=x >>>= 3x = x >>> 3逻辑右移后赋值(无符号)
&=x &= 0b1100x = x & 0b1100按位与后赋值
^=x ^= 0b1010x = x ^ 0b1010按位异或后赋值
`= ``x= 0b0011`
??=x ??= 10x = x ?? 10xnull/undefined,则赋值右侧值
&&=x &&= yx = 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 > 3true
<小于3 < 5true
>=大于等于5 >= 5true
<=小于等于3 <= 5true
==等于(弱相等,隐式转换)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:布尔表达式,结果为 truefalse
  • conditiontrue,执行代码块;否则退出 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 引擎会:

  1. 在对象自身查找。
  2. 若未找到,沿 [[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)。
    在严格模式下,thisundefined

    function foo() {
      console.log(this);
    }
    foo(); // 非严格模式:window;严格模式:undefined
  • 隐式绑定
    当函数作为对象的方法调用时,this 指向调用该方法的对象。

    const obj = {
      name: "Alice",
      sayHi() {
        console.log(this.name);
      }
    };
    obj.sayHi(); // "Alice"
  • 显式绑定
    使用 callapplybind 显式指定 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 }
最后修改:2025 年 05 月 11 日
如果觉得我的文章对你有用,请随意赞赏