React Native源碼中JavaScriptCore詳解

前言

最近在看React Native源碼的時(shí)候,發(fā)現(xiàn)內(nèi)部使用了一個(gè)JavaScriptCore框架,可以用來進(jìn)行OC和JS互相調(diào)用,借機(jī)寫了一篇OC和JS互相調(diào)用,為后續(xù)想了解React Native源碼的同學(xué)埋下伏筆,當(dāng)然后期也會把自己所了解的React Native源碼實(shí)現(xiàn)跟大家分享。

如果喜歡我的文章,可以關(guān)注我微博:袁崢Seemygo

一、JavaScriptCore常用的類

  •  JavaScriptCore作用:JavaScriptCore是蘋果原生API,用來JS和OC交互的。
    
  • JSContext: JS運(yùn)行環(huán)境,用它去執(zhí)行JS代碼,并且通過它去獲取JS里的數(shù)據(jù)
  • JSValue: 用于接收J(rèn)S中獲取的數(shù)據(jù)類型,可以是任一對象,方法。

二、OC調(diào)用JS

  • 本質(zhì):JS代碼中已經(jīng)定義好變量和方法,通過OC去獲取,并且調(diào)用
  • 步驟:
    • 1.創(chuàng)建JS運(yùn)行環(huán)境
    • 2.執(zhí)行JS代碼
    • 3.獲取JS數(shù)據(jù)(變量,方法)
    • 4.使用JS數(shù)據(jù),方法

2.1 獲取定義在JS中的變量

  • 可以直接通過OC修改JS中變量的值
#pragma mark - 獲取JS中定義的變量
- (void)getJSVar
{
    // JS代碼
    NSString *jsCode = @"var arr = [1,2,3]";
    
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // 執(zhí)行JS代碼
    [ctx evaluateScript:jsCode];
    
    // 因?yàn)樽兞恐苯佣x在JS中,所以可以直接通過JSContext獲取,根據(jù)變量名稱獲取,相當(dāng)于字典的Key
    // 只有先執(zhí)行JS代碼,才能獲取變量
    JSValue *jsArr = ctx[@"arr"];
    
    jsArr[0] = @5;
    
    // 打印結(jié)果:5,2,3
    NSLog(@"%@",jsArr);
}

2.2 獲取定義在JS中的方法,并且調(diào)用

  • 實(shí)現(xiàn)OC調(diào)用JS方法
#pragma mark - OC調(diào)用JS
// OC調(diào)用JS方法,并獲取返回結(jié)果
- (void)ocCallJSFunc
{
    NSString *jsCode = @"function hello(say){"
    " return say; "
    "}";
    
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // 因?yàn)榉椒ㄖ苯佣x在JS中,所以可以直接通過JSContext獲取,根據(jù)方法名稱獲取,相當(dāng)于字典的Key
    // 執(zhí)行JS代碼
    [ctx evaluateScript:jsCode];
    
    // 獲取JS方法,只有先執(zhí)行JS代碼,才能獲取
    JSValue *hello = ctx[@"hello"];
    
    // OC調(diào)用JS方法,獲取方法返回值
    JSValue *result = [hello callWithArguments:@[@"你好"]];
    
    // 打印結(jié)果:你好
    NSLog(@"%@",result);
}

三、JS調(diào)用OC中的block

  • 本質(zhì):一開始JS中并沒有OC的block,所以沒法直接調(diào)用OC的block,需要把OC的block,在JS中生成方法,然后在通過JS調(diào)用。
  • 步驟:
    • 1.創(chuàng)建JS運(yùn)行環(huán)境
    • 2.在JS中生成對應(yīng)的OC代碼
    • 3.使用JS調(diào)用,在JS環(huán)境中生成的block方法,就能調(diào)用到OC的block中.

3.1 JS調(diào)用OC中不帶參數(shù)的block

  • 想通過JS調(diào)用OC中不帶參數(shù)的block
#pragma mark - JS調(diào)用OC中不帶參數(shù)的block
- (void)jsCallOCBlock1WithNoneArguments
{
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // JS調(diào)用Block方式
    // 由于JS本身沒有OC這個(gè)代碼,需要給JS中賦值,就會自動生成右邊的代碼.
    // 相當(dāng)于在JS中定義一個(gè)叫eat的方法,eat的實(shí)現(xiàn)就是block中的實(shí)現(xiàn),只要調(diào)用eat,就會調(diào)用block
    ctx[@"eat"] = ^(){
        NSLog(@"吃東西");
    };
    
    // JS執(zhí)行代碼,就會直接調(diào)用到block中
    NSString *jsCode = @"eat()";
    
    [ctx evaluateScript:jsCode];
}

3.2 JS調(diào)用OC中帶參數(shù)的block

  • 想通過JS調(diào)用OC中帶參數(shù)的block
- (void)jsCallOCBlockWithArguments
{
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // 2.調(diào)用帶有參數(shù)的block
    // 還是一樣的寫法,會在JS中生成eat方法,只不過通過[JSContext currentArguments]獲取JS執(zhí)行方法時(shí)的參數(shù)
    ctx[@"eat"] = ^(){
        // 獲取JS調(diào)用參數(shù)
        NSArray *arguments = [JSContext currentArguments];
        NSLog(@"吃%@",arguments[0]);
    };
    
    // JS執(zhí)行代碼,調(diào)用eat方法,并傳入?yún)?shù)面包
    NSString *jsCode = @"eat('面包')";
    
    [ctx evaluateScript:jsCode];

}

