JavaScriptCore入门

xiaoren 2015-06-10

一、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;
}

更深入的学习,可以参考官方的源码,注释写的很详细。

相关推荐