JavaScript核心基础与现代特性
JavaScript是现代Web开发的核心语言,从简单的网页脚本发展为功能强大的编程语言,支持函数式编程、面向对象编程和异步编程等多种编程范式。掌握JavaScript的核心概念对于前端开发至关重要。
核心价值
JavaScript = 动态类型 + 函数式编程 + 面向对象 + 异步编程
- 🔄 动态类型系统:灵活的类型转换和运行时类型检查
- 🎯 函数式编程:高阶函数、闭包、纯函数等特性
- 🏗️ 面向对象编程:原型链、类、继承等机制
- ⚡ 异步编程:Promise、async/await、事件循环
- 🌐 跨平台能力:浏览器、Node.js、移动端统一语言
- 📦 丰富生态:npm生态系统和无数开源库
1. JavaScript数据类型系统
1.1 数据类型分类
JavaScript有两大类数据类型:原始类型(Primitive Types)和引用类型(Reference Types)。
数据类型特性对比
| 类型分类 | 存储方式 | 赋值行为 | 比较方式 | 可变性 |
|---|---|---|---|---|
| 原始类型 | 栈内存 | 值拷贝 | 值比较 | 不可变 |
| 引用类型 | 堆内存 | 引用拷贝 | 引用比较 | 可变 |
- 原始类型详解
- 引用类型详解
- 类型转换
原始类型深度解析
原始类型完整示例
javascript
1// 1. Number 数字类型2const integer = 42; // 整数3const float = 3.14159; // 浮点数4const scientific = 1.23e-4; // 科学计数法5const binary = 0b1010; // 二进制6const octal = 0o755; // 八进制7const hex = 0xFF; // 十六进制8const infinity = Infinity; // 无穷大9const notANumber = NaN; // 非数字1011// 数字类型检查和转换12console.log(Number.isInteger(42)); // true13console.log(Number.isNaN(NaN)); // true14console.log(Number.parseFloat('3.14')); // 3.1415console.log(Number.parseInt('42px')); // 421617// 2. String 字符串类型18const singleQuote = 'Hello World';19const doubleQuote = "Hello World";20const templateLiteral = `Hello ${name}`;21const multiLine = `22 这是一个23 多行字符串24`;2526// 字符串方法27const text = "JavaScript";28console.log(text.length); // 1029console.log(text.toUpperCase()); // "JAVASCRIPT"30console.log(text.slice(0, 4)); // "Java"31console.log(text.includes('Script')); // true32console.log(text.split('')); // ['J','a','v','a','S','c','r','i','p','t']3334// 3. Boolean 布尔类型35const isTrue = true;36const isFalse = false;3738// 布尔转换规则39console.log(Boolean(0)); // false40console.log(Boolean('')); // false41console.log(Boolean(null)); // false42console.log(Boolean(undefined)); // false43console.log(Boolean(NaN)); // false44console.log(Boolean([])); // true (空数组是真值)45console.log(Boolean({})); // true (空对象是真值)4647// 4. Undefined 和 Null48let undefinedVar; // undefined49const nullVar = null; // null5051console.log(typeof undefined); // "undefined"52console.log(typeof null); // "object" (历史遗留问题)53console.log(undefined == null); // true (宽松相等)54console.log(undefined === null); // false (严格相等)5556// 5. Symbol 符号类型 (ES6+)57const symbol1 = Symbol('description');58const symbol2 = Symbol('description');59console.log(symbol1 === symbol2); // false (每个Symbol都是唯一的)6061// Symbol的应用场景62const PRIVATE_KEY = Symbol('private');63const obj = {64 publicProperty: 'public',65 [PRIVATE_KEY]: 'private'66};6768// 6. BigInt 大整数类型 (ES2020+)69const bigInt1 = 123456789012345678901234567890n;70const bigInt2 = BigInt('123456789012345678901234567890');71console.log(bigInt1 === bigInt2); // true7273// BigInt运算74console.log(bigInt1 + 1n); // 需要使用BigInt进行运算75// console.log(bigInt1 + 1); // TypeError: 不能混合BigInt和Number引用类型深度解析
引用类型完整示例
javascript
1// 1. Object 对象类型2const person = {3 name: 'John',4 age: 30,5 address: {6 city: 'New York',7 country: 'USA'8 },9 greet() {10 return `Hello, I'm ${this.name}`;11 }12};1314// 对象操作15console.log(person.name); // "John"16console.log(person['age']); // 3017person.email = 'john@example.com'; // 动态添加属性18delete person.age; // 删除属性1920// 对象方法21console.log(Object.keys(person)); // 获取所有键22console.log(Object.values(person)); // 获取所有值23console.log(Object.entries(person)); // 获取键值对数组2425// 2. Array 数组类型26const numbers = [1, 2, 3, 4, 5];27const mixed = [1, 'hello', true, null, { name: 'object' }];2829// 数组方法 - 变异方法(修改原数组)30numbers.push(6); // 添加到末尾31numbers.pop(); // 删除末尾元素32numbers.unshift(0); // 添加到开头33numbers.shift(); // 删除开头元素34numbers.splice(2, 1, 'new'); // 删除并插入3536// 数组方法 - 非变异方法(返回新数组)37const doubled = numbers.map(n => n * 2);38const evens = numbers.filter(n => n % 2 === 0);39const sum = numbers.reduce((acc, n) => acc + n, 0);40const found = numbers.find(n => n > 3);41const hasLarge = numbers.some(n => n > 10);42const allPositive = numbers.every(n => n > 0);4344// 3. Function 函数类型45// 函数声明46function regularFunction(a, b) {47 return a + b;48}4950// 函数表达式51const functionExpression = function(a, b) {52 return a + b;53};5455// 箭头函数56const arrowFunction = (a, b) => a + b;57const singleParam = x => x * 2;58const noParams = () => 'Hello';5960// 高阶函数61function createMultiplier(factor) {62 return function(number) {63 return number * factor;64 };65}6667const double = createMultiplier(2);68console.log(double(5)); // 106970// 4. Date 日期类型71const now = new Date();72const specificDate = new Date('2023-12-25');73const timestamp = new Date(1640995200000);7475console.log(now.getFullYear()); // 当前年份76console.log(now.getMonth()); // 月份 (0-11)77console.log(now.getDate()); // 日期78console.log(now.toISOString()); // ISO格式字符串79console.log(now.toLocaleDateString()); // 本地日期格式8081// 5. RegExp 正则表达式82const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;83const phoneRegex = new RegExp('^\\d{3}-\\d{3}-\\d{4}$');8485console.log(emailRegex.test('user@example.com')); // true86console.log('Hello World'.match(/\w+/g)); // ['Hello', 'World']87console.log('abc123def'.replace(/\d+/g, 'XXX')); // 'abcXXXdef'8889// 6. Map 和 Set (ES6+)90const map = new Map();91map.set('name', 'John');92map.set(1, 'number key');93map.set(true, 'boolean key');9495console.log(map.get('name')); // "John"96console.log(map.has(1)); // true97console.log(map.size); // 39899const set = new Set([1, 2, 3, 3, 4]);100console.log(set); // Set {1, 2, 3, 4}101set.add(5);102set.delete(1);103console.log(set.has(2)); // true类型转换机制
类型转换完整示例
javascript
1// 1. 显式类型转换2// 转换为字符串3console.log(String(123)); // "123"4console.log(String(true)); // "true"5console.log(String(null)); // "null"6console.log(String(undefined)); // "undefined"7console.log((123).toString()); // "123"89// 转换为数字10console.log(Number('123')); // 12311console.log(Number('123.45')); // 123.4512console.log(Number('123px')); // NaN13console.log(Number(true)); // 114console.log(Number(false)); // 015console.log(Number(null)); // 016console.log(Number(undefined)); // NaN1718console.log(parseInt('123px')); // 12319console.log(parseFloat('123.45px')); // 123.452021// 转换为布尔值22console.log(Boolean(1)); // true23console.log(Boolean(0)); // false24console.log(Boolean('')); // false25console.log(Boolean('hello')); // true26console.log(Boolean([])); // true27console.log(Boolean({})); // true2829// 2. 隐式类型转换30// 字符串拼接31console.log('5' + 3); // "53"32console.log('5' + true); // "5true"33console.log('5' + null); // "5null"3435// 数学运算36console.log('5' - 3); // 237console.log('5' * 3); // 1538console.log('5' / 2); // 2.539console.log('5' % 2); // 14041// 比较运算42console.log('5' == 5); // true (宽松相等)43console.log('5' === 5); // false (严格相等)44console.log(null == undefined); // true45console.log(null === undefined); // false4647// 3. 特殊的类型转换规则48// 对象到原始值的转换49const obj = {50 valueOf() {51 return 42;52 },53 toString() {54 return 'object';55 }56};5758console.log(obj + 1); // 43 (调用valueOf)59console.log(String(obj)); // "object" (调用toString)6061// 数组的类型转换62console.log([1, 2, 3] + ''); // "1,2,3"63console.log([] + ''); // ""64console.log([1] + 1); // "11"65console.log([1, 2] + 1); // "1,21"6667// 4. 类型检测方法68function getType(value) {69 return Object.prototype.toString.call(value).slice(8, -1);70}7172console.log(typeof 123); // "number"73console.log(typeof 'hello'); // "string"74console.log(typeof true); // "boolean"75console.log(typeof undefined); // "undefined"76console.log(typeof null); // "object" (历史遗留)77console.log(typeof {}); // "object"78console.log(typeof []); // "object"79console.log(typeof function(){}); // "function"8081console.log(getType(123)); // "Number"82console.log(getType('hello')); // "String"83console.log(getType([])); // "Array"84console.log(getType({})); // "Object"85console.log(getType(null)); // "Null"8687console.log(Array.isArray([])); // true88console.log(Number.isInteger(42)); // true89console.log(Number.isNaN(NaN)); // true2. 变量与作用域
2.1 变量声明方式
JavaScript提供了三种变量声明方式:var、let、const,它们在作用域、提升和重复声明方面有重要区别。
变量声明对比表
| 特性 | var | let | const | 推荐使用 |
|---|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 | let/const |
| 变量提升 | 是 | 否(暂时性死区) | 否(暂时性死区) | let/const |
| 重复声明 | 允许 | 不允许 | 不允许 | let/const |
| 重新赋值 | 允许 | 允许 | 不允许 | 根据需求 |
| 全局对象属性 | 是 | 否 | 否 | let/const |
- var的问题
- let和const的优势
- 最佳实践
var声明的问题
var声明问题示例
javascript
1// 1. 函数作用域问题2function varScopeIssue() {3 if (true) {4 var x = 1;5 }6 console.log(x); // 1 - var没有块级作用域7}89// 2. 变量提升问题10console.log(hoistedVar); // undefined - 变量提升但未赋值11var hoistedVar = 'Hello';1213// 等价于:14// var hoistedVar;15// console.log(hoistedVar); // undefined16// hoistedVar = 'Hello';1718// 3. 循环中的问题19for (var i = 0; i < 3; i++) {20 setTimeout(() => {21 console.log('var:', i); // 输出: 3, 3, 322 }, 100);23}2425// 4. 重复声明问题26var name = 'John';27var name = 'Jane'; // 不会报错,但可能导致意外覆盖28console.log(name); // 'Jane'2930// 5. 全局对象污染31var globalVar = 'I am global';32console.log(window.globalVar); // 'I am global' (在浏览器中)let和const的优势
let和const优势示例
javascript
1// 1. 块级作用域2function blockScopeExample() {3 if (true) {4 let blockScoped = 'I am block scoped';5 const alsoBlockScoped = 'Me too';6 }7 // console.log(blockScoped); // ReferenceError8 // console.log(alsoBlockScoped); // ReferenceError9}1011// 2. 暂时性死区12function temporalDeadZone() {13 // console.log(letVar); // ReferenceError: Cannot access before initialization14 let letVar = 'Hello';15 16 // console.log(constVar); // ReferenceError: Cannot access before initialization17 const constVar = 'World';18}1920// 3. 循环中的正确行为21for (let i = 0; i < 3; i++) {22 setTimeout(() => {23 console.log('let:', i); // 输出: 0, 1, 224 }, 100);25}2627// 4. 不允许重复声明28// let name = 'John';29// let name = 'Jane'; // SyntaxError: Identifier 'name' has already been declared3031// 5. const的不可变性32const config = {33 apiUrl: 'https://api.example.com',34 timeout: 500035};3637// config = {}; // TypeError: Assignment to constant variable3839// 但可以修改对象属性40config.timeout = 10000; // 这是允许的41config.newProperty = 'new value'; // 这也是允许的4243// 如果需要完全不可变,使用Object.freeze()44const frozenConfig = Object.freeze({45 apiUrl: 'https://api.example.com',46 timeout: 500047});4849// frozenConfig.timeout = 10000; // 在严格模式下会抛出错误变量声明最佳实践
变量声明最佳实践
javascript
1// 1. 优先使用const,需要重新赋值时使用let2const API_URL = 'https://api.example.com';3const users = [];4let currentUser = null;56// 2. 在最小作用域内声明变量7function processData(data) {8 const processedData = [];9 10 for (let i = 0; i < data.length; i++) {11 const item = data[i];12 13 if (item.active) {14 const processedItem = {15 id: item.id,16 name: item.name.toUpperCase(),17 timestamp: Date.now()18 };19 processedData.push(processedItem);20 }21 }22 23 return processedData;24}2526// 3. 使用有意义的变量名27// 不好的命名28const d = new Date();29const u = users.filter(x => x.a);3031// 好的命名32const currentDate = new Date();33const activeUsers = users.filter(user => user.active);3435// 4. 常量使用大写字母和下划线36const MAX_RETRY_COUNT = 3;37const DEFAULT_TIMEOUT = 5000;38const API_ENDPOINTS = {39 USERS: '/api/users',40 POSTS: '/api/posts'41};4243// 5. 解构赋值优化变量声明44const user = {45 name: 'John',46 age: 30,47 address: {48 city: 'New York',49 country: 'USA'50 }51};5253// 传统方式54const name = user.name;55const age = user.age;56const city = user.address.city;5758// 解构赋值方式59const { name, age, address: { city } } = user;6061// 6. 模块化变量管理62// config.js63export const CONFIG = {64 API_URL: process.env.API_URL || 'http://localhost:3000',65 TIMEOUT: 5000,66 MAX_RETRIES: 367};6869// constants.js70export const HTTP_STATUS = {71 OK: 200,72 CREATED: 201,73 BAD_REQUEST: 400,74 UNAUTHORIZED: 401,75 NOT_FOUND: 404,76 INTERNAL_SERVER_ERROR: 50077};7879export const EVENTS = {80 USER_LOGIN: 'user:login',81 USER_LOGOUT: 'user:logout',82 DATA_LOADED: 'data:loaded'83};3. 函数与作用域
3.1 函数定义方式
JavaScript提供了多种函数定义方式,每种方式都有其特定的用途和特性。
- 函数类型
- 作用域链
- 闭包详解
函数定义方式详解
函数定义方式对比
javascript
1// 1. 函数声明 (Function Declaration)2function greet(name) {3 return `Hello, ${name}!`;4}56// 函数声明会被提升,可以在声明前调用7console.log(sayHello('World')); // "Hello, World!"89function sayHello(name) {10 return `Hello, ${name}!`;11}1213// 2. 函数表达式 (Function Expression)14const greetExpression = function(name) {15 return `Hello, ${name}!`;16};1718// 命名函数表达式19const greetNamed = function greetFunction(name) {20 return `Hello, ${name}!`;21};2223// 3. 箭头函数 (Arrow Function)24const greetArrow = (name) => {25 return `Hello, ${name}!`;26};2728// 简化的箭头函数29const greetShort = name => `Hello, ${name}!`;3031// 无参数箭头函数32const getCurrentTime = () => new Date().toISOString();3334// 多参数箭头函数35const add = (a, b) => a + b;3637// 返回对象字面量(需要括号)38const createUser = (name, age) => ({ name, age, id: Date.now() });3940// 4. 方法定义41const obj = {42 // 传统方法定义43 greet: function(name) {44 return `Hello, ${name}!`;45 },46 47 // ES6简写方法48 greetShort(name) {49 return `Hello, ${name}!`;50 },51 52 // 箭头函数方法(注意this绑定)53 greetArrow: (name) => {54 // 这里的this不指向obj55 return `Hello, ${name}!`;56 }57};5859// 5. 构造函数60function Person(name, age) {61 this.name = name;62 this.age = age;63 this.greet = function() {64 return `Hello, I'm ${this.name}`;65 };66}6768// 使用new关键字创建实例69const person = new Person('John', 30);7071// 6. 生成器函数72function* numberGenerator() {73 let i = 0;74 while (true) {75 yield i++;76 }77}7879const gen = numberGenerator();80console.log(gen.next().value); // 081console.log(gen.next().value); // 18283// 7. 异步函数84async function fetchData(url) {85 try {86 const response = await fetch(url);87 const data = await response.json();88 return data;89 } catch (error) {90 console.error('Fetch error:', error);91 throw error;92 }93}9495// 异步箭头函数96const fetchDataArrow = async (url) => {97 const response = await fetch(url);98 return response.json();99};100101// 8. 立即执行函数表达式 (IIFE)102(function() {103 console.log('IIFE executed immediately');104})();105106// 箭头函数IIFE107(() => {108 console.log('Arrow IIFE executed');109})();110111// 带参数的IIFE112((name) => {113 console.log(`Hello, ${name}!`);114})('World');115116// 9. 高阶函数117function createMultiplier(factor) {118 return function(number) {119 return number * factor;120 };121}122123const double = createMultiplier(2);124const triple = createMultiplier(3);125126console.log(double(5)); // 10127console.log(triple(4)); // 12128129// 10. 函数作为参数130function processArray(arr, callback) {131 const result = [];132 for (let i = 0; i < arr.length; i++) {133 result.push(callback(arr[i], i, arr));134 }135 return result;136}137138const numbers = [1, 2, 3, 4, 5];139const doubled = processArray(numbers, x => x * 2);140console.log(doubled); // [2, 4, 6, 8, 10]作用域与作用域链
作用域链详解
javascript
1// 全局作用域2var globalVar = 'I am global';3let globalLet = 'I am also global';4const globalConst = 'I am global too';56function outerFunction(outerParam) {7 // 外层函数作用域8 var outerVar = 'I am in outer function';9 let outerLet = 'I am outer let';10 11 console.log('In outer function:');12 console.log('- Can access globalVar:', globalVar);13 console.log('- Can access outerParam:', outerParam);14 console.log('- Can access outerVar:', outerVar);15 16 function innerFunction(innerParam) {17 // 内层函数作用域18 var innerVar = 'I am in inner function';19 let innerLet = 'I am inner let';20 21 console.log('In inner function:');22 console.log('- Can access globalVar:', globalVar);23 console.log('- Can access outerParam:', outerParam);24 console.log('- Can access outerVar:', outerVar);25 console.log('- Can access innerParam:', innerParam);26 console.log('- Can access innerVar:', innerVar);27 28 // 作用域链查找顺序:29 // 1. 当前函数作用域 (innerFunction)30 // 2. 外层函数作用域 (outerFunction)31 // 3. 全局作用域32 33 function deeperFunction() {34 console.log('In deeper function:');35 console.log('- Still can access outerParam:', outerParam);36 console.log('- Still can access innerVar:', innerVar);37 }38 39 return deeperFunction;40 }41 42 return innerFunction;43}4445// 调用示例46const inner = outerFunction('outer parameter');47const deeper = inner('inner parameter');48deeper();4950// 块级作用域示例51function blockScopeExample() {52 console.log('=== Block Scope Example ===');53 54 if (true) {55 var varInBlock = 'var in block';56 let letInBlock = 'let in block';57 const constInBlock = 'const in block';58 59 console.log('Inside block:');60 console.log('- varInBlock:', varInBlock);61 console.log('- letInBlock:', letInBlock);62 console.log('- constInBlock:', constInBlock);63 }64 65 console.log('Outside block:');66 console.log('- varInBlock:', varInBlock); // 可以访问67 // console.log('- letInBlock:', letInBlock); // ReferenceError68 // console.log('- constInBlock:', constInBlock); // ReferenceError69 70 // 循环中的作用域71 for (var i = 0; i < 3; i++) {72 // var i 在函数作用域中73 }74 console.log('var i after loop:', i); // 375 76 for (let j = 0; j < 3; j++) {77 // let j 在块级作用域中78 }79 // console.log('let j after loop:', j); // ReferenceError80}8182blockScopeExample();8384// 闭包示例85function createCounter() {86 let count = 0;87 88 return {89 increment() {90 count++;91 return count;92 },93 94 decrement() {95 count--;96 return count;97 },98 99 getCount() {100 return count;101 }102 };103}104105const counter1 = createCounter();106const counter2 = createCounter();107108console.log('Counter 1:', counter1.increment()); // 1109console.log('Counter 1:', counter1.increment()); // 2110console.log('Counter 2:', counter2.increment()); // 1111console.log('Counter 1 current:', counter1.getCount()); // 2112113// 模块模式114const Module = (function() {115 // 私有变量和方法116 let privateVar = 'I am private';117 118 function privateMethod() {119 return 'This is a private method';120 }121 122 // 公共接口123 return {124 publicVar: 'I am public',125 126 publicMethod() {127 return 'This is a public method';128 },129 130 accessPrivate() {131 return privateVar + ' - accessed through public method';132 },133 134 callPrivateMethod() {135 return privateMethod();136 }137 };138})();139140console.log(Module.publicVar); // "I am public"141console.log(Module.publicMethod()); // "This is a public method"142console.log(Module.accessPrivate()); // "I am private - accessed through public method"143// console.log(Module.privateVar); // undefined144145// 词法作用域与动态作用域对比146function lexicalScopeExample() {147 const name = 'Lexical';148 149 function inner() {150 console.log('Name in inner:', name); // 总是 'Lexical'151 }152 153 function callInner() {154 const name = 'Dynamic';155 inner(); // 仍然输出 'Lexical',因为JavaScript使用词法作用域156 }157 158 callInner();159}160161lexicalScopeExample();闭包深度解析
闭包详解与应用
javascript
1// 1. 闭包基础概念2function outerFunction(x) {3 // 外层函数的变量4 const outerVariable = x;5 6 // 内层函数(闭包)7 function innerFunction(y) {8 // 内层函数可以访问外层函数的变量9 return outerVariable + y;10 }11 12 // 返回内层函数13 return innerFunction;14}1516const closure = outerFunction(10);17console.log(closure(5)); // 151819// 2. 闭包的实际应用2021// 数据封装和私有变量22function createBankAccount(initialBalance) {23 let balance = initialBalance;24 25 return {26 deposit(amount) {27 if (amount > 0) {28 balance += amount;29 return balance;30 }31 throw new Error('Deposit amount must be positive');32 },33 34 withdraw(amount) {35 if (amount > 0 && amount <= balance) {36 balance -= amount;37 return balance;38 }39 throw new Error('Invalid withdrawal amount');40 },41 42 getBalance() {43 return balance;44 }45 };46}4748const account = createBankAccount(1000);49console.log(account.deposit(500)); // 150050console.log(account.withdraw(200)); // 130051console.log(account.getBalance()); // 130052// console.log(account.balance); // undefined - 无法直接访问私有变量5354// 3. 函数工厂55function createValidator(pattern, errorMessage) {56 const regex = new RegExp(pattern);57 58 return function(value) {59 if (regex.test(value)) {60 return { valid: true, value };61 } else {62 return { valid: false, error: errorMessage };63 }64 };65}6667const emailValidator = createValidator(68 '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',69 'Invalid email format'70);7172const phoneValidator = createValidator(73 '^\\d{3}-\\d{3}-\\d{4}$',74 'Phone number must be in format: 123-456-7890'75);7677console.log(emailValidator('user@example.com')); // { valid: true, value: 'user@example.com' }78console.log(phoneValidator('123-456-7890')); // { valid: true, value: '123-456-7890' }7980// 4. 记忆化(Memoization)81function memoize(fn) {82 const cache = {};83 84 return function(...args) {85 const key = JSON.stringify(args);86 87 if (key in cache) {88 console.log('Cache hit for:', key);89 return cache[key];90 }91 92 console.log('Computing for:', key);93 const result = fn.apply(this, args);94 cache[key] = result;95 96 return result;97 };98}99100// 斐波那契数列(递归版本)101const fibonacci = memoize(function(n) {102 if (n <= 1) return n;103 return fibonacci(n - 1) + fibonacci(n - 2);104});105106console.log(fibonacci(10)); // 计算并缓存107console.log(fibonacci(10)); // 从缓存获取108109// 5. 偏函数应用110function partial(fn, ...presetArgs) {111 return function(...laterArgs) {112 return fn(...presetArgs, ...laterArgs);113 };114}115116function multiply(a, b, c) {117 return a * b * c;118}119120const multiplyBy2 = partial(multiply, 2);121const multiplyBy2And3 = partial(multiply, 2, 3);122123console.log(multiplyBy2(3, 4)); // 2 * 3 * 4 = 24124console.log(multiplyBy2And3(5)); // 2 * 3 * 5 = 30125126// 6. 柯里化127function curry(fn) {128 return function curried(...args) {129 if (args.length >= fn.length) {130 return fn.apply(this, args);131 } else {132 return function(...nextArgs) {133 return curried(...args, ...nextArgs);134 };135 }136 };137}138139const curriedMultiply = curry(multiply);140const step1 = curriedMultiply(2);141const step2 = step1(3);142const result = step2(4);143console.log(result); // 24144145// 或者链式调用146console.log(curriedMultiply(2)(3)(4)); // 24147148// 7. 事件处理中的闭包149function setupEventHandlers() {150 const buttons = document.querySelectorAll('.btn');151 152 for (let i = 0; i < buttons.length; i++) {153 // 使用闭包保存循环变量154 buttons[i].addEventListener('click', (function(index) {155 return function() {156 console.log(`Button ${index} clicked`);157 };158 })(i));159 }160 161 // 或者使用let的块级作用域162 for (let i = 0; i < buttons.length; i++) {163 buttons[i].addEventListener('click', function() {164 console.log(`Button ${i} clicked`);165 });166 }167}168169// 8. 模块模式的高级应用170const AdvancedModule = (function() {171 // 私有变量172 let privateCounter = 0;173 const privateData = [];174 175 // 私有方法176 function privateMethod() {177 return 'This is private';178 }179 180 function validateInput(input) {181 return input != null && input !== '';182 }183 184 // 公共接口185 return {186 // 公共方法187 increment() {188 privateCounter++;189 return privateCounter;190 },191 192 decrement() {193 privateCounter--;194 return privateCounter;195 },196 197 getCounter() {198 return privateCounter;199 },200 201 addData(item) {202 if (validateInput(item)) {203 privateData.push(item);204 return true;205 }206 return false;207 },208 209 getData() {210 return [...privateData]; // 返回副本,防止外部修改211 },212 213 // 特权方法(可以访问私有成员)214 reset() {215 privateCounter = 0;216 privateData.length = 0;217 return 'Module reset';218 }219 };220})();221222console.log(AdvancedModule.increment()); // 1223console.log(AdvancedModule.addData('test')); // true224console.log(AdvancedModule.getData()); // ['test']225226// 9. 闭包的内存管理227function createLeakyFunction() {228 const largeData = new Array(1000000).fill('data');229 230 return function() {231 // 这个函数保持对largeData的引用232 return largeData.length;233 };234}235236// 避免内存泄漏的方法237function createOptimizedFunction() {238 const largeData = new Array(1000000).fill('data');239 const dataLength = largeData.length;240 241 return function() {242 // 只保存需要的值,而不是整个数组243 return dataLength;244 };245}246247// 10. 闭包与异步操作248function createAsyncProcessor() {249 let processingQueue = [];250 let isProcessing = false;251 252 async function processQueue() {253 if (isProcessing || processingQueue.length === 0) {254 return;255 }256 257 isProcessing = true;258 259 while (processingQueue.length > 0) {260 const task = processingQueue.shift();261 try {262 await task();263 } catch (error) {264 console.error('Task failed:', error);265 }266 }267 268 isProcessing = false;269 }270 271 return {272 addTask(asyncTask) {273 processingQueue.push(asyncTask);274 processQueue();275 },276 277 getQueueLength() {278 return processingQueue.length;279 },280 281 isProcessing() {282 return isProcessing;283 }284 };285}286287const processor = createAsyncProcessor();288289processor.addTask(async () => {290 console.log('Task 1 started');291 await new Promise(resolve => setTimeout(resolve, 1000));292 console.log('Task 1 completed');293});294295processor.addTask(async () => {296 console.log('Task 2 started');297 await new Promise(resolve => setTimeout(resolve, 500));298 console.log('Task 2 completed');299});4. 对象与原型
4.1 对象创建与操作
JavaScript中的对象是键值对的集合,是语言的核心数据结构。
- 对象创建
- 对象操作
- 原型链
对象创建方式
对象创建方式详解
javascript
1// 1. 对象字面量(最常用)2const person = {3 name: 'John',4 age: 30,5 city: 'New York',6 7 // 方法8 greet() {9 return `Hello, I'm ${this.name}`;10 },11 12 // 计算属性名13 ['full' + 'Name']: 'John Doe',14 15 // 属性简写16 // 如果变量名和属性名相同,可以简写17 // name, // 等价于 name: name18};1920// 2. Object构造函数21const person2 = new Object();22person2.name = 'Jane';23person2.age = 25;24person2.greet = function() {25 return `Hello, I'm ${this.name}`;26};2728// 3. Object.create()29const personPrototype = {30 greet() {31 return `Hello, I'm ${this.name}`;32 },33 34 setAge(age) {35 this.age = age;36 }37};3839const person3 = Object.create(personPrototype);40person3.name = 'Bob';41person3.age = 35;4243// 4. 构造函数44function Person(name, age) {45 this.name = name;46 this.age = age;47}4849Person.prototype.greet = function() {50 return `Hello, I'm ${this.name}`;51};5253const person4 = new Person('Alice', 28);5455// 5. 类语法(ES6+)56class PersonClass {57 constructor(name, age) {58 this.name = name;59 this.age = age;60 }61 62 greet() {63 return `Hello, I'm ${this.name}`;64 }65 66 static createAnonymous() {67 return new PersonClass('Anonymous', 0);68 }69}7071const person5 = new PersonClass('Charlie', 32);7273// 6. 工厂函数74function createPerson(name, age) {75 return {76 name,77 age,78 greet() {79 return `Hello, I'm ${this.name}`;80 }81 };82}8384const person6 = createPerson('David', 27);8586// 7. 使用Object.assign()87const defaultPerson = {88 name: 'Unknown',89 age: 0,90 city: 'Unknown'91};9293const person7 = Object.assign({}, defaultPerson, {94 name: 'Eve',95 age: 2996});9798// 8. 使用扩展运算符99const person8 = {100 ...defaultPerson,101 name: 'Frank',102 age: 31103};104105// 9. 动态属性创建106function createObjectWithDynamicProps(props) {107 const obj = {};108 109 for (const [key, value] of Object.entries(props)) {110 obj[key] = value;111 }112 113 return obj;114}115116const dynamicPerson = createObjectWithDynamicProps({117 name: 'Grace',118 age: 26,119 occupation: 'Developer'120});121122// 10. 使用Proxy创建对象123const personProxy = new Proxy({}, {124 set(target, property, value) {125 if (property === 'age' && (typeof value !== 'number' || value < 0)) {126 throw new Error('Age must be a positive number');127 }128 target[property] = value;129 return true;130 },131 132 get(target, property) {133 if (property === 'info') {134 return `${target.name} is ${target.age} years old`;135 }136 return target[property];137 }138});139140personProxy.name = 'Henry';141personProxy.age = 33;142console.log(personProxy.info); // "Henry is 33 years old"对象操作详解
对象操作完整示例
javascript
1const user = {2 id: 1,3 name: 'John Doe',4 email: 'john@example.com',5 age: 30,6 address: {7 street: '123 Main St',8 city: 'New York',9 country: 'USA'10 },11 hobbies: ['reading', 'coding', 'gaming']12};1314// 1. 属性访问15console.log(user.name); // 点号访问16console.log(user['email']); // 方括号访问17console.log(user.address.city); // 嵌套属性访问1819// 动态属性访问20const propertyName = 'age';21console.log(user[propertyName]); // 302223// 2. 属性检查24console.log('name' in user); // true25console.log(user.hasOwnProperty('name')); // true26console.log(user.hasOwnProperty('toString')); // false (继承的属性)2728// 3. 属性添加和修改29user.phone = '123-456-7890'; // 添加新属性30user.age = 31; // 修改现有属性31user.address.zipCode = '10001'; // 添加嵌套属性3233// 4. 属性删除34delete user.phone;35console.log(user.phone); // undefined3637// 5. 对象遍历38// for...in 遍历所有可枚举属性(包括继承的)39for (const key in user) {40 if (user.hasOwnProperty(key)) {41 console.log(`${key}: ${user[key]}`);42 }43}4445// Object.keys() 只遍历自有可枚举属性46Object.keys(user).forEach(key => {47 console.log(`${key}: ${user[key]}`);48});4950// Object.entries() 获取键值对数组51Object.entries(user).forEach(([key, value]) => {52 console.log(`${key}: ${value}`);53});5455// Object.values() 获取所有值56Object.values(user).forEach(value => {57 console.log(value);58});5960// 6. 对象复制61// 浅拷贝62const shallowCopy1 = Object.assign({}, user);63const shallowCopy2 = { ...user };6465// 深拷贝(简单版本,不处理函数、日期等特殊对象)66const deepCopy = JSON.parse(JSON.stringify(user));6768// 深拷贝(完整版本)69function deepClone(obj) {70 if (obj === null || typeof obj !== 'object') {71 return obj;72 }73 74 if (obj instanceof Date) {75 return new Date(obj.getTime());76 }77 78 if (obj instanceof Array) {79 return obj.map(item => deepClone(item));80 }81 82 if (typeof obj === 'object') {83 const clonedObj = {};84 for (const key in obj) {85 if (obj.hasOwnProperty(key)) {86 clonedObj[key] = deepClone(obj[key]);87 }88 }89 return clonedObj;90 }91}9293const deepCopyComplete = deepClone(user);9495// 7. 对象合并96const additionalInfo = {97 occupation: 'Developer',98 company: 'Tech Corp'99};100101const mergedUser = {102 ...user,103 ...additionalInfo,104 age: 32 // 覆盖原有属性105};106107// 深度合并108function deepMerge(target, source) {109 const result = { ...target };110 111 for (const key in source) {112 if (source.hasOwnProperty(key)) {113 if (114 source[key] && 115 typeof source[key] === 'object' && 116 !Array.isArray(source[key])117 ) {118 result[key] = deepMerge(result[key] || {}, source[key]);119 } else {120 result[key] = source[key];121 }122 }123 }124 125 return result;126}127128// 8. 对象属性描述符129Object.defineProperty(user, 'id', {130 writable: false, // 不可写131 enumerable: true, // 可枚举132 configurable: false // 不可配置133});134135// user.id = 2; // 在严格模式下会抛出错误136137// 获取属性描述符138const descriptor = Object.getOwnPropertyDescriptor(user, 'id');139console.log(descriptor);140141// 定义多个属性142Object.defineProperties(user, {143 firstName: {144 get() {145 return this.name.split(' ')[0];146 },147 enumerable: true148 },149 lastName: {150 get() {151 return this.name.split(' ')[1];152 },153 enumerable: true154 }155});156157// 9. 对象冻结和密封158const frozenUser = Object.freeze({ ...user });159// frozenUser.name = 'Jane'; // 无效,对象被冻结160161const sealedUser = Object.seal({ ...user });162sealedUser.name = 'Jane'; // 可以修改现有属性163// sealedUser.newProp = 'value'; // 无效,不能添加新属性164165// 检查对象状态166console.log(Object.isFrozen(frozenUser)); // true167console.log(Object.isSealed(sealedUser)); // true168console.log(Object.isExtensible(user)); // true169170// 10. 对象比较171function objectsEqual(obj1, obj2) {172 const keys1 = Object.keys(obj1);173 const keys2 = Object.keys(obj2);174 175 if (keys1.length !== keys2.length) {176 return false;177 }178 179 for (const key of keys1) {180 if (!keys2.includes(key)) {181 return false;182 }183 184 if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {185 if (!objectsEqual(obj1[key], obj2[key])) {186 return false;187 }188 } else if (obj1[key] !== obj2[key]) {189 return false;190 }191 }192 193 return true;194}195196// 11. 对象转换197// 转换为数组198const userEntries = Object.entries(user);199const userKeys = Object.keys(user);200const userValues = Object.values(user);201202// 从数组创建对象203const objFromEntries = Object.fromEntries([204 ['name', 'John'],205 ['age', 30],206 ['city', 'New York']207]);208209// 12. 对象方法链210const FluentObject = {211 data: {},212 213 set(key, value) {214 this.data[key] = value;215 return this; // 返回this以支持链式调用216 },217 218 get(key) {219 return this.data[key];220 },221 222 delete(key) {223 delete this.data[key];224 return this;225 },226 227 clear() {228 this.data = {};229 return this;230 },231 232 toJSON() {233 return JSON.stringify(this.data);234 }235};236237// 链式调用238const result = FluentObject239 .set('name', 'John')240 .set('age', 30)241 .set('city', 'New York')242 .delete('age')243 .toJSON();244245console.log(result); // {"name":"John","city":"New York"}原型链详解
原型链完整解析
javascript
1// 1. 原型链基础概念2function Animal(name) {3 this.name = name;4}56Animal.prototype.speak = function() {7 return `${this.name} makes a sound`;8};910Animal.prototype.eat = function() {11 return `${this.name} is eating`;12};1314function Dog(name, breed) {15 Animal.call(this, name); // 调用父构造函数16 this.breed = breed;17}1819// 设置原型链20Dog.prototype = Object.create(Animal.prototype);21Dog.prototype.constructor = Dog;2223// 添加Dog特有的方法24Dog.prototype.bark = function() {25 return `${this.name} barks: Woof!`;26};2728// 重写父类方法29Dog.prototype.speak = function() {30 return `${this.name} barks`;31};3233const dog = new Dog('Buddy', 'Golden Retriever');3435console.log(dog.name); // 'Buddy'36console.log(dog.bark()); // 'Buddy barks: Woof!'37console.log(dog.speak()); // 'Buddy barks' (重写的方法)38console.log(dog.eat()); // 'Buddy is eating' (继承的方法)3940// 2. 原型链查找过程41console.log('=== 原型链查找 ===');42console.log(dog.hasOwnProperty('name')); // true (实例属性)43console.log(dog.hasOwnProperty('bark')); // false (原型方法)4445// 原型链:dog -> Dog.prototype -> Animal.prototype -> Object.prototype -> null46console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true47console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true48console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype); // true49console.log(Object.getPrototypeOf(Object.prototype) === null); // true5051// 3. instanceof 操作符52console.log(dog instanceof Dog); // true53console.log(dog instanceof Animal); // true54console.log(dog instanceof Object); // true5556// 4. 原型方法57// Object.create() 创建对象并设置原型58const animalPrototype = {59 speak() {60 return `${this.name} makes a sound`;61 },62 63 eat() {64 return `${this.name} is eating`;65 }66};6768const cat = Object.create(animalPrototype);69cat.name = 'Whiskers';70cat.meow = function() {71 return `${this.name} meows`;72};7374console.log(cat.speak()); // 'Whiskers makes a sound'75console.log(cat.meow()); // 'Whiskers meows'7677// 5. 原型污染示例(需要避免)78// 不要这样做!79// Object.prototype.customMethod = function() {80// return 'This affects all objects';81// };8283// 6. 现代类语法与原型84class ModernAnimal {85 constructor(name) {86 this.name = name;87 }88 89 speak() {90 return `${this.name} makes a sound`;91 }92 93 eat() {94 return `${this.name} is eating`;95 }96 97 static getSpecies() {98 return 'Unknown species';99 }100}101102class ModernDog extends ModernAnimal {103 constructor(name, breed) {104 super(name);105 this.breed = breed;106 }107 108 bark() {109 return `${this.name} barks: Woof!`;110 }111 112 speak() {113 return `${this.name} barks`;114 }115 116 static getSpecies() {117 return 'Canis lupus';118 }119}120121const modernDog = new ModernDog('Max', 'Labrador');122123// 类语法实际上也是基于原型的124console.log(modernDog instanceof ModernDog); // true125console.log(modernDog instanceof ModernAnimal); // true126console.log(ModernDog.prototype.isPrototypeOf(modernDog)); // true127128// 7. 原型方法的动态添加129ModernAnimal.prototype.sleep = function() {130 return `${this.name} is sleeping`;131};132133// 所有实例都会获得新方法134console.log(modernDog.sleep()); // 'Max is sleeping'135136// 8. 原型链的实际应用 - 扩展内置对象137// 注意:在生产环境中要谨慎扩展内置对象138Array.prototype.last = function() {139 return this[this.length - 1];140};141142const numbers = [1, 2, 3, 4, 5];143console.log(numbers.last()); // 5144145// 9. 原型链调试146function debugPrototypeChain(obj) {147 const chain = [];148 let current = obj;149 150 while (current) {151 chain.push(current.constructor.name || 'Object');152 current = Object.getPrototypeOf(current);153 }154 155 return chain.join(' -> ');156}157158console.log(debugPrototypeChain(modernDog)); 159// 'ModernDog -> ModernAnimal -> Object'160161// 10. 原型链性能考虑162// 深层原型链会影响属性查找性能163function createDeepChain(depth) {164 let proto = {};165 166 for (let i = 0; i < depth; i++) {167 const newProto = Object.create(proto);168 newProto[`prop${i}`] = `value${i}`;169 proto = newProto;170 }171 172 return proto;173}174175const deepObject = createDeepChain(100);176console.time('deep property access');177for (let i = 0; i < 10000; i++) {178 deepObject.prop0; // 需要遍历整个原型链179}180console.timeEnd('deep property access');181182// 11. 原型链的安全性183// 防止原型污染184function safeObjectCreate(proto) {185 if (proto === null || typeof proto === 'object') {186 return Object.create(proto);187 }188 throw new Error('Prototype must be an object or null');189}190191// 12. 混入模式(Mixin)192const Flyable = {193 fly() {194 return `${this.name} is flying`;195 }196};197198const Swimmable = {199 swim() {200 return `${this.name} is swimming`;201 }202};203204// 创建具有多种能力的对象205function createDuck(name) {206 const duck = Object.create(animalPrototype);207 duck.name = name;208 209 // 混入多个能力210 Object.assign(duck, Flyable, Swimmable);211 212 return duck;213}214215const duck = createDuck('Donald');216console.log(duck.speak()); // 'Donald makes a sound'217console.log(duck.fly()); // 'Donald is flying'218console.log(duck.swim()); // 'Donald is swimming'面试题
1. 解释JavaScript中的变量提升
答案: 变量提升是JavaScript引擎在执行代码前,将变量和函数声明移动到其作用域顶部的行为。
- var声明:变量声明被提升,但赋值不会提升,初始值为undefined
- let/const声明:声明被提升,但存在暂时性死区,访问会报错
- 函数声明:整个函数定义都会被提升
- 函数表达式:只有变量声明被提升,函数定义不会
2. 什么是闭包?闭包有什么用途?
答案: 闭包是指内部函数可以访问外部函数作用域中变量的特性,即使外部函数已经执行完毕。
用途:
- 数据封装和私有变量
- 模块模式
- 函数工厂
- 回调函数中保持状态
- 记忆化和缓存
3. 解释JavaScript的原型链
答案:
原型链是JavaScript实现继承的机制。每个对象都有一个内部属性[[Prototype]],指向其原型对象。当访问对象属性时,如果对象本身没有该属性,就会沿着原型链向上查找。
查找顺序:对象本身 → 对象原型 → 原型的原型 → ... → Object.prototype → null
4. this关键字的绑定规则是什么?
答案: this的绑定遵循以下规则(优先级从高到低):
- new绑定:使用new调用函数时,this绑定到新创建的对象
- 显式绑定:使用call、apply、bind时,this绑定到指定对象
- 隐式绑定:作为对象方法调用时,this绑定到该对象
- 默认绑定:独立函数调用时,this绑定到全局对象(严格模式下为undefined)
箭头函数不遵循这些规则,它的this由词法作用域决定。
5. 如何实现对象的深拷贝?
答案:
javascript
1function deepClone(obj) {2 if (obj === null || typeof obj !== 'object') {3 return obj;4 }5 6 if (obj instanceof Date) {7 return new Date(obj.getTime());8 }9 10 if (obj instanceof Array) {11 return obj.map(item => deepClone(item));12 }13 14 if (typeof obj === 'object') {15 const clonedObj = {};16 for (const key in obj) {17 if (obj.hasOwnProperty(key)) {18 clonedObj[key] = deepClone(obj[key]);19 }20 }21 return clonedObj;22 }23}其他方法:
JSON.parse(JSON.stringify(obj))(有限制)- 使用Lodash的
cloneDeep - 使用structuredClone(现代浏览器)
通过掌握这些JavaScript核心概念,可以编写更高质量、更可维护的代码,并为学习更高级的JavaScript特性打下坚实基础。
参与讨论