四、JS調(diào)用OC中的類

  • 本質(zhì):一開始JS中并沒有OC的類,需要先在JS中生成OC的類,然后在通過JS調(diào)用。
  • 步驟
    • 1.OC類必須遵守JSExport協(xié)議,只要遵守JSExport協(xié)議,JS才會生成這個(gè)類
    • 2.但是還不夠,類里面有屬性和方法,也要在JS中生成
    • 3.JSExport本身不自帶屬性和方法,需要自定義一個(gè)協(xié)議,繼承JSExport,在自己的協(xié)議中暴露需要在JS中用到的屬性和方法
    • 4.這樣自己的類只要繼承自己的協(xié)議就好,JS就會自動生成類,包括自己協(xié)議中聲明的屬性和方法

4.1 JS調(diào)用OC自定義類

  • 自定義協(xié)議(PersonJSExport)
@protocol PersonJSExport <JSExport>

@property (nonatomic, strong) NSString *name;

- (void)play;

// 調(diào)用多個(gè)參數(shù)的方法,JS函數(shù)命名規(guī)則和OC還不一樣,很可能調(diào)用不到對應(yīng)的JS生成的函數(shù),為了保證生成的JS函數(shù)和OC方法名一致,OC提供了一個(gè)宏JSExportAs,用來告訴JS應(yīng)該生成什么樣的函數(shù)對應(yīng)OC的方法,這樣就不會調(diào)錯(cuò)了。

// PropertyName:JS函數(shù)生成的名字
// Selector:OC方法名
// JS就會自動生成playGame這個(gè)方法
JSExportAs(playGame, - (void)playWithGame:(NSString *)game time:(NSString *)time);

@end

  • 自定義類(Person)
@interface Person : NSObject <PersonJSExport>

@property (nonatomic, strong) NSString *name;


- (void)playWithGame:(NSString *)game time:(NSString *)time;

@end


@implementation Person

- (void)play
{
    
    NSLog(@"%@玩",_name);
}

- (void)playWithGame:(NSString *)game time:(NSString *)time
{
    NSLog(@"%@在%@玩%@",_name,time,game);
}


@end

  • 通過JS調(diào)用OC自定義類


#pragma mark - JS調(diào)用OC自定義類
- (void)jsCallOCCustomClass
{
    // 創(chuàng)建Person對象
    Person *p = [[Person alloc] init];
    p.name = @"yz";
    
    JSContext *ctx = [[JSContext alloc] init];
    
    // 會在JS中生成Person對象,并且擁有所有值
    // 前提:Person對象必須遵守JSExport協(xié)議,
    ctx[@"person"] = p;
    
    // 執(zhí)行JS代碼
    // 注意:這里的person一定要跟上面聲明的一樣,因?yàn)樯傻膶ο笫怯胮erson引用
    // NSString *jsCode = @"person.play()";
    NSString *jsCode = @"person.playGame('德州撲克','晚上')";
    
    [ctx evaluateScript:jsCode];

}

4.1 JS調(diào)用OC系統(tǒng)類

  • 問題:系統(tǒng)自帶的類,想要通過JS調(diào)用怎么辦,我們又沒辦法修改系統(tǒng)自帶類的文件

    • 和調(diào)用自定義類一樣,也要弄個(gè)自定義協(xié)議繼承JSExport,描述需要暴露哪些屬性(想要把系統(tǒng)類的哪些屬性暴露,就在自己的協(xié)議聲明)
    • 通過runtime,給類添加協(xié)議
  • 自定義協(xié)議(UILabelJSExport)

@protocol UILabelJSExport <JSExport>

@property (nonatomic, strong) NSString *text;

@end
  • JS調(diào)用OC系統(tǒng)類
#pragma mark - JS調(diào)用OC系統(tǒng)類
- (void)jsCallOCSystemClass
{
    // 給系統(tǒng)類添加協(xié)議
    class_addProtocol([UILabel class], @protocol(UILabelJSExport));
    
    // 創(chuàng)建UILabel
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    [self.view addSubview:label];
    
    JSContext *ctx = [[JSContext alloc] init];
    // 就會在JS中生成label對象,并且用laebl引用
    ctx[@"label"] = label;
    
    // 利用JS給label設(shè)置文本內(nèi)容
    NSString *jsCode = @"label.text = 'Oh Year'";
    
    [ctx evaluateScript:jsCode];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynot...
    知識小集閱讀 15,282評論 11 172
  • 注:本文copy自http://www.lxweimin.com/p/ac534f508fb0,純屬當(dāng)筆記使用。 概...
    BookKeeping閱讀 742評論 1 3
  • 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動效的,...
    等開會閱讀 14,502評論 6 69
  • 一. JavaScriptCore 簡介 1.1 JavaScriptCore 和 JavaScriptCore ...
    GShining閱讀 867評論 0 0
  • 【五律?長江神游】(平水韻) 未見長江水,先聞四百州。 六朝煙影去,九派浪濤留。 懷古思鄉(xiāng)國,登高望客舟。 故園三...
    依嵐聽雪閱讀 494評論 4 7