文章目录
- 第一章:变量与数据类型
-
- [1.1 变量的声明方式](#1.1 变量的声明方式)
-
- [1.1.1 var(不推荐在现代代码中使用)](#1.1.1 var(不推荐在现代代码中使用))
- [1.1.2 let(现代 JavaScript 的首选)](#1.1.2 let(现代 JavaScript 的首选))
- [1.1.3 const(声明常量)](#1.1.3 const(声明常量))
- [1.1.4 三种声明方式对比](#1.1.4 三种声明方式对比)
- [1.2 基本数据类型](#1.2 基本数据类型)
-
- [1.2.1 字符串(String)](#1.2.1 字符串(String))
- [1.2.2 数字(Number)](#1.2.2 数字(Number))
- [1.2.3 布尔值(Boolean)](#1.2.3 布尔值(Boolean))
- [1.2.4 null 和 undefined](#1.2.4 null 和 undefined)
- [1.2.5 Symbol(ES6)](#1.2.5 Symbol(ES6))
- [1.2.6 BigInt(ES2020)](#1.2.6 BigInt(ES2020))
- [1.3 引用数据类型](#1.3 引用数据类型)
-
- [1.3.1 基本类型 vs 引用类型的区别](#1.3.1 基本类型 vs 引用类型的区别)
- [1.3.2 对象(Object)](#1.3.2 对象(Object))
- [1.3.3 数组(Array)](#1.3.3 数组(Array))
- [1.4 类型检测](#1.4 类型检测)
-
- [1.4.1 typeof 运算符](#1.4.1 typeof 运算符)
- [1.4.2 instanceof 运算符](#1.4.2 instanceof 运算符)
- [1.4.3 Array.isArray()](#1.4.3 Array.isArray())
- [1.4.4 Object.prototype.toString.call()](#1.4.4 Object.prototype.toString.call())
- [1.5 类型转换](#1.5 类型转换)
-
- [1.5.1 显式转换(强制转换)](#1.5.1 显式转换(强制转换))
- [1.5.2 隐式转换(自动转换)](#1.5.2 隐式转换(自动转换))
- [1.5.3 类型转换规则表](#1.5.3 类型转换规则表)
- 第二章:运算符
-
- [2.1 算术运算符](#2.1 算术运算符)
- [2.2 赋值运算符](#2.2 赋值运算符)
- [2.3 比较运算符](#2.3 比较运算符)
- [2.4 逻辑运算符](#2.4 逻辑运算符)
- [2.5 位运算符(了解即可)](#2.5 位运算符(了解即可))
- [2.6 三元运算符](#2.6 三元运算符)
- [2.7 运算符优先级](#2.7 运算符优先级)
- 第三章:流程控制
-
- [3.1 条件语句](#3.1 条件语句)
-
- [3.1.1 if...else](#3.1.1 if...else)
- [3.1.2 switch...case](#3.1.2 switch...case)
- [3.2 循环语句](#3.2 循环语句)
-
- [3.2.1 for 循环](#3.2.1 for 循环)
- [3.2.2 while 和 do...while](#3.2.2 while 和 do...while)
- [3.2.3 for...in(遍历对象属性)](#3.2.3 for...in(遍历对象属性))
- [3.2.4 for...of(遍历可迭代对象)](#3.2.4 for...of(遍历可迭代对象))
- [3.3 跳转语句](#3.3 跳转语句)
- 附录:学习建议
第一章:变量与数据类型
1.1 变量的声明方式
JavaScript 中有三种声明变量的方式:var、let 和 const。理解它们的区别是掌握 JavaScript 的第一步。
1.1.1 var(不推荐在现代代码中使用)
var 是 JavaScript 最早的变量声明方式,但存在诸多问题,现代开发中已不推荐使用。
javascript
// var 的特点
var name = '张三';
var name = '李四'; // ✅ 可以重复声明,不会报错
// 函数作用域
function example() {
var x = 10;
if (true) {
var x = 20; // 同一个变量
console.log(x); // 20
}
console.log(x); // 20(不是 10!)
}
// 变量提升
console.log(age); // undefined(不会报错,但值是 undefined)
var age = 25;
// 实际执行顺序:var age; console.log(age); age = 25;
var 的问题总结:
- 可以重复声明,容易覆盖之前的变量
- 没有块级作用域,只在函数内有效
- 存在变量提升,可能导致意外的行为
1.1.2 let(现代 JavaScript 的首选)
let 是 ES6(ES2015)引入的变量声明方式,解决了 var 的诸多问题。
javascript
// let 的特点
let name = '张三';
// let name = '李四'; // ❌ 报错:不能重复声明
name = '李四'; // ✅ 可以重新赋值
// 块级作用域
function example() {
let x = 10;
if (true) {
let x = 20; // 不同的变量!
console.log(x); // 20
}
console.log(x); // 10(不受影响)
}
// 暂时性死区(TDZ)
console.log(age); // ❌ 报错:Cannot access 'age' before initialization
let age = 25;
let 的优势:
- 不能重复声明,避免变量被意外覆盖
- 具有块级作用域
{},代码更清晰 - 不存在变量提升,必须先声明后使用
1.1.3 const(声明常量)
const 也是 ES6 引入的,用于声明常量------一旦赋值就不能再改变。
javascript
// const 的基本用法
const PI = 3.14159;
// PI = 3.14; // ❌ 报错:不能给常量重新赋值
// 必须初始化
// const NAME; // ❌ 报错:常量必须初始化
// 对于对象和数组,const 不保护内容,只保护引用
const person = { name: '张三', age: 25 };
person.name = '李四'; // ✅ 可以修改对象属性
person.age = 30; // ✅ 可以修改对象属性
// person = {}; // ❌ 报错:不能重新赋值整个对象
const numbers = [1, 2, 3];
numbers.push(4); // ✅ 可以添加元素
numbers[0] = 10; // ✅ 可以修改元素
// numbers = [4, 5, 6]; // ❌ 报错:不能重新赋值整个数组
const 的使用建议:
- 默认使用
const声明变量 - 只有当需要重新赋值时,才使用
let - 避免使用
var
1.1.4 三种声明方式对比
| 特性 | var | let | const |
|---|---|---|---|
| 能否重复声明 | ✅ 可以 | ❌ 不可以 | ❌ 不可以 |
| 能否重新赋值 | ✅ 可以 | ✅ 可以 | ❌ 不可以 |
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | ✅ 有 | ❌ 没有(TDZ) | ❌ 没有(TDZ) |
| 推荐使用 | ❌ 不推荐 | ✅ 需要重新赋值时用 | ✅ 默认使用 |
1.2 基本数据类型
JavaScript 有 7 种基本数据类型(原始类型):string、number、boolean、null、undefined、symbol、bigint。
1.2.1 字符串(String)
字符串用于表示文本数据,可以用单引号、双引号或反引号包裹。
javascript
// 字符串的创建
let str1 = 'Hello'; // 单引号
let str2 = "World"; // 双引号
let str3 = `Hello World`; // 模板字符串(ES6)
// 字符串长度
let text = 'JavaScript';
console.log(text.length); // 10
// 字符串索引(从0开始)
console.log(text[0]); // 'J'
console.log(text[4]); // 'S'
console.log(text[-1]); // undefined(不支持负数索引)
console.log(text.charAt(0)); // 'J'
// 字符串不可变性
let greeting = 'Hello';
greeting[0] = 'h'; // 不会报错,但不会生效
console.log(greeting); // 仍然是 'Hello'
// 模板字符串的强大功能
let name = '张三';
let age = 25;
let message = `我叫${name},今年${age}岁`; // 我叫张三,今年25岁
// 模板字符串支持多行
let html = `
<div>
<h1>标题</h1>
<p>内容</p>
</div>
`;
// 模板字符串中可以进行运算
let a = 10, b = 20;
console.log(`${a} + ${b} = ${a + b}`); // 10 + 20 = 30
字符串常用方法:
javascript
let str = 'Hello JavaScript';
// 查找
str.indexOf('Java'); // 6(找到返回索引,找不到返回-1)
str.lastIndexOf('a'); // 10(最后一个'a'的位置)
str.includes('Script'); // true(是否包含)
str.startsWith('Hello'); // true(是否以...开头)
str.endsWith('Script'); // true(是否以...结尾)
// 提取
str.slice(6, 10); // 'Java'(提取子串,不包括结束位置)
str.substring(6, 10); // 'Java'(类似slice)
str.substr(6, 4); // 'Java'(从位置6开始,提取4个字符)
// 大小写转换
str.toUpperCase(); // 'HELLO JAVASCRIPT'
str.toLowerCase(); // 'hello javascript'
// 去除空白
' hello '.trim(); // 'hello'(去除首尾空格)
' hello '.trimStart(); // 'hello '(去除开头空格)
' hello '.trimEnd(); // ' hello'(去除结尾空格)
// 替换
str.replace('Java', 'Type'); // 'Hello TypeScript'(只替换第一个)
str.replaceAll('a', '@'); // 'Hello J@v@Script'(替换所有,ES2021)
// 分割
'apple,banana,orange'.split(','); // ['apple', 'banana', 'orange']
'hello'.split(''); // ['h', 'e', 'l', 'l', 'o']
// 重复
'ha'.repeat(3); // 'hahaha'
// 检查是否为空
let empty = '';
empty.length === 0; // true
!empty; // true(空字符串在布尔上下文中为false)
1.2.2 数字(Number)
JavaScript 中所有数字都是 64 位浮点数,没有单独的整数类型。
javascript
// 数字的表示
let integer = 42; // 整数
let decimal = 3.14; // 小数
let negative = -10; // 负数
let scientific = 1.5e3; // 科学计数法,等于 1500
// 特殊数值
console.log(Infinity); // 正无穷大
console.log(-Infinity); // 负无穷大
console.log(NaN); // Not a Number,表示不是一个有效数字
// NaN 的特点
console.log(NaN === NaN); // false!NaN 不等于任何值,包括它自己
console.log(isNaN(NaN)); // true(判断是否为 NaN)
console.log(Number.isNaN(NaN)); // true(更可靠的判断方式)
// 数字运算
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次方)
// 数字的精度问题(重要!)
console.log(0.1 + 0.2); // 0.30000000000000004,不是 0.3!
// 解决方法
console.log((0.1 + 0.2).toFixed(2)); // '0.30'(字符串)
console.log(Math.round((0.1 + 0.2) * 100) / 100); // 0.3
// 进制转换
let num = 255;
num.toString(16); // 'ff'(转16进制)
num.toString(2); // '11111111'(转2进制)
num.toString(8); // '377'(转8进制)
parseInt('ff', 16); // 255(16进制转10进制)
parseInt('11111111', 2); // 255(2进制转10进制)
Math 对象常用方法:
javascript
// 绝对值
Math.abs(-5); // 5
// 取整
Math.floor(3.7); // 3(向下取整)
Math.ceil(3.2); // 4(向上取整)
Math.round(3.5); // 4(四舍五入)
Math.trunc(3.7); // 3(去除小数部分)
// 最值
Math.max(1, 5, 3); // 5
Math.min(1, 5, 3); // 1
Math.max(...[1, 5, 3]); // 5(结合展开运算符)
// 随机数
Math.random(); // 0 到 1 之间的随机小数(不包括1)
// 生成 1 到 10 的随机整数
Math.floor(Math.random() * 10) + 1;
// 生成指定范围的随机整数
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
randomInt(5, 15); // 5 到 15 之间的随机整数
// 其他常用
Math.sqrt(16); // 4(平方根)
Math.pow(2, 3); // 8(幂运算,同 2**3)
Math.PI; // 3.141592653589793
Math.E; // 2.718281828459045(自然常数)
1.2.3 布尔值(Boolean)
布尔值只有两个:true(真)和 false(假)。
javascript
let isActive = true;
let isDeleted = false;
// 逻辑运算
let a = true, b = false;
// 与(AND):两边都为 true,结果才为 true
console.log(a && b); // false
console.log(a && true); // true
// 或(OR):只要有一边为 true,结果就为 true
console.log(a || b); // true
console.log(false || false); // false
// 非(NOT):取反
console.log(!a); // false
console.log(!b); // true
// 短路求值(重要!)
let result1 = true || someFunction(); // someFunction 不会执行
let result2 = false && someFunction(); // someFunction 不会执行
// 实际应用:设置默认值
let username = input || '匿名用户'; // 如果 input 为假值,使用默认值
// 实际应用:条件执行
isReady && startApp(); // 如果 isReady 为 true,才执行 startApp()
1.2.4 null 和 undefined
这两个都表示"空"或"不存在",但有细微区别。
javascript
// undefined:未定义,变量声明但未赋值
let x;
console.log(x); // undefined
console.log(typeof x); // 'undefined'
// 函数没有返回值时,默认返回 undefined
function doNothing() {}
console.log(doNothing()); // undefined
// 对象不存在的属性
let obj = {};
console.log(obj.name); // undefined
// null:空值,需要显式赋值
let y = null;
console.log(y); // null
console.log(typeof y); // 'object'(历史遗留bug)
// 区别
console.log(null == undefined); // true(宽松相等)
console.log(null === undefined); // false(严格不相等)
// 使用场景
let user = null; // 明确表示"没有用户"
// 区别于 undefined(变量存在但不知道值是什么)
// 判断是否为 null 或 undefined
if (value === null || value === undefined) {
console.log('值为空');
}
// 更简洁的判断(但会把 0、''、false 也视为空)
if (!value) {
console.log('值为假值');
}
// 空值合并运算符(ES2020)
let value = null ?? '默认值'; // '默认值'
let value2 = undefined ?? '默认值'; // '默认值'
let value3 = 0 ?? '默认值'; // 0(0 不是 null/undefined)
1.2.5 Symbol(ES6)
Symbol 是 ES6 引入的原始数据类型,表示唯一的标识符。
javascript
// 创建 Symbol
let sym1 = Symbol();
let sym2 = Symbol('描述'); // 添加描述,便于调试
let sym3 = Symbol('描述');
// 每个 Symbol 都是唯一的
console.log(sym2 === sym3); // false,即使描述相同
// 用作对象的唯一属性名
let id = Symbol('id');
let user = {
name: '张三',
[id]: 12345 // 使用 Symbol 作为键
};
console.log(user.name); // '张三'
console.log(user[id]); // 12345
console.log(user.id); // undefined(不能用点号访问)
// Symbol 属性不会被常规方法遍历
console.log(Object.keys(user)); // ['name'],不包含 Symbol
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)]
// 全局 Symbol 注册表
let globalSym = Symbol.for('app.id'); // 创建或获取全局 Symbol
let sameSym = Symbol.for('app.id');
console.log(globalSym === sameSym); // true
console.log(Symbol.keyFor(globalSym)); // 'app.id'(获取全局 Symbol 的键)
1.2.6 BigInt(ES2020)
BigInt 用于表示任意精度的整数,可以处理超过 Number 安全范围的大数。
javascript
// 创建 BigInt
let big1 = 123456789012345678901234567890n; // 数字后加 n
let big2 = BigInt('123456789012345678901234567890'); // 使用 BigInt 函数
// Number 的安全范围
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
// 超出安全范围的计算会出错
console.log(9007199254740991 + 1); // 9007199254740992
console.log(9007199254740991 + 2); // 9007199254740992(错误!)
// BigInt 可以正确处理
let big = 9007199254740991n;
console.log(big + 1n); // 9007199254740992n
console.log(big + 2n); // 9007199254740993n(正确!)
// BigInt 运算
let a = 100n, b = 30n;
console.log(a + b); // 130n
console.log(a - b); // 70n
console.log(a * b); // 3000n
console.log(a / b); // 3n(除法只保留整数部分)
console.log(a % b); // 10n
// BigInt 和 Number 不能混用
// console.log(10n + 5); // ❌ 报错
console.log(10n + BigInt(5)); // ✅ 15n
console.log(Number(10n) + 5); // ✅ 15(但可能丢失精度)
// 比较运算可以混用
console.log(10n < 20); // true
console.log(10n == 10); // true(宽松相等)
console.log(10n === 10); // false(严格不相等,类型不同)
1.3 引用数据类型
引用类型包括:Object、Array、Function 等。与基本类型的主要区别在于存储方式。
1.3.1 基本类型 vs 引用类型的区别
javascript
// 基本类型:值存储在栈中
let a = 10;
let b = a; // 复制值
b = 20;
console.log(a); // 10(a 不受影响)
// 引用类型:值存储在堆中,变量存储的是引用(地址)
let obj1 = { name: '张三' };
let obj2 = obj1; // 复制引用(地址)
obj2.name = '李四';
console.log(obj1.name); // '李四'(obj1 也被修改了!)
// 数组也是引用类型
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4](arr1 也被修改了)
// 如何真正复制对象?
let obj3 = { name: '张三' };
let obj4 = { ...obj3 }; // 浅拷贝(展开运算符)
obj4.name = '李四';
console.log(obj3.name); // '张三'(obj3 不受影响)
// 或者使用 Object.assign
let obj5 = Object.assign({}, obj3);
// 深拷贝(对象嵌套时使用)
let deepObj = { user: { name: '张三' } };
let deepCopy = JSON.parse(JSON.stringify(deepObj));
deepCopy.user.name = '李四';
console.log(deepObj.user.name); // '张三'
1.3.2 对象(Object)
对象是键值对的集合,是 JavaScript 中最常用的数据结构。
javascript
// 对象的创建
let person = {
name: '张三',
age: 25,
isStudent: false,
hobbies: ['读书', '游泳', '编程'],
address: {
city: '北京',
zipCode: '100000'
},
// 对象方法
sayHello: function() {
console.log('你好,我叫' + this.name);
},
// ES6 简写方法
sayGoodbye() {
console.log('再见!');
}
};
// 属性访问
console.log(person.name); // '张三'(点号访问)
console.log(person['age']); // 25(括号访问)
console.log(person.hobbies[0]); // '读书'
console.log(person.address.city); // '北京'
// 动态属性名
let prop = 'name';
console.log(person[prop]); // '张三'
// 添加属性
person.email = 'zhangsan@example.com';
person['phone'] = '13800138000';
// 修改属性
person.age = 26;
// 删除属性
delete person.isStudent;
// 检查属性是否存在
console.log('name' in person); // true
console.log(person.hasOwnProperty('name')); // true(不包括继承的属性)
// 遍历对象
for (let key in person) {
console.log(key + ': ' + person[key]);
}
// 获取所有键、值、键值对
console.log(Object.keys(person)); // ['name', 'age', ...]
console.log(Object.values(person)); // ['张三', 26, ...]
console.log(Object.entries(person)); // [['name', '张三'], ['age', 26], ...]
// 对象解构
let { name, age, hobbies } = person;
console.log(name); // '张三'
// 解构时重命名
let { name: userName, age: userAge } = person;
console.log(userName); // '张三'
// 解构时设置默认值
let { gender = '未知' } = person;
console.log(gender); // '未知'
// 对象展开运算符
let personCopy = { ...person, age: 30 }; // 复制并修改 age
1.3.3 数组(Array)
数组是有序的数据集合,可以存储任意类型的元素。
javascript
// 数组的创建
let arr1 = [1, 2, 3, 4, 5];
let arr2 = new Array(5); // [empty × 5],长度为5的空数组
let arr3 = new Array(1, 2, 3); // [1, 2, 3]
let arr4 = Array.of(1, 2, 3); // [1, 2, 3](ES6)
let arr5 = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o'](ES6)
// 数组可以存储任意类型
let mixed = [1, 'hello', true, null, { a: 1 }, [1, 2, 3]];
// 访问和修改元素
let fruits = ['苹果', '香蕉', '橙子'];
console.log(fruits[0]); // '苹果'
console.log(fruits.length); // 3
fruits[1] = '葡萄'; // 修改元素
fruits[3] = '西瓜'; // 添加元素
// 数组方法(详见数组章节)
fruits.push('芒果'); // 末尾添加
fruits.pop(); // 末尾删除
fruits.unshift('草莓'); // 开头添加
fruits.shift(); // 开头删除
// 数组解构
let [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
// 跳过元素
let [a, , c] = [1, 2, 3];
console.log(a, c); // 1, 3
// 默认值
let [x = 10, y = 20] = [5];
console.log(x, y); // 5, 20
1.4 类型检测
1.4.1 typeof 运算符
javascript
// 基本类型的检测
console.log(typeof 'hello'); // 'string'
console.log(typeof 42); // 'number'
console.log(typeof true); // 'boolean'
console.log(typeof undefined); // 'undefined'
console.log(typeof Symbol()); // 'symbol'
console.log(typeof 123n); // 'bigint'
// 引用类型的检测(有局限)
console.log(typeof {}); // 'object'
console.log(typeof []); // 'object'(数组也是 object)
console.log(typeof null); // 'object'(历史遗留 bug)
console.log(typeof function(){}); // 'function'
// typeof 的使用场景
function process(value) {
if (typeof value === 'string') {
return value.toUpperCase();
}
if (typeof value === 'number') {
return value * 2;
}
return value;
}
1.4.2 instanceof 运算符
javascript
// 检测对象是否是某个类的实例
let arr = [];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true(数组也是对象)
let obj = {};
console.log(obj instanceof Object); // true
console.log(obj instanceof Array); // false
let date = new Date();
console.log(date instanceof Date); // true
// 自定义类
class Person {}
let person = new Person();
console.log(person instanceof Person); // true
1.4.3 Array.isArray()
javascript
// 专门用于检测数组
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray('hello')); // false
console.log(Array.isArray(null)); // false
// 实际应用
function processData(data) {
if (Array.isArray(data)) {
return data.map(item => item * 2);
}
return data;
}
1.4.4 Object.prototype.toString.call()
javascript
// 最可靠的类型检测方法
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
console.log(getType('hello')); // 'String'
console.log(getType(42)); // 'Number'
console.log(getType([])); // 'Array'
console.log(getType({})); // 'Object'
console.log(getType(null)); // 'Null'
console.log(getType(undefined)); // 'Undefined'
console.log(getType(new Date())); // 'Date'
console.log(getType(/abc/)); // 'RegExp'
1.5 类型转换
1.5.1 显式转换(强制转换)
javascript
// 转字符串
String(123); // '123'
String(true); // 'true'
String(null); // 'null'
String(undefined); // 'undefined'
(123).toString(); // '123'
(15).toString(16); // 'f'(转16进制)
// 转数字
Number('123'); // 123
Number('12.34'); // 12.34
Number(''); // 0
Number(' '); // 0
Number('hello'); // NaN
Number(true); // 1
Number(false); // 0
Number(null); // 0
Number(undefined); // NaN
// parseInt 和 parseFloat(更灵活的转换)
parseInt('123px'); // 123(提取整数部分)
parseInt('101', 2); // 5(按2进制解析)
parseFloat('12.34px'); // 12.34
// 转布尔值
Boolean(1); // true
Boolean(0); // false
Boolean(''); // false
Boolean('hello'); // true
Boolean(null); // false
Boolean(undefined); // false
Boolean([]); // true(空数组也是 true!)
Boolean({}); // true(空对象也是 true!)
1.5.2 隐式转换(自动转换)
javascript
// 字符串拼接触发转字符串
console.log('10' + 5); // '105'(数字转字符串)
console.log(10 + '5'); // '105'
console.log(10 + 5 + 'px'); // '15px'
// 算术运算触发转数字
console.log('10' - 5); // 5(字符串转数字)
console.log('10' * 2); // 20
console.log('10' / 2); // 5
console.log('10' % 3); // 1
// 比较运算的隐式转换
console.log('5' == 5); // true(字符串转数字)
console.log('5' === 5); // false(严格相等,不转换)
// 逻辑运算的隐式转换
console.log(!0); // true(0 转 false,再取反)
console.log(!''); // true
console.log(!!'hello'); // true(转布尔值)
// 条件判断的隐式转换
if ('hello') { } // true(非空字符串)
if ('') { } // false(空字符串)
if (0) { } // false
if (1) { } // true
if ([]) { } // true(空数组)
if ({}) { } // true(空对象)
if (null) { } // false
if (undefined) { } // false
// 有趣的例子
console.log([] + []); // ''(空字符串)
console.log([] + {}); // '[object Object]'
console.log({} + []); // 0(在浏览器控制台是 0,严格模式下可能不同)
console.log(true + true); // 2(true 转 1)
console.log(true - true); // 0
console.log('10' - - '5'); // 15('10' - (-5))
1.5.3 类型转换规则表
| 原始值 | 转字符串 | 转数字 | 转布尔值 |
|---|---|---|---|
| 0 | '0' | 0 | false |
| 1 | '1' | 1 | true |
| '' | '' | 0 | false |
| 'hello' | 'hello' | NaN | true |
| '123' | '123' | 123 | true |
| null | 'null' | 0 | false |
| undefined | 'undefined' | NaN | false |
| true | 'true' | 1 | true |
| false | 'false' | 0 | false |
| [] | '' | 0 | true |
| [1,2,3] | '1,2,3' | NaN | true |
| {} | '[object Object]' | NaN | true |
第二章:运算符
2.1 算术运算符
javascript
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(幂运算,ES2016)
// 字符串的加法(拼接)
console.log('Hello' + ' ' + 'World'); // 'Hello World'
console.log('10' + 5); // '105'(数字转字符串)
// 一元运算符
let x = 5;
console.log(+x); // 5(正号,无实际作用)
console.log(-x); // -5(负号,取反)
// 递增和递减
let y = 5;
console.log(++y); // 6(先加1,再使用)
console.log(y++); // 6(先使用,再加1,y变成7)
console.log(y); // 7
let z = 5;
console.log(--z); // 4(先减1,再使用)
console.log(z--); // 4(先使用,再减1,z变成3)
console.log(z); // 3
// 实际应用中的递增
let count = 0;
function increment() {
return ++count; // 返回增加后的值
}
2.2 赋值运算符
javascript
let x = 10; // 基本赋值
// 复合赋值运算符
x += 5; // x = x + 5; 结果:15
x -= 3; // x = x - 3; 结果:12
x *= 2; // x = x * 2; 结果:24
x /= 4; // x = x / 4; 结果:6
x %= 4; // x = x % 4; 结果:2
x **= 3; // x = x ** 3; 结果:8
// 连续赋值(从右向左)
let a, b, c;
a = b = c = 10; // a=10, b=10, c=10
// 解构赋值(ES6)
let [m, n] = [1, 2]; // m=1, n=2
let [p, , q] = [1, 2, 3]; // p=1, q=3(跳过中间)
let [r = 10, s = 20] = [5]; // r=5, s=20(默认值)
let { name, age } = { name: '张三', age: 25 };
let { name: userName, age: userAge } = { name: '张三', age: 25 };
2.3 比较运算符
javascript
// 相等和不相等(会进行类型转换)
console.log(5 == 5); // true
console.log(5 == '5'); // true(字符串转数字)
console.log(0 == false); // true
console.log('' == false); // true
console.log(null == undefined); // true
console.log(5 != '5'); // false
// 严格相等和严格不相等(推荐!不进行类型转换)
console.log(5 === 5); // true
console.log(5 === '5'); // false(类型不同)
console.log(0 === false); // false
console.log(null === undefined); // false
console.log(5 !== '5'); // true
// 大小比较
console.log(10 > 5); // true
console.log(10 >= 10); // true
console.log(5 < 10); // true
console.log(5 <= 5); // true
// 字符串比较(按 Unicode 编码逐字符比较)
console.log('apple' > 'banana'); // false(a < b)
console.log 'Z' > 'A'); // true
console.log('10' < '5'); // true('1' < '5')
// 特殊比较
console.log(NaN === NaN); // false(NaN 不等于任何值)
console.log(NaN == NaN); // false
console.log(Object.is(NaN, NaN)); // true(ES6,正确处理 NaN)
console.log(Object.is(+0, -0)); // false(区分 +0 和 -0)
console.log(+0 === -0); // true
// 对象比较(比较引用,不是内容)
let obj1 = { a: 1 };
let obj2 = { a: 1 };
let obj3 = obj1;
console.log(obj1 == obj2); // false(不同引用)
console.log(obj1 === obj2); // false
console.log(obj1 == obj3); // true(相同引用)
2.4 逻辑运算符
javascript
// 逻辑与(AND):两边都为 true,结果才为 true
console.log(true && true); // true
console.log(true && false); // false
console.log(false && true); // false(短路,右边不执行)
// 逻辑或(OR):只要一边为 true,结果就为 true
console.log(true || false); // true(短路,右边不执行)
console.log(false || true); // true
console.log(false || false); // false
// 逻辑非(NOT):取反
console.log(!true); // false
console.log(!false); // true
console.log(!!'hello'); // true(转布尔值)
// 非布尔值的逻辑运算(返回决定结果的那个值)
console.log('hello' && 'world'); // 'world'(两边都真,返回最后一个)
console.log('' && 'world'); // ''(第一个为假,直接返回)
console.log('hello' || 'world'); // 'hello'(第一个为真,直接返回)
console.log('' || 'world'); // 'world'(第一个为假,返回第二个)
console.log(null || 'default'); // 'default'(常用:设置默认值)
// 实际应用
let username = input || '匿名用户';
let port = options.port || 8080;
// 可选链操作符(ES2020)
let user = { address: { city: '北京' } };
console.log(user?.address?.city); // '北京'
console.log(user?.profile?.avatar); // undefined(不会报错)
// 空值合并运算符(ES2020)
let value = null ?? 'default'; // 'default'
let value2 = 0 ?? 'default'; // 0(0 不是 null/undefined)
let value3 = '' ?? 'default'; // ''(空字符串也不是 null/undefined)
// 与 || 的区别
let count = 0;
console.log(count || 10); // 10(0 被视为假值)
console.log(count ?? 10); // 0(0 不是 null/undefined)
2.5 位运算符(了解即可)
javascript
// 按位与、或、异或、非
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 (按位取反)
// 左移和右移
console.log(5 << 1); // 10 (101 左移1位 = 1010)
console.log(5 >> 1); // 2 (101 右移1位 = 10)
console.log(-5 >>> 1); // 无符号右移
// 实际应用:判断奇偶
function isOdd(n) {
return n & 1; // 奇数返回1,偶数返回0
}
// 交换两个数(不使用临时变量)
let a = 5, b = 3;
a = a ^ b;
b = a ^ b;
a = a ^ b;
console.log(a, b); // 3, 5
2.6 三元运算符
javascript
// 语法:条件 ? 表达式1 : 表达式2
let age = 20;
let status = age >= 18 ? '成年人' : '未成年人';
// 嵌套三元(不推荐过多嵌套,可读性差)
let score = 85;
let grade = score >= 90 ? 'A' :
score >= 80 ? 'B' :
score >= 60 ? 'C' : 'D';
// 与 if-else 的对比
// 三元运算符(适合简单赋值)
let max = a > b ? a : b;
// if-else(适合复杂逻辑)
let max;
if (a > b) {
max = a;
} else {
max = b;
}
2.7 运算符优先级
javascript
// 优先级从高到低(常用)
// 1. 括号 ()
// 2. 成员访问 . 、[]、new
// 3. 函数调用 ()
// 4. 递增递减 ++、--
// 5. 逻辑非 !、按位非 ~
// 6. 乘除 *、/、%
// 7. 加减 +、-
// 8. 比较 <、>、<=、>=
// 9. 相等 ==、!=、===、!==
// 10. 逻辑与 &&
// 11. 逻辑或 ||
// 12. 三元 ?:
// 13. 赋值 =、+= 等
// 例子
let result = 10 + 2 * 3; // 16(先乘后加)
let result2 = (10 + 2) * 3; // 36(括号优先)
let a = 5, b = 3, c = 2;
let result3 = a > b && b > c; // true(先比较,再逻辑与)
let x = 0 || 1 && 2; // 2(&& 优先级高于 ||)
let y = (0 || 1) && 2; // 2
// 建议:不确定时加括号,提高可读性
第三章:流程控制
3.1 条件语句
3.1.1 if...else
javascript
// 基本形式
let age = 20;
if (age >= 18) {
console.log('成年人');
}
// if...else
if (age >= 18) {
console.log('成年人');
} else {
console.log('未成年人');
}
// 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('不及格');
}
// 条件中的隐式转换
let name = '';
if (name) {
console.log('有名字');
} else {
console.log('名字为空'); // 执行这里
}
// 常见假值:false、0、''、null、undefined、NaN
3.1.2 switch...case
javascript
// 基本形式
let day = 3;
let dayName;
switch (day) {
case 1:
dayName = '星期一';
break;
case 2:
dayName = '星期二';
break;
case 3:
dayName = '星期三';
break;
case 4:
dayName = '星期四';
break;
case 5:
dayName = '星期五';
break;
default:
dayName = '周末';
}
// 多个 case 共享代码
let grade = 'B';
switch (grade) {
case 'A':
case 'B':
case 'C':
console.log('通过');
break;
case 'D':
case 'F':
console.log('未通过');
break;
default:
console.log('无效成绩');
}
// switch 使用严格相等(===)
let value = '5';
switch (value) {
case 5:
console.log('数字5'); // 不会执行
break;
case '5':
console.log('字符串5'); // 执行这里
break;
}
3.2 循环语句
3.2.1 for 循环
javascript
// 基本 for 循环
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// 循环结构:初始化; 条件; 迭代
// for (①初始化; ②条件判断; ④迭代) { ③执行代码 }
// 执行顺序:① → ② → ③ → ④ → ② → ③ → ④ ...
// 省略部分表达式
let i = 0;
for (; i < 5;) {
console.log(i);
i++;
}
// 死循环
// for (;;) { }
// 多个变量
for (let i = 0, j = 10; i < j; i++, j--) {
console.log(i, j);
}
// 遍历数组
let arr = ['a', 'b', 'c'];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// 优化:缓存长度
for (let i = 0, len = arr.length; i < len; i++) {
console.log(arr[i]);
}
// 倒序遍历
for (let i = arr.length - 1; i >= 0; i--) {
console.log(arr[i]);
}
// 跳过和终止
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // 跳过当前迭代
if (i === 7) break; // 终止整个循环
console.log(i); // 0, 1, 2, 4, 5, 6
}
3.2.2 while 和 do...while
javascript
// while 循环(先判断,后执行)
let count = 0;
while (count < 5) {
console.log(count);
count++;
}
// 可能一次都不执行
let num = 10;
while (num < 5) {
console.log('不会执行');
}
// do...while(先执行,后判断)
let n = 0;
do {
console.log(n);
n++;
} while (n < 5);
// 至少执行一次
let m = 10;
do {
console.log('执行一次'); // 会执行
} while (m < 5);
// 实际应用:用户输入验证
let input;
do {
input = prompt('请输入一个正数:');
} while (isNaN(input) || input <= 0);
3.2.3 for...in(遍历对象属性)
javascript
let person = {
name: '张三',
age: 25,
city: '北京'
};
// 遍历对象的所有可枚举属性(包括继承的)
for (let key in person) {
console.log(key + ': ' + person[key]);
}
// name: 张三
// age: 25
// city: 北京
// 只遍历自身属性
for (let key in person) {
if (person.hasOwnProperty(key)) {
console.log(key + ': ' + person[key]);
}
}
// 遍历数组(不推荐,会遍历到非数字索引)
let arr = ['a', 'b', 'c'];
for (let index in arr) {
console.log(index, arr[index]); // '0' 'a', '1' 'b'...
}
// 注意:index 是字符串,不是数字
3.2.4 for...of(遍历可迭代对象)
javascript
// 遍历数组(推荐)
let arr = ['a', 'b', 'c'];
for (let item of arr) {
console.log(item); // 'a', 'b', 'c'
}
// 遍历字符串
for (let char of 'hello') {
console.log(char); // 'h', 'e', 'l', 'l', 'o'
}
// 遍历 Set
let set = new Set([1, 2, 3]);
for (let value of set) {
console.log(value);
}
// 遍历 Map
let map = new Map([['a', 1], ['b', 2]]);
for (let [key, value] of map) {
console.log(key, value);
}
// 获取索引:使用 entries()
for (let [index, item] of arr.entries()) {
console.log(index, item);
}
// 与 for...in 的区别
// for...in:遍历对象的键(属性名)
// for...of:遍历可迭代对象的值
3.3 跳转语句
javascript
// break:终止整个循环
for (let i = 0; i < 10; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}
// continue:跳过当前迭代
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 0, 1, 3, 4
}
// break 跳出指定标签(很少使用)
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);
}
}
// return:从函数返回
function findNumber(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
return i; // 找到就返回,终止函数
}
}
return -1; // 没找到
}
// throw:抛出异常
try {
let age = -5;
if (age < 0) {
throw new Error('年龄不能为负数');
}
} catch (e) {
console.log(e.message); // '年龄不能为负数'
}
附录:学习建议
最佳实践
- 变量声明 :默认使用
const,需要重新赋值时用let,避免使用var - 严格相等 :比较时优先使用
===和!==,避免==和!= - 类型检查 :使用
typeof、Array.isArray()等进行类型验证 - 代码可读性:复杂的条件表达式加括号,即使不是必须的
常见错误
javascript
// 错误1:混淆赋值和比较
if (x = 5) { } // 赋值操作,永远为 true
if (x === 5) { } // 正确:比较
// 错误2:浮点数比较
if (0.1 + 0.2 === 0.3) { } // false!
if (Math.abs(0.1 + 0.2 - 0.3) < 0.0001) { } // 正确
// 错误3:隐式转换的坑
console.log([] == ![]); // true(难以理解!)
// 解释:![] = false,[] == false,[] 转数字为 0,false 转数字为 0
// 错误4:var 的变量提升
console.log(x); // undefined(不会报错)
var x = 5;
// 错误5:循环中的闭包问题
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
// 解决:使用 let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
以上就是 JavaScript 基础语法的完整内容。建议按照章节顺序学习,每学完一部分就进行实践练习,加深理解。