与浏览器JavaScript的区别
很多前端同学学Node.js时会有困惑:"这跟浏览器里的JavaScript有啥区别?" 说实话,区别挺大的。这节课就给你讲清楚,免得到处踩坑。
一、 运行环境不同
浏览器JavaScript
- 跑在Chrome、Firefox、Safari等浏览器里
- 受限于浏览器提供的API
- 无法操作系统文件
- 有同源策略安全限制
Node.js JavaScript
- 跑在服务器上(命令行里)
- 可以操作本地文件系统
- 可以监听网络端口
- 没有浏览器安全限制
二、 全局对象不同
浏览器有
// window - 浏览器窗口对象 window.document // DOM window.alert() // 弹窗 window.location // URL信息
// navigator - 浏览器信息 navigator.userAgent
// history - 浏览历史 history.back()
Node.js有
// global - 全局对象(Node.js独有的) global.console global.setTimeout
// process - 进程对象(Node.js独有的) process.argv // 命令行参数 process.env // 环境变量 process.cwd() // 当前工作目录 process.exit() // 退出进程
// dirname 和 filename(Node.js独有) console.log(dirname); // 当前文件所在目录 console.log(filename); // 当前文件完整路径
三、 模块系统
浏览器(旧版)
以前浏览器里没有模块系统,只能用:
// 1. script标签引入(全局污染)
// 2. 命名空间(容易冲突) var MyApp = {}; MyApp.utils = {};
Node.js - CommonJS
Node.js自带模块系统:
// 导出 module.exports = { name: '小明' };
// 或者 exports.name = '小明';
// 导入 const myModule = require('./myModule');
浏览器(现代)- ES Modules
现在浏览器也支持ES Modules了:
// 导出 export function sayHello() {}
// 导入 import { sayHello } from './module.js';
四、 API差异
DOM操作 - 只有浏览器有
// 这些在Node.js里会报错 document.querySelector('.box') element.innerHTML = 'hello' window.alert('hi')
文件操作 - 只有Node.js有
// 这些在浏览器里会报错 const fs = require('fs'); fs.readFileSync('a.txt') fs.writeFileSync('b.txt', 'content')
console对象 - 两者都有,但有点区别
// 浏览器console console.log('显示在控制台'); console.warn('警告'); console.error('错误'); console.table([{a:1}, {b:2}]); // 浏览器特有
// Node.js console console.log('显示在终端'); console.time('test'); console.timeEnd('test'); console.trace(); // 打印堆栈
五、 异步编程
两边都用异步,但场景不同。
浏览器 - 主要是事件和回调
// setTimeout/setInterval setTimeout(() => {}, 1000);
// AJAX / Fetch fetch('/api/data').then(r => r.json());
// 事件监听 button.addEventListener('click', () => {});
Node.js - 事件 + 回调 + Promise
// 读取文件 const fs = require('fs'); fs.readFile('a.txt', 'utf8', (err, data) => {});
// Promise方式 fs.promises.readFile('a.txt', 'utf8').then(data => {});
// 或者用await const data = await fs.promises.readFile('a.txt', 'utf8');
// EventEmitter const EventEmitter = require('events'); const emitter = new EventEmitter(); emitter.on('event', () => {});
六、 Error处理
浏览器
try { JSON.parse('invalid'); } catch (e) { console.error(e.message); // 浏览器里错误不会导致程序崩溃 }
Node.js
try { JSON.parse('invalid'); } catch (e) { console.error(e.message); }
// 未捕获的异常会中断程序 process.on('uncaughtException', (err) => { console.error('程序要挂了!', err); process.exit(1); });
七、 模块查找规则
浏览器(ES Modules)
import axios from 'axios'; // 找 node_modules/axios // 或者 ../node_modules/axios
必须加路径或者包名,不能相对路径缩写。
Node.js
const axios = require('axios'); // 自动找: // 1. 内置模块(fs, path, http) // 2. node_modules/axios // 3. ../node_modules/axios
更智能,./相对路径也可以。
八、 this的指向
浏览器
// 函数内部this指向window(严格模式除外) function test() { console.log(this); // window }
// 箭头函数this继承外层 const arrow = () => { console.log(this); // window };
Node.js
// 函数内部this是undefined(严格模式) function test() { console.log(this); // undefined }
// 全局this是global对象 global.console.log(global === this); // true
// 箭头函数this不改变 const arrow = () => { console.log(this); // 外层的this };
九、 Buffer和二进制
浏览器
浏览器里用ArrayBuffer和TypedArrays:
const buffer = new ArrayBuffer(8); const view = new Int32Array(buffer);
Node.js
Node.js有额外的Buffer类:
const buf = Buffer.alloc(8); // 分配8字节 buf.write('hello'); console.log(buf.toString());
// 处理文件、图片等二进制数据特别方便
十、 总结对比表
| 特性 | 浏览器JS | Node.js |
|---|---|---|
| 运行环境 | 浏览器 | 服务器/命令行 |
| DOM | ✅ 有 | ❌ 没有 |
| 文件系统 | ❌ 没有 | ✅ 有 |
| 网络请求 | fetch/ajax | http/axios |
| 模块系统 | ES Modules | CommonJS + ESM |
| 全局对象 | window | global |
| Buffer | ❌ | ✅ |
| process | ❌ | ✅ |
| __dirname | ❌ | ✅ |
| console | 有 | 有(更丰富) |
十一、 代码复用
虽然有区别,但很多代码是可以共用的:
// 工具函数可以两边跑 function formatDate(date) { return new Date(date).toLocaleDateString(); }
function validateEmail(email) { return /^[^\s@]+@[^\s@]+.[^\s@]+$/.test(email); }
// 业务逻辑可以复用 function calculateTotal(items) { return items.reduce((sum, item) => sum + item.price, 0); }
这些函数既可以在浏览器里用,也可以在Node.js里用。
十二、 学完能做什么
- 浏览器JS:做网页特效、交互、Vue/React开发
- Node.js:写后端服务、CLI工具、构建工具
两者结合,就是全栈工程师!
下节课我们继续聊下一个主题,敬请期待!