====== 第一章 JavaScript基础 ====== ===== 1.1 JavaScript简介 ===== ==== 1.1.1 JavaScript的历史 ==== JavaScript诞生于1995年,由网景公司(Netscape)的Brendan Eich在仅仅10天内设计完成。最初命名为LiveScript,后来为了借助Java的流行改名为JavaScript。尽管名字中有"Java",但两者在语言设计上有本质区别。 **发展历程**: * **1995年**:JavaScript 1.0发布,用于Netscape Navigator 2.0 * **1996年**:微软发布JScript,作为IE 3.0的脚本语言 * **1997年**:ECMAScript 1.0标准发布,统一了JavaScript规范 * **1999年**:ECMAScript 3发布,奠定了现代JavaScript的基础 * **2009年**:ECMAScript 5发布,引入了严格模式、JSON支持等 * **2015年**:ECMAScript 6(ES2015)发布,是JavaScript历史上最大的更新 * **2016年至今**:每年发布一个新版本(ES2016、ES2017...ES2023) ==== 1.1.2 JavaScript的特点 ==== **JavaScript是一门什么样的语言?** * **解释型语言**:代码在运行时逐行解释执行 * **动态类型**:变量类型在运行时确定,可以随时改变 * **弱类型**:不同类型之间可以自动转换 * **基于原型**:面向对象通过原型实现,而非类(ES6引入class语法糖) * **一等函数**:函数可以作为参数、返回值,赋值给变量 * **事件驱动**:通过事件监听实现异步编程 * **跨平台**:可以在浏览器、服务器、移动端等各种环境运行 ==== 1.1.3 JavaScript的应用场景 ==== **前端开发**: * 网页交互效果(动画、表单验证、动态内容) * 单页应用(SPA)开发(React、Vue、Angular) * 移动端Web应用(PWA) **后端开发**: * Node.js服务器开发 * RESTful API设计 * 微服务架构 **其他领域**: * 桌面应用(Electron) * 移动应用(React Native、Flutter) * 游戏开发(Phaser、Three.js) * 物联网(IoT) * 机器学习(TensorFlow.js) ===== 1.2 JavaScript运行环境 ===== ==== 1.2.1 浏览器环境 ==== 浏览器是JavaScript最传统的运行环境。每个浏览器都内置了JavaScript引擎: * **V8**:Chrome、Edge浏览器(也是Node.js的引擎) * **SpiderMonkey**:Firefox浏览器 * **JavaScriptCore**:Safari浏览器(也称为Nitro) **浏览器中的JavaScript能力**: * 操作DOM(文档对象模型) * 响应用户事件(点击、键盘输入等) * 发送HTTP请求(Ajax、Fetch) * 操作浏览器存储(localStorage、sessionStorage) * 访问地理位置、摄像头等设备API **Hello World示例**: 第一个JavaScript程序

Hello JavaScript

