Cocoa プログラミング
JavaScriptCore
JavaScript では、定義されているよりも少ない引数を指定してメソッドを呼び出せるようになっていますが、JavaScriptCore を使ってネイティブコードで関数を実装する場合、引数が省略されたかどうかを知ることができます。
JavaScriptCore で引数を省略して呼び出された場合に対応する
JavaScript では、定義されているよりも少ない引数を指定してメソッドを呼び出せるようになっていますが、JavaScriptCore を使ってネイティブコードで関数を実装する場合、引数が省略されたかどうかを知ることができます。
JavaScript でメソッドに渡す引数が省略された場合の挙動
JavaScript で、メソッドが引数が省略されて呼び出されると、その引数にはundefined
という値が渡されてきます。
たとえば、次のようなネイティブコードで実装したメソッドがあったとします。
- (NSString*)join:(NSArray*)values :(NSString*)separator
{
return [values componentsJoinedByString:separator];
}
このとき、JavaScriptCore からobject.join(['a', 'b']);
というスクリプトが呼び出されると、省略された第2引数にはundefined
が渡されてきます。
その後の引数の扱われ方は、純粋な JavaScript で書かれたメソッドと原則同じなのですけど、ここで特に注意しておきたいのは、今回のようにネイティブコード側で引数の型がNSString
になっていると、文字列型に変換し終えたものが渡されてきます。
今回のように引数が省略された場合はundefined
が文字列に変換された状態、すなわちundefined
という文字列で渡されてきます。
そうなると、JavaScript の未定義値が渡されたのかundefined
という文字列そのものが渡されたのか、ネイティブコードからは判断できなくなります。
null
も同様で、JavaScript 側から
null
が引数に渡された場合もネイティブコードで
nil
にはならず、
null
という文字列に変換されて渡されてくるようです。
ネイティブコードで実装したメソッドで引数の省略に対応する
ネイティブコードのメソッドで、引数が省略された場合に対応したい場合には、引数の型をJSValue
にします。
- (NSString*)join:(NSArray*)values :(JSValue*)separator
{
NSString *separatorString;
if ([separator isUndefined])
{
separatorString = @",";
}
else
{
separatorString = [separator toString];
}
return [values componentsJoinedByString:separatorString];
}
このようにすることで引数には、まだ変換を行っていない JavaScript で渡された値を受け取れるので、そこでisUndefined
メソッドを使って、渡された値がundefined
であるかを判定できます。
たとえば上の例では、引数が省略された場合は@","
が指定されたものとみなし、そうでなければ受け取った値を文字列として使用しています。
JavaScriptCore 用に提供する機能ではJSValue
型がオススメ
JavaScriptCore で使う機能の引数や戻り値の型としてNSString
型のようなネイティブな型を使っても、ちょっとした JavaScript ならそれでもあまり問題にならないかもしれませんけど、ユーザーが自由に JavaScript を書けるようにしたりとか、ネイティブコードと連携するくらいになると、思いがけない値を使って処理を進めてしまうことにもなりかねません。
厳密なプログラミングをしたい場合は、本来のJSValue
型の値を直接扱うのが良いかもしれません。
今回の例では、戻り値がNSString
型にしてありますが、必要に応じてここもJSValue
型にすることで、空文字以外にもnull
やundefined
といった、本来の JavaScript の表現で値を返せるようになります。
NSString
型にしているときに、戻り値として
nil
を返したとすると、JavaScript 側では
undefined
が得られる様子でした。
null
ではないので少し注意が必要そうです。