JavaScript 学习文档(二)

1.JavaScript 基础语法

目录

[1.JavaScript 基础语法](#1.JavaScript 基础语法)

变量声明:给数据贴上"标签"

三种声明方式:let、const、var

数据类型:容器里能装什么?

[1. 基本类型(值类型):存储的是实际的值](#1. 基本类型(值类型):存储的是实际的值)

[2. 引用类型:存储的是对内存中对象的引用(地址)](#2. 引用类型:存储的是对内存中对象的引用(地址))

[3. 基本类型包装对象](#3. 基本类型包装对象)

[4. 类型检测:如何知道一个值是什么类型?](#4. 类型检测:如何知道一个值是什么类型?)

[5. 类型转换:数据之间的"变身"术](#5. 类型转换:数据之间的“变身”术)

隐式转换(自动转换)

显式转换(手动转换)

[6. ToPrimitive 抽象操作(对象转原始类型)](#6. ToPrimitive 抽象操作(对象转原始类型))

运算符:对数据进行操作的符号

[1. 算术运算符](#1. 算术运算符)

[2. 比较运算符](#2. 比较运算符)

[3. 逻辑运算符](#3. 逻辑运算符)

[4. 位运算符(直接操作整数在内存中的二进制位)](#4. 位运算符(直接操作整数在内存中的二进制位))

[5. 赋值运算符与复合赋值](#5. 赋值运算符与复合赋值)

控制流:决定代码的执行顺序

[1. 条件语句:根据条件选择执行不同的代码](#1. 条件语句:根据条件选择执行不同的代码)

[iif...else if...else 语句](#iif...else if...else 语句)

三元运算符(条件运算符):简化版的if...else

[2. 循环与迭代:重复执行某段代码](#2. 循环与迭代:重复执行某段代码)

基本循环

for循环:适合已知循环次数的情况

while循环:适合不确定循环次数,只关注循环条件的情况

do...while循环:至少会执行一次代码块

[循环控制:break 和 continue](#循环控制:break 和 continue)

[break、continue 和 return 的区别](#break、continue 和 return 的区别)

高级迭代循环

for...in循环:遍历对象的可枚举属性(包括原型链上的)

for...of循环:遍历可迭代对象(数组、字符串、Map、Set等)的值

[for...in vs for...of](#for...in vs for...of)

函数基础:可重复使用的代码块

[1. 函数声明与函数表达式](#1. 函数声明与函数表达式)

函数声明:会提升(可以在声明前调用)

函数表达式:不会提升,必须先定义后调用

箭头函数(ES6新增):更简洁的函数表达式语法

[函数声明提升 vs 函数表达式不提升](#函数声明提升 vs 函数表达式不提升)

[2. 箭头函数](#2. 箭头函数)

无参数:必须使用圆括号

单个参数:可以省略圆括号

多个参数:必须使用圆括号

函数体有多条语句:必须使用大括号

返回对象字面量:需要用小括号包裹对象

箭头函数的this指向问题

[3. 参数:传递给函数的输入](#3. 参数:传递给函数的输入)

默认参数(ES6)

剩余参数(ES6):收集不定数量的参数到一个数组中

命名参数(通过对象解构实现)

[4. 返回值与递归](#4. 返回值与递归)

递归的基本要素

[基准条件(Base Case)](#基准条件(Base Case))

[递归条件(Recursive Case)](#递归条件(Recursive Case))

递归的注意事项

基本代码规范与注释风格

[1. 代码风格建议](#1. 代码风格建议)

[2. 注释风格](#2. 注释风格)

单行注释示例

多行注释示例

JSDoc风格注释示例(推荐用于函数)

注意事项


变量声明:给数据贴上"标签"

变量就像一个容器或一个标签,用来存储各种信息(数字、文字、复杂对象等),方便在程序中反复使用和修改。

声明一个变量,就像创建一个带名字的盒子,你可以在里面放东西,也可以随时更换。

三种声明方式:let、const、var

|-----------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------|------------------------------------------------|
| 声明方式 | 特点 | 一句话理解 | 适用场景 |
| let | 块级作用域可重复赋值不允许重复声明存在暂时性死区****csdn.net | 像一次性贴纸,贴上后可以撕下来重贴,但不能在同一位置贴两次。 | 当你需要一个值会改变的变量时。 |
| const | 块级作用域声明后必须立即赋值不能再重新赋值 (基本类型),属性可以修改 (引用类型),不允许重复声明存在暂时性死区****csdn.net | 像带锁的保险箱,一旦锁上(声明并赋值),就不能再换别的东西,但箱子里的文件(属性)还是可以改的。 | 当你需要一个常量 ,或者一个不会改变引用但内容可变的变量(如对象、数组)时。 |
| var | 函数作用域可重复声明变量提升(在声明前可用,值为undefined),没有块级作用域 | 像老式黑板,可以随意擦写,但在任何地方都能看到它(作用域),且可能会出现"写之前先擦"的情况(变量提升)。 | 现代开发中尽量避免使用 ,主要存在于老旧代码中。 |

默认使用 const,当需要重新赋值时再使用 let,尽量避免使用 var。

这样能避免许多潜在的作用域问题,让代码更安全、更易读。

数据类型:容器里能装什么?

JavaScript 的数据类型分为两大类:基本类型和引用类型。

这决定了数据在内存中如何存储和操作。

1. 基本类型(值类型):存储的是实际的值

|---------------|----------------------------|--------------------------------------------|----------------------------------------------------|
| 类型 | 描述 | 示例 | 注意 |
| Undefined | 变量已声明但未赋值 | let a; console.log(a); // undefined | 表示"缺少值 " |
| Null | 表示一个的、不存在的对象引用 | let b = null; console.log(b); // null | 表示"有意为之的空值 " |
| Boolean | 布尔值,表示真或假 | let isTrue = true; let isFalse = false; | 主要用于条件判断 |
| Number | 数字(整数或浮点数) | let age = 25; let price = 3.14; | Infinity(无穷大)、-Infinity(无穷小)、NaN(Not a Number,非数值) |
| BigInt | ES2020新增,用于表示任意大的整数 | let bigNum = 9007199254740991n; | 数字末尾加 n,或使用 BigInt() 构造函数 |
| String | 字符串(文本),用单引号'、双引号"或反引号`包裹 | let name = 'Alice'; let message = "Hello"; | 转义字符:\n换行、\t制表符、\\反斜杠本身、\'单引号等 |
| Symbol | ES6新增,表示独一无二的值 | let sym = Symbol('description'); | 常用于对象属性名,防止冲突 |

2. 引用类型:存储的是对内存中对象的引用(地址)

|--------------|------------------------|------------------------------------------|
| 类型 | 描述 | 示例 |
| Object | 对象,包含属性方法的集合 | let person = { name: 'Bob', age: 30 }; |
| Array | 数组,有序的列表,可存储任意类型数据 | let colors = ['red', 'green', 'blue']; |
| Function | 函数,一段可重复执行的代码块 | function greet() { console.log('Hi!'); } |

3. 基本类型包装对象

当你尝试对基本类型值(如字符串、数字、布尔值)调用方法或访问属性时,JavaScript 会临时创建一个包装对象,操作完成后立即销毁。

这是为了方便地操作基本类型。

javascript 复制代码
let str = "hello";

console.log(str.length); // 5

// 临时创建了 String 包装对象,访问了 length 属性,然后销毁

let num = 123.456;

console.log(num.toFixed(2)); // "123.46"

// 临时创建了 Number 包装对象,调用了 toFixed() 方法,然后销毁

let bool = true;

console.log(bool.toString()); // "true"

// 临时创建了 Boolean 包装对象,调用了 toString() 方法,然后销毁

⚠️ 注意:不要手动创建包装对象(如 new String("text")),这会导致类型判断混乱(typeof new String("text") 返回 "object",而 typeof "text" 返回 "string")。直接使用基本类型值即可,JavaScript 会自动处理包装。

4. 类型检测:如何知道一个值是什么类型?

|--------------------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
| 方法 | 描述 | 返回值示例 | 适用场景 |
| typeof | 返回一个字符串,表示操作数的数据类型csdn.net | typeof 42 → "number" typeof "Hello" → "string" typeof true → "boolean" typeof undefined → "undefined" typeof null → "object" (历史遗留问题) typeof Symbol('sym') → "symbol" typeof 123n → "bigint" typeof {} → "object" typeof [] → "object" typeof function(){} → "function" | 快速判断基本类型 (除 null 外)和函数。 |
| instanceof | 检查构造函数的 prototype 属性是否出现在对象的原型链中csdn.net | [] instanceof Array → true {} instanceof Object → true function(){} instanceof Function → true new Date() instanceof Date → true | 判断引用类型 的具体类型(如数组、日期、自定义对象实例)。 |
| Object.prototype.toString.call() | 返回一个表示该对象的字符串,格式为 "[object Type]"segmentfault.com+2 | Object.prototype.toString.call([]) → "[object Array]" Object.prototype.toString.call({}) → "[object Object]" Object.prototype.toString.call(null) → "[object Null]" Object.prototype.toString.call(undefined) → "[object Undefined]" Object.prototype.toString.call(123) → "[object Number]" | 最准确、最可靠的类型检测方法 ,能准确区分所有内置类型和自定义类型。 |

类型检测示例

javascript 复制代码
let num = 42;

let str = "Hello";

let bool = true;

let und = undefined;

let nul = null;

let obj = {};

let arr = [];

let func = function(){};

let date = new Date();
typeof
javascript 复制代码
console.log(typeof num); // "number"

console.log(typeof str); // "string"

console.log(typeof bool); // "boolean"

console.log(typeof und); // "undefined"

console.log(typeof nul); // "object" (注意这个历史遗留问题)

console.log(typeof obj); // "object"

console.log(typeof arr); // "object"

console.log(typeof func); // "function"
instanceof
javascript 复制代码
console.log(arr instanceof Array); // true

console.log(obj instanceof Object); // true

console.log(date instanceof Date); // true
Object.prototype.toString.call()
javascript 复制代码
console.log(Object.prototype.toString.call(num)); // "[object Number]"

console.log(Object.prototype.toString.call(str)); // "[object String]"

console.log(Object.prototype.toString.call(bool)); // "[object Boolean]"

console.log(Object.prototype.toString.call(und)); // "[object Undefined]"

console.log(Object.prototype.toString.call(nul)); // "[object Null]"

console.log(Object.prototype.toString.call(arr)); // "[object Array]"

console.log(Object.prototype.toString.call(obj)); // "[object Object]"

console.log(Object.prototype.toString.call(func)); // "[object Function]"

console.log(Object.prototype.toString.call(date)); // "[object Date]"

5. 类型转换:数据之间的"变身"术

隐式转换(自动转换)

|-------------------------|-----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 场景 | 转换规则 | 示例 |
| 算术运算(除 + 外) | 将非Number类型转换为Number | '10' - 5 → 5 (字符串转数字) '10' * 2 → 20 (字符串转数字) true - 1 → 0 (布尔转数字: true=1) null - 1 → -1 (null转数字: null=0) undefined - 1 → NaN (undefined转数字: undefined=NaN) |
| + 运算 | 字符串拼接优先 :如果一侧是String,则另一侧也转为String进行拼接;否则进行数值相加 | '1' + 1 → '11' (字符串拼接) 1 + true → 2 (true转数字1) 1 + null → 1 (null转数字0) 1 + undefined → NaN (undefined转数字NaN) '1' + '1' → '11' (字符串拼接) 1 + 1 → 2 (数值相加) |
| == 比较 | 先进行类型转换,再比较值(但不推荐使用,推荐使用===) | 1 == '1' → true (字符串转数字) null == undefined → true (它们被视为相等) 0 == false → true (false转数字0) '' == false → true (空字符串转数字0, false转数字0) [] == 0 → true (数组转数字0) |

这就像一个"自动翻译器",在不同类型的值进行运算或比较时,它会自动将它们转换为同一类型。

但有时会"翻译错",导致意想不到的结果。

显式转换(手动转换)

|---------------------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 方法 | 描述 | 示例 |
| Number() | 将值转换为数字 | Number('123') → 123 Number('123abc') → NaN (无法转换) Number('') → 0 (空字符串转0) Number(true) → 1 Number(false) → 0 Number(null) → 0 Number(undefined) → NaN |
| String() | 将值转换为字符串 | String(123) → "123" String(true) → "true" String(null) → "null" String(undefined) → "undefined" String({}) → "[object Object]" String([1,2,3]) → "1,2,3" |
| Boolean() | 将值转换为布尔值 | Boolean(123) → true Boolean('hello') → true Boolean([]) → true Boolean({}) → true Boolean(0) → false Boolean('') → false Boolean(null) → false Boolean(undefined) → false Boolean(NaN) → false Boolean(false) → false |
| parseInt() / parseFloat() | 从字符串中解析出整数或浮点数 | parseInt('123abc') → 123 parseFloat('123.45abc') → 123.45 parseInt('abc123') → NaN (开头无法解析) parseInt('10', 2) → 2 (第二个参数是进制,解析二进制字符串) |
| toString() | 将值转换为字符串(除null/undefined外) | (123).toString() → "123" true.toString() → "true" [1,2,3].toString() → "1,2,3" {}.toString() → "[object Object]" // null.toString() // ❌ 报错 // undefined.toString() // ❌ 报错 |

这就像"手动指定翻译目标",更可控、更安全。

隐式转换示例
javascript 复制代码
console.log(5 + '5'); // "55" (字符串拼接)

console.log(5 - '5'); // 0 (数值相减)

console.log('10' > '2'); // false (字符串比较,按字典序)

console.log(10 > '2'); // true (数字比较,字符串转数字)

console.log(null == undefined); // true

console.log(0 == false); // true

console.log('' == false); // true
显式转换示例
javascript 复制代码
console.log(String(123)); // "123"

console.log(Number('123')); // 123

console.log(Boolean('hello')); // true

console.log(parseInt('123abc')); // 123

console.log(parseFloat('123.45abc')); // 123.45

console.log((123).toString()); // "123"

6. ToPrimitive 抽象操作(对象转原始类型)

当对象需要转换为原始类型(数字、字符串、布尔)时,JavaScript 会调用内部的 ToPrimitive 抽象操作。

其规则如下:

如果对象有 Symbol.toPrimitive 方法,优先调用该方法。

否则,根据预期类型(hint):

  • Hint "number"(如数学运算)
  1. 先调用 valueOf(),如果返回原始值则使用;
  2. 否则调用 toString(),如果返回原始值则使用;
  3. 否则报错。
  • Hint "string"(如字符串拼接)
  1. 先调用 toString(),如果返回原始值则使用;
  2. 否则调用 valueOf(),如果返回原始值则使用;
  3. 否则报错。
javascript 复制代码
const obj = {

  valueOf() { return 10; },

  toString() { return '20'; }

};

console.log(obj + 5); // 15 (hint为"number",使用valueOf返回的10)

console.log(String(obj)); // "20" (hint为"string",使用toString返回的"20")

运算符:对数据进行操作的符号

运算符就像"操作员",对变量和值进行操作并产生结果。

1. 算术运算符

|---------|-----------------------------|---------------------------------------------------------------------|
| 运算符 | 描述 | 示例 |
| + | 加法 / 字符串拼接 | 10 + 5 → 15 'Hello' + ' World' → 'Hello World' |
| - | 减法 | 10 - 5 → 5 |
| * | 乘法 | 10 * 5 → 50 |
| / | 除法 | 10 / 5 → 2 |
| % | 取模(求余数) | 10 % 3 → 1 (10除以3余1) |
| ++ | 自增(i++ 是先使用再加1,++i 是先加1再使用) | let i = 5; console.log(i++); // 5 let j = 5; console.log(++j); // 6 |
| -- | 自减(i-- 是先使用再减1,--i 是先减1再使用) | let i = 5; console.log(i--); // 5 let j = 5; console.log(--j); // 4 |

2. 比较运算符

|---------|-----------------------------|----------------------------------|
| 运算符 | 描述 | 示例 |
| == | 相等(会进行类型转换不推荐使用) | 5 == '5' → true |
| === | 严格相等(不进行类型转换推荐使用) | 5 === '5' → false 5 === 5 → true |
| != | 不相等(会进行类型转换) | 5 != '5' → false |
| !== | 严格不相等(不进行类型转换) | 5 !== '5' → true 5 !== 5 → false |
| > | 大于 | 10 > 5 → true |
| < | 小于 | 5 < 10 → true |
| >= | 大于或等于 | 10 >= 5 → true |
| <= | 小于或等于 | 5 <= 10 → true |

始终使用 === 和 !==,除非你非常明确自己需要隐式类型转换的行为。这能避免许多潜在的bug。

3. 逻辑运算符

|----------|------------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 运算符 | 描述 | 短路行为 | 示例 |
| && | 逻辑与(AND ) | 如果第一个操作数是假值,则返回第一个操作数;否则返回第二个操作数。 | true && true → true true && false → false 0 && 'hello' → 0 (0是假值,直接返回0) 'hello' && 'world' → 'world' (hello是真值,返回world) |
| || | 逻辑或(OR ) | 如果第一个操作数是真值,则返回第一个操作数;否则返回第二个操作数。 | true || false → true false || true → true 'hello' || 'world' → 'hello' (hello是真值,直接返回hello) '' || 'world' → 'world' (空字符串是假值,返回world) |
| ! | 逻辑非(NOT ) | 返回操作数的布尔取反值。 | !true → false !false → true !0 → true !'hello' → false |
| ?? | 空值合并运算符 (ES2020新增) | 如果左侧操作数是null undefined,则返回右侧操作数;否则返回左侧操作数cnblogs.com+2 。 | null ?? 'default' → 'default' undefined ?? 'default' → 'default' 0 ?? 'default' → 0 (0不是null或undefined) '' ?? 'default' → '' (空字符串不是null或undefined) false ?? 'default' → false (false不是null或undefined) |
| ?. | 可选链运算符 (ES2020新增) | 允许安全地访问嵌套对象属性,如果链中的任何引用是 null 或 undefined,则短路返回 undefined,而不会抛出错误cnblogs.com+2 。 | const adventurer = { name: 'Alice', cat: { name: 'Dinah' } }; adventurer.dog?.name → undefined (dog不存在,返回undefined,不报错) adventurer.someNonExistentMethod?.() → undefined (方法不存在,返回undefined,不报错) |

逻辑运算符的"短路"行为

逻辑运算符的"短路"行为非常重要,它意味着:

  • &&:如果第一个操作数是假值,第二个操作数根本不会被求值(因为结果已经确定为假)。
  • ||:如果第一个操作数是真值,第二个操作数根本不会被求值(因为结果已经确定为真)。
使用 || 设置默认值(但要注意0和空字符串会被认为是假值)
javascript 复制代码
function greet(name) {

  name = name || 'Guest'; // 如果name是假值(如'', 0, null, undefined),则使用'Guest'

  console.log('Hello, ' + name);

}

greet(); // Hello, Guest

greet('Alice'); // Hello, Alice
使用 ?? 更精确地设置默认值(只处理null和undefined)
javascript 复制代码
function greet(name) {

  name = name ?? 'Guest'; // 只有当name是null或undefined时,才使用'Guest'

  console.log('Hello, ' + name);

}

greet(); // Hello, Guest

greet(''); // Hello,  (空字符串被保留)

greet(0); // Hello, 0 (数字0被保留)

4. 位运算符(直接操作整数在内存中的二进制位)

|------------|-------------------------------|-------------------------------------------------|
| 运算符 | 描述 | 示例 |
| & | 按位与(AND) | 5 & 3 → 1 (二进制: 101 & 011 = 001) |
| | | 按位或(OR) | 5 | 3 → 7 (二进制: 101 | 011 = 111) |
| ^ | 按位异或(XOR) | 5 ^ 3 → 6 (二进制: 101 ^ 011 = 110) |
| ~ | 按位非(NOT) | ~5 → -6 (二进制取反) |
| << | 左移(各二进位全部左移若干位) | 5 << 1 → 10 (二进制: 101 << 1 = 1010 = 十进制10) |
| >> | 右移(各二进位全部右移若干位,正数高位补0,负数高位补1) | 5 >> 1 → 2 (二进制: 101 >> 1 = 010 = 十进制2) |
| >>> | 无符号右移(各二进位全部右移若干位,高位始终补0) | 5 >>> 1 → 2 (二进制: 101 >>> 1 = 010 = 十进制2) |

注意:位运算符会将操作数转换为32位整数,然后进行操作,结果也是一个32位整数。对于非常大的数字,这可能会导致精度丢失。位运算符主要用于底层操作、性能优化等场景。

5. 赋值运算符与复合赋值

|-------------|-----------|------------------------------|
| 运算符 | 描述 | 等同于 |
| = | 赋值 | let x = 10; |
| += | 加并赋值 | x += 5 → x = x + 5 |
| -= | 减并赋值 | x -= 5 → x = x - 5 |
| *= | 乘并赋值 | x *= 5 → x = x * 5 |
| /= | 除并赋值 | x /= 5 → x = x / 5 |
| %= | 取模并赋值 | x %= 5 → x = x % 5 |
| **= | 幂并赋值(ES6) | x **= 2 → x = x ** 2 |
| <<= | 左移并赋值 | x <<= 2 → x = x << 2 |
| >>= | 右移并赋值 | x >>= 2 → x = x >> 2 |
| >>>= | 无符号右移并赋值 | x >>>= 2 → x = x >>> 2 |
| &= | 按位与并赋值 | x &= 3 → x = x & 3 |
| |= | 按位或并赋值 | x |= 3 → x = x | 3 |
| ^= | 按位异或并赋值 | x ^= 3 → x = x ^ 3 |

javascript 复制代码
let x = 10;

x += 5; // x = 10 + 5 = 15

x = 2; // x = 15  2 = 30

console.log(x); // 30

控制流:决定代码的执行顺序

控制流语句就像"交通指挥灯"或"岔路口的指示牌",决定代码执行哪条路径。

1. 条件语句:根据条件选择执行不同的代码

if...else if...else 语句
javascript 复制代码
let score = 85;

if (score >= 90) {

  console.log('优秀!');

} else if (score >= 80) {

  console.log('良好!'); // 输出这个

} else if (score >= 60) {

  console.log('及格!');

} else {

  console.log('不及格!');

}
三元运算符(条件运算符):简化版的if...else
javascript 复制代码
// 条件 ? 表达式1 (条件为真时执行) : 表达式2 (条件为假时执行)

let grade = score >= 60 ? '及格' : '不及格';

console.log(grade); // "及格"

// switch 语句:用于基于不同条件执行不同代码(特别适合枚举值)

let day = 'Monday';

switch (day) {

  case 'Monday':

    console.log('星期一');

    break; // 遇到break则跳出switch,继续执行后面的代码

  case 'Tuesday':

    console.log('星期二');

    break;

  case 'Wednesday':

    console.log('星期三');

    break;

  default: // 相当于else

    console.log('其他');

}

2. 循环与迭代:重复执行某段代码

循环就像"唱片机或跑步机,反复做同一件事。

基本循环
for循环:适合已知循环次数的情况
javascript 复制代码
for (let i = 0; i < 5; i++) {

  console.log('for循环:当前数字是:' + i);

}

// 输出:

// for循环:当前数字是:0

// for循环:当前数字是:1

// for循环:当前数字是:2

// for循环:当前数字是:3

// for循环:当前数字是:4
while循环:适合不确定循环次数,只关注循环条件的情况
javascript 复制代码
let j = 0;

while (j < 3) {

  console.log('while循环:' + j);

  j++;

}

// 输出:

// while循环:0

// while循环:1

// while循环:2
do...while循环:至少会执行一次代码块
javascript 复制代码
let k = 0;

do {

  console.log('do-while循环:' + k);

  k++;

} while (k < 3);

// 输出:

// do-while循环:0

// do-while循环:1

// do-while循环:2
循环控制:break 和 continue

|--------------|------------------------------------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| 语句 | 作用 | 适用循环 | 示例 |
| break | 立即终止 当前循环csdn.net+1 | for, while, do...while, for...in, for...of | javascript for (let i = 0; i < 10; i++) { if (i === 5) { break; // 当i等于5时,终止循环 } console.log(i); } // 输出: 0, 1, 2, 3, 4 |
| continue | 跳过 当前循环迭代的剩余代码,立即开始下一次迭代****csdn.net+1 | for, while, do...while, for...in, for...of | javascript for (let i = 0; i < 5; i++) { if (i % 2 === 0) { continue; // 当i是偶数时,跳过当前迭代 } console.log(i); } // 输出: 1, 3 |

break、continue 和 return 的区别

|--------------|------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 语句 | 作用范围 | 示例 |
| break | 终止当前循环 或switch语句 | javascript for (let i = 0; i < 10; i++) { if (i === 5) { break; // 终止循环 } console.log(i); } |
| continue | 跳过当前循环迭代 | javascript for (let i = 0; i < 5; i++) { if (i % 2 === 0) { continue; // 跳过当前迭代 } console.log(i); } |
| return | 从函数中返回一个值,并终止函数的执行 (如果在循环中,也会终止整个函数)csdn.net | javascript function findFirstPositive(numbers) { for (let i = 0; i < numbers.length; i++) { if (numbers[i] > 0) { return numbers[i]; // 找到第一个正数,立即返回并终止函数 } } return -1; } |

break和continue只影响当前所在的循环。

如果存在嵌套循环,它们只影响包含它们的那一层循环。

javascript 复制代码
// 嵌套循环中的break和continue

for (let i = 0; i < 2; i++) {

  console.log('外层循环 i:', i);

  for (let j = 0; j < 3; j++) {

    if (j === 1) {

      break; // 只终止内层循环

    }

    console.log('  内层循环 j:', j);

  }

}

// 输出:

// 外层循环 i: 0

//   内层循环 j: 0

// 外层循环 i: 1

//   内层循环 j: 0
高级迭代循环
for...in循环:遍历对象的可枚举属性(包括原型链上的)
javascript 复制代码
// 注意:不推荐用于遍历数组,顺序可能不保证

const person = {

  name: 'Alice',

  age: 30,

  job: 'Developer'

};

for (const key in person) {

  console.log(key + ': ' + person[key]);

}

// 输出:

// name: Alice

// age: 30

// job: Developer
for...of循环:遍历可迭代对象(数组、字符串、Map、Set等)的值
javascript 复制代码
// 注意:不能遍历普通对象

const colors = ['red', 'green', 'blue'];

for (const color of colors) {

  console.log(color);

}

// 输出:

// red

// green

// blue

// 遍历字符串

const str = "hello";

for (const char of str) {

  console.log(char);

}

// 输出:

// h

// e

// l

// l

// o
for...in vs for...of

|----------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
| 特性 | for...in | for...of |
| 遍历目标 | 对象 的可枚举属性(键) | 可迭代对象 的值(如数组、字符串、Map、Set、arguments、NodeList等) |
| 遍历内容 | (key) | (value) |
| 适用场景 | 遍历对象属性 | 遍历数组、字符串等迭代器对象 |
| 顺序 | 不一定 按顺序(对于数组) | 按顺序 |
| 原型链 | 会遍历原型链上的可枚举属性 | 只遍历对象自身的可迭代属性 |
| 示例 | javascript const obj = {a:1, b:2}; for (const key in obj) { console.log(key); // 输出: a, b } | javascript const arr = [1, 2, 3]; for (const value of arr) { console.log(value); // 输出: 1, 2, 3 } |

  • 遍历数组:for...of 或 forEach、map、filter 等数组方法;
  • 遍历对象:for...in 或 Object.keys()/Object.values()/Object.entries()。

函数基础:可重复使用的代码块

函数就像"机器"或"工具",你给它一些输入(参数),它就经过内部处理,然后给你一个输出(返回值)。

定义一次,可以多次调用,大大提高了代码的复用性和可维护性。

1. 函数声明与函数表达式

函数声明:会提升(可以在声明前调用)
javascript 复制代码
function sayHello(name) {

  console.log('Hello, ' + name + '!');

}

sayHello('Alice'); // Hello, Alice!
函数表达式:不会提升,必须先定义后调用
javascript 复制代码
const sayGoodbye = function(name) {

  console.log('Goodbye, ' + name + '!');

};

sayGoodbye('Bob'); // Goodbye, Bob!
箭头函数(ES6新增):更简洁的函数表达式语法

|----------------------|----------------------------------------------------|---------------------------------|
| 特性 | 箭头函数 | 普通函数 |
| 语法 | (params) => { statements } 或 param => expression | function(params) { statements } |
| this 绑定 | 没有自己的 this,会捕获其所在上下文的this值csdn.net+2 | 有自己的this,取决于如何调用(谁调用它,this就指向谁) |
| arguments 对象 | 没有 arguments对象 ,可以使用剩余参数代替 | 有自己的arguments对象 |
| new 调用 | 不能作为构造函数使用 (不能用new调用) | 可以作为构造函数使用 |
| super 调用 | 没有 super | 有super(用于调用父类方法) |
| yield | 不能使用 yield关键字 (不能用作生成器函数) | 可以使用yield(用作生成器函数) |

javascript 复制代码
const sayHi = (name) => {

  console.log('Hi, ' + name + '!');

};

sayHi('Charlie'); // Hi, Charlie!
函数声明提升 vs 函数表达式不提升

函数声明会被"提升"到当前作用域的顶部,可以在函数声明之前调用它。

javascript 复制代码
sayHello(); // Hello! (可以调用)

function sayHello() {

  console.log('Hello!');

}

函数表达式(包括箭头函数)不会被提升,必须先定义,然后才能调用。

javascript 复制代码
// sayGoodbye(); // ❌ 报错: sayGoodbye is not a function

const sayGoodbye = function() {

  console.log('Goodbye!');

};

sayGoodbye(); // Goodbye!

2. 箭头函数

箭头函数是ES6引入的更简洁的函数书写方式,但它与普通函数也有一些重要区别。

无参数:必须使用圆括号
javascript 复制代码
const greet = () => console.log('Hello!');
单个参数:可以省略圆括号
javascript 复制代码
const double = num => num*2;
多个参数:必须使用圆括号
javascript 复制代码
const add = (a, b) => a + b;
函数体有多条语句:必须使用大括号
javascript 复制代码
const describe = (name, age) => {

  console.log('Name:', name);

  console.log('Age:', age);

};
返回对象字面量:需要用小括号包裹对象
javascript 复制代码
const createPerson = (name, age) => ({ name, age });

 // 等同于: return { name, age };
箭头函数的this指向问题
javascript 复制代码
const person = {

  name: 'Alice',

  regularFunc: function() {

    console.log(this.name); // "Alice" (this指向person对象)

  },

  arrowFunc: () => {

    console.log(this.name); // undefined (this指向定义时的上下文,这里是全局或window,没有name属性)

  }

};

person.regularFunc(); // Alice

person.arrowFunc(); // undefined

3. 参数:传递给函数的输入

默认参数(ES6)
javascript 复制代码
// 为参数设置默认值

function greet(name = 'Guest', greeting = 'Hello') {

  console.log(greeting + ', ' + name + '!');

}

greet(); // Hello, Guest!

greet('Alice'); // Hello, Alice!

greet('Bob', 'Hi'); // Hi, Bob!
剩余参数(ES6):收集不定数量的参数到一个数组中
javascript 复制代码
// 使用...args收集所有剩余参数

function sum(...args) {

  return args.reduce((total, current) => total + current, 0);

}

console.log(sum(1, 2, 3)); // 6

console.log(sum(1, 2, 3, 4, 5)); // 15

// 剩余参数必须放在最后

function describePerson(name, ...hobbies) {

  console.log('Name:', name);

  console.log('Hobbies:', hobbies);

}

describePerson('Alice', 'reading', 'coding', 'traveling');

// Name: Alice

// Hobbies: [ 'reading', 'coding', 'traveling' ]
命名参数(通过对象解构实现)
javascript 复制代码
// 通过对象解构实现命名参数,使代码更清晰

function createUser({ name, age = 30, isAdmin = false }) {

  return {

    name,

    age,

    isAdmin

  };

}

const user = createUser({ name: 'Alice', age: 25 });

console.log(user); // { name: 'Alice', age: 25, isAdmin: false }

4. 返回值与递归

javascript 复制代码
// 函数返回值:如果没有显式返回,则返回undefined

function add(a, b) {

  return a + b; // 返回计算结果

}

const sum = add(5, 3);

console.log(sum); // 8

// 没有return语句的函数

function sayHello() {

  console.log('Hello!'); // 这条语句会执行

  // 没有return,隐式返回undefined

}

const result = sayHello();

console.log(result); // undefined

// 递归函数:函数调用自身

function factorial(n) {

  if (n === 0 || n === 1) {

    return 1; // 基准条件:结束递归

  } else {

    return n  factorial(n - 1); // 递归条件:调用自身

  }

}

console.log(factorial(5)); // 120 (5  4  3  2  1)
递归的基本要素
基准条件(Base Case)

一个或多个可以直接求解而无需递归的情况,用于终止递归。例如上面factorial函数中的n === 0 || n === 1。

递归条件(Recursive Case)

将问题分解为更小的、与原问题形式相同的问题,并调用自身来求解这些子问题。例如上面factorial函数中的n factorial(n - 1)。

递归的注意事项

确保基准条件能够被触发,否则会导致无限递归,引发"栈溢出"(Stack Overflow)错误。

递归调用的参数必须向基准条件逼近。

javascript 复制代码
// 无限递归的例子(会导致栈溢出)

function infiniteRecursion() {

  return infiniteRecursion(); // 没有基准条件

}

// infiniteRecursion(); // ❌ RangeError: Maximum call stack size exceeded

基本代码规范与注释风格

代码规范就像"语法和拼写规则",而注释就像"课文中的解释说明"。它们都为了让你的代码更容易被别人(以及未来的自己)理解和维护。

1. 代码风格建议

|---------|---------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|--------------------------------------------------------------------|
| 规则 | 建议 | 好的例子 | 不好的例子 |
| 缩进 | 使用2 个空格4 个空格不要混用****Tab 和空格 ) | javascript function add(a, b) { return a + b; } | javascript function add(a,b){return a+b;} |
| 命名 | 变量和函数使用驼峰命名法(camelCase);常量使用全大写****+ 下划线 (UPPER_SNAKE_CASE);类使用帕斯卡命名法(PascalCase) | let userName = 'Alice'; const MAX_SIZE = 100; class UserService {} | let user_name = 'Alice'; const maxsize = 100; class userService {} |
| 空行 | 在函数之间逻辑块之间添加空行,增加可读性 | javascript function a() {} <br><br> function b() {} | javascript function a(){} function b(){} |
| 分号 | 虽然JavaScript有自动分号插入(**ASI 机制,但建议始终使用分号**,避免潜在问题 | javascript let x = 5; | javascript let x = 5 (在某些情况下可能导致问题) |
| 字符串 | 优先使用单引号(')或反引号(`),双引号也可以,但要保持一致 | javascript let str = 'hello'; | javascript let str = "hello" (混用) |
| 比较 | 始终使用严格相等 ===和严格不相等 !== | javascript if (x === 5) {} | javascript if (x == 5) {} |
| 大括号 | 始终使用大括号 包裹代码块,即使只有一条语句(不要省略大括号) | javascript if (true) { console.log('yes'); } | javascript if (true) console.log('yes'); |

2. 注释风格

单行注释示例
javascript 复制代码
let userName = 'Alice'; // 用户名
多行注释示例
javascript 复制代码
/**

  *这是一个多行注释

  *可以写很多行

  *通常用于解释一段复杂的逻辑

*/
JSDoc风格注释示例(推荐用于函数)
javascript 复制代码
/**

  * 根据用户ID获取用户信息

  * @param {number} userId - 用户ID

  * @returns {Object|null} 用户对象或null(如果未找到)

  * @example

  * const user = getUserById(1);

  * console.log(user.name); // 输出用户名

 */

function getUserById(userId) {

  // ...实现代码

}
注意事项
  1. 解释"为什么",而不是"是什么"(代码本身已经说了"是什么")。
  2. 保持注释最新,过时的注释比没有注释更糟糕。
  3. 避免不必要的注释,如果代码本身已经很清晰,就不需要注释。
  4. 对复杂的算法、临时解决方案、重要的业务逻辑进行注释。
相关推荐
A9better1 小时前
C++——不一样的I/O工具与名称空间
开发语言·c++·学习
这儿有一堆花2 小时前
Vue 是什么:一套为「真实业务」而生的前端框架
前端·vue.js·前端框架
AI职业加油站2 小时前
职业提升之路:我的大数据分析师学习与备考分享
大数据·人工智能·经验分享·学习·职场和发展·数据分析
全栈前端老曹2 小时前
【MongoDB】深入研究副本集与高可用性——Replica Set 架构、故障转移、读写分离
前端·javascript·数据库·mongodb·架构·nosql·副本集
四谎真好看2 小时前
JavaWeb学习笔记(Day13)
笔记·学习·学习笔记·javaweb
ZH15455891312 小时前
Flutter for OpenHarmony Python学习助手实战:机器学习算法实现的实现
python·学习·flutter
NCDS程序员2 小时前
v-model: /v-model/ :(v-bind)三者核心区别
前端·javascript·vue.js
夏幻灵2 小时前
CSS三大特性:层叠、继承与优先级解析
前端·css
承渊政道3 小时前
Linux系统学习【Linux基础开发工具】
linux·运维·笔记·学习·centos·编辑器