==== 1.2.2 Node.js环境 ==== Node.js是2009年诞生的JavaScript运行时,让JavaScript可以在服务器端运行。 **Node.js的特点**: * 基于Chrome V8引擎 * 事件驱动、非阻塞I/O模型 * 庞大的npm生态系统(超过200万个包) * 适合构建高并发、I/O密集型的应用 **安装Node.js**: 在Linux/macOS上: # 使用nvm安装(推荐) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash nvm install 20 nvm use 20 # 验证安装 node --version npm --version **第一个Node.js程序**: 创建文件 `hello.js`: // hello.js console.log("Hello from Node.js!"); // 获取命令行参数 const args = process.argv.slice(2); if (args.length > 0) { console.log(`你输入的参数是: ${args.join(', ')}`); } // 使用Node.js内置模块 const os = require('os'); console.log(`操作系统: ${os.platform()}`); console.log(`CPU架构: ${os.arch()}`); 运行: node hello.js arg1 arg2 ==== 1.2.3 代码编辑器推荐 ==== **Visual Studio Code**(推荐): * 免费开源 * 丰富的插件生态 * 内置Git支持 * 智能代码补全 * 强大的调试功能 推荐插件: * ESLint - 代码检查 * Prettier - 代码格式化 * JavaScript (ES6) code snippets - 代码片段 * Live Server - 本地开发服务器 ===== 1.3 基本语法 ===== ==== 1.3.1 语句和表达式 ==== **语句(Statement)**:执行操作的完整指令,以分号结束。 let x = 5; // 变量声明语句 console.log(x); // 函数调用语句 if (x > 0) { // 条件语句 x = x + 1; } **表达式(Expression)**:产生一个值的代码片段。 5 + 3 // 算术表达式,值为8 x > 0 // 比较表达式,值为true "Hello" + "World" // 字符串表达式,值为"HelloWorld" **表达式语句**:表达式后面加上分号就变成了语句。 // 表达式 5 + 3 // 表达式语句 5 + 3; // 计算结果但不使用,没有实际意义 // 有意义的表达式语句 let sum = 5 + 3; // 赋值表达式的结果赋值给变量 ==== 1.3.2 注释 ==== 注释是代码中不会被执行的部分,用于解释代码。 // 单行注释 /* * 多行注释 * 可以跨越多行 * 常用于函数说明 */ /** * JSDoc风格的文档注释 * @param {number} a - 第一个数字 * @param {number} b - 第二个数字 * @returns {number} 两数之和 */ function add(a, b) { return a + b; } ==== 1.3.3 严格模式 ==== 严格模式(Strict Mode)是ES5引入的一种限制性更强的JavaScript运行模式。 **启用严格模式**: // 为整个脚本启用 'use strict'; // 为单个函数启用 function strictFunction() { 'use strict'; // 函数体内的代码在严格模式下运行 } **严格模式的限制**: * 不允许使用未声明的变量 * 不允许删除变量或函数 * 不允许重复参数名 * 不允许使用八进制字面量 * 不允许修改只读属性 * 保留字不能用作变量名 'use strict'; // 错误:未声明变量 x = 10; // ReferenceError: x is not defined // 正确:先声明 let x = 10; ===== 1.4 变量声明 ===== ==== 1.4.1 var声明(不推荐) ==== var name = "张三"; var age = 25; **var的问题**: * 函数作用域而非块级作用域 * 存在变量提升 * 允许重复声明 // 变量提升 - 声明被提升到作用域顶部 console.log(x); // undefined(不会报错) var x = 5; // 实际等价于 var x; console.log(x); // undefined x = 5; // 没有块级作用域 if (true) { var message = "Hello"; } console.log(message); // "Hello" - 在if外部仍可访问 ==== 1.4.2 let声明(推荐) ==== let name = "李四"; let age = 30; **let的特点**: * 块级作用域 * 不存在变量提升(暂时性死区) * 不允许重复声明 // 块级作用域 if (true) { let message = "Hello"; } // console.log(message); // ReferenceError: message is not defined // 暂时性死区 // console.log(y); // ReferenceError: Cannot access 'y' before initialization let y = 10; // 不允许重复声明 let z = 1; // let z = 2; // SyntaxError: Identifier 'z' has already been declared ==== 1.4.3 const声明(常量) ==== const PI = 3.14159; const API_URL = "https://api.example.com"; **const的特点**: * 必须立即初始化 * 不能重新赋值 * 块级作用域 // 必须初始化 // const X; // SyntaxError: Missing initializer in const declaration // 不能重新赋值 const MAX_SIZE = 100; // MAX_SIZE = 200; // TypeError: Assignment to constant variable // 对象和数组的特殊情况 const user = { name: "Alice" }; user.name = "Bob"; // ✓ 可以修改对象属性 user.age = 25; // ✓ 可以添加属性 // user = {}; // ✗ 不能重新赋值整个对象 const numbers = [1, 2, 3]; numbers.push(4); // ✓ 可以修改数组 // numbers = []; // ✗ 不能重新赋值整个数组 **命名约定**: * 普通变量:camelCase(userName、totalCount) * 常量:UPPER_SNAKE_CASE(MAX_VALUE、API_KEY) * 类名:PascalCase(UserAccount、DataManager) ==== 1.4.4 变量声明最佳实践 ==== // 1. 默认使用 const const API_BASE = "https://api.example.com"; const MAX_RETRIES = 3; // 2. 需要重新赋值时使用 let let currentUser = null; let retryCount = 0; // 3. 避免使用 var // var data = []; // 不推荐 // 4. 一条语句声明一个变量 const firstName = "John"; const lastName = "Doe"; // 不推荐:const firstName = "John", lastName = "Doe"; // 5. 在作用域顶部声明变量 function processData() { const config = getConfig(); let result = []; // 使用变量... } ===== 1.5 数据类型 ===== JavaScript有 **8种数据类型**,分为两类: * **基本类型**(Primitive):7种,值直接存储在变量中 * **引用类型**(Reference):1种,变量存储的是指向内存地址的引用 ==== 1.5.1 Number(数字) ==== JavaScript只有一种数字类型:双精度64位浮点数(IEEE 754标准)。 // 整数 let integer = 42; let negative = -17; // 浮点数 let float = 3.14159; let scientific = 2.5e10; // 25000000000 // 特殊数值 let infinity = Infinity; // 正无穷 let negInfinity = -Infinity; // 负无穷 let notANumber = NaN; // 非数字 // 进制表示 let binary = 0b1010; // 10(二进制) let octal = 0o17; // 15(八进制) let hex = 0xFF; // 255(十六进制) **Number的常用方法**: let num = 123.456; // 转字符串 num.toString(); // "123.456" num.toString(16); // "7b.74bc6a7ef9db"(十六进制) // 格式化 num.toFixed(2); // "123.46"(保留2位小数) num.toPrecision(4); // "123.5"(总共4位有效数字) // 其他方法 Number.isInteger(42); // true Number.isFinite(1/0); // false Number.isNaN(NaN); // true // 全局函数(与Number方法略有不同) parseInt("123px"); // 123 parseFloat("3.14abc"); // 3.14 isNaN("hello"); // true(尝试转换为数字后判断) **Number的精度问题**: // 浮点数精度问题 0.1 + 0.2 === 0.3; // false! 0.1 + 0.2; // 0.30000000000000004 // 解决方案 Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true // 或使用toFixed后转数字 parseFloat((0.1 + 0.2).toFixed(10)); // 0.3 // 大整数使用BigInt const bigNumber = 9007199254740991n; // BigInt类型 ==== 1.5.2 BigInt(大整数) ==== ES2020引入,用于表示任意精度的整数。 // 创建BigInt const big1 = 9007199254740993n; const big2 = BigInt("9007199254740993"); const big3 = BigInt(9007199254740993); // Number.MAX_SAFE_INTEGER = 9007199254740991 Number.MAX_SAFE_INTEGER + 2 === Number.MAX_SAFE_INTEGER + 3; // true(精度丢失) // BigInt可以精确表示 9007199254740991n + 2n === 9007199254740991n + 3n; // false // 运算 const sum = 100n + 200n; // 300n const product = 10n * 20n; // 200n const division = 100n / 3n; // 33n(整数除法) // 不能混合运算 // 100n + 50; // TypeError 100n + BigInt(50); // 150n ==== 1.5.3 String(字符串) ==== 字符串是Unicode字符序列,可以用单引号、双引号或反引号定义。 // 字符串定义 let str1 = '单引号字符串'; let str2 = "双引号字符串"; let str3 = `模板字符串`; // 字符串长度 "Hello".length; // 5 "你好".length; // 2 // 字符串索引 "Hello"[0]; // "H" "Hello".charAt(1); // "e" // 连接字符串 let firstName = "John"; let lastName = "Doe"; let fullName = firstName + " " + lastName; // 模板字符串(ES6) let age = 25; let greeting = `你好,${fullName}!你今年${age}岁。`; // 多行字符串(模板字符串) let poem = ` 春眠不觉晓, 处处闻啼鸟。 夜来风雨声, 花落知多少。 `; // 转义字符 let quote = "他说:\"Hello\""; let newline = "第一行\n第二行"; let tab = "列1\t列2\t列3"; **字符串常用方法**: let text = "JavaScript Programming"; // 查找 text.indexOf("Script"); // 4 text.lastIndexOf("m"); // 19 text.includes("Java"); // true text.startsWith("Java"); // true text.endsWith("ing"); // true text.search(/Pro/); // 11(正则匹配) // 提取 text.slice(0, 4); // "Java" text.substring(0, 4); // "Java" text.substr(11, 7); // "Program"(已废弃) text.charAt(0); // "J" // 修改(返回新字符串,原字符串不变) text.toLowerCase(); // "javascript programming" text.toUpperCase(); // "JAVASCRIPT PROGRAMMING" text.replace("Java", "Type"); // "TypeScript Programming" text.replaceAll("m", "M"); // "JavaScript PrograMMing" // 分割和连接 text.split(" "); // ["JavaScript", "Programming"] ["Hello", "World"].join(" "); // "Hello World" // 去除空白 " hello ".trim(); // "hello" " hello ".trimStart(); // "hello " " hello ".trimEnd(); // " hello" // 重复和填充 "ha".repeat(3); // "hahaha" "5".padStart(3, "0"); // "005" "5".padEnd(3, "0"); // "500" ==== 1.5.4 Boolean(布尔) ==== 布尔类型只有两个值:true 和 false。 let isActive = true; let isDeleted = false; // 比较产生布尔值 5 > 3; // true 10 === "10"; // false // 逻辑运算 true && false; // false(与) true || false; // true(或) !true; // false(非) **真值和假值**: 在布尔上下文中,以下值被视为假值(falsy): * false * 0、-0、0n(BigInt零) * ""、''、``(空字符串) * null * undefined * NaN 其他所有值都被视为真值(truthy),包括: * true * 任何非零数字 * 任何非空字符串 * 任何对象(包括空对象{}、空数组[]) // 真值假值示例 if (0) console.log("不会执行"); if ("") console.log("不会执行"); if (null) console.log("不会执行"); if (undefined) console.log("不会执行"); if (NaN) console.log("不会执行"); if (1) console.log("会执行"); if ("hello") console.log("会执行"); if ({}) console.log("会执行"); if ([]) console.log("会执行"); ==== 1.5.5 Undefined(未定义) ==== undefined 表示变量已声明但尚未赋值。 let x; console.log(x); // undefined // 函数无返回值时 function doNothing() {} console.log(doNothing()); // undefined // 访问不存在的属性 let obj = {}; console.log(obj.nonExistent); // undefined // 显式赋值(不推荐) let y = undefined; // 可以但没必要 ==== 1.5.6 Null(空值) ==== null 表示一个"空"值,通常用于表示"无"或"不存在"。 let user = null; // 明确表示没有用户 // null的使用场景 function findUser(id) { // 如果找不到用户,返回null if (id < 0) return null; return { id, name: "User" }; } // typeof null 的bug typeof null; // "object"(历史遗留bug) typeof undefined; // "undefined" **undefined vs null**: undefined == null; // true(宽松相等) undefined === null; // false(严格不相等) // undefined:系统级别的"缺少值" // null:程序级别的"无值" // 最佳实践:使用null表示"无",undefined留给系统 ==== 1.5.7 Symbol(符号) ==== ES6引入的新类型,表示唯一的标识符。 // 创建Symbol const sym1 = Symbol(); const sym2 = Symbol("description"); const sym3 = Symbol("description"); // 每个Symbol都是唯一的 sym2 === sym3; // false // 用作对象属性键 const id = Symbol("id"); const user = { name: "Alice", [id]: 12345 // 符号属性 }; console.log(user[id]); // 12345 console.log(Object.keys(user)); // ["name"](符号属性不可枚举) // 全局Symbol注册表 const globalSym = Symbol.for("app.id"); const sameSym = Symbol.for("app.id"); globalSym === sameSym; // true Symbol.keyFor(globalSym); // "app.id" ==== 1.5.8 Object(对象) ==== 对象是键值对的集合,是JavaScript中最复杂也是最重要的数据类型。 // 对象字面量 const person = { name: "张三", age: 30, isStudent: false, hobbies: ["读书", "游泳"], address: { city: "北京", zipCode: "100000" }, greet: function() { return `你好,我是${this.name}`; }, // ES6简写方法 sayBye() { return "再见!"; } }; // 访问属性 person.name; // "张三" person["age"]; // 30 // 添加/修改属性 person.email = "zhangsan@example.com"; person["phone"] = "13800138000"; // 删除属性 delete person.isStudent; // 检查属性 "name" in person; // true person.hasOwnProperty("age"); // true // 遍历对象 Object.keys(person); // 所有可枚举属性名 Object.values(person); // 所有可枚举属性值 Object.entries(person); // [key, value]数组 **对象的引用特性**: // 基本类型 - 值复制 let a = 5; let b = a; b = 10; console.log(a); // 5(a不变) // 对象 - 引用复制 let obj1 = { x: 1 }; let obj2 = obj1; obj2.x = 2; console.log(obj1.x); // 2(obj1也变了!) // 真正复制对象 let obj3 = { ...obj1 }; // 浅拷贝(ES6) let obj4 = Object.assign({}, obj1); // 浅拷贝 let obj5 = JSON.parse(JSON.stringify(obj1)); // 深拷贝(有限制) ===== 1.6 类型转换 ===== ==== 1.6.1 隐式类型转换 ==== JavaScript在运算时会自动进行类型转换。 // 字符串拼接 "5" + 3; // "53"(数字转字符串) "5" + true; // "5true"(布尔转字符串) // 算术运算(非加号) "5" - 3; // 2(字符串转数字) "5" * 2; // 10 "10" / "2"; // 5 // 比较运算 "5" == 5; // true(字符串转数字) 0 == false; // true "" == false; // true null == undefined; // true // 逻辑运算 !!"hello"; // true(转布尔) !!""; // false !!0; // false !!{}; // true ==== 1.6.2 显式类型转换 ==== // 转字符串 String(123); // "123" (123).toString(); // "123" 123 + ""; // "123" // 转数字 Number("123"); // 123 Number("12.34"); // 12.34 Number(""); // 0 Number(" "); // 0 Number("123abc"); // NaN Number(true); // 1 Number(false); // 0 Number(null); // 0 Number(undefined); // NaN // 更灵活的转数字 parseInt("123px"); // 123 parseInt("101", 2); // 5(二进制转十进制) parseFloat("12.34.56"); // 12.34 parseInt("FF", 16); // 255 // 转布尔 Boolean(1); // true Boolean(0); // false Boolean(""); // false Boolean("hello"); // true Boolean(null); // false Boolean(undefined); // false Boolean({}); // true // 一元加号转数字 +"123"; // 123 +true; // 1 +false; // 0 ===== 1.7 练习题 ===== ==== 练习1:变量声明 ==== 判断以下代码的正误,错误的说明原因: // 1 var x = 1; var x = 2; // 2 let y = 1; let y = 2; // 3 const z; z = 3; // 4 const user = { name: "Tom" }; user.name = "Jerry"; // 5 const arr = [1, 2]; arr.push(3); ==== 练习2:数据类型判断 ==== 写出以下表达式的结果: typeof undefined; typeof null; typeof {}; typeof []; typeof function() {}; typeof NaN; typeof 123n; typeof Symbol(); Array.isArray([]); [] instanceof Array; ==== 练习3:类型转换 ==== 预测以下表达式的结果: "42" + 8; "42" - 8; "42" * "2"; "42" == 42; "42" === 42; null == undefined; null === undefined; !!"false"; !!""; Number("12px"); parseInt("12px"); ==== 练习4:编程题 ==== 1. 编写一个程序,声明一个名为 `userInfo` 的常量对象,包含 `name`、`age`、`email` 属性,然后尝试修改 `age` 属性,观察结果。 2. 创建一个函数 `convertToNumber`,接受一个参数,尝试将其转换为数字,如果转换失败返回 `null` 而不是 `NaN`。 3. 编写代码验证浮点数精度问题,并提供解决方案。 ===== 参考答案 ===== **练习1答案**: * 1: 正确,var允许重复声明 * 2: 错误,let不允许重复声明,会抛出SyntaxError * 3: 错误,const必须初始化 * 4: 正确,const对象的属性可以修改 * 5: 正确,const数组的内容可以修改 **练习2答案**: typeof undefined; // "undefined" typeof null; // "object"(bug) typeof {}; // "object" typeof []; // "object" typeof function() {}; // "function" typeof NaN; // "number" typeof 123n; // "bigint" typeof Symbol(); // "symbol" Array.isArray([]); // true [] instanceof Array; // true **练习3答案**: "42" + 8; // "428" "42" - 8; // 34 "42" * "2"; // 84 "42" == 42; // true "42" === 42; // false null == undefined; // true null === undefined; // false !!"false"; // true(非空字符串) !!""; // false Number("12px"); // NaN parseInt("12px"); // 12 **练习4参考答案**: // 第1题 const userInfo = { name: "张三", age: 25, email: "zhangsan@example.com" }; userInfo.age = 26; // ✓ 成功 console.log(userInfo.age); // 26 // userInfo = {}; // ✗ 错误!不能重新赋值 // 第2题 function convertToNumber(value) { const num = Number(value); return isNaN(num) ? null : num; } console.log(convertToNumber("123")); // 123 console.log(convertToNumber("abc")); // null console.log(convertToNumber("12.34")); // 12.34 // 第3题 function safeAdd(a, b) { return Math.round((a + b) * 1e10) / 1e10; } console.log(0.1 + 0.2); // 0.30000000000000004 console.log(safeAdd(0.1, 0.2)); // 0.3 ===== 本章小结 ===== 本章介绍了JavaScript的基础知识: * JavaScript的历史和特点 * 浏览器和Node.js运行环境 * 基本语法规则(语句、注释、严格模式) * 三种变量声明方式(var、let、const)及其区别 * 8种数据类型及其特点和用法 * 类型转换(隐式和显式) 掌握这些基础知识是深入学习JavaScript的前提。下一章将学习运算符和表达式,为编写复杂逻辑打下基础。 ===== 延伸阅读 ===== * [[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_types|MDN - 语法和数据类型]] * [[https://github.com/getify/You-Dont-Know-JS|你不知道的JavaScript]](书籍) * [[https://javascript.info/types|JavaScript.info - 数据类型]]