1. 程式人生 > >Node.js And C++__2. V8引擎介紹

Node.js And C++__2. V8引擎介紹

說明:

V8是node很重要的一個模組,是執行js程式碼的引擎。我們主要通過使用V8的API來完成C++ 和 Node之間的變數型別轉換、非同步呼叫的實現和類的封裝等。

主要知識點:

1.Nodejs主要由V8和libuv組成;

2.V8是js執行的引擎;

3.Nodejs變數的建立和記憶體的釋放都是通過V8;

4.V8可以是C++ Addon訪問和轉換js變數;

5.儲存單元在JS和C++中都沒有引用的情況下才會被回收;

6.可以把Local 看成 智慧指標。

Isolates

1.每一個isolate都有一個獨立的堆,儲存建立的變數和執行環境;

2.Nodejs只有一個isolate,而且不能再建立;(原始碼中加鎖限制其他執行緒建立)

3.不能再其他執行緒中呼叫isolate;

4.可以把多個V8嵌入C++程式中,是程式可以建立多個isolate,但是要有時候保持isolate的同步是個棘手的問題。

Contexts

1.包含了執行環境和堆:全域性變數、變數、函式、系統函式、物件、模組、控制代碼、文件等等;

2.每個上下文都是獨立的,包含自己的執行環境和堆;

3.我們只能通過V8獲取、建立、修改上下文的內容。

JavaScript variables, C++ Handles

這裡寫圖片描述

這裡寫圖片描述

HandleScope

1.非同步函式和回撥引數只能通過HandleScope獲取相應的引數;

2.HandleScope不能建立;

3.Local handle 在回撥或者C++返回到js的時候進行垃圾回收;

4.在很多例子中都能看到,直接使用了HandleScope,那是因為Nodejs在呼叫我們寫的Addon之前建立了一個HandleScope 物件。

V8 Data Type

這裡寫圖片描述

基本型別

Strings Numbers Booleans null undefined

通過V8 API操作基礎資料,而不是直接操作又三個原因:

  • js基本型別的儲存機制不可變:js中var x = 5,分配了一個儲存單元 a;當 x = 6,a儲存單元的值沒有改變,而是新建立了一個值為6的儲存單元b,x指向b。

  • js呼叫C++時,通過引數(傳值的方式),內部空間對引數的操作不會影響外部變數。

  • Handles能記錄變數的引用數,在引用數為零的時候,可以通過垃圾回收機制清空無效引用。

Number -> double

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

 // 方式1
 Local<Number> target = args[0]->ToNumber();
 // 方式2
 double value = target->NumberValue();

 // value is now OUTSIDE of V8 - we can use it in
 // all the C++ standard ways.
 }

新建Number並返回

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

// 如果 引數為空,則返回NAN ; NAN+=42 還是等於NAN
double value = args[0]->NumberValue();

value+= 42;

// 新建,必須獲取V8核心的獨立堆
// 在V8核心的堆上床架一個儲存單元,並把value賦值給儲存單元
// retval指向這個儲存單元
Local<Number> retval = Number::New(isolate, value);
// 返回
 args.GetReturnValue().Set(retval);
 }

JS呼叫

 const addon = require('./build/Release/primitive');

 var number_returned = addon.pass_number(23);
 console.log(number_returned); // prints 65

 var number_returned = addon.pass_number(0.5);
 console.log(number_returned); // prints 42.5

Number -> int

void PassInteger(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
     // 如果傳進來的是是5.7 則v8會自動取整為5
    int value = args[0]->Int32Value();
    Local<Number> retval = Int32::New(isolate, value + 42);
    args.GetReturnValue().Set(retval);
}

Boolean -> bool

void PassBoolean(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    // Null、未定義、0和空字串總是被轉換為false。
    // 其他一切(包括空物件和陣列)都被認為是正確的。
    bool value = args[0]->BooleanValue();
    Local<Boolean> retval = Boolean::New(isolate, !value);
    args.GetReturnValue().Set(retval);
}

String->string

void PassString(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    v8::String::Utf8Value s(args[0]);
    // 就像數字物件一樣,字串是不可變的
    // 現在操縱字串的C++表示,而不是V8的儲存單元
    std::string str(*s);

    // 返回轉換後的字串,我們必須通過建立一個新的字串將它移回V8。
    Local<String> retval =
    String::NewFromUtf8(isolate, str.c_str());
    args.GetReturnValue().Set(retval);
}

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

    // js String 型別轉換成 v8 String 型別 
    Local<String> cString = Local<String>::Cast(args[0]); 
    String::Utf8Value utfValueD(cString); 
    std::string str(*utfValueD); 

    // 返回轉換後的字串,我們必須通過建立一個新的字串將它移回V8。
    Local<String> retval =
    String::NewFromUtf8(isolate, str.c_str());
    args.GetReturnValue().Set(retval);
}

