js零基础入门
下面是一份连贯完整的「JavaScript 从基础到进阶」Markdown 教程,按从易到难的学习路径组织,每节都配有要点与可运行代码示例。你可以直接复制为 docs/JS-Guide.md 使用。
JavaScript 从基础到进阶(附完整示例)
目录
- 语言概览与运行环境
- 值与类型系统
- 变量与作用域
- 表达式与运算符
- 流程控制
- 函数与参数
- this、call/apply/bind
- 原型、原型链与面向对象
- 对象与数组进阶
- 模块化(ES Modules)
- 异步:Promise、async/await、并发控制
- 事件循环、微任务与宏任务
- 迭代协议、生成器、迭代器
- 错误处理与异常机制
- 高级内置类型:Map/Set、Symbol、BigInt、TypedArray
- 函数式编程在 JS 中的实践
- 性能与内存:拷贝、闭包与垃圾回收
- 工程化与规范化(简述)
- 常见易错与最佳实践清单
- 附录:标准库速查与精选练习
1. 语言概览与运行环境
- JS 是动态、单线程、原型继承、弱类型(但有严格比较)、基于事件循环的语言。
- 运行环境:浏览器(Web APIs)、Node.js(fs、net 等)、Deno(更现代的权限模型)。
- 规范:ECMAScript(语言核心),各环境在其上提供宿主 API。
示例:浏览器与 Node 的差异
```js
// 浏览器
console.log(‘hello’); // OK
// alert(‘Hi’); // 浏览器独有
// Node.js
// const fs = require(‘fs’); // Node 独有 (CommonJS)
// import fs from ‘fs’; // ESM in Node 13+
---## 2. 值与类型系统原始类型:`number`、`string`、`boolean`、`null`、`undefined`、`symbol`、`bigint`
引用类型:`object`(含 `function`、`array`、`date` 等)类型检测:
```js
console.log(typeof 1); // 'number'
console.log(typeof null); // 'object' (历史包袱)
console.log(Array.isArray([])); // true
console.log(Object.prototype.toString.call(new Date())); // [object Date]
装箱与拆箱:
// 拆箱:对象转原始值
const obj = { valueOf() { return 42; } };
console.log(obj + 8); // 50// 装箱:原始值调用方法时临时装箱
console.log('abc'.toUpperCase()); // 'ABC'
3. 变量与作用域
var
:函数作用域、变量提升、可重复声明let
:块级作用域、暂时性死区(TDZ)const
:块级作用域、只读绑定(引用内容可变)
if (true) {var a = 1; // 函数级let b = 2; // 块级
}
console.log(a); // 1
// console.log(b); // ReferenceError// 暂时性死区
// console.log(x); // ReferenceError
let x = 10;
4. 表达式与运算符
-
相等:
==
会做类型转换,===
严格比较 -
可选链:
a?.b?.()
,空值合并:x ?? y
-
逻辑赋值:
||=、&&=、??=
console.log(0 == false); // true
console.log(0 === false); // false
const user = { profile: { name: ‘Tom’ } };
console.log(user.profile?.name?.toUpperCase());
let n = 0;
n ||= 100; // 因 0 为假,n -> 100
let m = null;
m ??= 42; // 因 null/undefined 才触发,m -> 42
---## 5. 流程控制```js
// if / switch
const level = 'gold';
switch(level) {case 'gold': console.log('VIP'); break;default: console.log('normal');
}// for / for..of / for..in(枚举键)
const arr = [10, 20, 30];
for (const v of arr) console.log(v);// try..catch..finally
function parseJSON(str) {try {return JSON.parse(str);} catch (e) {return null;}
}
6. 函数与参数
-
形参与实参、默认值、剩余参数、参数解构
-
箭头函数:无
this
、无arguments
、不能作构造器
function sum(a = 0, b = 0) { return a + b; }
console.log(sum(2)); // 2
function collect(…args) { return args.join(’,’); }
console.log(collect(1,2,3)); // 1,2,3
const f = ({x, y} = {x:0, y:0}) => x + y;
console.log(f({x:2, y:3})); // 5
---## 7. this、call/apply/bind- `this` 取决于调用位置(箭头函数除外)
- `call`/`apply` 修改调用时 `this`,`bind` 返回绑定后的新函数```js
function show() { console.log(this.name); }
const obj = { name:'A' };
show.call(obj); // 'A'const bound = show.bind({ name:'B' });
bound(); // 'B'// 箭头函数捕获词法 this
const mod = {name: 'M',run() {setTimeout(() => { console.log(this.name); }, 0); // 'M'}
};
mod.run();
8. 原型、原型链与面向对象
-
每个对象都有隐藏属性
[[Prototype]]
,通过Object.getPrototypeOf()
访问 -
class
是语法糖,底层仍是原型
function Person(name) { this.name = name; }
Person.prototype.say = function() { console.log(‘Hi’, this.name); };
const p = new Person(‘Tom’);
p.say(); // ‘Hi Tom’
console.log(Object.getPrototypeOf§ === Person.prototype); // true
class Animal {
constructor(name){ this.name = name; }
speak(){ console.log(${this.name} speaks
); }
}
class Dog extends Animal {
speak(){ super.speak(); console.log(‘wang’); }
}
new Dog(‘aa’).speak();
---## 9. 对象与数组进阶- 属性描述符、不可扩展/冻结、浅拷贝与深拷贝
- 解构、扩展运算符```js
const obj = {};
Object.defineProperty(obj, 'id', { value:1, writable:false });
console.log(obj.id); // 1
// obj.id = 2; // 严格模式下报错// 浅拷贝
const a = { nested: { x:1 } };
const b = { ...a };
a.nested.x = 9;
console.log(b.nested.x); // 9// 深拷贝(简法):结构可 JSON 化时
const deep = JSON.parse(JSON.stringify(a));
10. 模块化(ES Modules)
-
export
/export default
/import
-
注意:浏览器 ESM 需要
<script type="module">
或打包工具
// math.js
export const PI = 3.14;
export default function area® { return PIrr; }
// main.js
import area, { PI } from ‘./math.js’;
console.log(PI, area(2));
---## 11. 异步:Promise、async/await、并发控制```js
// Promise 基础
function delay(ms) {return new Promise(resolve => setTimeout(resolve, ms));
}delay(500).then(()=>console.log('after 0.5s'));// async/await & 错误处理
async function getData() {try {const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');return await res.json();} catch (e) {return null;}
}// 并发控制:一次并发 2 个任务
async function parallelLimit(tasks, limit = 2) {const ret = []; let i = 0;const workers = new Array(limit).fill(0).map(async () => {while (i < tasks.length) {const cur = i++;ret[cur] = await tasks[cur]();}});await Promise.all(workers);return ret;
}
12. 事件循环、微任务与宏任务
-
宏任务:setTimeout、setInterval、I/O 等
-
微任务:Promise.then、queueMicrotask、MutationObserver
console.log(‘start’);
setTimeout(()=>console.log(‘timeout’)); // 宏任务
Promise.resolve().then(()=>console.log(‘micro’)); // 微任务
console.log(‘end’);
// 输出:start -> end -> micro -> timeout
---## 13. 迭代协议、生成器、迭代器```js
// 可迭代对象:实现 [Symbol.iterator]
const iterable = {data: [1,2,3],[Symbol.iterator]() {let i = 0, self = this;return {next() {return i < self.data.length? { value: self.data[i++], done:false }: { done:true };}}}
};
for (const v of iterable) console.log(v);// 生成器
function* gen() {yield 1; yield 2; return 3;
}
const g = gen();
console.log(g.next(), g.next(), g.next());
14. 错误处理与异常机制
-
try/catch/finally
、throw
自定义错误 -
全局未捕获:Node 的
process.on('uncaughtException')
、浏览器window.onerror
class AppError extends Error {
constructor(msg, code) { super(msg); this.code = code; }
}
function parse(str) {
if (typeof str !== ‘string’) throw new AppError(‘bad type’, 400);
return JSON.parse(str);
}
try {
parse(42);
} catch(e) {
if (e instanceof AppError) console.log(e.code, e.message);
}
---## 15. 高级内置类型:Map/Set、Symbol、BigInt、TypedArray```js
// Map / Set
const map = new Map([['a',1]]);
map.set({id:1}, 'objKey');
console.log(map.size);const set = new Set([1,2,2,3]);
console.log([...set]); // [1,2,3]// Symbol 作私有/唯一键
const k = Symbol('secret');
const o = { [k]: 123 };
console.log(Object.getOwnPropertySymbols(o)); // [Symbol(secret)]// BigInt
const big = 9007199254740991n + 1n;
console.log(big);// TypedArray
const buf = new ArrayBuffer(8);
const view = new Int32Array(buf);
view[0] = 42;
console.log(view[0]);
16. 函数式编程在 JS 中的实践
-
纯函数、不可变数据、组合
-
高阶函数:map/filter/reduce、compose/pipe
const add = x => y => x + y;
const inc = add(1);
console.log([1,2,3].map(inc)); // [2,3,4]
// 组合
const compose = (…fns) => x => fns.reduceRight((v,f)=>f(v), x);
const pipe = (…fns) => x => fns.reduce((v,f)=>f(v), x);
const trim = s => s.trim();
const toLower = s => s.toLowerCase();
const slug = pipe(trim, toLower, s=>s.replace(/\s+/g,’-’));
console.log(slug(’ Hello World ')); // ‘hello-world’
---## 17. 性能与内存:拷贝、闭包与垃圾回收- 闭包引用外层变量可能导致不必要的持有
- 避免在热路径创建大对象/正则;注意去抖/节流```js
// 去抖
function debounce(fn, wait=200) {let timer;return function(...args){clearTimeout(timer);timer = setTimeout(()=>fn.apply(this,args), wait);}
}
18. 工程化与规范化(简述)
- 代码规范:ESLint + Prettier
- 构建/打包:Vite/Rollup/Webpack
- 类型:JSDoc 注释或 TypeScript 渐进引入
- 单元测试:Vitest/Jest + @testing-library
示例 ESLint 简配置(.eslintrc.json):
{"env": { "es2022": true, "browser": true, "node": true },"extends": ["eslint:recommended"],"parserOptions": { "sourceType": "module" },"rules": {"no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],"eqeqeq": "error"}
}
19. 常见易错与最佳实践清单
- 一律用
===
,除非刻意使用==
差异(比如:x == null
同时判断null
/undefined
) - 优先
let/const
,不用var
- 尽量避免在循环里创建函数捕获迭代变量(使用
let
块作用域或 IIFE) - 使用可选链与空值合并减少
undefined
宕机 - Promise 链必须有错误处理;
async
函数外层也要try/catch
- 深拷贝注意循环引用(考虑
structuredClone
或库)
20. 附录:标准库速查与精选练习
Array 常用
```js
[1,2,3].map(x=>x*2);
[1,2,3].filter(x=>x>1);
[1,2,3].reduce((a,b)=>a+b,0);
[1,2,3].find(x=>x===2);
[1,2,3].some(x=>x>2); // true
[1,2,3].every(x=>x>0); // true
[1,2,3].flatMap(x=>[x,x]); // [1,1,2,2,3,3]
### Object 常用```js
Object.keys({a:1,b:2}); // ['a','b']
Object.values({a:1,b:2}); // [1,2]
Object.entries({a:1,b:2}); // [['a',1],['b',2]]
Object.fromEntries([['a',1],['b',2]]); // {a:1,b:2}
练习建议(由浅入深)
- 实现
once(fn)
:函数只执行一次 - 手写
debounce/throttle
- 实现
compose/pipe
- 实现
Promise.all/race/allSettled
- 用生成器实现一个无限斐波那契序列迭代器
- 写一个并发池
pLimit
(限制并发数量)
示例:once
```js
function once(fn){
let called = false, value;
return function(…args){
if (!called) { called = true; value = fn.apply(this,args); }
return value;
}
}
const init = once(()=>console.log(‘init’));
init(); init(); // 只打印一次
---