能让 JS 执行的 JavascriptCore ,到底是啥

2022-07-29 08:34:34 浏览数 (2)

在这篇文章中我们知道,ISO 版微信小程序逻辑层中的 JavaScript 代码运行在 JavaScriptCore 中,那么 JavascriptCore 到底有什么神奇的地方,能让 JS 在 IOS 中执行呢?

Swift 自 2014 年推出以来,人气飙升,但是 JavaScript 是一种与 Swift 完全相反的语言,比如 Swift 在编译时做了很多保障安全性的措施,而 JavaScript 则是一门弱类型语言,它只在执行时运行。可能它们两个也没想到有一天能够一起协作,制作一个流畅的 iOS 应用程序! 但是,你知道为什么 JS 能在 IOS 中运行吗?本篇文章,我们就来说说能让 JS 在 IOS 运行的 JavascriptCore 框架到底是什么。你将了解到以下知识点:

  • JavaScriptCore 框架的组成。
  • 如何从 iOS/Swift 代码中调用 JavaScript 。
  • 如何从 JavaScript 访问 IOS/Swift 代码。

开始

JavaScriptCore 框架提供对 WebKit 的 JavaScript 引擎的访问。最初,该框架有一个仅限 Mac 的 C API,但 iOS 7 和 OS X 10.9 附带了一个更好的 Objective-C 包装器。该框架能够使你的 Swift/Objective-C 和 JavaScript 代码之间具有强大的操作性。

在底层,JavaScriptCore 由几个关键组件组成:JSVirtualMachine、JSContext 和 JSValue。那它们是如何组合在一起的呢?往下看。

JSVirtualMachine:JS 虚拟机

JavaScript 代码在由 JSVirtualMachine 类表示的虚拟机中执行。你通常不必直接与此类交互,但它有一个主要作用:能够支持 JavaScript 并发执行。由于在单个 JSVirtualMachine 中,不可能同时执行多个线程。因此,为了支持并行性,必须使用多个虚拟机。 JSVirtualMachine 的每个实例都有自己的堆和垃圾收集器,这意味着你不能在虚拟机之间传递对象。因为,如果你那样做的话,虚拟机的垃圾收集器会不知道如何处理来自不同堆的值。

JSContext

JSContext 对象代表 JavaScript 代码的执行环境。 它对应于单个全局对象它如同 Web 开发中的 window 对象。与虚拟机不同,你可以在上下文之间自由传递对象(假设它们位于同一虚拟机中)。

JSValue

JSValue 是你必须使用的主要数据类型:它可以表示任何可能的 JavaScript 值JSValue 的实例与它所在的 JSContext 对象相关联来自上下文对象的任何值都将是 JSValue 类型。 下图显示了上述每个部分之间是如何协同工作的:

image.png

现在你对 JavaScriptCore 框架中已经有了初步的了解。接下来,我们来看看 IOS 代码与 JS 代码之间是如何调用的。

在 IOS 中调用 JS 代码

在 IOS 中要调用 JS,首先要在 swift 文件顶部引入 JavaScriptCore 包:

代码语言:javascript复制
import JavaScriptCore

接着,你可以在 Swift 代码中创建一个 JSContext 对象,使用这个对象的 evaluateScript 方法,可以执行一段 JS 代码:

代码语言:javascript复制
let context = JSContext()
JSValue *value = context.evaluateScript(params); // 参数以字符串形式传入

通过 evaluateScript 执行的 JS 代码将会得到一个 JSValue 类型的值。

在 JS 中调用 ISO 代码

这部分应该是前端工程师比较关注的。

默认情况下,Swift/Oc 类的任何方法或属性都不会暴露给 JavaScript。相反,必须选择要导出的方法和属性。对于类遵循的每个协议,如果该协议包含 JSExport 协议,JavaScriptCore 会将该协议解释为要导出到 JavaScript 的方法和属性的列表。 那 JSExport 协议是什么呢?

JSExport是JavaScriptCore框架里的一个协议。如果一个协议遵守了JSExport,那么该协议的方法会对JS开放,允许JS直接调用)。JS Export 协议可以将你的 Swift 类及其实例方法、类方法和属性导出到 JavaScript 代码。

实际的原理是,当你在 Swift/Oc 代码中导出需要的属性时,JavascriptCore 框架会在原型上创建一个 Javascript 可访问属性。如果你导出的是一个类的方法时,JavascriptCore 框架则会在构造函数上创建一个 Javascript 函数。

如下代码所示,显示了 JS 调用 OC/Swift 代码的过程:

代码语言:javascript复制
@protocol MyPointExports <JSExport>
@property double x; // @property 声明的属性决定了 JavaScript 中相应的属性的可读、枚举情况
@property double y;
(MyPoint *)makePointWithX:(double)x y:(double)y;
@end

@interface MyPoint : NSObject <MyPointExports>

@end

@implementation MyPoint
// ...
@end

在 JS 中可以这样调用上面的 OC/Swift 代码:

代码语言:javascript复制
// 使用构造函数语法调用 OC 初始化程序
var p = MyPoint(, );
// OC 类方法成为构造函数对象上的函数
var q = MyPoint.makePointWithXY(, );

在实际开发中,JS 与 IOS 工程师需要确定好调用的方法名称(假设为 jsToOc);其次,ISO 工程师需要在 JSContext 中注册 OCJSBridge 对象为 [OCJSBridge new]。此时,OCJSBridge.jsToOc 便是在 JS 需要调用到的。一旦在 JS 调用了,IOS 中的 jsToOc 方法就会响应 JS 的调用请求,并解析随方法携带过来的参数。

0 人点赞