大家好,我是橘朵~ 上一篇我们说的是JS代码的嵌入方式(<script>与<noscript>标签),这一篇我们讲一讲「JavaScript的语言规则」——就像说话要讲语法,写JS代码也有明确的规则,只有吃透这些规则,才能写出规范、不报错、易维护的代码,为后续学习函数、DOM操作、框架打下坚实基础。
适合人群:零基础前端新手、刚入门JS,写代码经常报错(语法错误、变量问题)的开发者;想规范代码风格,避免低级坑的初学者。
阅读收获:吃透JS基本语法规则、标识符命名规范、变量/常量声明(var/let/const区别)、数据类型、类型转换、运算符、流程控制、函数定义等核心规则,掌握8个实战避坑技巧,能独立写出规范的基础JS代码。
1. 核心语法规则
有些人会觉得”规矩是束缚“,但在实际开发中,不遵守规则会带来很多麻烦:比如代码报错无法运行、多人协作开发时别人看不懂你的代码、代码维护困难。
过往的经验教会我,重视语法规则,能避免很多不必要的错误。新手一定要记住:语言规则是JS的“地基”,地基打牢了,后续学习才会更轻松。
1.1 基本语法规则
1.1.1 区分大小写
JavaScript 是大小写敏感的。userName和 username是两个不同的变量。
// 区分大小写示例
let userName = "橘朵"; // 变量1:userName(驼峰命名)
let username = "前端新手"; // 变量2:username(全小写)
let UserName = "JS学习者"; // 变量3:UserName(帕斯卡命名)
console.log(userName); // 输出:橘朵
console.log(username); // 输出:前端新手
console.log(UserName); // 输出:JS学习者
// 三个变量是不同的,不会互相覆盖
在开发时,一定要注意保持变量名大小写的一致性,尤其是函数名、属性名,一定要注意大小写。
1.1.2 语句分隔
语句通常以分号 ; 结束。虽然浏览器在某些情况下会自动补全分号,但还是建议显式的添加分号,这样可以避免潜在的错误或是代码压缩时产生问题,同时,在某些情况下也有助于提升性能(不加分号解析器会尝试在合适的位置补上分号来纠正语法错误)。
// 错误写法:省略分号(不推荐)
let a = 10
let b = 20
console.log(a + b)
// 正确写法:显式加分号(推荐)
let c = 10;
let d = 20;
console.log(c + d); // 输出:30
// 隐藏坑:省略分号可能导致代码解析错误
let e = 5
let f = e + 3 // 看似没问题,但压缩后可能变成 let e=5let f=e+3,报错
从一开始就养成”每句代码结束加分号“的习惯,不用纠结”什么时候可以省“,直接全加,避免踩坑。
1.1.3 代码块
使用花括号 {}来定义代码块,比如if语句、循环和函数等。就算代码块中只有一条语句,也建议使用 {},这样能提高代码清晰度,也能避免不必要的错误。
// 有效,但容易导致错误,应该避免
if (test)
console.log(test);
// 推荐
if (test) {
console.log(test);
}
1.1.4 注释:代码的“说明书”
注释是代码的解释,注释的主要作用是:方便后续维护代码。
- 单行注释:以
//开头。 - 多行注释:以
/*开头,以*/结尾。
// 定义一个变量,存储用户姓名(单行注释,说明变量用途)
let userName = "橘朵";
/*
* 定义一个函数,计算两个数字的和
* 参数a:第一个数字
* 参数b:第二个数字
* 返回值:两个数字的和
*/
function add(a, b) {
return a + b; // 单行注释:返回计算结果
}
复杂逻辑一定要写注释,否则过几天自己都看不懂;不要注释掉代码后忘记删除,导致代码冗余。
1.1.5 严格模式:规范代码,提前报错
严格模式是一种不同的 JavaScript 解析和执行模型,ES5引入这个特性,ES6+完全兼容并推荐使用,能禁用ECMAScript 3的不规范写法,对不安全的操作抛出错误(比如未声明变量就使用),帮助我们提前发现问题,避免隐藏bug。
对整个脚本启用严格模式,可以在脚本开头加上这一行:
"use strict";//预处理指令
let x = 10;
// y = 20; // 报错:y未声明(严格模式下,未声明的变量不能使用)
单独指定一个函数在严格模式下执行,可以放到函数体开头:
function doSomething() {
"use strict";
let m = 5;
// n = 10; // 报错:n未声明
console.log(m); // 输出:5
}
1.1.6 模块化:import和export(ES6特性,代码复用)
ES6 引入了模块系统,使用 import和 export关键字来导入和导出模块,目前已成为JS模块化的标准,替代了过去的CommonJS(require/module.exports)、AMD等方案,所有现代浏览器和Node.js(14.13+)均全面支持,是当前主流的代码复用方式。
第一步:创建模块文件utils.js,导出函数/变量:
// utils.js(模块文件)
// 导出单个函数
export function add(a, b) {
return a + b;
}
// 导出单个变量
export const PI = 3.14159;
// 导出多个函数/变量(推荐)
const multiply = (x, y) => x * y;
const subtract = (x, y) => x - y;
export { multiply, subtract };
// 补充:默认导出(ES6+新增,适用于单个核心导出)
export default function divide(x, y) {
return x / y;
}
第二步:在主文件main.js中,导入并使用模块:
// main.js(主文件)
// 导入模块(注意路径要写对,相对路径需加./)
import { add, PI, multiply, subtract } from "./utils.js";
// 导入默认导出(无需大括号,可以自定义名称)
import divide from "./utils.js";
// 使用导入的函数/变量
console.log(add(2, 3)); // 输出:5
console.log(PI); // 输出:3.14159
console.log(multiply(4, 5)); // 输出:20
console.log(subtract(10, 3)); // 输出:7
console.log(divide(10, 2)); // 输出:5
模块化需要在支持ES6模块的环境中运行(浏览器需要给script标签加type="module",Node.js中需要把文件后缀改为.mjs或在package.json中设置"type": "module");路径一定要写对,相对路径必须加./或../,否则导入失败;默认导出与按需导出不可以混用(需分开导入)。
1.2 标识符和命名
标识符就是变量、函数、属性或函数参数的名称。命名有明确的规则,违反会报错;命名方式是推荐,可以让代码更易读。
1.2.1 命名规则
- 第一个字符必须以字母、下划线
_或美元符号$开头。 - 后续字符可以是字母、数字、下划线或美元符号。
- 不能使用 JS 的关键字和保留字(比如
if,let,function等,JS有专门的关键字列表,新手不用死记,编辑器会提示)。 - ES6+新增:可以使用Unicode字符(如中文、希腊字母)命名,但不推荐(影响兼容性和可读性)。
// 合法命名(推荐)
let userName = "橘朵"; // 字母开头,驼峰命名
let _age = 25; // 下划线开头
let $userId = 1001; // 美元符号开头
let user123Name = "张三"; // 后续字符包含数字
// 非法命名(报错)
// let 123user = "李四"; // 错误:以数字开头
// let user-name = "王五"; // 错误:包含连字符(-)
// let let = "变量"; // 错误:使用关键字let
// let 123 = 10; // 错误:以数字开头
1.2.2 命名方式(推荐遵守,提升可读性)
没有强制要求,但行业内有通用的命名方式,新手建议遵循,方便多人协作,也符合当前主流编码规范:
- 最常见的命名方式是驼峰命名法(camelCase),第一个字母小写,后面的单词首字母大写,比如userName、userNameCode。适用于变量、函数、属性。
- 帕斯卡命名法(PascalCase),也就是每个单词首字母都大写,比如MyClass。适用于构造函数、类名、组件(前端框架常用)。
- 常量通常全部大写,单词之间用下划线连接,比如MAX_SIZE。
- ES6+类/模块:类名必须用帕斯卡命名,模块导出的函数/变量优先用驼峰命名。
// 驼峰命名法(变量、函数)
let userEmail = "test@example.com";
function calculateTotalPrice() {
return 100;
}
// 帕斯卡命名法(类名)
class User {
constructor(name) {
this.name = name;
}
}
// 常量命名(全部大写)
const MAX_UPLOAD_SIZE = 5 * 1024 * 1024; // 5MB
const API_BASE_URL = "https://api.example.com";
// 模块导出命名(驼峰)
export function formatTime() {
return new Date().toLocaleString();
}
命名要“见名知意”,不要用a、b、c这种无意义的名称;尽量使用英文命名,避免中文(中文可能导致编码问题和兼容性问题);遵循命名方式,让代码更专业、易读。
1.3 变量与常量声明
在JavaScript中,可以使用var、let来声明变量和const来声明常量。
var:通过var声明的变量会被提升到函数或全局代码顶部,具有函数作用域或全局作用域。var因为存在很多问题(作用域混乱、变量提升等),已经被行业淘汰。let:在ES6 引入,通过let声明的变量不存在变量提升,作用域由{}界定的跨级作用域,而且在声明前访问会报错,称为”暂时性死区“。const:和let类似,只是const声明的是常量,而且必须初始化,不可以重新赋值,也就是说不可更改。但使用const声明的对象或数组,可以修改内部的属性或元素。
推荐使用 let 和 const 代替 var。const优先,let次之。
| 声明方式 | 作用域 | 变量提升 | 重复声明 | 赋值规则 |
|---|---|---|---|---|
| var | 函数作用域/全局作用域 | 存在(声明提升到顶部,值为undefined) | 允许 | 可重复赋值 |
| let | 块级作用域({}界定) | 不存在(暂时性死区,无法提前访问) | 不允许(同一作用域) | 可重复赋值 |
| const | 块级作用域({}界定) | 不存在(暂时性死区,无法提前访问) | 不允许(同一作用域) | 必须初始化,不可重复赋值(对象/数组可修改内部属性) |
// 1. var的问题(不推荐,已淘汰)
var a = 10;
var a = 20; // 允许重复声明,不报错
console.log(a); // 输出:20
function testVar() {
if (true) {
var b = 30; // 函数作用域,整个函数内都能访问
}
console.log(b); // 输出:30(if块外也能访问,不符合直觉)
}
testVar();
// 2. let的用法(推荐,变量)
let c = 10;
// let c = 20; // 不允许重复声明,报错
c = 20; // 可重复赋值
console.log(c); // 输出:20
function testLet() {
if (true) {
let d = 30; // 块级作用域,只在if块内有效
}
// console.log(d); // 报错:d未定义(块外无法访问,符合直觉)
}
testLet();
// 3. const的用法(优先推荐,常量)
const PI = 3.14; // 必须初始化,否则报错
// const PI; // 报错:未初始化
// PI = 3.1415; // 不允许重复赋值,报错
// 注意:const声明的对象/数组,可修改内部属性/元素
const user = { name: "橘朵" };
user.name = "张三"; // 允许,不报错
console.log(user.name); // 输出:张三
const arr = [1, 2, 3];
arr.push(4); // 允许,不报错
console.log(arr); // 输出:[1,2,3,4]
1.4 数据类型
JavaScript 是动态类型语言,变量在声明时不需要指定类型,运行时可以改变类型。数据类型分为原始类型(基本类型)和引用类型(对象类型),二者区别很大,新手容易混淆。ES6新增了BigInt、Symbol两种原始类型,目前已全面支持,是最新规范。
1.4.1 原始数据类型(不可变,共7种)
- Undefined:变量已声明但未赋值。
- Null:空值或不存在的对象。
- Boolean:
true或false。 - Number:整数和浮点数。
- BigInt:用于表示任意精度的整数,在数字末尾加
n,如12345678901234567890n。用于解决Number类型的精度限制。 - String:文本数据,可用单引号
''、双引号""或反引号``(模板字符串)定义。 - Symbol:用于表示唯一值,常用于对象属性的键。
// 1. Undefined:变量已声明但未赋值
let undef;
console.log(undef); // 输出:undefined
console.log(typeof undef); // 输出:"undefined"
// 2. Null:空值或不存在的对象
let nullVar = null;
console.log(nullVar); // 输出:null
console.log(typeof nullVar); // 输出:"object"(历史遗留bug,记住即可)
// 3. Boolean:布尔值(true/false)
let isSuccess = true;
let isError = false;
console.log(isSuccess); // 输出:true
// 4. Number:整数和浮点数(存在精度限制,最大安全整数2^53-1)
let intNum = 10; // 整数
let floatNum = 3.14; // 浮点数
let nanNum = NaN; // 非数字(Not a Number),NaN !== NaN(唯一不与自身相等的值)
console.log(intNum + floatNum); // 输出:13.14
console.log(Number.MAX_SAFE_INTEGER); // 输出:9007199254740991(最大安全整数)
// 5. BigInt:ES6+新增,任意精度整数(数字末尾加n,解决Number精度问题)
let bigNum = 12345678901234567890n;
console.log(bigNum * 2n); // 输出:24691357802469135780n(不会溢出)
// 注意:BigInt不能与Number直接运算,需先转换类型
console.log(Number(bigNum) + 10); // 输出:12345678901234567900(可能丢失精度)
// 6. String:文本数据(单引号、双引号、反引号,反引号为ES6+新增)
let str1 = '单引号字符串';
let str2 = "双引号字符串";
let str3 = `反引号字符串(模板字符串),可以换行:
第二行内容`;
let name = "橘朵";
let str4 = `你好,我是${name}`; // 模板字符串,插入变量(ES6+新增)
console.log(str4); // 输出:"你好,我是橘朵"
// 7. Symbol:ES6+新增,唯一值(常用于对象属性键,避免属性名冲突)
let sym1 = Symbol("key");
let sym2 = Symbol("key");
console.log(sym1 === sym2); // 输出:false(即使描述相同,也是不同的值)
// 应用:对象唯一属性
const obj = {
[sym1]: "value1",
[sym2]: "value2"
};
console.log(obj[sym1]); // 输出:"value1"
1.4.2 引用数据类型(对象类型,可变):
引用类型是复杂数据类型,值可变,本质是对象。
- Object:各种对象的基类。
- Array:数组是特殊的对象,用于表示有序列表。
- Function:函数也是对象。
- 其他:如
Date,RegExp,Map,Set等。Map、Set是ES6新增的引用类型,比传统对象、数组更灵活,当前已广泛使用。
// 1. Object:键值对集合(基础引用类型)
let user = {
name: "橘朵",
age: 25,
isStudent: false
};
console.log(user.name); // 输出:"橘朵"(访问属性)
user.age = 26; // 修改属性(可变)
console.log(user.age); // 输出:26
// 2. Array:有序数据集合(ES6+新增诸多方法,如push、map、filter等)
let fruits = ["苹果", "香蕉", "橙子"];
console.log(fruits[0]); // 输出:"苹果"(访问元素)
fruits.push("葡萄"); // 添加元素(可变)
console.log(fruits); // 输出:["苹果","香蕉","橙子","葡萄"]
// ES6+新增方法示例
console.log(fruits.map(fruit => fruit + "(新鲜)")); // 输出:["苹果(新鲜)",...]
// 3. Function:函数(也是对象,ES6+支持箭头函数)
function sayHello() {
console.log("你好!");
}
sayHello(); // 输出:"你好!"(调用函数)
// 4. Date:日期对象(无版本更新,沿用至今)
let now = new Date();
console.log(now); // 输出当前日期时间
console.log(now.getFullYear()); // 输出当前年份
// 5. Map:ES6+新增,映射(键可以是任意类型,解决对象键只能是字符串/符号的问题)
let map = new Map();
map.set("name", "橘朵");
map.set(Symbol("id"), 1001);
map.set(123, "数字键");
console.log(map.get("name")); // 输出:"橘朵"
console.log(map.size); // 输出:3(获取映射长度)
// 6. Set:ES6+新增,集合(不重复元素,自动去重)
let set = new Set([1, 2, 2, 3]);
console.log(set); // 输出:Set(3) {1,2,3}(自动去重)
set.add(4); // 添加元素
set.delete(2); // 删除元素
console.log(set.has(3)); // 输出:true(判断元素是否存在)
1.5 类型转换
JavaScript在执行操作时会自动进行隐式类型转换。比如字符串与数字相加转换为字符串,这是JS的特点,也是新手最容易踩坑的地方。 也可以使用Number(), String(), Boolean()等函数进行显式转换(推荐) 。
1.5.1 隐式类型转换
隐式转换是JS自动进行的,新手要记住常见的转换规则,避免报错。
- 字符串+数字 转换为 字符串
- 数字 + 布尔值 转换为 数字
- 比较运算符
==进行隐式转换,===严格相等不进行转换 - 逻辑运算符转为布尔值,除
false、0、""(空字符串)、null、undefined、NaN外,其他都是真值 BigInt只能与BigInt运算Symbol禁止隐式转换,会报错
// 1. 字符串 + 数字 → 字符串(最常见坑)
let num1 = 10;
let str1 = "20";
console.log(num1 + str1); // 输出:"1020"(不是30,新手容易误以为是加法)
// 2. 数字 + 布尔值 → 数字(true=1,false=0)
let num2 = 5;
let bool1 = true;
console.log(num2 + bool1); // 输出:6(true转换为1)
// 3. 比较运算符的隐式转换(== vs ===)
console.log(10 == "10"); // 输出:true(==会进行隐式转换,认为10和"10"相等)
console.log(10 === "10"); // 输出:false(===严格相等,不进行转换,类型不同不相等)
console.log(null == undefined); // 输出:true(特殊规则)
console.log(null === undefined); // 输出:false
// 4. 逻辑运算符的隐式转换(转为布尔值)
// 假值:false、0、""(空字符串)、null、undefined、NaN
// 真值:除假值外的所有值
console.log(!!0); // 输出:false(双感叹号显式转为布尔值)
console.log(!!""); // 输出:false
console.log(!!"hello"); // 输出:true
console.log(!!null); // 输出:false
// 5. ES6+新增:BigInt的隐式转换(仅能与BigInt运算)
let big1 = 10n;
let big2 = 20n;
console.log(big1 + big2); // 输出:30n(正常运算)
// console.log(big1 + 10); // 报错:BigInt不能与Number隐式运算
// 6. ES6+新增:Symbol的隐式转换(禁止隐式转换,会报错)
let sym = Symbol("key");
// console.log(sym + "123"); // 报错:Cannot convert a Symbol value to a string
// console.log(Number(sym)); // 报错:Cannot convert a Symbol value to a number
1.5.2 显式类型转换(推荐)
显式转换是手动调用函数进行转换,清晰、不易错,新手推荐使用。
- 转为数字:
Number()、parseInt()、parseFloat() - 转为字符串:
String()、toString() - 转为布尔值:
Boolean() BigInt()Symbol()
// 1. 转为数字:Number()、parseInt()、parseFloat()
let strToNum1 = Number("123");
console.log(strToNum1); // 输出:123(成功转换)
let strToNum2 = Number("123abc");
console.log(strToNum2); // 输出:NaN(转换失败)
let parseIntNum = parseInt("123.45abc");
console.log(parseIntNum); // 输出:123(只解析整数部分)
let parseFloatNum = parseFloat("123.45abc");
console.log(parseFloatNum); // 输出:123.45(解析浮点数部分)
// 2. 转为字符串:String()、toString()
let numToString1 = String(123);
console.log(numToString1); // 输出:"123"
let boolToString = String(true);
console.log(boolToString); // 输出:"true"
let numToString2 = 123.toString();
console.log(numToString2); // 输出:"123"
// 3. 转为布尔值:Boolean()
let numToBool = Boolean(123);
console.log(numToBool); // 输出:true
let nullToBool = Boolean(null);
console.log(nullToBool); // 输出:false
// 4. ES6+新增:BigInt的显式转换
let numToBig = BigInt(123);
console.log(numToBig); // 输出:123n
let bigToString = String(123n);
console.log(bigToString); // 输出:"123"
// 5. ES6+新增:Symbol的显式转换(仅能转为字符串)
let sym = Symbol("key");
console.log(String(sym)); // 输出:"Symbol(key)"
// console.log(Number(sym)); // 报错:无法转为数字
1.5.3小结
- 尽量使用
===(严格相等)代替==(相等),避免隐式转换导致的判断错误,比如10 == "10"返回true。 - 字符串和数字相加时,一定要先显式转换类型,避免得到拼接字符串,比如
Number(str1) + num1。 - 记住JS的“假值列表”,逻辑判断时避免出错。
BigInt只能与BigInt运算,Symbol不能隐式转换,否则会报错。
1.6 运算符(JS的“计算工具”,必学)
运算符用于执行运算(算术、赋值、比较、逻辑等),是JS的基础工具,新手要掌握常用运算符的用法和优先级。
- 算术运算符:
+,-,*,/,%(取模),**(指数幂)。 - 赋值运算符:
=,+=,-=,*=,/=,%=。 - 比较运算符:
<,>,<=,>=,==(相等,会进行类型转换),===(严格相等,推荐),!=,!==。 - 逻辑运算符:
&&(逻辑与),||、??(逻辑或),!(逻辑非) 。支持短路求值。 - 三元运算符:
condition ? expr1 : expr2。 - 可选链运算符:
? - 扩展运算符:
...
// 1. 算术运算符:+(加)、-(减)、*(乘)、/(除)、%(取模)、**(指数幂,ES6+新增)
let a = 10, b = 3;
console.log(a + b); // 输出:13(加法)
console.log(a - b); // 输出:7(减法)
console.log(a * b); // 输出:30(乘法)
console.log(a / b); // 输出:3.333...(除法)
console.log(a % b); // 输出:1(取模,余数)
console.log(a ** b); // 输出:1000(指数幂,10^3,ES6+新增,替代Math.pow)
// 2. 赋值运算符:=、+=、-=、*=、/=、%=、**=(ES6+新增)
let c = 5;
c += 3; // 等价于 c = c + 3
console.log(c); // 输出:8
c **= 2; // 等价于 c = c ** 2(ES6+新增)
console.log(c); // 输出:64
// 3. 比较运算符:<、>、<=、>=、==、===、!=、!==(无新增,沿用规范)
let d = 10, e = "10";
console.log(d > 5); // 输出:true
console.log(d == e); // 输出:true(隐式转换)
console.log(d === e); // 输出:false(严格相等,推荐)
console.log(d != e); // 输出:false
console.log(d !== e); // 输出:true(推荐)
// 4. 逻辑运算符:&&(逻辑与)、||(逻辑或)、!(逻辑非)(支持短路求值)
// ES6+新增:空值合并运算符??(解决||的假值误判问题)
let f = true, g = false;
console.log(f && g); // 输出:false(逻辑与,都为真才真)
console.log(f || g); // 输出:true(逻辑或,一个为真就真)
console.log(!f); // 输出:false(逻辑非,取反)
// 短路求值(实用技巧)
let username = "";
let defaultName = username || "默认用户"; // 若username为假值,取默认值(存在假值误判)
let defaultName2 = username ?? "默认用户"; // ES6+新增,仅当username为null/undefined时取默认值
console.log(defaultName); // 输出:"默认用户"
console.log(defaultName2); // 输出:""(空字符串不是null/undefined,不替换)
// 5. ES6+新增:可选链运算符?.(安全访问对象属性,避免报错)
let userInfo = { name: "橘朵" };
// console.log(userInfo.age.name); // 报错:Cannot read properties of undefined
console.log(userInfo.age?.name); // 输出:undefined(不会报错,安全访问)
// 6. 三元运算符:condition ? expr1 : expr2(条件判断,简化if语句)
let score = 85;
let result = score >= 60 ? "及格" : "不及格";
console.log(result); // 输出:"及格"
// 7. ES6+新增:扩展运算符...(用于数组/对象的展开和合并)
let arr1 = [1, 2, 3];
let arr2 = [4, 5, ...arr1]; // 展开arr1
console.log(arr2); // 输出:[4,5,1,2,3]
let obj1 = { name: "橘朵" };
let obj2 = { age: 25, ...obj1 }; // 展开obj1
console.log(obj2); // 输出:{age:25, name:"橘朵"}
小结:
- 算术运算符中,
+既可以做加法,也可以做字符串拼接,新手要注意类型(数字+数字=加法,数字+字符串=拼接); - 比较运算符中,推荐使用
===和!==,避免隐式转换; - 逻辑运算符支持短路求值,ES6+新增的
??和?.运算符更实用,??避免假值误判,?.避免对象属性不存在报错; - 三元运算符适合简单的二选一判断,复杂逻辑还是用
if语句,避免代码可读性变差; - 扩展运算符
...常用于数组/对象的合并、传递参数,是当前主流用法。
1.7 流程控制
流程控制用于控制程序的执行顺序,包括条件语句(根据条件执行不同代码)和循环语句(重复执行代码),是编写复杂逻辑的基础。ES6+新增了for...of循环、for await...of(异步循环,新手暂不涉及),优化了流程控制的便捷性,当前主流用法以ES6+为准。
- 条件语句
if...else、else if:用于基于不同条件执行不同代码分支。switch:用于多条件分支选择,注意每个case后通常要加break防止穿透。
- 循环语句
for:传统计数循环。while:当条件为真时循环。do...while:至少执行一次循环体,再判断条件。for...of:遍历可迭代对象(如数组、字符串、Map、Set)的值。for...in:遍历对象的可枚举属性(通常包括原型链上的),不推荐使用for...in遍历数组。
1.7.1 条件语句(if...else/else if、switch)
根据不同的条件执行不同的代码。
// 1. if...else/else if(适用于少量条件分支)
let score = 85;
if (score >= 90) {
console.log("优秀");
} else if (score >= 80) {
console.log("良好");
} else if (score >= 60) {
console.log("及格");
} else {
console.log("不及格");
}
// 输出:"良好"
// 2. switch(适用于多条件分支,注意break防止穿透)
let fruit = "苹果";
switch (fruit) {
case "苹果":
console.log("苹果单价:5元/斤");
break; // 必须加break,否则会穿透到下一个case
case "香蕉":
console.log("香蕉单价:3元/斤");
break;
case "橙子":
console.log("橙子单价:4元/斤");
break;
default:
console.log("暂无此水果价格"); // 所有case不匹配时执行
}
1.7.2 循环语句(for、while、do...while、for...of、for...in)
重复执行一段代码,适合处理批量数据(比如数组遍历)。
// 1. for循环(传统计数循环,适用于已知循环次数)
console.log("for循环:");
for (let i = 0; i < 5; i++) { // 初始化→条件判断→循环体→更新变量
console.log(i); // 输出:0,1,2,3,4
}
// 2. while循环(适用于未知循环次数,条件为真时循环)
console.log("while循环:");
let j = 0;
while (j < 5) {
console.log(j); // 输出:0,1,2,3,4
j++;
}
// 3. do...while循环(至少执行一次循环体,再判断条件)
console.log("do...while循环:");
let k = 0;
do {
console.log(k); // 输出:0,1,2,3,4
k++;
} while (k < 5);
// 4. for...of循环(遍历可迭代对象,如数组、字符串,推荐)
console.log("for...of循环(数组):");
let fruits = ["苹果", "香蕉", "橙子"];
for (let fruit of fruits) {
console.log(fruit); // 输出:"苹果","香蕉","橙子"
}
console.log("for...of循环(字符串):");
let str = "hello";
for (let char of str) {
console.log(char); // 输出:"h","e","l","l","o"
}
// 5. for...in循环(遍历对象可枚举属性,不推荐遍历数组)
console.log("for...in循环(对象):");
let user = { name: "橘朵", age: 25 };
for (let key in user) {
console.log(`${key}: ${user[key]}`); // 输出:"name: 橘朵", "age: 25"
}
// 不推荐用for...in遍历数组(会遍历原型链上的属性)
let arr = [1, 2, 3];
Array.prototype.test = "测试"; // 给数组原型添加属性
console.log("for...in循环(数组,不推荐):");
for (let index in arr) {
console.log(arr[index]); // 输出:1,2,3,"测试"(意外遍历到test属性)
}
1.8 函数定义(JS的“功能模块”,一等公民)
函数是 JavaScript 的一等公民,可以像变量一样被传递、赋值、作为参数,是代码复用和组织的核心。JS有三种常见的函数定义方式:函数声明、函数表达式、箭头函数。
- 函数声明:
function functionName(parameters) { // 函数体 }。 - 函数表达式:
const functionName = function(parameters) { // 函数体 }。 - 箭头函数:
const functionName = (parameters) => { // 函数体 }或const functionName = parameter => expression。
// 1. 函数声明(function关键字,有函数提升,可在声明前调用)
console.log(add(2, 3)); // 输出:5(声明前调用有效)
function add(a, b) {
return a + b;
}
// 2. 函数表达式(赋值给变量,无函数提升,不能在声明前调用)
// console.log(subtract(5, 2)); // 报错:subtract未定义(声明前调用无效)
const subtract = function(a, b) {
return a - b;
};
console.log(subtract(5, 2)); // 输出:3
// 3. 箭头函数(ES6特性,简洁,无this绑定,无arguments)
const multiply = (a, b) => {
return a * b;
};
console.log(multiply(4, 5)); // 输出:20
// 简化写法:只有一个参数时,括号可省;函数体只有一条return语句时,花括号和return可省
const double = num => num * 2;
console.log(double(6)); // 输出:12
// 箭头函数无this绑定(新手注意:不适合作为对象方法)
const user = {
name: "橘朵",
sayHello: () => {
console.log(`你好,我是${this.name}`); // this指向全局,不是user对象
}
};
user.sayHello(); // 输出:"你好,我是undefined"(不推荐这样用)
1.9 对象与数组
对象和数组是JS中最基础、最常用的引用类型,贯穿整个前端开发,新手必须熟练掌握他们的核心定义和基础用法,为后续复杂逻辑开发打下基础。
- 对象:是键值对的集合。可以使用字面量
{}创建,属性键可以是字符串或 Symbol,值可以是任何类型。对象的属性可动态添加、修改和删除,是存储复杂数据的核心载体。 - 数组:是有序的数据集合。可以使用字面量
[]创建,数组元素可以是任何类型,并且数组有很多内置方法(如push,pop,map,filter,reduce等),这些方法能高效处理数组数据,简化开发流程。
// 对象的基础用法
const person = { // 字面量创建对象
name: "橘朵", // 字符串键
[Symbol("id")]: 1001, // Symbol键(避免属性名冲突)
age: 25, // 数字值
isStudent: false, // 布尔值
hobbies: ["看书", "敲代码"], // 数组值(引用类型)
sayHi: function() { // 函数值(引用类型)
console.log(`你好,我是${this.name}`);
}
};
// 访问属性
console.log(person.name); // 输出:橘朵
console.log(person[Symbol("id")]); // 输出:undefined(Symbol键需用变量存储访问)
// 修改属性
person.age = 26;
// 添加属性
person.gender = "女";
// 删除属性
delete person.isStudent;
person.sayHi(); // 输出:你好,我是橘朵
// 数组的基础用法+常用内置方法
const fruits = ["苹果", "香蕉", "橙子"]; // 字面量创建数组
// 访问元素
console.log(fruits[0]); // 输出:苹果
// 内置方法使用
fruits.push("葡萄"); // 新增元素,输出:4(数组长度)
fruits.pop(); // 删除最后一个元素,输出:葡萄
const newFruits = fruits.map(fruit => fruit + "(新鲜)"); // 映射新数组
console.log(newFruits); // 输出:["苹果(新鲜)", "香蕉(新鲜)", "橙子(新鲜)"]
const filterFruits = fruits.filter(fruit => fruit !== "香蕉"); // 筛选数组
console.log(filterFruits); // 输出:["苹果", "橙子"]
2 一些小建议
-
始终使用
let和const,避免使用var。 -
使用严格相等运算符
===和!==。 -
总是使用分号
;明确结束语句。 -
即使代码块只有一条语句,也使用花括号
{}。 -
为变量和函数使用描述性的名称并遵循命名惯例。
-
合理使用注释解释复杂的逻辑。
-
注意隐式类型转换可能带来的陷阱。
-
考虑使用严格模式。
3 结尾总结
这篇文章,我们吃透了JavaScript的核心语言规则,从基本语法、命名规则,到变量常量、数据类型、运算符、流程控制、函数定义、对象与数组,覆盖了JS语法的“地基”知识点。
总结一下核心要点:
- 基础语法:区分大小写、显加分号、用花括号包裹代码块、合理写注释、启用严格模式;
- 命名规则:见名知意,变量/函数用驼峰命名,类用帕斯卡命名,常量全大写;
- 变量常量:优先用
const,其次用let,放弃var;const声明的对象/数组可修改内部属性; - 数据类型:分为原始类型(7种:
Undefined、Null、Boolean、Number、BigInt、String、Symbol,值不可变)和引用类型(本质是对象,如Object、Array、Function等,值可变),核心区别是赋值时传递值本身还是引用; - 类型转换:优先用显式转换(
Number()、String()、Boolean()),避免隐式转换坑,判断相等用===; - 运算符:掌握常用算术、赋值、逻辑运算符,善用短路求值和ES6新增运算符,避免+号拼接陷阱;
- 流程控制:
if...else适合少量分支,switch需加break防穿透;遍历数组优先用for...of,避免for...in; - 函数定义:三种方式各有场景,箭头函数无this绑定,不适合作为对象方法。
- 对象与数组:对象是键值对的集合,用
{}创建;数组是是有序的数据集合,用[]创建。
下一篇文章,我们将聚焦「关键字与保留字」——这是JS语法的“红线”。