JavaScript代码由语句和表达式组成,理解它们的区别是学习JavaScript的基础。
表达式是产生值的代码片段,可以出现在任何需要值的地方:
// 字面量表达式
42
"东巴文"
true
// 变量表达式
name
user.age
// 算术表达式
1 + 2
a * b
// 函数调用表达式
Math.max(1, 2, 3)
greet("东巴文")
// 比较表达式
a > b
name === "东巴文"
// 逻辑表达式
a && b
!false
// 三元表达式
age >= 18 ? "成年" : "未成年"
东巴文要点:表达式总是返回一个值,可以嵌套使用。
语句是执行操作的代码片段,以分号结尾:
// 变量声明语句
let name = "东巴文";
const PI = 3.14159;
// 表达式语句
console.log("Hello");
a + b;
// 条件语句
if (age >= 18) {
console.log("成年");
}
// 循环语句
for (let i = 0; i < 10; i++) {
console.log(i);
}
// 函数声明语句
function greet(name) {
return `你好,${name}`;
}
// 返回语句
return value;
// 空语句
;
| 特性 | 表达式 | 语句 | 东巴文比喻 |
|---|---|---|---|
| 作用 | 产生值 | 执行操作 | 表达式是名词,语句是动词 |
| 返回值 | 有 | 无(或undefined) | 表达式有结果,语句是动作 |
| 独立使用 | 不能单独存在 | 可以独立存在 | 表达式需要上下文 |
// 表达式 - 产生值
1 + 1 // 值为2
// 语句 - 执行操作
let sum = 1 + 1; // 声明变量并赋值
// 表达式可以放在语句中
if (a > b) { // a > b 是表达式
console.log("a更大"); // 整行是语句
}
表达式可以作为语句使用,称为表达式语句:
// 函数调用
greet("东巴文");
// 赋值
a = 10;
// 自增
counter++;
// 这些都是表达式语句
标识符是用来命名变量、函数、属性等的名称。
| 规则 | 说明 | 示例 |
|---|---|---|
| 首字符 | 字母、下划线(_)、美元符号($) | name, _private, $elem |
| 后续字符 | 字母、下划线、美元符号、数字 | name1, user_name, $btn1 |
| 大小写 | 区分大小写 | name 和 Name 是不同的 |
| 保留字 | 不能使用保留字 | ❌ let, function, class |
// 合法的标识符
let name = "东巴文";
let userName = "张三";
let _private = "私有";
let $element = document.getElementById("app");
let user1 = "用户1";
let 用户名 = "中文也可以"; // 不推荐
// 非法的标识符
// let 1name = "错误"; // 不能以数字开头
// let user-name = "错误"; // 不能包含连字符
// let let = "错误"; // 不能使用保留字
东巴文推荐的命名风格:
| 命名风格 | 格式 | 适用场景 | 示例 |
|---|---|---|---|
| 小驼峰命名 | firstSecond | 变量、函数 | userName, getUserInfo |
| 大驼峰命名 | FirstSecond | 类、构造函数 | Person, UserController |
| 全小写 | lowercase | 常量、配置 | pi, max_size |
| 全大写下划线 | UPPER_CASE | 全局常量 | MAX_SIZE, API_URL |
| 下划线前缀 | _name | 私有属性 | _privateMethod |
// 变量和函数 - 小驼峰
let userName = "东巴文";
function getUserInfo() {}
// 类 - 大驼峰
class UserController {}
function Person(name) {}
// 常量 - 全大写下划线
const MAX_SIZE = 100;
const API_BASE_URL = "https://api.example.com";
// 私有属性 - 下划线前缀
class User {
constructor(name) {
this._name = name; // 约定为私有
}
}
// 好的命名 - 见名知意
let userName = "东巴文";
let isLoggedIn = true;
let userList = ["张三", "李四"];
function calculateTotal(prices) {}
function isValidEmail(email) {}
// 不好的命名 - 含义不清
let a = "东巴文";
let flag = true;
let arr = ["张三", "李四"];
function calc(p) {}
function check(e) {}
东巴文原则:命名应该准确描述其用途,让代码自解释。
关键字是JavaScript语言保留的词汇,有特殊用途:
// 声明关键字
var let const function class
// 控制流关键字
if else switch case default break continue
for while do in of
// 函数相关
return yield async await
// 其他
typeof instanceof new delete void this
import export from as extends super
try catch finally throw
static get set
保留字是未来可能成为关键字的词汇:
// 严格模式下的保留字
implements interface package private protected public
// 其他保留字
enum await
// 避免使用内置对象名
// let Object = {}; // ❌
// let Array = []; // ❌
// let String = ""; // ❌
// let Number = 0; // ❌
// let Boolean = true; // ❌
// 避免使用内置函数名
// let parseInt = 0; // ❌
// let parseFloat = 0; // ❌
// 避免使用全局变量名
// let undefined = 0; // ❌
// let NaN = 0; // ❌
// let Infinity = 0; // ❌
东巴文提醒:使用ESLint等工具可以帮助检测命名冲突问题。
严格模式(strict mode)是ES5引入的一种运行模式,它使JavaScript在更严格的条件下运行。
// 整个脚本启用严格模式
"use strict";
// 函数内部启用严格模式
function strict() {
"use strict";
// 函数代码
}
| 限制 | 非严格模式 | 严格模式 | 东巴文评价 |
|---|---|---|---|
| 未声明变量 | 创建全局变量 | 抛出ReferenceError | ✅ 防止意外全局变量 |
| 只读属性 | 静默失败 | 抛出TypeError | ✅ 及早发现错误 |
| 删除不可删除属性 | 静默失败 | 抛出TypeError | ✅ 防止危险操作 |
| 重复参数名 | 允许 | 抛出SyntaxError | ✅ 避免混淆 |
| 八进制字面量 | 允许 | 抛出SyntaxError | ✅ 统一规范 |
| with语句 | 允许 | 抛出SyntaxError | ✅ 避免混淆 |
| this默认值 | 指向全局对象 | undefined | ✅ 更安全 |
| eval作用域 | 创建局部作用域 | 独立作用域 | ✅ 更安全 |
"use strict";
// 1. 必须声明变量
x = 10; // ReferenceError: x is not defined
let x = 10; // 正确
// 2. 不能删除不可删除的属性
delete Object.prototype; // TypeError
// 3. 不能有重复的参数名
function add(a, a, c) {} // SyntaxError
// 4. 不能使用八进制字面量
let num = 010; // SyntaxError
// 5. this不指向全局对象
function showThis() {
console.log(this); // undefined
}
showThis();
// 6. 不能给只读属性赋值
const obj = {};
Object.defineProperty(obj, "name", { value: "东巴文", writable: false });
obj.name = "新名字"; // TypeError
"use strict";
// 1. 捕获常见错误
let obj = {};
Object.defineProperty(obj, "name", { value: "东巴文", writable: false });
obj.name = "新名字"; // 立即报错,而不是静默失败
// 2. 提高性能
// 引擎可以进行更多优化,因为某些危险操作被禁止
// 3. 为未来做准备
// 一些在严格模式下的限制在ES6+中是默认行为
东巴文建议:所有新项目都应该使用严格模式,现代JavaScript模块默认启用严格模式。
代码块是用花括号{}包围的一组语句:
// 条件语句代码块
if (true) {
let message = "条件为真";
console.log(message);
}
// 循环语句代码块
for (let i = 0; i < 3; i++) {
console.log(i);
}
// 函数代码块
function greet() {
let name = "东巴文";
console.log(`你好,${name}`);
}
// 独立代码块
{
let x = 10;
console.log(x);
}
作用域决定了变量的可访问范围。
// 全局变量 - 在任何地方都可以访问
var globalVar = "全局变量";
let globalLet = "全局let";
const GLOBAL_CONST = "全局常量";
function showGlobal() {
console.log(globalVar); // "全局变量"
console.log(globalLet); // "全局let"
}
showGlobal();
console.log(globalVar); // "全局变量"
function myFunction() {
// 函数作用域变量
var functionVar = "函数变量";
if (true) {
var stillFunctionVar = "仍然是函数变量";
console.log(functionVar); // "函数变量"
}
console.log(stillFunctionVar); // "仍然是函数变量"
}
myFunction();
// console.log(functionVar); // ReferenceError
// let和const具有块级作用域
function blockScope() {
if (true) {
let blockLet = "块级let";
const blockConst = "块级const";
var functionVar = "函数var";
console.log(blockLet); // "块级let"
console.log(blockConst); // "块级const"
}
console.log(functionVar); // "函数var"
// console.log(blockLet); // ReferenceError
// console.log(blockConst); // ReferenceError
}
blockScope();
| 特性 | var | let | const | 东巴文建议 |
|---|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 | 优先使用let/const |
| 变量提升 | 是 | 否(暂时性死区) | 否(暂时性死区) | let/const更安全 |
| 重复声明 | 允许 | 不允许 | 不允许 | let/const更严格 |
| 重新赋值 | 允许 | 允许 | 不允许 | const用于常量 |
| 全局属性 | 成为window属性 | 不成为window属性 | 不成为window属性 | let/const更干净 |
// var的问题
console.log(varVar); // undefined(变量提升)
var varVar = "var变量";
var varVar = "重复声明"; // 允许
if (true) {
var x = 10;
}
console.log(x); // 10(函数作用域)
// let的优势
// console.log(letVar); // ReferenceError(暂时性死区)
let letVar = "let变量";
// let letVar = "重复声明"; // SyntaxError
if (true) {
let y = 10;
}
// console.log(y); // ReferenceError(块级作用域)
// const的使用
const PI = 3.14159;
// PI = 3.14; // TypeError
const obj = { name: "东巴文" };
obj.name = "新名字"; // 允许,修改属性
// obj = {}; // TypeError,不能重新赋值
let global = "全局";
function outer() {
let outerVar = "外层";
function inner() {
let innerVar = "内层";
console.log(innerVar); // "内层" - 当前作用域
console.log(outerVar); // "外层" - 外层作用域
console.log(global); // "全局" - 全局作用域
}
inner();
// console.log(innerVar); // ReferenceError
}
outer();
东巴文图解:
全局作用域
├── global = "全局"
└── outer函数作用域
├── outerVar = "外层"
└── inner函数作用域
└── innerVar = "内层"
JavaScript有自动分号插入(ASI)机制,但不应依赖它。
// 这些情况下会自动插入分号
let a = 1
let b = 2
function greet() {
return
"Hello" // 自动插入分号,返回undefined
}
// 等价于
let a = 1;
let b = 2;
function greet() {
return; // 自动插入分号
"Hello";
}
// 问题1:return语句
function getValue() {
return
42; // 实际返回undefined
}
// 问题2:break、continue、throw
while (true) {
break
42; // 永远不会执行
}
// 问题3:自执行函数
let x = 1
(function() {
console.log("IIFE");
})() // 被解析为 1(function()...)(),报错
// 1. 语句开头是 [ 或 (
let a = 1;
(function() {})(); // 需要分号
[1, 2, 3].forEach(n => console.log(n)); // 需要分号
// 2. 语句开头是 +、-、/、*、,、.
let b = 2;
-1; // 需要分号,否则变成 2-1
// 3. 行尾是 ++ 或 --
let c = 3;
c
++ // 需要分号
let d = 4;
// 推荐:显式使用分号
let name = "东巴文";
function greet() {
return "Hello";
}
// 或者:使用工具自动添加
// Prettier等工具可以自动处理分号
东巴文建议:无论选择哪种风格,都要保持一致。推荐显式添加分号,避免意外错误。
掌握了基本语法后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
📚 东巴文寄语:语法是语言的基础,就像建筑的地基。掌握好语法规则,才能写出高质量的代码。在 db-w.cn,我们帮你打好每一个基础!