1.JavaScript 基础语法

目录
1.JavaScript 基础语法
变量声明:给数据贴上“标签”
三种声明方式:let、const、var
数据类型:容器里能装什么?
1. 基本类型(值类型):存储的是实际的值
2. 引用类型:存储的是对内存中对象的引用(地址)
3. 基本类型包装对象
4. 类型检测:如何知道一个值是什么类型?
5. 类型转换:数据之间的“变身”术
隐式转换(自动转换)
显式转换(手动转换)
6. ToPrimitive 抽象操作(对象转原始类型)
运算符:对数据进行操作的符号
1. 算术运算符
2. 比较运算符
3. 逻辑运算符
4. 位运算符(直接操作整数在内存中的二进制位)
5. 赋值运算符与复合赋值
控制流:决定代码的执行顺序
1. 条件语句:根据条件选择执行不同的代码
iif…else if…else 语句
三元运算符(条件运算符):简化版的if…else
2. 循环与迭代:重复执行某段代码
基本循环
for循环:适合已知循环次数的情况
while循环:适合不确定循环次数,只关注循环条件的情况
do…while循环:至少会执行一次代码块
循环控制:break 和 continue
break、continue 和 return 的区别
高级迭代循环
for…in循环:遍历对象的可枚举属性(包括原型链上的)
for…of循环:遍历可迭代对象(数组、字符串、Map、Set等)的值
for…in vs for…of
函数基础:可重复使用的代码块
1. 函数声明与函数表达式
函数声明:会提升(可以在声明前调用)
函数表达式:不会提升,必须先定义后调用
箭头函数(ES6新增):更简洁的函数表达式语法
函数声明提升 vs 函数表达式不提升
2. 箭头函数
无参数:必须使用圆括号
单个参数:可以省略圆括号
多个参数:必须使用圆括号
函数体有多条语句:必须使用大括号
返回对象字面量:需要用小括号包裹对象
箭头函数的this指向问题
3. 参数:传递给函数的输入
默认参数(ES6)
剩余参数(ES6):收集不定数量的参数到一个数组中
命名参数(通过对象解构实现)
4. 返回值与递归
递归的基本要素
基准条件(Base Case)
递归条件(Recursive Case)
递归的注意事项
基本代码规范与注释风格
1. 代码风格建议
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 会临时创建一个包装对象,操作完成后立即销毁。
这是为了方便地操作基本类型。
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]" |
最准确、最可靠的类型检测方法,能准确区分所有内置类型和自定义类型。 |
类型检测示例
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
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
console.log(arr instanceof Array); // true
console.log(obj instanceof Object); // true
console.log(date instanceof Date); // true
Object.prototype.toString.call()
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() // ❌ 报错 |
这就像“手动指定翻译目标”,更可控、更安全。
隐式转换示例
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
显式转换示例
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"(如数学运算)
- Hint "string"(如字符串拼接)
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和空字符串会被认为是假值)
function greet(name) {
name = name || 'Guest'; // 如果name是假值(如'', 0, null, undefined),则使用'Guest'
console.log('Hello, ' + name);
}
greet(); // Hello, Guest
greet('Alice'); // Hello, Alice
使用 ?? 更精确地设置默认值(只处理null和undefined)
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 |
let x = 10;
x += 5; // x = 10 + 5 = 15
x = 2; // x = 15 2 = 30
console.log(x); // 30
控制流:决定代码的执行顺序
控制流语句就像“交通指挥灯”或“岔路口的指示牌”,决定代码执行哪条路径。
1. 条件语句:根据条件选择执行不同的代码
if…else if…else 语句
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
// 条件 ? 表达式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循环:适合已知循环次数的情况
for (let i = 0; i < 5; i++) {
console.log('for循环:当前数字是:' + i);
}
// 输出:
// for循环:当前数字是:0
// for循环:当前数字是:1
// for循环:当前数字是:2
// for循环:当前数字是:3
// for循环:当前数字是:4
while循环:适合不确定循环次数,只关注循环条件的情况
let j = 0;
while (j < 3) {
console.log('while循环:' + j);
j++;
}
// 输出:
// while循环:0
// while循环:1
// while循环:2
do…while循环:至少会执行一次代码块
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只影响当前所在的循环。
如果存在嵌套循环,它们只影响包含它们的那一层循环。
// 嵌套循环中的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循环:遍历对象的可枚举属性(包括原型链上的)
// 注意:不推荐用于遍历数组,顺序可能不保证
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等)的值
// 注意:不能遍历普通对象
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. 函数声明与函数表达式
函数声明:会提升(可以在声明前调用)
function sayHello(name) {
console.log('Hello, ' + name + '!');
}
sayHello('Alice'); // Hello, Alice!
函数表达式:不会提升,必须先定义后调用
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(用作生成器函数) |
const sayHi = (name) => {
console.log('Hi, ' + name + '!');
};
sayHi('Charlie'); // Hi, Charlie!
函数声明提升 vs 函数表达式不提升
函数声明会被“提升”到当前作用域的顶部,可以在函数声明之前调用它。
sayHello(); // Hello! (可以调用)
function sayHello() {
console.log('Hello!');
}
函数表达式(包括箭头函数)不会被提升,必须先定义,然后才能调用。
// sayGoodbye(); // ❌ 报错: sayGoodbye is not a function
const sayGoodbye = function() {
console.log('Goodbye!');
};
sayGoodbye(); // Goodbye!
2. 箭头函数
箭头函数是ES6引入的更简洁的函数书写方式,但它与普通函数也有一些重要区别。
无参数:必须使用圆括号
const greet = () => console.log('Hello!');
单个参数:可以省略圆括号
const double = num => num*2;
多个参数:必须使用圆括号
const add = (a, b) => a + b;
函数体有多条语句:必须使用大括号
const describe = (name, age) => {
console.log('Name:', name);
console.log('Age:', age);
};
返回对象字面量:需要用小括号包裹对象
const createPerson = (name, age) => ({ name, age });
// 等同于: return { name, age };
箭头函数的this指向问题
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)
// 为参数设置默认值
function greet(name = 'Guest', greeting = 'Hello') {
console.log(greeting + ', ' + name + '!');
}
greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!
greet('Bob', 'Hi'); // Hi, Bob!
剩余参数(ES6):收集不定数量的参数到一个数组中
// 使用…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' ]
命名参数(通过对象解构实现)
// 通过对象解构实现命名参数,使代码更清晰
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. 返回值与递归
// 函数返回值:如果没有显式返回,则返回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)错误。
递归调用的参数必须向基准条件逼近。
// 无限递归的例子(会导致栈溢出)
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. 注释风格
单行注释示例
let userName = 'Alice'; // 用户名
多行注释示例
/**
*这是一个多行注释
*可以写很多行
*通常用于解释一段复杂的逻辑
*/
JSDoc风格注释示例(推荐用于函数)
/**
* 根据用户ID获取用户信息
* @param {number} userId – 用户ID
* @returns {Object|null} 用户对象或null(如果未找到)
* @example
* const user = getUserById(1);
* console.log(user.name); // 输出用户名
*/
function getUserById(userId) {
// …实现代码
}



