JavaScript 跨平台运行时差异
JavaScript 可运行在浏览器、Node.js、Deno、Bun 等多种运行时环境,各环境存在显著差异。
主流运行时环境
| 运行时 | 主要场景 | 引擎 |
|---|---|---|
| Chrome | 浏览器 | V8 |
| Firefox | 浏览器 | SpiderMonkey |
| Safari | 浏览器 | JavaScriptCore |
| Node.js | 服务端 | V8 |
| Deno | 服务端 | V8 |
| Bun | 服务端 | JavaScriptCore |
全局对象差异
浏览器环境
JavaScript
// 全局对象
window.alert('Hello');
document.getElementById('app');
navigator.userAgent;
// this 指向
console.log(this); // window
Node.js 环境
JavaScript
// 全局对象
global.console.log('Hello');
process.env.NODE_ENV;
Buffer.from('data');
// 模块中的 this
console.log(this); // {}(模块级)
统一访问方式
JavaScript
// 获取全局对象的通用方法
const globalThis =
typeof window !== 'undefined' ? window :
typeof global !== 'undefined' ? global :
typeof self !== 'undefined' ? self :
this;
// ES2020 标准化
console.log(globalThis);
模块系统差异
CommonJS(Node.js)
JavaScript
// 导出
module.exports = { foo, bar };
// 或
exports.foo = foo;
// 导入
const { foo, bar } = require('./module');
ES Modules(浏览器/现代Node)
JavaScript
// 导出
export const foo = 1;
export default class Foo {}
// 导入
import { foo } from './module.js';
import Foo from './module.js';
差异对比
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 加载时机 | 运行时 | 编译时 |
| 输出 | 值的拷贝 | 值的引用 |
| this | {} | undefined |
| 循环依赖 | 可能出问题 | 静态分析 |
API 差异
DOM API
JavaScript
// 浏览器独有
document.querySelector('.app');
localStorage.setItem('key', 'value');
fetch('/api/data');
alert('Hello');
// Node.js 替代方案
// 使用 jsdom、node-fetch 等库
Node.js 核心 API
JavaScript
// Node.js 独有
const fs = require('fs');
const path = require('path');
const http = require('http');
// 读写文件
fs.readFileSync('./file.txt', 'utf-8');
// 创建服务器
http.createServer((req, res) => {
res.end('Hello');
}).listen(3000);
共有 API
JavaScript
// 各环境都支持
console.log('Hello');
setTimeout(() => {}, 1000);
setInterval(() => {}, 1000);
Promise.resolve();
Array.isArray([1, 2, 3]);
JSON.stringify({ a: 1 });
异步处理差异
setTimeout 精度
JavaScript
// 浏览器:最小 4ms(HTML5 规范)
setTimeout(() => {}, 0); // 实际约 4ms
// Node.js:可设置 1ms
setTimeout(() => {}, 1);
// Node.js 立即执行
setImmediate(() => {
// 下一次事件循环立即执行
});
// 浏览器替代方案
requestAnimationFrame(() => {
// 帧同步回调
});
微任务差异
JavaScript
// 两者一致
Promise.resolve().then(() => {
console.log('microtask');
});
// Node.js 特有
process.nextTick(() => {
console.log('nextTick');
// 优先于其他微任务
});
环境检测
检测浏览器
JavaScript
const isBrowser = typeof window !== 'undefined' &&
typeof document !== 'undefined';
检测 Node.js
JavaScript
const isNode = typeof process !== 'undefined' &&
process.versions &&
process.versions.node;
检测特定运行时
JavaScript
// Deno
const isDeno = typeof Deno !== 'undefined';
// Bun
const isBun = typeof Bun !== 'undefined';
// Electron
const isElectron = process.versions.electron !== undefined;
跨平台兼容策略
特性检测
JavaScript
// ✅ 检测特性是否存在
if (typeof fetch === 'function') {
fetch('/api/data');
} else {
// 使用 polyfill 或替代方案
xhrRequest('/api/data');
}
// ❌ 不要检测浏览器
if (navigator.userAgent.includes('Chrome')) {
// 不可靠
}
Polyfill
JavaScript
// 使用 polyfill 填充缺失功能
if (typeof Promise === 'undefined') {
require('promise-polyfill');
}
if (typeof Object.assign !== 'function') {
Object.assign = function(target, ...sources) {
sources.forEach(source => {
Object.keys(source).forEach(key => {
target[key] = source[key];
});
});
return target;
};
}
条件导出(package.json)
JSON
{
"exports": {
".": {
"browser": "./dist/browser.js",
"node": "./dist/node.js",
"default": "./dist/index.js"
}
}
}
Buffer vs ArrayBuffer
Node.js Buffer
JavaScript
// Node.js
const buf = Buffer.from('Hello', 'utf-8');
console.log(buf.toString());
console.log(buf.length); // 字节长度
浏览器 ArrayBuffer
JavaScript
// 浏览器
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
view[0] = 72; // 'H'
转换方法
JavaScript
// Node.js 15+
const { Buffer } = require('buffer');
const arrayBuffer = buffer.buffer;
// 浏览器环境
const buffer = Buffer.from(arrayBuffer);
要点总结
| 差异类型 | 浏览器 | Node.js |
|---|---|---|
| 全局对象 | window | global |
| 模块系统 | ES Modules | CommonJS/ESM |
| 文件系统 | 无 | fs 模块 |
| DOM | 有 | 无 |
| Buffer | ArrayBuffer | Buffer |
- 使用
globalThis获取全局对象 - 优先使用特性检测而非环境检测
- 编写库时考虑多种导出格式
- 注意 setTimeout 的精度差异
- 使用 package.json exports 做条件导出
📝 发现内容有误?点击此处直接编辑