语法基础
约 2902 字大约 10 分钟
2025-11-05
变量声明
JavaScript 中有三个关键字来声明变量:var、let 和 const。
var
var 声明的变量有以下特点:
- 函数作用域,函数退出时销毁
- 存在变量提升
- 允许重复声明
- 会成为 window 对象(可通过 window.a 访问)
在函数内部使用 var 创建的变量,函数内部可以访问。在函数退出时销毁。
function add() {
var a = 1;
}
add();
console.log(a); // ReferenceError: a is not defined由于 var 具有变量提升,会将声明的变量拉到函数顶部,所以允许在声明前使用,且可以多次声明。
function add() {
console.log(a);
var a = 1;
var a = 2; // 允许重新声明
}
// 由于变量提升,上面代码等价于:
function add() {
var a;
console.log(a);
a = 1;
a = 2;
}
add();
// 输出:undefined
for (var i = 0; i < 5; i++) {
// ...
}
console.log(i); // var 将会被提升到函数顶部,因此输出5let
let 声明的变量有以下特点:
- 块作用域
- 不允许重复声明
- 不存在变量提升
若 let 变量是声明在 for 、if 等块域中,let 变量只在当前块域有效。且由于没有变量提升,存在暂时性失去,在声明前使用会抛出 ReferenceError 错误。
for (let i = 0; i < 5; i++) {
console.log(i); // 输出:0 1 2 3 4
}
console.log(i); // ReferenceError: i is not definedconst
const 与 let 较为相似,唯一区别在于 const 声明变量时必须初始化:
- 块作用域
- 不允许重复声明
- 不存在变量提升
- 必须初始化,且声明的基本类型不可变,但引用类型可修改内部属性。
const greeting = "Hello, World!";
greeting = "Hello, JavaScript!"; // 将会报错:Assignment to constant variable ,const 声明的变量不允许重新赋值
const user = {
name: "Alice",
age: 30,
};
user.name = "Bob"; // const 声明的引用类型可以修改内部属性总结
- var 是函数作用域,所以在循环中共享同一个闭包。
- let 与 const 都是块级作用域,在每次循环中创建新的词法环境,会有一个新的变量实例。
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0);
}
// 输出:5 5 5 5 5
for (let i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0);
}
// 输出:0 1 2 3 4- const 有变量提升,let 与 const 没有。
数据类型
在 JavaScript 中,数据类型分为 基本数据类型 和 引用数据类型 。
- 基本数据类型:存储简单值的类型,如数字、字符串、布尔值、Null、Undefined、Symbol 和 BigInt。
- 引用数据类型:存储对象的类型,如对象、数组、函数等。
Undefined
当一个变量被声明但未赋值,或者函数没有返回值时,其值为 undefined。
Null
表示一个空对象指针,通常用于手动将变量置为空。
undefined表示变量已声明但未赋值,或属性/函数无返回值。是“缺少值”的预期状态。null是开发者显式赋值的值,表示“有意为空”或“无对象”。
区别:
- 类型:
typeof undefined→'undefined';typeof null→'object'。 - 语义:
undefined是“未定义”,null是“空值”。 - 赋值:通常用
null初始化可能后续引用对象的变量,而undefined多由 JS 引擎自动赋予。
Boolean
只有两个值,true 和 false,用于逻辑判断。
Number
表示整数和浮点数,包括特殊值 NaN(非数字,例如计算失败时 - 0 除以其它数字)、Infinity(正无穷)和 -Infinity(负无穷)。
有 3 个函数可以将非数值转换为数值:Number()、parseInt()和 parseFloat()。Number()是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。
String
String(字符串)数据类型表示零或多个 16 位 Unicode 字符序列。字符串可以使用双引号(")、单引号(')或反引号(`)标示。
Symbol
Symbol 是 ES6 引入的一种原始数据类型,用于创建唯一且不可变的标识符。
创建对象的唯一属性键(避免属性名冲突):
const id = Symbol("id");
const obj = {
name: "Hoey",
[id]: 123,
};BigInt
Bigint ES2020 引入,用于表示任意大的整数,通过在整数后面加 n 来创建。
引用数据类型
Object 最基本的引用数据类型,可以包含各种键值对,如普通对象、数组、函数等。 Array 一种特殊的对象,用于存储有序的数据集合。 Function 也是一种对象,用于封装可重复使用的代码块。
确定类型
JavaScript 中使用 typeof 运算符来确定变量的类型。
let s = "Nicholas";
let b = true;
let i = 22;
let u;
let n = null;
let o = new Object();
console.log(typeof s); // string
console.log(typeof i); // number
console.log(typeof b); // boolean
console.log(typeof u); // undefined
console.log(typeof n); // object
console.log(typeof o); // object可以看出 typeof 对原始数据类型很有用,但对引用类型都还返回 object。为了解决这个问题,ECMAScript 提供了 instanceof 操作符, 来判断一个对象是否属于某个构造函数的实例。
console.log(person instanceof Object); // 变量person是Object吗?
console.log(colors instanceof Array); // 变量colors是Array吗?
console.log(pattern instanceof RegExp); // 变量pattern是RegExp吗?操作符
赋值操作符
=:基本赋值let x = 10;+=:加后赋值let a = 5; a += 3; // a 现在是 8-=:减后赋值let b = 10; b -= 4; // b 现在是 6*=:乘后赋值let c = 3; c *= 2; // c 现在是 6/=:除后赋值let d = 20; d /= 5; // d 现在是 4%=:取余后赋值let e = 10; e %= 3; // e 现在是 1**
**=**:幂后赋值let f = 2; f **= 3; // f 现在是 8&&=:逻辑与赋值(仅当左值为真时赋值)let g = 5; g &&= 2; // g 现在是 2(因为 5 是真值)||=:逻辑或赋值(仅当左值为假时赋值)let h = 0; h ||= 10; // h 现在是 10(因为 0 是假值)??=:空值合并赋值(仅当左值为null或undefined时赋值)let i = null; i ??= "default"; // i 现在是 "default"
算术操作符
+:加法console.log(3 + 4); // 7-:减法console.log(10 - 6); // 4*:乘法console.log(5 * 2); // 10/:除法console.log(15 / 3); // 5%:取余console.log(7 % 3); // 1**
****:幂运算console.log(2 ** 4); // 16++(前缀):先自增再使用let n = 1; console.log(++n); // 输出 2,n 变为 2++(后缀):先使用再自增let m = 1; console.log(m++); // 输出 1,m 变为 2--(前/后缀):类似++,但为自减let p = 5; console.log(p--); // 输出 5,p 变为 4一元
+:转为数字console.log(+"42"); // 42一元
-:取负或转为数字console.log(-5); // -5 cosnole.log(-"5"); // -5
比较操作符
==:宽松相等(会类型转换)console.log(5 == "5"); // true!=:宽松不等console.log(5 != "6"); // true===:严格相等(类型和值都相同)console.log(5 === "5"); // false!==:严格不等console.log(5 !== "5"); // true<、>、<=、>=:大小比较console.log(3 < 5); // true console.log(4 >= 4); // truein:检查属性是否存在于对象(含原型链)console.log("length" in "hello"); // true(字符串有 length)instanceof:检查是否为某构造函数的实例console.log([] instanceof Array); // true
逻辑操作符
&&:逻辑与(短路求值)console.log(true && "ok"); // 'ok'||:逻辑或(短路求值)console.log(false || "fallback"); // 'fallback'!:逻辑非console.log(!true); // false??:空值合并(仅对null/undefined有效)console.log(0 ?? "zero"); // 0(因为 0 不是 null/undefined) console.log(null ?? "empty"); // 'empty'
五、位操作符
&:按位与console.log(5 & 3); // 1(二进制 101 & 011 = 001)|:按位或console.log(5 | 3); // 7(101 | 011 = 111)^:按位异或console.log(5 ^ 3); // 6(101 ^ 011 = 110)~:按位非console.log(~5); // -6(对整数按位取反后加 -1)<<:左移console.log(3 << 1); // 6(二进制 11 → 110)>>:算术右移(保留符号)console.log(-8 >> 1); // -4>>>:无符号右移(高位补 0)console.log(-1 >>> 1); // 2147483647(在 32 位系统中)
其他重要操作符
typeof:返回类型字符串console.log(typeof 42); // 'number' console.log(typeof null); // 'object'(历史 bug)void:返回undefinedconsole.log(void 0); // undefineddelete:删除对象属性let obj = { a: 1 }; delete obj.a; // true,obj 变为 {}await:等待 Promise(仅在async函数中)async function f() { let res = await fetch("/api"); }yield:在生成器中暂停function* gen() { yield 1; }三元操作符
? ::条件表达式let msg = age >= 18 ? "adult" : "minor";逗号操作符
,:返回最后一个表达式let x = (1, 2, 3); // x 是 3可选链
?.:安全访问嵌套属性console.log(obj?.user?.name); // 若 obj 或 user 为 null/undefined,返回 undefined扩展操作符
...:展开或收集let arr = [1, ...[2, 3]]; // [1, 2, 3] function sum(...nums) { /* nums 是数组 */ }
语句
提示
注意区分 JS 中 语句与表达式 的区别。
语句是指控制流语句、声明语句、表达式语句。通常使用一或多个关键字完成既定的任务,例如 if、for、复制变量等。 而表达式则返回一个值,例如 1 + 2、true、filter()。
条件分支
if (x > 0) {
/* ... */
} else {
/* ... */
}
switch (status) {
case "ok":
console.log("OK");
break;
default:
console.log("Unknown");
}循环
for (let i = 0; i < 3; i++) {}
while (n--) {}
do {
/* ... */
} while (false);
for (let k in obj) {
} // 遍历对象属性
for (let v of arr) {
} // 遍历可迭代对象跳转
return:从函数返回break/continue:控制循环throw:抛出错误try...catch...finally:异常处理try { riskyOperation(); } catch (err) { console.error(err); } finally { cleanup(); }
标签语句
为语句(通常是循环)添加标识,配合 break/continue 精确控制跳转。
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outer; // 直接跳出外层循环
}
console.log(i, j);
}
}
// 输出到 (1,0) 后终止添加标签虽合法,但现代代码中应避免使用,可用函数封装或标志变量替代。
with
临时将对象加入作用域链顶部,使属性可直接访问。
const obj = { a: 1, b: 2 };
with (obj) {
console.log(a + b); // 3(相当于 obj.a + obj.b)
}with 语句已废弃,不建议使用。
推荐使用:对象解构
const { a, b } = obj;
console.log(a + b);函数
在 JavaScript 中函数是可调用的特殊对象,属于 Function 类型,同时继承自 Object。它允许我们创建可重用的代码块,并传递参数以执行特定的任务。
函数声明
通过 function 关键字声明,具有函数提升。
function greet(name) {
return `Hello, ${name}!`;
}函数表达式
将函数赋值给变量,无提升,常用于条件定义或作为回调。
const greet = function (name) {
return `Hello, ${name}!`;
};匿名函数
没有函数名的函数,通常作为函数表达式或回调使用。
setTimeout(function () {
console.log("Delayed!");
}, 1000);
// 或作为立即调用函数表达式(IIFE)
(function () {
console.log("Run once");
})();箭头函数
语法简洁,没有自己的 this、Arguments对象、super 或 new.target,始终是匿名的(但可赋值给变量)。
基本语法:
// 无参数
const sayHi = () => "Hi";
// 单参数(可省略括号)
const double = (x) => x * 2;
// 多参数或无参(需括号)
const add = (a, b) => a + b;
// 多语句(需大括号 + return)
const multiply = (a, b) => {
const result = a * b;
return result;
};关键特性:
词法 this:继承外层作用域的 this(适合回调、事件处理器)。
class Timer {
start() {
// 箭头函数中的 this 指向 Timer 实例
setTimeout(() => console.log(this), 1000);
}
}- 不能用作构造函数:
new (() => {})会报错。 - 无 Arguments 对象:可用 rest 参数
...args替代。 - 不能使用 yield:不能作为生成器函数。
function add(a, b) {
console.log(this); // 输出 window
console.log(arguments[1]); // 输出 7
return a + b;
}
add(3, 7);
const mult = (a, b) => {
console.log(this); // 输出 window ,继承了外部作用域的 this
console.log(arguments[1]); // arguments is not defined
return a * b;
};
mult(5, 6);