Objects

void PassObject(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    Local<Object> target = args[0]->ToObject();
    // 如果沒有 y 欄位回新增一個 y 欄位,並把10賦值給y
    // 如果有 y 欄位則 修改 y的值為10,不論 y 之前是什麼值
    // 給 y 賦值 實質是新分配了一個 y 的值,y 指向這個分配的值
    target->Set(String::NewFromUtf8(isolate, "y"),
    Number::New(isolate, 10));
    args.GetReturnValue().Set(target);
}
// -----------------------------------------------------------------
//js呼叫
var retval = addon.pass_object({x : 3});
console.log(retval); // prints {x : 3, y: 10}

var retval = addon.pass_object({x : 3, y : "hello"});
console.log(retval); // still prints {x : 3, y: 10}
void PassObject(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    Local<Object> target = args[0]->ToObject();
    //
    Local<String> prop = String::NewFromUtf8(isolate, "y");
    double y = target->Get(prop)->NumberValue();
    // “修改”物件的屬性,也需要分配一個新的值,讓物件的屬性指向新分配的值空間
    target->Set(prop, Number::New(isolate, y + 42));
     args.GetReturnValue().Set(target);
}
// -----------------------------------------------------------------
//js呼叫
var retval = addon.pass_object({x : 3, y: 10});
console.log(retval); // prints {x : 3, y: 52}

var retval = addon.pass_object({x : 3, y: "hello"});
console.log(retval); // prints {x : 3, y: NaN}

var retval = addon.pass_object({x : 3});
console.log(retval); // prints {x : 3, y: NaN}

new Objects From c++

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

     Local<Object> obj = Object::New(isolate);
     // 構造 json 物件
     obj->Set(String::NewFromUtf8(isolate, "lon"), Number::New(isolate, 114));
     obj->Set(String::NewFromUtf8(isolate, "lat"), Number::New(isolate, 29));

     args.GetReturnValue().Set(obj);
}
// -----------------------------------------------------------------
//js呼叫
var retval = addon.get_obj_from_cpp();
console.log(retval); // prints {lon : 114, lat: 29}

Array


void IncrementArray(const FunctionCallbackInfo<Value>& args) {
  Isolate * isolate = args.GetIsolate();
  Local<Array> array = Local<Array>::Cast(args[0]);

  for (unsigned int i = 0; i < array->Length(); i++ ) {
    // 通過索引獲取值
    double value = array->Get(i)->NumberValue();
    // 修改陣列的值
    array->Set(i, Number::New(isolate, value + 1));
  }

  // JavaScript陣列可能是稀疏的——我們應該考慮一下
  // 在我們的addon中迴圈遍歷陣列。下面的addon程式碼避免了處理
  // 任何尚未被JavaScript設定的索引
  for (unsigned int i = 0; i < array->Length(); i++ ) {
    if (array->Has(i)) {
        double value = array->Get(i)->NumberValue();
        array->Set(i, Number::New(isolate, value + 1));
    }
  }

  // 沒有返回值,類似於C++ 傳引用
}

