相关链接: https://groups.google.com/forum/#!topic/v8-users/gQVpp1HmbqM
这次API变动涉及的规模非常之庞大,绝大部分我们会使用到的接口都要更新一遍。这里简单解读一下变动的原因以及新接口的使用方法。首先来看下面这个示例:
Local<Value> x = some_value; Local<String> s = x.ToString(); // 方法内部如果发生异常会返回空Handle s->Anything(); // 访问空Handle导致崩溃!
这是一段非常常见的代码,我们有个类型是 Local<Value>
的变量 x
,现在需要对它调用 toString()
方法来获取字符串。如果 toString()
方法因为栈内存不足或者其他任何原因发生异常,这时候返回的 Local<String>
就会是一个空Handle,而接着访问空Handle就会导致程序崩溃。正确的写法应该是这样:
if(!s.IsEmpty()){ s->Anything(); // 判断不是空Handle才执行后续代码 }
使用Local Handle前我们需要加一个 IsEmpty()
的判断,阻止对空Handle的访问才可以避免程序崩溃。问题就出在这个API Style 上,V8现有的API没能有效的提醒开发者哪些返回值是需要检查 IsEmpty()
的。由于返回都是Local Handle,再加上开发者自己的函数也传递的各种 Local Handle,如果所有 Local Handle 使用前都检查一次太不现实了。所以实际情况是大家有的检查有的不检查,大大增加了整个系统崩溃的概率。
为了解决这个问题,V8引入了一组新的接口: Maybe
和 MaybeLocal
。
旧API里类似这样的函数:
double NumberValue(); Local<String> ToString();
都会变成这样:
Maybe<double> NumberValue(Local<Context>); MaybeLocal<String> ToString(Local<Context>);
总结就是:凡是可能返回空Handle的API,都改成了返回 Maybe Handle,而不是 Local Handle。MaybeLocal的意思就是可能有值,也可能是空的。将 Maybe Handle 转换为 Local Handle 使用如下方法:
MaybeLocal<Value> x = some_value; if(x.IsEmpty()){ Local<Value> s = x.ToLocalChecked(); }
注意这里如果不判断 IsEmpty()
,如果 x 确实是空的, ToLocalChecked()
方法就会强制抛出一个异常。只有你非常肯定 x 确定不是空的,才可以省略 IsEmpty()
。
可以看出这套新API可以有效提醒开发者对空的 Handle 进行检查,因为你必须把 Maybe Handle 转换为 Local Handle 才可以使用,转换的过程再加上 IsEmpty()
的判断,可以从机制上避免写出不稳定的系统。到目前为止,V8里绝大部分API已经都改成了返回 Maybe Handle 的形式。根据原文的描述,等全部改完,会有六周的共存时间,然后直接返回 Local Handle 的旧API会全部移除。所以大家从现在开始全都使用返回 Maybe Handle 的版本吧。
这里最后提一下为何返回 Maybe Handle 的版本都多了一个 Context 参数?这里主要是因为要重载旧API,只有返回值不同在C++里不算合法重载,所以加了个Context参数,哭。不过在全局声明一个 Context 用就行,也不是什么难事。