JavaScriptCore入门
一、Objective-C中执行JavaScript代码
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; JSValue *result = [context evaluateScript:@"1 + 2"]; NSLog(@"1 + 2 = %d", [result toInt32]); // 1 + 2 = 3 return 0; }
二、Objective-C中调用JavaScript函数
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; [context evaluateScript:@"function sum(a, b){ return a + b; }"]; JSValue *sum = context[@"sum"]; JSValue *result = [sum callWithArguments:@[@1, @2]]; NSLog(@"sum(1, 2) = %d", [result toInt32]); // sum(1, 2) = 3 return 0; }
三、创建JavaScript变量
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; JSValue *intVar = [JSValue valueWithInt32:123 inContext:context]; context[@"bar"] = intVar; JSValue *result = [context evaluateScript:@"bar++"]; NSLog(@"bar = %d", [result toInt32]); // bar = 123 return 0; }
更简单的方式:
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; [context evaluateScript:@"var bar = 123;"]; NSLog(@"bar = %@", context[@"bar"]); // bar = 123 return 0; }
四、监控JavaScript的异常
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context.exceptionHandler = ^(JSContext *ctx, JSValue *exception) { NSLog(@"%@", exception); // ReferenceError: Can't find variable: name }; [context evaluateScript:@"name.firstName = Eric"]; return 0; }
五、JavaScript中调用Objective-C函数
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context[@"sum"] = ^(int a, int b) { return a + b; }; JSValue *result = [context evaluateScript:@"sum(1, 2)"]; NSLog(@"sum(1, 2) = %d", [result toInt32]); // sum(1, 2) = 3 return 0; }
六、函数内部为JSContext定义新的变量
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context[@"sum"] = ^(int a, int b) { JSContext *ctx = [JSContext currentContext]; ctx[@"foo"] = @123; return a + b; }; JSValue *result = [context evaluateScript:@"sum(1, 2)"]; NSLog(@"sum(1, 2) = %d", [result toInt32]); // sum(1, 2) = 3 NSLog(@"foo = %@", context[@"foo"]); // foo = 123 return 0; }
不能使用context[@"foo"] = @123,必须使用[JSContext currentContext]来获取当前的context,此时获取到的context和context[@"sum"]中的context相同。
七、JavaScript中调用Objective-C函数时动态传参
#import <JavaScriptCore/JavaScriptCore.h> int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context[@"count"] = ^() { NSArray *array = [JSContext currentArguments]; return array.count; }; JSValue *result = [context evaluateScript:@"count(1, 2)"]; NSLog(@"count(1, 2) = %d", [result toInt32]); // count(1, 2) = 2 JSValue *result2 = [context evaluateScript:@"count(1, 2, 3, 4, 5, 6)"]; NSLog(@"count(1, 2, 3, 4, 5, 6) = %d", [result2 toInt32]); // count(1, 2, 3, 4, 5, 6) = 6 return 0; }
八、JavaScript中操作Objective-C的类
(1)Point3D.h
#import <Foundation/Foundation.h> #import <JavaScriptCore/JavaScriptCore.h> @protocol Point3DExport <JSExport> @property double x; @property double y; @property double z; - (double)length; @end @interface Point3D : NSObject <Point3DExport> { JSContext *context; } - (id)initWithContext:(JSContext *)ctx; @end
(2)Point3D.m
#import "Point3D.h" @implementation Point3D @synthesize x; @synthesize y; @synthesize z; - (id)initWithContext:(JSContext *)ctx { if (self == [super init]) { context = ctx; context[@"Point3D"] = [Point3D class]; } return self; } - (double)length { return sqrt(self.x * self.x + self.y * self.y + self.z * self.z); } @end
(3)调用示例
#import <JavaScriptCore/JavaScriptCore.h> #import "Point3D.h" int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; Point3D *point3D = [[Point3D alloc] initWithContext:context]; point3D.x = 1; point3D.y = 2; point3D.z = 3; context[@"point3D"] = point3D; JSValue *result = [context evaluateScript:@"point3D.x = 4;point3D.y = 5;point3D.z = 6;point3D.length()"]; NSLog(@"point3D.length() = %f", [result toDouble]); // point3D.length() = 8.774964 return 0; }
这里Point3D的定义中遵循了JSExport协议,协议中定义了属性(x、y、z)和方法(length)用来暴露给JavaScript,从而使JavaScript可以使用这些属性(x、y、z)和方法(length)。
九、将JavaScript提取到单独的JS文件中
(1)test.js
var foo = function(a) { return "I'm foo in JS. Value is " + a; }; function bar(a) { return "I'm bar in JS. Value is " + a; }; foo(123); bar(456);
(2)调用示例
#import <JavaScriptCore/JavaScriptCore.h> void loadScript(JSContext *context, NSString *fileName) { NSString *filePath = [NSString stringWithFormat:@"%@/JS/%@", [[NSBundle mainBundle] resourcePath], fileName]; NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; [context evaluateScript:script]; } int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; loadScript(context, @"test.js"); return 0; }
十、给JavaScript添加console.log功能
(1)Console.h
#import <Foundation/Foundation.h> #import <JavaScriptCore/JavaScriptCore.h> @protocol ConsoleExport <JSExport> - (void)log; @end @interface Console : NSObject <ConsoleExport> { JSContext *context; } - (id)initWithContext:(JSContext *)ctx; @end
(2)Console.m
#import "Console.h" @implementation Console - (id)initWithContext:(JSContext *)ctx { if (self == [super init]) { context = ctx; context[@"Console"] = [Console class]; } return self; } - (void)log { NSArray *args = [JSContext currentArguments]; NSLog(@"%@", [args componentsJoinedByString:@","]); } @end
(3)test.js
var foo = function(a) { return "I'm foo in JS. Value is " + a; }; function bar(a) { return "I'm bar in JS. Value is " + a; }; console.log(foo(123)); // I'm foo in JS. Value is 123 console.log(bar(456)); // I'm bar in JS. Value is 456
(4)调用示例
#import <JavaScriptCore/JavaScriptCore.h> #import "Console.h" void loadScript(JSContext *context, NSString *fileName) { NSString *filePath = [NSString stringWithFormat:@"%@/JS/%@", [[NSBundle mainBundle] resourcePath], fileName]; NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; [context evaluateScript:script]; } int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context[@"console"] = [[Console alloc] initWithContext:context]; loadScript(context, @"test.js"); return 0; }
十一、完善JavaScript的console功能
(1)mock-console.js
(function(){ console.debug = console.info = console.warn = console.error = console.log; var timer = {}; console.time = function(name) { timer[name] = Date.now(); }; console.timeEnd = function(name) { var timeStart = timer[name]; if (!timeStart) return; var timeElapsed = Date.now() - timeStart; console.log(name + ":" + timeElapsed + "ms"); delete timer[name]; }; console.log("=== mock console ok ==="); })();
(2)test.js
console.log(Date.now()); // 1433760502752 console.time("sum"); var sum = 0; for (var i = 0; i < 1E5; i++) { sum += i; } console.info(sum); // 4999950000 console.timeEnd("sum"); // sum:32ms
(3)调用示例
int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context[@"console"] = [[Console alloc] initWithContext:context]; loadScript(context, @"mock-console.js"); loadScript(context, @"test.js"); return 0; }
十二、JavaScript获取硬件信息
(1)test.js
console.log(language()); // en console.log(deviceInfo()); // iPhone Simulator,iPhone OS
(2)调用示例
#import <JavaScriptCore/JavaScriptCore.h> #import <UIKit/UIKit.h> #import "Console.h" void loadScript(JSContext *context, NSString *fileName) { NSString *filePath = [NSString stringWithFormat:@"%@/JS/%@", [[NSBundle mainBundle] resourcePath], fileName]; NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; [context evaluateScript:script]; } void prepareLanguage(JSContext *context) { context[@"language"] = ^() { NSString *language = [NSLocale preferredLanguages][0]; return language; }; } void prepareDeviceInfo(JSContext *context) { context[@"deviceInfo"] = ^() { NSString *deviceName = [[UIDevice currentDevice] name]; NSString *systemName = [[UIDevice currentDevice] systemName]; return @[deviceName, systemName]; }; } int main(int argc, char *argv[]) { JSContext *context = [[JSContext alloc] init]; context[@"console"] = [[Console alloc] initWithContext:context]; prepareLanguage(context); prepareDeviceInfo(context); loadScript(context, @"test.js"); return 0; }
更深入的学习,可以参考官方的源码,注释写的很详细。