基本语法

语句与表达式

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
大小写 区分大小写 nameName 是不同的
保留字 不能使用保留字 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的区别

特性 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)机制,但不应依赖它。

ASI规则

// 这些情况下会自动插入分号
let a = 1
let b = 2

function greet() {
    return
    "Hello"  // 自动插入分号,返回undefined
}

// 等价于
let a = 1;
let b = 2;

function greet() {
    return;  // 自动插入分号
    "Hello";
}

ASI的问题

// 问题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等工具可以自动处理分号

东巴文建议:无论选择哪种风格,都要保持一致。推荐显式添加分号,避免意外错误。

下一步

掌握了基本语法后,让我们继续学习:

  1. 数据类型概述 - 了解数据类型
  2. 原始类型 - 深入学习原始类型
  3. 引用类型 - 深入学习引用类型

东巴文(db-w.cn) - 让编程学习更简单

📚 东巴文寄语:语法是语言的基础,就像建筑的地基。掌握好语法规则,才能写出高质量的代码。在 db-w.cn,我们帮你打好每一个基础!