Skip to content

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 / GPU

Native ​

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,所以行为完全相同 ​

底层运行时、平台能力和资源生命周期都可能不同。


十、练习与答案 ​

  1. JavaScript Engine 和 JSB 分别负责什么?
  2. Cocos2d-x 与 Creator JavaScript 层是什么关系?
  3. 为什么 Web 正常不能证明 Native 一定正常?
  4. 一次 JS 调用 C++ 可能经过哪些步骤?

答案:

  1. JavaScript Engine 执行脚本并管理 JS 对象;JSB 负责 JS 与 C++ 之间的绑定和数据转换。
  2. JavaScript 层提供 Creator 运行时和业务接口,C++ 层提供许多底层引擎与平台能力。
  3. 两者使用不同运行时、文件系统、线程、渲染和生命周期实现。
  4. 查找绑定函数、检查参数、类型转换、调用 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 只在对应原生运行时中存在。业务代码应通过声明/适配层访问,不应在每个模块直接假设全局对象存在。

实验步骤 ​

  1. 在 Web 预览运行一次。
  2. 通过 Creator 构建同一 Native 测试包。
  3. 用同一账号和动作读写一条设置、加载一张图、播放音频、切换场景。
  4. 分别记录公开 API 入口、结果、错误、最终路径和环境。
  5. 清缓存后再做一次冷启动。

记录表:

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。

  1. JSB 是什么? JavaScript 与 Native/C++ 能力之间的绑定/调用边界。
  2. 相同 TS 为什么行为不同? 执行引擎、平台 API、资源布局、权限、线程和图形后端不同。
  3. 能看到 jsb 就能断言所有 API 都走 Native 吗? 不能,必须追踪具体入口和实现。
  4. 为什么要做适配层? 集中平台差异、错误、权限和线程处理,避免业务散落环境分支。

十一、本课总结 ​

text
TypeScript 最终变成 JavaScript。
JavaScript Engine 负责执行。
JSB 连接 JavaScript 与 C++。
C++ Engine 连接渲染和平台能力。
Web 与 Native 的公共 API 不等于完全相同的底层行为。

十二、下一课预告 ​

text
Lesson074|一个 Creator API 如何沿调用链进入 Native