// -----------------------------------------------------------------
//js呼叫
var data = [1, 2, "1""""hello", "world", 3];
addon.increment_array(data);
console.log(data); // prints [ 2, 3, 2, NaN, NaN, NaN, 4 ]
void GetArray(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    Local<Array> a = Array::New(isolate);
    // 陣列不需要連續  也不需要型別一致
    a->Set(0, Number::New(isolate, 10));
    a->Set(2, String::NewFromUtf8(isolate, "string"));
    args.GetReturnValue().Set(a);
}

// -----------------------------------------------------------------
//js呼叫
console.log(addon.GetArray()); // prints [ 10, , 'string' ]

Local Handles

HandleScope 可以在addon中為儲存單元建立本地控制代碼,將這些本地控制代碼傳遞給不同的函式.

Local<Value> make_return(Isolate * isolate, const Local<Object> input ) {
    Local<String> x_prop = String::NewFromUtf8(isolate, "x");
    Local<String> y_prop = String::NewFromUtf8(isolate, "y");
    Local<String> sum_prop = String::NewFromUtf8(isolate, "sum");
    Local<String> product_prop = String::NewFromUtf8(isolate, "product");
    double x = input->Get(x_prop)->NumberValue();
    double y = input->Get(y_prop)->NumberValue();
    Local<Object> obj = Object::New(isolate);
    obj->Set(sum_prop, Number::New(isolate, x + y));
    obj->Set(product_prop, Number::New(isolate, x * y));
    return obj;
}

// 對PassObject的呼叫開始之前,Nodejs建立了一個HandleScope並保持活躍
// 重要的是,在makereturn中建立的obj控制代碼
// 當函式返回時,不會被銷燬,因為它屬於原始的
// 在這個isolate被摧毀之前,HandleScope將會繼續“存活”,而由isolate儲存的變數一致能被訪問
void PassObject(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    Local<Object> target = args[0]->ToObject();
    Local<Value> obj = make_return(isolate, target);
    args.GetReturnValue().Set(obj);
}

危險用法

Local<Value> make_return(Isolate * isolate, const Local<Object> input ) {
    .. same as above...

    HandleScope scope(isolate); // DANGER! All handles contained in this now.
    Local<Object> obj = Object::New(isolate);
    obj->Set(sum_prop, Number::New(isolate, x + y));
    obj->Set(product_prop, Number::New(isolate, x * y));
    return obj; // 一旦函式返回,scope就會被進行垃圾回收,很多變數的值就沒有了
}

void PassObject(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    Local<Object> target = args[0]->ToObject();
    Local<Value> obj = make_return(isolate, target);
    // 在函式返回的時候 isolate 已經被釋放掉了,所以線面語句訪問將會報異常
    Local<Object> target = args[0]->ToObject();
    args.GetReturnValue().Set(obj);
}

正確用法

Local<Value> make_return(Isolate * isolate, const Local<Object> input ) {
    .. same as above...

    // 函式返回時,不會立即進行垃圾回收,然函式體內的變數能被返回給接收者
    EscapableHandleScope scope(isolate);

    Local<Object> obj = Object::New(isolate);
    obj->Set(sum_prop, Number::New(isolate, x + y));
    obj->Set(product_prop, Number::New(isolate, x * y));

     return scope.Escape(obj); // 強行續命,然函式返回時跳過垃圾回收機制
}

// 即使能估計程式碼中的scope很小,但是也建議不要自己建立scope,因為回首不當會使程式有異常
// 當scope離開函式體,會對scope進行垃圾回收。不是obj等local 控制代碼離開時進行回收。

Persistent Handles

1.持久的物件不會因為函式返回而釋放掉記憶體;

2.在多次addon呼叫能把狀態延續記錄下來,類似於全域性變數;

3.在多執行緒和非同步程式設計中非常有用;

4.持久化物件不能以任何方式進行拷貝;

5.持久化物件的釋放/賦值都只能他通過Reset來操作

Persistent<Object> persist;   // 持久物件,老生代記憶體
int increment;                // 增量

void Increment(const FunctionCallbackInfo<Value>& args) {
    Isolate * isolate = args.GetIsolate();
    Local<Object> target = Local<Object>::New(isolate, persist);
    Local<String> prop = String::NewFromUtf8(isolate, "x");
    double x = target->Get(prop)->NumberValue();
    target->Set(prop, Number::New(isolate, x + increment));
}

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

     // set global variables so they survive returning from this call
     persist.Reset(isolate, args[0]->ToObject());
     increment = args[1]->NumberValue();
}
var target = { x: 3 };

addon.init(target, 10);
console.log(target);

for ( var i = 0; i < 3; i++ ) {
    addon.increment();
    console.log(target);
}

//列印結果
//  { x: 3 }
//  { x: 13 }
//  { x: 23 }
//  { x: 33 }

相關推薦

Node.js And C++__2. V8引擎介紹

說明: V8是node很重要的一個模組,是執行js程式碼的引擎。我們主要通過使用V8的API來完成C++ 和 Node之間的變數型別轉換、非同步呼叫的實現和類的封裝等。 主要知識點: 1.Nodejs主要由V8和libuv組成; 2.V8

Node.js And C++__6.V8Node介面抽象

在這本書的前五章中,我們一直在假設我們正在建立的addon使用的Node.js版衛0.12到6.0。這是一個相當寬泛的版本,但是有較早版本的節點。我們的addons不會編譯/工作的js。在使用雲託管服務時,這可能是一個很重要的問題,因為它可能會使用早期版本的no

Node.js And C++__9.Addons替代解決方案

​ 注:本附錄主要取自作者網站blog.scottfrees.com上的一系列部落格文章,其中涵蓋了將現有的C/C++程式*整合到一個Node.js 的各種方法。這個系列是獨立於本書的,因此,您可能會發現其中的一些內容是多餘的,但是它展示了一個真實的例子,它

Node.js And C++__​10.Buffers

本書的第2章介紹了在將資料移動到C++外掛時使用典型的JavaScript資料型別。Node.js 引入了一種新的資料型別, `Buffer`,這在標準JavaScript中是找不到的(儘管新版本的JavaScript現在已經有了型別化陣列,它們提供了許多相同的

Ubuntu 18.04上安裝 node.js and npm and vuejs project

soooooooooooooooooooooooooooooooooooooooooooooooooo easy  ! sudo apt install nodejs sudo apt install npm 確認安裝版本 : $ nodejs --ve

how to install node.js and npm on Ubuntu

To install Node.js, type the following command in your terminal: sudo apt-get install nodejs Then install the Node package manager, npm: sud

node.js 6個常用核心模組介紹

一、HTTP模組 作用:處理網路客戶端的請求 二、URL模組  作用:處理客戶端請求過來的url 三、Query Strings模組 作用:處理客戶端通過get/post請求傳遞過來的引數  四、File System模組 作用:在服務端操作檔案,可能是需要將瀏

node.js呼叫C++函式

新手寫的不好勿噴要想node.js呼叫C++的函式等,須先將C++程式碼編譯成二進位制的.node檔案。node.js官方文件https://nodejs.org/dist/lates... 中的C++ addons介紹瞭如何將C++的程式碼編譯為二進位制的.nod

Web scraping with Node.JS and Cheerio

Almost all the information on the web exists in the form of HTML pages. The information in these pages is structured as paragraphs, headings,

Node.jsC++ 之間的型別轉換

我非常喜歡使用 Node.js,但是當涉及到計算密集型的場景時 Node.js 就不能夠很好地勝任了。而在這樣的情況下 C++ 是一個很好的選擇,非常幸運 Node.js 官方提供了 C/C++ Addons 的機制讓我們能夠使用 V8 API 把 Node.js 和 C++ 結合起來。 雖然在 Node.

Node.js中async庫同步介面介紹

series: 序列執行,一個函式陣列中的每個函式,每一個函式執行完成之後才能執行下一個函式。 parallel: 並行執行多個函式,每個函式都是立即執行,不需要等待其它函式先執行。傳給最終callback的陣列中的資料按照tasks中宣告的順序,而不是執行完成的順序。 whilst: 相當於while,但

如何在VS2015中搭建可以寫node.jsC++ Addon的環境

如何在VS2015中搭建可以寫node.js的C++Addon的環境     由於專案需要,編寫一些提供給node.js呼叫的C++實現的外掛,所以在vs中搭建開發node addon 的環境。

[Tools] Batch Create Markdown Files from a Template with Node.js and Mustache

let luke code lap output view eight ade str Creating Markdown files from a template is a straightforward process with Node.js and Mustach

vs.code調試node.jsC++擴展

clu min 很多 執行過程 ide 安裝 font include pat 其實也很簡單 點擊“Add Configration..”後,會在launch.json增加一個節點,稍調整兩個位置 以上完了後,就能在cpp源碼裏加上自己的

Node.js學習(14)----EJS模板引擎

有的人說寫乾淨整潔的程式碼應該養成每天的習慣,但是我感覺EJS是一個絕佳的助手,如果你在面對以下幾種情況: 1.用JavaScript建立HTML字串 正如我們在新手教程中所討論的,在JavaScript中拼字串的缺點是可維護性不好。當你在JavaScript中將這些字串拼到一起時,很難看出你正在寫的H

node.js and express學習筆記與說明

node簡介 node不是一門程式語言 node.exe是js程式碼可以在瀏覽器外跨平臺的直接運行於計算機上的執行環境,類似於java的JVM虛擬機器。 核心運用的是谷歌一樣的V8引擎。 優勢,可以前後端都用js來寫,更方便程式的執行。 node架構與優勢

[Docker] Linking Node.js and MongoDB Containers

install ren netcore con mon gre work rom trigger To do communcation between containers, we need to do link between containers. 1. Run

詳解Node.js API系列C/C++ Addons(2) Google V8引擎

回顧,前文由node.js寫的基於addon的hello world例子 #include <node.h> #include <v8.h> using namespace v8; Handle<Value> Method(const

學習用Node.js和Elasticsearch構建搜索引擎(6):實際項目中常用命令使用記錄

nds 黃色 ati cat htm action last shard open 1、檢測集群是否健康。 curl -XGET ‘localhost:9200/_cat/health?v‘#後面加一個v表示讓輸出內容表格顯示表頭 綠色表示一切正常,黃色表示所有