资讯专栏INFORMATION COLUMN

在 V8 中从 JavaScript 到 C++ 的类型转换

betacat / 2921人阅读

摘要:本文转载自众成翻译译者乱发小生链接原文学习怎样传递信息从到是一个非常难的事情。原因在于和两种语言类型之间的巨大差异。你可以在这里查看所有的类型。这两个组件松散和严格的表明一系列函数接收不同类型的参数,,,和和它们的返回值。

本文转载自:众成翻译
译者:乱发小生
链接:http://www.zcfy.cc/article/3360
原文:https://nodeaddons.com/type-conversions-from-javascript-to-c-in-v8/

学习怎样传递信息从JavaScript到C++是一个非常难的事情。原因在于JavaScript和C++两种语言类型之间的巨大差异。虽然C++是一门强类型语言("42"不是一个整数类型,它只是一个字符串!),JavaScript非常渴望帮我们转换这些类型。

JavaScript语言包含 String,Numbers,Booleans,null,undefined这5种原始类型。V8使用继承方式,JavaScript的类型都继承于C++的Value,和Primitive的子类。除了标准的JavaScript语言之外,V8同时支持整型(Int32 and Uint32)。你可以在 这里查看所有的类型。

所有JavaScript值的引用都通过C++的Handle对象来保存-在大多数情况下是Local。Handle对象把运行中的JavaScript指向V8的存储单元。你可以在我上一篇文章学习更多的存储单元的知识。

当你通过API来为这些基础单元工作时,你会注意到没有分配Local对象很奇怪!这是有很重要的如以下三个原因:

JavaScript语句就是指向V8的存储单元的。比如var x = 5; ,就是使X指向一个5的存储单元,重新分配x=6并没有改变这个存储单元,它只是使x指向6.如果xy都赋值为10,那么他们都指向同一个存储单元。
2.函数调用值的传递,所以JavaScript 调用带有参数的C++插件,如果这个值是一个原始类型数据,它始终是一个独特的副本,改变它的值对调用的代码没有任何影响。

Handles(Local)都引用存储单元,基于上述第一点,让handle的值改变是没有任何意义的,因为它的基本数据类型没有变。

希望这是有道理的,然后你仍有可能修改V8的变量,我们只需要重新赋值给它。

几个例子

现在,让我们看来一下Number 这种基本类型,当我们构建一个用JavaScript写的C++插件,看看它会从JavaScript接收什么。我写了一个C++PassNumber函数的例子:

void PassNumber(const FunctionCallbackInfo& args) {
    Isolate * isolate = args.GetIsolate();

    double value = args[0]->NumberValue();

    value+= 42;

    Local retval = Number::New(isolate, value);

    args.GetReturnValue().Set(retval);
}

完整的插件代码在这里。

