外观
Stage06|JSB & Native Bridge
Lesson073|JavaScript Engine、JSB 与 Cocos2d-x 的关系
Tags: #Creator2.x #Stage06 #JavaScriptEngine #JSB #Cocos2dxDifficulty: ⭐⭐⭐⭐☆
真实问题:同一段 TypeScript,为什么 Web 和 Native 行为不同
代码在浏览器能保存设置,原生包却返回路径错误;WebGL 能播放音频,iOS 首次点击没有声音;Native 性能更好,却在回调销毁后崩溃。
“TypeScript 一样”只说明业务源码一样,不代表执行链一样:
text
TypeScript/JavaScript
→ JavaScript Engine
→ Creator 运行时封装
→ JSB(Native 时)
→ C++ 引擎/平台适配
→ OS/GPU/文件/网络Web 可能是 JavaScript Engine 直接访问 Web API、WebGL 和浏览器安全模型。JSB 是边界,不是一个“Native 模式开关函数”。
一、本课目标
前面课程已经从 API 追踪到 Engine Loop、资源、Renderer 和 GPU。本课继续向下追问:
Creator 2.x 的 JavaScript 代码在原生包里由谁执行,又如何调用 C++ 引擎和平台能力?
完成本课后,你应该能够:
- 区分 TypeScript、JavaScript Engine、JSB、C++ Engine 和平台 API。
- 解释 Web 与 Native 为什么共用业务代码却走不同底层路径。
- 理解 JSB 是绑定桥梁,不是网络协议或独立线程。
- 画出一条 Creator API 进入原生层的高层调用链。
二、先建立整体分层
text
TypeScript 源码
↓ 编译 / 构建
JavaScript 业务代码
↓
JavaScript Engine 执行
↓
JSB / Binding 转换调用和数据
↓
C++ Engine / Cocos2d-x
↓
Renderer / Audio / File / Platform API
↓
操作系统和 GPU这张图是高层模型。Creator 2.4.x 不同平台和功能模块的实际实现可能不完全相同,阅读源码时要确认当前版本、构建目标和宏分支。
三、TypeScript 最终由谁执行
开发时写:
ts
this.node.x = 100;TypeScript 会经过编译或转换,最终由 JavaScript 运行环境执行。Native 包中不是操作系统直接理解 TypeScript,而是:
text
TypeScript
→ JavaScript
→ JavaScript Engine 解析或执行JavaScript Engine 负责:
- 执行 JavaScript。
- 管理 JS 对象和作用域。
- 提供 GC。
- 调用已经注册到 JS 环境中的原生函数。
- 抛出和传播脚本异常。
Creator 2.x Native 常见运行环境与 SpiderMonkey、se::ScriptEngine 等实现有关,但具体版本必须以项目使用的 Creator 引擎源码为准。
四、JSB 是什么
JSB 可以理解为 JavaScript 和 C++ 之间的绑定层:
text
JavaScript Value
↓ 类型检查和转换
C++ Parameter
↓ 调用
Native Function
↓ 返回值转换
JavaScript Value它解决的问题包括:
- 把 C++ 类和函数暴露给 JavaScript。
- 把数字、字符串、对象和数组转换为 C++ 类型。
- 建立 JS 对象与 C++ 对象的映射。
- 处理返回值和异常。
- 协调两边对象生命周期。
JSB 不是:
- 一个后台线程。
- 一种网络传输协议。
- 自动让所有 C++ API 都能被 JavaScript 调用。
- 完全没有成本的函数跳转。
五、Cocos2d-x 在哪里
Creator 2.x Native 的许多底层能力来自或构建在 Cocos2d-x 和原生引擎代码之上,例如:
text
文件系统
纹理和渲染
音频
平台生命周期
原生视图
网络或下载辅助能力Creator 的 JavaScript 层提供组件化、编辑器数据和业务接口;C++ 层更接近平台、渲染和原生资源。
不能简单理解为“所有 Node 都在 C++,JavaScript 只存一个壳”。不同版本、模块和渲染架构可能在 JS 与 Native 两侧分配不同职责。
六、Web 和 Native 的路径差异
Web
text
JavaScript
→ Browser JavaScript Engine
→ Web APIs / WebGL
→ Browser / GPUNative
text
JavaScript
→ Embedded JavaScript Engine
→ JSB / Binding
→ C++ Engine / Platform APIs
→ GPU / OS业务 API 看起来一致,但下面这些行为可能不同:
- 文件路径和权限。
- 线程和异步回调。
- 纹理格式与上传。
- 音频实现。
- JavaScript Engine 性能与 GC。
- 原生对象生命周期。
所以 Web 预览通过不代表 Native 一定没有问题。
七、一个简化调用示例
假设 JavaScript 调用一个原生方法:
ts
nativeBridge.saveText('profile.json', content);高层过程:
text
JavaScript 查找 nativeBridge.saveText
↓
进入绑定函数
↓
检查参数数量和类型
↓
把 JS string 转为 C++ string
↓
调用原生文件实现
↓
把成功、失败或异常转换回 JS真实项目还要处理线程、权限、编码、路径、错误码和平台差异。
八、为什么要理解这条链
出现以下问题时,只看 TypeScript 往往不够:
text
Web 正常,Native 崩溃
Native 文件读取失败
频繁原生调用导致卡顿
JS 对象已回收但 C++ 仍回调
C++ 对象销毁后 JS 仍持有代理
不同平台返回值不一致需要判断问题发生在:
text
业务代码
JavaScript Engine
Binding 参数转换
C++ Engine
平台实现九、常见误区
误区一:TypeScript 在 Native 中直接编译成机器码
Creator 2.x 主线中通常先形成 JavaScript,再由嵌入式 JavaScript Engine 执行。
误区二:JSB 会把所有 JavaScript 自动翻译成 C++
JSB 负责绑定调用和数据转换,不是整段业务代码翻译器。
误区三:跨 JSB 只是普通 JavaScript 函数调用
它可能包含参数检查、类型转换、对象映射和原生调用。
误区四:Web 与 Native 使用相同 API,所以行为完全相同
底层运行时、平台能力和资源生命周期都可能不同。
十、练习与答案
- JavaScript Engine 和 JSB 分别负责什么?
- Cocos2d-x 与 Creator JavaScript 层是什么关系?
- 为什么 Web 正常不能证明 Native 一定正常?
- 一次 JS 调用 C++ 可能经过哪些步骤?
答案:
- JavaScript Engine 执行脚本并管理 JS 对象;JSB 负责 JS 与 C++ 之间的绑定和数据转换。
- JavaScript 层提供 Creator 运行时和业务接口,C++ 层提供许多底层引擎与平台能力。
- 两者使用不同运行时、文件系统、线程、渲染和生命周期实现。
- 查找绑定函数、检查参数、类型转换、调用 Native、转换返回值或错误。
双环境实验:先确认执行链再解释差异
RuntimeProbe.ts
ts
const { ccclass } = cc._decorator;
@ccclass
export default class RuntimeProbe extends cc.Component {
start() {
cc.log(
'[runtime]',
'isNative=', cc.sys.isNative,
'platform=', cc.sys.platform,
'os=', cc.sys.os,
'engine=', cc.ENGINE_VERSION
);
const hasNativeGlobal =
typeof jsb !== 'undefined';
cc.log(
'[runtime] native global=',
hasNativeGlobal
);
}
}jsb 只在对应原生运行时中存在。业务代码应通过声明/适配层访问,不应在每个模块直接假设全局对象存在。
实验步骤
- 在 Web 预览运行一次。
- 通过 Creator 构建同一 Native 测试包。
- 用同一账号和动作读写一条设置、加载一张图、播放音频、切换场景。
- 分别记录公开 API 入口、结果、错误、最终路径和环境。
- 清缓存后再做一次冷启动。
记录表:
text
公开 API 入口
Web 结果/错误
Native 结果/错误
是否跨 JSB
权限/文件/网络条件实验目标不是把所有差异都归因 JSB,而是区分 Engine、平台 API、资源布局和权限。
Cocos2d-x 在链路中的位置
Cocos2d-x/原生引擎层通常负责 Node、Scene、Renderer、资源和平台适配等 Native 能力;Creator 脚本层通过绑定访问其中一部分。
不要把“C++ 实现”理解为每个 JavaScript 属性都立即跨一次边界。具体实现可能缓存字段、批量同步或由脚本层完成。稳定表述应是:
某 API 是否跨 JSB、跨几次、是否复制数据,要由当前版本实现和平台路径验证。
真实项目故障:Web 文件 API 直接带入 Native
开发者在业务层使用浏览器绝对 URL 与 Blob 逻辑,Native 没有同样的文件对象;随后在原生层用平台路径拼接,升级后又失效。
修复是定义能力接口:
ts
interface SaveService {
write(key: string, data: string): Promise<void>;
read(key: string): Promise<string | null>;
}Web/Native 各自实现,业务只依赖语义,不依赖路径和全局对象。
诊断顺序
text
确认 cc.sys.isNative 与平台
→ 查看资源/文件/网络最终路径
→ 查权限与构建配置
→ 确认 API 是否异步和回调线程
→ 固定 Creator/原生引擎版本
→ 对比 Web/Native 最小复现版本边界与练习答案
Creator 2.4.x 的 JSB、JS Engine 和 Cocos2d-x 绑定随平台构建方式变化;源码定位时固定引擎 tag/commit。
- JSB 是什么? JavaScript 与 Native/C++ 能力之间的绑定/调用边界。
- 相同 TS 为什么行为不同? 执行引擎、平台 API、资源布局、权限、线程和图形后端不同。
- 能看到 jsb 就能断言所有 API 都走 Native 吗? 不能,必须追踪具体入口和实现。
- 为什么要做适配层? 集中平台差异、错误、权限和线程处理,避免业务散落环境分支。
十一、本课总结
text
TypeScript 最终变成 JavaScript。
JavaScript Engine 负责执行。
JSB 连接 JavaScript 与 C++。
C++ Engine 连接渲染和平台能力。
Web 与 Native 的公共 API 不等于完全相同的底层行为。十二、下一课预告
text
Lesson074|一个 Creator API 如何沿调用链进入 Native