目录
iOS的JavaScriptCore框架
JavaScriptCore是苹果公司提供的JavaScript引擎,用于在iOS和macOS上执行JavaScript代码。
它是WebKit框架的一部分,WebKit是苹果用于处理Web内容的开源引擎。
JavaScriptCore提供了与JavaScript交互的接口,使开发者能够在原生应用中使用JavaScript来实现一些功能。
iOS中可以使用JSCore的地方有多处,比如封装在UIWebView中的JSCore,封装在WKWebView中的JSCore,以及系统提供的JSCore。
很有必要了解的概念只有4个:JSVM,JSContext,JSValue,JSExport。
JSVirtualMachine
一个JSVirtualMachine(以下简称JSVM)实例代表了一个自包含的JS运行环境,或者是一系列JS运行所需的资源。
该类有两个主要的使用用途:一是支持并发的JS调用,二是管理JS和Native之间桥对象的内存。
JSContext
是一个关键的类,它代表了JavaScript的执行环境。
JSValue
可以表示JavaScript中的各种基本类型,包括数字、字符串、布尔值、对象、数组和函数等。是类型转换的桥梁。
JSExport
通过采用JSExport协议,你可以将Objective-C对象的方法和属性暴露给JavaScript环境,使得它们在JavaScript代码中可以直接调用。
一、代码演示
1、执行一段js代码
JSContext *context = [[JSContext alloc] init];
// 执行一段js代码,并获取返回的结果,JSValue代表js对象
JSValue *result = [context evaluateScript:@"2 + 2"];
// JS对象转为OC对象
NSLog(@"Result: %@", [result toString]);
2、从js环境中设置和获取属性(字符串)
JSContext *context = [[JSContext alloc] init];
// 向js环境中设置一个字符串
[context setObject:@"Hello from Objective-C!" forKeyedSubscript:@"message"];
//或者这种方式
context[@"message"] = @"Hello from Objective-C!";
// 从js环境中获取一个字符串
JSValue *message = context[@"message"];
NSLog(@"Message from JavaScript: %@", [message toString]);
3、从js环境中设置和获取属性(对象)
JSContext *context = [[JSContext alloc] init];
// 向js环境中设置一个对象
JSValue *dataValue = [JSValue valueWithObject:@{@"key": @"value"} inContext:context];
[context setObject:dataValue forKeyedSubscript:@"myData"];
// 从js环境中获取并修改对象
JSValue *myVarValue = context[@"myVar"];
[myVarValue setValue:@42 forKeyedSubscript:@"value"];
4、从js环境中设置和获取方法
JSContext *context = [[JSContext alloc] init];
// 从js环境中获取一个方法并调用
JSValue *function = context[@"myFunction"];
JSValue *result = [function callWithArguments:@[@"arg1", @"arg2"]];
NSLog(@"Result from JavaScript function: %@", [result toString]);
// 向js环境中添加一个方法:myObjectiveCMethod,这个方法js可以调用myObjectiveCMethod()
context[@"myObjectiveCMethod"] = ^() {
NSLog(@"Objective-C method called from JavaScript");
};
通过 context[key] = value
这种方式添加到js环境中的属性,都会被添加到一个全局的对象下边,可以通过下面这种方式查看
context.globalObject.toDictionary
二、js 和 native 的相互调用
JSContext *context = [[JSContext alloc] init];
// 在Objective-C中调用JavaScript函数
// 调用定义在js环境中方法:myJavaScriptFunction
[context evaluateScript:@"myJavaScriptFunction();"];
// 从JavaScript中调用Objective-C方法
// 向js环境中添加一个方法:myObjectiveCMethod
context[@"myObjectiveCMethod"] = ^() {
NSLog(@"Objective-C method called from JavaScript");
};
JSExport协议
通过采用JSExport协议,你可以将Objective-C对象的方法和属性暴露给JavaScript环境,使得它们在JavaScript代码中可以直接调用。
1、定义在Objective-C中的接口:
在Objective-C中,你定义一个遵循JSExport协议的协议,并在其中声明你想要在JavaScript中使用的方法和属性。
#import <JavaScriptCore/JavaScriptCore.h>
@protocol XYJSPointExports <JSExport, NSObject>
@property (nonatomic, assign) double x;
@property (nonatomic, assign) double y;
- (NSString *)description;
+ (XYJSPoint *)makePointWithX:(double)x Y:(double)y;
//JSExportAs(makePoint,
//+ (MyPoint *)makePointWithX:(double)x Y:(double)y
//);
@end
2、在Objective-C类中实现JSExport协议:
在Objective-C中的类中,实现上述定义的MyJSExport协议。
@interface XYJSPoint : NSObject <XYJSPointExports>
- (instancetype)initWithX:(double)x Y:(double)y;
@end
@implementation XYJSPoint
@synthesize x, y;
- (instancetype)initWithX:(double)x
Y:(double)y {
self = [super init];
if (self) {
self.x = x;
self.y = y;
}
return self;
}
- (NSString *)description {
NSString *str = [NSString stringWithFormat:@"(%f,%f)",self.x,self.y];
return str;
}
+ (id)makePointWithX:(double)x Y:(double)y {
XYJSPoint *point = [[XYJSPoint alloc] initWithX:x Y:y];
return point;
}
@end
3、关联JSContext:
将MyObject类的实例关联到JSContext,以便在JavaScript中可以访问它的属性和方法。
JSContext *context = [[JSContext alloc] init];
XYJSPoint *point = [[MyObject alloc] init];
context[@"point"] = point;
4、在JavaScript中使用:
在JavaScript中,你可以直接访问myObject的属性和方法,就像在JavaScript中调用普通的对象一样。
point.x = 3;
point.description();
通过使用JSExport协议,你可以轻松地在Objective-C和JavaScript之间定义和传递接口,使得两种语言之间的交互更加方便。
这对于在iOS应用中嵌入Web内容,或者在JavaScript中使用Objective-C实现的功能非常有用。
5、关系图如下
oc对象属性 - js对象的原型对象中属性
oc对象方法 - js对象的原型对象中方法
oc类方法 - js对象构造函数的静态方法
oc的继承关系 - js的原型链继承
6、内存管理
不要在JS中给OC对象增加成员变量
OC对象不要直接强引用JSValue对象(有解决方法,查阅参考文档)
Demo
1、factorial.js内的代码如下:
// 计算阶乘
var factorial = function (n) {
if (n < 0)
return;
if (n === 0)
return 1;
return n * factorial(n-1)
};
// 计算两点间的距离
var euclideanDistance = function(p1, p2) {
var xDelta = p2.x - p1.x;
var yDelta = p2.y - p1.y;
return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
};
// 计算中点
var midpoint = function(p1, p2) {
var xDelta = (p2.x - p1.x) / 2;
var yDelta = (p2.y - p1.y) / 2;
return XYJSPoint.makePointWithXY(p1.x + xDelta, p1.y + yDelta);
};
2、加载js代码
- (NSString *)loadJSFromBundle:(NSString *)jsfileName {
NSString * path = [[NSBundle xy_bundleName:@"XYTestModule"] pathForResource:jsfileName ofType:nil];
NSString *jsStr = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
return jsStr;
}
3、Demo1
// oc调用js代码中的方法
- (IBAction)jscoreTest1:(id)sender {
JSContext *context = [[JSContext alloc] init];
NSString *jsStr = [self loadJSFromBundle:@"factorial.js"];
// 加载js代码
[context evaluateScript:jsStr];
JSValue *func = context[@"factorial"];
JSValue * result = [func callWithArguments:@[@4]];
NSLog(@"lxy:result = %d",[result toInt32]);
}
3、Demo2
//JSExport协议的使用
- (IBAction)jscoreTest3:(id)sender {
// 加载js代码
JSContext *context = [[JSContext alloc] init];
NSString *jsStr = [self loadJSFromBundle:@"factorial.js"];
[context evaluateScript:jsStr];
// 创建2个点
XYJSPoint *point1 = [[XYJSPoint alloc] initWithX:0.0 Y:0.0];
XYJSPoint *point2 = [[XYJSPoint alloc] initWithX:3.0 Y:4.0];
// 调用JS方法,求得两点间的距离
JSValue *function = context[@"euclideanDistance"];
JSValue *result = [function callWithArguments:@[point1, point2]];
NSLog(@"result = %f",[result toDouble]);
// 调用JS方法,求得两点间的中点
context[@"XYJSPoint"] = [XYJSPoint class];
JSValue *function2 = context[@"midpoint"];
JSValue *jsResult = [function2 callWithArguments:@[point1, point2]];
XYJSPoint *midpoint = [jsResult toObject];
NSLog(@"midpoint = %@",midpoint);
}
行者常至,为者常成!