本文由我们团队的王瑞华童鞋撰写。
OS X Mavericks 和 iOS 7 引入了 JavaScriptCore 库,它把 WebKit 的 JavaScript 引擎用 Objective-C 封装,提供了简单,快速以及安全的方式接入世界上最流行的语言。在项目的实际开发中,由于需要与 H5 端有交互,所以采用了JavaScriptCore的交互方式,借此参与该项目的机会,对JavaScriptCore也有了一些简单的了解。
JSContext/JSValue
JSContext 是 JavaScript 的执行环境。所有 JavaScript 执行发生的背景,以及所有 JavaScript 值被绑在这一个上下文中。JSContext 类似于 UIWindow 的概念,所有的执行都在改环境中发生:
1 2 3 4 5 6 7 |
JSContext *context = [[JSContext alloc] init]; [context evaluateScript:@"var num = 5 + 5"]; [context evaluateScript:@"var names = ['Grace', 'Ada', 'Margaret']"]; [context evaluateScript:@"var triple = function(value) { return value * 3 }"]; JSValue *tripleNum = [context evaluateScript:@"triple(num)"]; NSLog(@"Tripled: %d", [tripleNum toInt32]); // Tripled: 30 |
代码最后一行,每个 JSValue 起源于 JSContext 和 持有它的强引用。当一个 JSValue 实例方法创造一个新的 JSValue时,该新 JSValue 起源于同一个 JSContext。 JSValue 包装了每一个可能的 JavaScript 值:字符串和数字;数组、对象和方法;甚至错误和特殊的 JavaScript 值诸如 null 和 undefined。
OBJECTIVE-C TYPE | JAVASCRIPT TYPE |
---|---|
nil | undefined |
NSNull | null |
NSString | string |
NSNumber | number, boolean |
NSDictionary | Object object |
NSArray | Array object |
NSDate | Date object |
NSBlock (1) | Function object (1) |
id (2) | Wrapper object (2) |
Class (3) | Constructor object (3) |
下标支持
作为下标传递的对象键将被转换为一个 JavaScript 值, 然后值转换为一个用作获取属性名称的字符串。
1 2 3 4 |
JSValue *names = context[@"names"]; JSValue *initialName = names[0]; NSLog(@"The first name: %@", [initialName toString]); // The first name: Grace |
该简易方法对应于以下两个方法:
1 2 |
- (JSValue *)objectForKeyedSubscript:(id)key; - (JSValue *)objectAtIndexedSubscript:(NSUInteger)index; |
调用方法
JSValue 包装了一个 JavaScript 函数,我们可以从 Objective-C 代码中使用 Foundation 类型作为参数来直接调用该函数。
1 2 3 |
JSValue *tripleFunction = context[@"triple"]; JSValue *result = [tripleFunction callWithArguments:@[@5] ]; NSLog(@"Five tripled: %d", [result toInt32]); |
JavaScript 调用
上面说明的 OC 调用 JavaScript 的方法。那么接下来,说明 JavaScript 访问 OC 定义的对象和方法。我粗浅的认为就是在遵守该协议后,JavaScript 就可以调用 OC 实现的方法和属性等。
让 JSContext 访问我们的本地客户端代码的方式主要有两种:JSExport 协议和block。
JSExport 协议
JSExport 提供一个将 OC 中的类、实例方法和属性等导出为 JavaScript 函数的方法。
该协议只包含了一个方法:
1 |
JSExportAs(PropertyName, Selector) |
即当导出到 JavaScript 时,重命名一个 selector。
这就需要熟悉它的做法,当带有一个或多个参数的 seletor 转变为 JavaScript 的属性名字时,在默认情况下一个属性名字将采用以下方式生成:
- 所有的冒号将从 Selector 当中移除掉
- 任何一个后边跟着冒号的小写字母开头的单词将被改换为大写。
在这种默认的转换方式下,一个 selector 如 doFoo:withBar: 将被导为doFooWithBar 。这种默认的改变有可能被 JSExportAs 宏 所改写,例如将 doFoo:withBar: 导出为 doFoo 。实际写法如下:
1 2 3 4 5 |
@protocol MyClassJavaScriptMethods JSExportAs(doFoo, - (void)doFoo:(id)foo withBar:(id)bar ); @end |
请注意 JSExport 宏只可能适用于带有一个或更多的参数的selector。
Blocks
当一个 Objective-C block 被赋给 JSContext 里的一个标识符,JavaScriptCore 会自动的把 block 封装在 JavaScript 函数里。如下:
1 2 3 4 5 6 7 8 |
JSContext *context = [[JSContext alloc] init]; context[@"simplifyString"] = ^(NSString *input) { NSMutableString *mutableString = [input mutableCopy]; CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, ass="crayon-e">__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, ܌借此参与该项目的机会,对JavaScriptCore也有了一些简单的了解。
JSContext/JSValueJSContext 是 JavaScript 的执行环境。所有 JavaScript 执行发生的背景,以及所有 JavaScript 值被绑在这一个上下文中。JSContext 类似于 UIWindow 的概念,所有的执行都在改环境中发生:
代码最后一行,每个 JSValue 起源于 JSContext 和 持有它的强引用。当一个 JSValue 实例方法创造一个新的 JSValue时,该新 JSValue 起源于同一个 JSContext。 JSValue 包装了每一个可能的 JavaScript 值:字符串和数字;数组、对象和方法;甚至错误和特殊的 JavaScript 值诸如 null 和 undefined。
下标支持作为下标传递的对象键将被转换为一个 JavaScript 值, 然后值转换为一个用作获取属性名称的字符串。
该简易方法对应于以下两个方法:
调用方法JSValue 包装了一个 JavaScript 函数,我们可以从 Objective-C 代码中使用 Foundation 类型作为参数来直接调用该函数。
JavaScript 调用上面说明的 OC 调用 JavaScript 的方法。那么接下来,说明 JavaScript 访问 OC 定义的对象和方法。我粗浅的认为就是在遵守该协议后,JavaScript 就可以调用 OC 实现的方法和属性等。 让 JSContext 访问我们的本地客户端代码的方式主要有两种:JSExport 协议和block。 JSExport 协议JSExport 提供一个将 OC 中的类、实例方法和属性等导出为 JavaScript 函数的方法。 该协议只包含了一个方法:
即当导出到 JavaScript 时,重命名一个 selector。 这就需要熟悉它的做法,当带有一个或多个参数的 seletor 转变为 JavaScript 的属性名字时,在默认情况下一个属性名字将采用以下方式生成:
在这种默认的转换方式下,一个 selector 如 doFoo:withBar: 将被导为doFooWithBar 。这种默认的改变有可能被 JSExportAs 宏 所改写,例如将 doFoo:withBar: 导出为 doFoo 。实际写法如下:
请注意 JSExport 宏只可能适用于带有一个或更多的参数的selector。 Blocks当一个 Objective-C block 被赋给 JSContext 里的一个标识符,JavaScriptCore 会自动的把 block 封装在 JavaScript 函数里。如下:
|