近年来来看的JS返回函数的有些开。WebView与原先生交互JavaScriptCore

因此如果传入2就要返回一个函数,调用相对应的方法可转为对应的OC变量

JS返回值题一直都是观要,面试以及笔试中也常常提到到,说一样游说自己近年赶上的一些有趣的JS返回函数问题。

JavaScriptCore简介

事先看过相同志有意思的题目,说有一个sum函数,用户可以透过sum(2,3)来抱到2+3
= 5
的结果,但是片用户会并发失误的操作,有或错误的描写成sum(2)(3),现在求加客户之容错率,让sum(2)(3)也能正确的回结果。

JavaScriptCore是苹果Safari浏览器的JavaScript引擎,就同Google的V8引擎一样用来解析js代码,苹果于iOS7引入了JavaScriptCore框架,使得Objective-C
和 JavaScript 代码直接的交互变得愈加的简要方便。

思路:sum(2,3)就是函数传入了少单参数2跟3然后能回到2+3这结果,而sum(2)(3)则是传播2随后得到一个结果,然后再次传播3最后得2+3之是结果,也就是说函数要多尽同样差,因此如果传入2纵使回来一个函数,这里可以如此写:

大概利用

 

     //1.创建JS上下文(()
     JSContext *ctx = [[JSContext alloc] init];
     //定义js异常处理器
     ctx.exceptionHandler = ^(JSContext *context, JSValue *exception){
       NSLog(@"js异常:%@",exception);//当执行js出现异常时会在控制台上打印
     };
     //2.定义执行的js脚本
     NSString *script;
     //3.定义js变量
     JSValue *result;
     //js脚本
     script = @"1 + 2";
     result = [ctx evaluateScript:script];//执行js脚本
     NSLog(@"script.result = %lf",[result toDouble]);
function sum(num1,num2)
{
    if(num2 == undefined){
        return function(num){
            return num1 + num;
        }
    }else{
        return num1 + num2;
    }
}

console.log(sum(2,3)); //5
console.log(sum(2)(3)); //5


还有一道比较有意思的问题是说once这个只执行一次的函数,题目要求传入一个函数对它进行包装,要求返回的结果不管执行几次只会返回第一次的结果。例如:

g = once(func);
g(2); //func(2)的结果
g(3); //还是func(2)的结果

思路:这道题不用说肯定需要一个全局变量来判断函数时候执行过,需要一个数据来存放第一次执行的结果,根据前一个变量来判断是否执行过,如果没有执行后返回结果,如果执行过则不需要再次执行,直接返回第一次的结果。这题一个考察点是全局变量的控制,还有一个考察点就是怎么利用这个传入的func来得到结果。话不多说,直接上代码:

function fn(num)
{
    return num*num;
}
function once(func)
{
    if(typeof func != "function"){
        alert("请传入函数类型");
    }
    flag =true;
    window.result;
    return function() {
        if (flag) {
            result = func.apply(this, arguments);
            flag = false;
            return result;
        } else {
            return result;
        }
    }
}

g = once(fn);
console.log(g(2)); //4
console.log(g(3)); //4

这里注意运用apply传入arguments来调用func,arguments是一个function特有的属性,表示的是传入的所有参数,是一个类数组但不是真正意义上的数组,如果想将它转换成数组可以使用:

Array.prototype.slice.call(arguments);

这里还要说一下关于apply的相关知识,函数一般改变this上下文有三种方法:call()、apply()和bind()
call()和apply()差不多,如果都是传入一个参数的话它俩是一样的,如果传入多个参数,apply(this,arr)的第二个参数是一个数组,也就是你需要传入的各个参数组成的数组,而call(this,a,b)后面跟的是需要传入的各个属性。这两个最后得到的是改变this之后函数的执行结果。
bind()是ES5中加入的一个方法这个是直接将一个对象绑定到函数中,返回一个新的函数,这个函数里面的this属性会始终指向绑定的对象。

上述就是是JavaScriptCore框架最简单易行的动,定义了一样截js脚本然后执行,这里可以起认识及JSContext,顾名思义就是JS上下文,你得把它掌握呢js的推行环境,evaluateScript也施行js脚本的方,JSValue就是js变量,JSValue与Native
OC中之变量有相关的相应关系,如json对象对承诺着Dictonary,js数组对承诺着NSArray等,调用相对应之艺术可转为对应的OC变量,如上调用了toDouble转为double类型。

概念函数

     JSContext *ctx = [[JSContext alloc] init];
     //js函数以block的形式在Native的呈现
     //在ctx中定义一个sum函数
     ctx[@"sum"] = ^(int a,int b){
       return a + b;
     };
     result = [ctx evaluateScript:@"sum(1,5)"];

