JavaScript 基础语法完全指南

文章目录

  • 第一章:变量与数据类型
    • [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 中有三种声明变量的方式:varletconst。理解它们的区别是掌握 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 种基本数据类型(原始类型):stringnumberbooleannullundefinedsymbolbigint

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);  // '年龄不能为负数'
}

附录:学习建议

最佳实践

  1. 变量声明 :默认使用 const,需要重新赋值时用 let,避免使用 var
  2. 严格相等 :比较时优先使用 ===!==,避免 ==!=
  3. 类型检查 :使用 typeofArray.isArray() 等进行类型验证
  4. 代码可读性:复杂的条件表达式加括号,即使不是必须的

常见错误

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 基础语法的完整内容。建议按照章节顺序学习,每学完一部分就进行实践练习,加深理解。

相关推荐
peepeeman1 小时前
vue组件透传
前端·javascript·vue.js
小陈的进阶之路1 小时前
Python系列课(9)——面向对象
开发语言·python
两年半的个人练习生^_^2 小时前
什么是内存泄漏?什么是内存溢出?
java·开发语言
曦夜日长2 小时前
C++ STL容器string(二):删除与插入、数据查找、自定义输入
java·开发语言·c++
jimy12 小时前
C语言中的inline function specifier(函数说明符、关键字)
c语言·开发语言
赏金术士2 小时前
Kotlin 协程底层原理(Continuation)详解
java·开发语言·kotlin
ZGi.ai2 小时前
私有化大模型接入企业系统:SSO+权限+API网关完整方案
java·开发语言·大模型·私有化部署·sso·企业架构
一念春风2 小时前
记事本(C#)
开发语言·c#
a1117762 小时前
细胞结构实验室(react 开源)
前端·javascript·开源·html