在 V8 中從 JavaScript 到 C++ 的類型轉換

本文轉載自:衆成翻譯
譯者:亂髮小生
連接:http://www.zcfy.cc/article/3360
原文:https://nodeaddons.com/type-conversions-from-javascript-to-c-in-v8/javascript

學習怎樣傳遞信息從JavaScript到C++是一個很是難的事情。緣由在於JavaScript和C++兩種語言類型之間的巨大差別。雖然C++是一門強類型語言("42"不是一個整數類型,它只是一個字符串!),JavaScript很是渴望幫咱們轉換這些類型。html

JavaScript語言包含 String,Numbers,Booleans,null,undefined這5種原始類型。V8使用繼承方式,JavaScript的類型都繼承於C++的Value,和Primitive的子類。除了標準的JavaScript語言以外,V8同時支持整型(Int32 and Uint32)。你能夠在 這裏查看全部的類型。java

全部JavaScript值的引用都經過C++的Handle對象來保存-在大多數狀況下是Local。Handle對象把運行中的JavaScript指向V8的存儲單元。你能夠在我上一篇文章學習更多的存儲單元的知識。node

當你經過API來爲這些基礎單元工做時,你會注意到沒有分配Local對象很奇怪!這是有很重要的如如下三個緣由:git

  1. JavaScript語句就是指向V8的存儲單元的。好比var x = 5; ,就是使X指向一個5的存儲單元,從新分配x=6並無改變這個存儲單元,它只是使x指向6.若是xy都賦值爲10,那麼他們都指向同一個存儲單元。
    2.函數調用值的傳遞,因此JavaScript 調用帶有參數的C++插件,若是這個值是一個原始類型數據,它始終是一個獨特的副本,改變它的值對調用的代碼沒有任何影響。github

  2. Handles(Local)都引用存儲單元,基於上述第一點,讓handle的值改變是沒有任何意義的,由於它的基本數據類型沒有變。npm

但願這是有道理的,而後你仍有可能修改V8的變量,咱們只須要從新賦值給它。數組

幾個例子

如今,讓咱們看來一下Number 這種基本類型,當咱們構建一個用JavaScript寫的C++插件,看看它會從JavaScript接收什麼。我寫了一個C++PassNumber函數的例子:函數

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

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

    value+= 42;

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

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

完整的插件代碼在這裏post

這個插件沒有作對函數的參數作任何處理,甚至它存不存在都不能保證。下面是相關的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<Value>& args) {
    Isolate * isolate = args.GetIsolate();

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

    Local<String> 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<Value>& 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<String> 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當發現等效代碼。若是你有興趣,請點擊這裏瞭解如何讓你的副本中的所有內容和信息。

相關文章
相關標籤/搜索