上述是因此OC代码来定义一个js函数,sum函数在JSContext的存形式为”function
sum(a,b){return a + b}”,可以死清晰的找到呼应关系。

概念一个好像对象,供js使用

     JSContext *ctx = [[JSContext alloc] init];
    //定义一个Point3D对象
     Point3D *point3D = [[Point3D alloc] initWithContext:ctx];
     ctx[@"point3D"] = point3D;//在JSContext中声明该对象,然后就可以在js中使用了
     point3D.x = 3;
     point3D.y = 4;
     point3D.z = 5;

     script = @"point3D.lenght()";
     result = [ctx evaluateScript:script];

     NSLog(@"script.result = %lf",[result toDouble]);

Point3D.h

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol Point3DExport <JSExport>
//在此协议定义的东西都可以在JS中调用
@property (nonatomic, assign) double x;
@property (nonatomic, assign) double y;
@property (nonatomic, assign) double z;
- (double) length;
@end

@interface Point3D : NSObject<Point3DExport>{
    JSContext *context;
}
- (id)initWithContext:(JSContext *)ctx;

@end

Point3D.m

#import "Point3D.h"
@implementation Point3D

@synthesize x;
@synthesize y;
@synthesize z;

- (id)initWithContext:(JSContext *)ctx{
    if (self = [super init]) {
        context = ctx;
        //在JS上下文中声明该类
        context[@"Point3D"] = [Point3D class];
    }
    return self;
}

- (double) length{
    return (self.x * self.y * self.z);
}

上述就经JSExport 商讨定义了一个Point3D对象供js中利用,JSExport
合计得以将OC对象暴露被JS使用,注意将供js使用的参数与方法勾勒在商谈被,然后该类遵循协议,实现协议中之参数与方。

其实利用

实际行使最多的应有就是是WebView与原生的并行了,OC调用JS方法相对简单,这里只简述JS调用原生以及哪些获得返回值。获取WebView中的JSContext是我们的首要任务,我们好透过KVC的不二法门于WebView的代理方webViewDidFinishLoad中取得JSContext。

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    //获取webView的JSContext,然后就可以为所欲为喇
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
}

观假设:获取用户信息。

jsBridge.api(1, JSON.stringify({}),(function(res){
  //这里获取到用户信息res,json对象,做出相关处理
  }).toString());
}

上述是js调用获取原生App用户信息之方,第一单参数为接口编号,这里拿1定为博得用户信息,后续有其他接口可长对应编号,第二独也流传参数,为json字符串,第三单呢回调函数,用来接返回值,并做出相应处理。这里转为string是为兼容android,因为android无法直接解析函数。

原生端实现

JsBrige.h

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol JSBridgeExport<JSExport>
- (void)api;
@end

@interface JSBridge : NSObject<JSBridgeExport>{
    JSContext *context;
}
- (instancetype)initWithContext:(JSContext *)context;

@end

JsBrige.m

#import "JSBridge.h"
@implementation JSBridge
- (id)initWithContext:(JSContext *)ctx{
    if (self = [super init]) {
        context = ctx;
        //在JS上下文中声明该类
        context[@"JSBridge"] = [JSBridge class];
    }
    return self;
}

- (void)api{
    //动态参数数组
    NSArray *args = [JSContext currentArguments];
    if (args.count < 3) return;

    //获取接口编号
    JSValue *apiIdValue = args.firstObject;
    int apiId = apiIdValue.toInt32;

    //获取传入参数的json对象
    JSValue *paramsJSValue = args[1];
    NSDictionary *params = paramsJSValue.toString.mj_keyValues;

    //获取回调方法
    JSValue *backFunc = args.lastObject;
    NSString *backFuncStr = backFunc.toString;

    if (apiId == 1) {//根据apiID调用不同的接口,可定义一个枚举来接收该值,此处为了简便
        User *user = [UserTool getUser];//模拟用户信息
        //调用回调函数,返回用户信息
        //将返回参数拼接上去
        [NSString stringWithFormat:@"(%@)(JSON.stringify(%@))",backFuncStr,user.mj_JSONString];
        [context evaluateScript:script];//执行回调函数
    }
}

@end

在项目被,我动用了这种办法来兑现JS与原生的通信,采用回调函数来收返回值,是坐有时信息并非同步返回,而是通过异步方式来取得,使用回调函数就坏好解决了此问题,需要注意的是js匿名函数的执行方为“(匿名函数体)(传入参数)”。