这个插件没有做对函数的参数做任何处理,甚至它存不存在都不能保证。下面是相关的mocha测试,我们可以看到V8如果处理这些数字,更重要的是,其他输入能不能转成数字在JavaScript中。

 describe("pass_number()", function () {
  it("return input + 42 when given a valid number", function () {
    assert.equal(23+42, loose.pass_number(23));
    assert.equal(0.5+42, loose.pass_number(0.5));
  });

  it("return input + 42 when given numeric as a string", function () {
    assert.equal(23+42, loose.pass_number("23"));
    assert.equal(0.5+42, loose.pass_number("0.5"));
  });

  it("return 42 when given null (null converts to 0)", function () {
    assert.equal(42, loose.pass_number(null));
  });

  it("return NaN when given undefined", function () {
    assert(isNaN(loose.pass_number()));
    assert(isNaN(loose.pass_number(undefined)));
  });

  it("return NaN when given a non-number string", function () {
    assert(isNaN(loose.pass_number("this is not a number")));
  });
完整的类型转换清单

我创建了一个git仓库里面有类型转换清单,我认为是非常有用的。为拿到它,获取它:

`> git clone https://github.com/freezer333/nodecpp-demo.git`

建立这两个组件,进入loosestrict 目录,并执行在每个目录执行 node-gyp configure build命令。首选你需要在全局安装node-gyp。如果你完成了这两步, 来看看这里。

> cd nodecpp-demo/conversion/loose
> node-gyp configure build
...
> cd ../strict
> node-gyp configure build

The two addons (loose and strict) expose a series of functions that accept different types - Numbers, Integers, Strings, Booleans, Objects, and Arrays - and perform (somewhat silly) operations on them before returning a value. I’ve included a JavaScript test program that shows you the expected outputs of each function - but the real learning value is in the addons’ C++ code (strict/loose)

这两个组件(松散和严格的)表明一系列函数接收不同类型的参数-Number,Integers,String,Booleans,Objects和Arrays和它们的返回值。我已经包含了JavaScript的测试程序,它会显示每个函数的预期产出-但真正的学习值在插件C ++代码(严格 / 宽松

在运行测试时,你需要先安装 mocha,进入conversions 目录(含 index.js):

`> npm test`

在“宽松”addons组件有很宽松的类型检查-它基本上模仿纯JavaScript函数将如何工作。例如,pass_string函数接受任何可能在JavaScript中转换为字符串值,并返回它的倒序排列:

 describe("pass_string()", function () {
  var str = "The truth is out there";

  it("reverse a proper string", function () {
    assert.equal(reverse(str), loose.pass_string(str));
  });
  it("reverse a numeric/boolean since numbers are turned into strings", function () {
    assert.equal("24", loose.pass_string(42));
    assert.equal("eurt", loose.pass_string(true));
  });
  it("return "llun" when given null - null is turned into "null"", function () {
    assert.equal("llun", loose.pass_string(null));
  });
  it("return "denifednu" when given undefined", function () {
    assert.equal(reverse("undefined"), loose.pass_string(undefined));
  });
  it("return reverse of object serialized to string", function () {
    assert.equal(reverse("[object Object]"), loose.pass_string({x: 5}));
  });
  it("return reverse of array serialized to string", function () {
    assert.equal(reverse("9,0"), loose.pass_string([9, 0]));
  });
});

下面是字符串输入的宽松转换C ++代码:

void PassString(const FunctionCallbackInfo& args) {
    Isolate * isolate = args.GetIsolate();

    v8::String::Utf8Value s(args[0]);
    std::string str(*s);
    std::reverse(str.begin(), str.end());    

    Local retval = String::NewFromUtf8(isolate, str.c_str());
    args.GetReturnValue().Set(retval);
}

所述“严格”的插件执行完整的类型和错误检查,表现更像一个JavaScript C ++函数。对于所有的附加严格的方法,如果输入和预期不相符合便会返回一个undefined。例如,pass_string函数的行为和宽松的解释完全不同:

describe("pass_string()", function () {

  it("return reverse a proper string", function () {
    var str = "The truth is out there";
    it("reverse a proper string", function () {
      assert.equal(reverse(str), strict.pass_string(str));
    });
  });

  it("return undefined for non-strings", function () {
    assert.equal(undefined, strict.pass_string(42));
    assert.equal(undefined, strict.pass_string(true));
    assert.equal(undefined, strict.pass_string(null));
    assert.equal(undefined, strict.pass_string(undefined));
    assert.equal(undefined, strict.pass_string({x: 5}));
    assert.equal(undefined, strict.pass_string([9, 0]));
  });
});
void PassString(const FunctionCallbackInfo& args) {
    Isolate * isolate = args.GetIsolate();

    if ( args.Length() < 1 ) {
        return;
    }
    else if ( args[0]->IsNull() ) {
        return;
    }
    else if ( args[0]->IsUndefined() ) {
        return;
    }
    else if (!args[0]->IsString()) {
        // This clause would catch IsNull and IsUndefined too...
        return ;
    }

    v8::String::Utf8Value s(args[0]);
    std::string str(*s);
    std::reverse(str.begin(), str.end());    

    Local retval = String::NewFromUtf8(isolate, str.c_str());
    args.GetReturnValue().Set(retval);
}

前往前进,下载查看完整的源代码,并看看-该代码是在/conversions目录中。你会使用整数,布尔值,对象和数组见的例子。

Looking for more info?

This post is actually a small excerpt from a book I’ve published - Node.js C++ Addons that covers this in detail. In it, you’ll also find equivalent code when using NaN. If you are interested, click here for the full contents and info on how to get your copy.

寻找更多信息?

这篇文章实际上是从一本书,我已经出版了一本小摘录- Node.js的C ++扩展中心覆盖此详细。在这里面,你还可以使用NaN当发现等效代码。如果你有兴趣,请点击这里了解如何让你的副本中的全部内容和信息。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84848.html

相关文章

  • javascript引擎——V8

    摘要:类将源代码解释并构建成抽象语法树,使用类来创建它们,并使用类来分配内存。类抽象语法树的访问者类,主要用来遍历抽象语法树。在该函数中,先使用类来生成抽象语法树再使用类来生成本地代码。 通过上一篇文章,我们知道了JavaScript引擎是执行JavaScript代码的程序或解释器,了解了JavaScript引擎的基本工作原理。我们经常听说的JavaScript引擎就是V8引擎,这篇文章我们...

    luoyibu 评论0 收藏0
  • 通俗方式理解动态类型,静态类型;强类型,弱类型

    摘要:不允许隐式转换的是强类型,允许隐式转换的是弱类型。拿一段代码举例在使用调用函数的时候会先生成一个类模板运行时生成,执行的时候会生成类模板,执行的时候会生成类模板。 0 x 01 引言 今天和一个朋友讨论 C++ 是强类型还是弱类型的时候,他告诉我 C++ 是强类型的,他和我说因为 C++ 在写的时候需要 int,float 等等关键字去定义变量,因此 C++ 是强类型的,我告诉他 C+...

    周国辉 评论0 收藏0
  • [译] V8 使用者文档

    摘要:注意句柄栈并不是调用栈中的一部分,但句柄域却在栈中。一个依赖于构造函数和析构函数来管理下层对象的生命周期。对象模板用来配置将这个函数作为构造函数而创建的对象。 如果你已经阅读过了上手指南,那么你已经知道了如何作为一个单独的虚拟机使用 V8 ,并且熟悉了一些 V8 中的关键概念,如句柄,域 和上下文。在本文档中,还将继续深入讨论这些概念并且介绍其他一些在你的 C++ 应用中使用 V8 的...

    lei___ 评论0 收藏0
  • 2017-07-31 前端日报

    摘要:前端日报精选中的组件通信问题详解页面的渲染过程面试中问什么问题加实现图片前端压缩并上传用画一个迷宫中文译当不使用框架时疯狂的技术宅在翻译面向编程连续改造个网页掘金周刊技术周刊期知乎专栏技术周刊包管理的前世今生众成翻译版发布 2017-07-31 前端日报 精选 React中的组件通信问题详解 Weex 页面的渲染过程面试中问什么React问题?HTML5 file API加canvas...

    shadowbook 评论0 收藏0
  • JavaScript工作原理(二):V8引擎和5招高效代码

    摘要:引擎可以用标准解释器或即时编译器来实现,即时编译器以某种形式将代码编译为字节码。这里的主要区别在于不生成字节码或任何中间代码。请注意,不使用中间字节码表示法,不需要解释器。这允许在正常执行期间非常短的暂停。 本系列的第一篇文章重点介绍了引擎,运行时和调用栈的概述。第二篇文章将深入V8的JavaScript引擎的内部。我们还会提供一些关于如何编写更好的JavaScript代码的技巧。 概...

    leone 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<