首頁 web前端 js教程 Node.js插件的正確編寫方式_node.js

Node.js插件的正確編寫方式_node.js

May 16, 2016 pm 04:40 PM
nodejs 外掛

Node.js在利用JavaScript編寫後端方面效果拔群,值得我們多加嘗試。不過如果大家需要一些無法直接使用的功能甚至是根本無從實現的模組使用,那麼能否從C/C 庫當中引入此類成果呢?答案是肯定的,大家要做的就是寫一個插件,並藉此在自己的JavaScript程式碼中使用其它程式碼庫的資源。下面我們就一同開始今天的探詢之旅。

介紹

正如Node.js在官方說明文件中所言,插件是以動態方式進行連結的共享式對象,能夠將JavaScript程式碼與C/C 函式庫接駁起來。這意味著我們可以引用任何來自C/C 庫中的內容,並透過建立外掛程式的方式將其納入到Node.js當中。

作為實例,我們將為標準std::string物件建立一套封裝。

準備工作

在我們開始寫工作之前,大家首先需要確保自己已經準備好所有後續模組編譯所需的素材。大家需要node-gyp及其全部依賴關係。大家可以利用以下指令安裝node-gyp:

npm install -g node-gyp
登入後複製

 在依賴性方面,我們需要為Unix系統準備以下項目:• Python (要求2.7版本, 3.x無法正常起效)

• make

• 一款C 編譯器工具鏈(例如gpp或g )

舉例來說,在Ubuntu上大家可以利用以下指令安裝所有上述項目(其中Python 2.7應該已經預先安裝完畢了):

sudo apt-get install build-essentials 
登入後複製

在Windows系統環境下,大家需要的是:

• Python (2.7.3版本, 3.x無法正常起效)

• 微軟Visual Studio C 2010 (適用於Windows XP/Vista)

• 微軟Visual Studio C 2012 for Windows Desktop (適用於Windows 7/8)

強調一點,Visual Studio的Express版本也能正常起效。

binding.gyp檔

該檔案由node-gyp使用,旨在為我們的插件產生適當的build檔案。大家可以點擊這裡查看維基百科提供的.gyp文件說明文檔,但今天我們要使用的實例非常簡單、因此只需使用以下程式碼即可:

{ 
  "targets": [ 
    { 
      "target_name": "stdstring", 
      "sources": [ "addon.cc", "stdstring.cc" ] 
    } 
  ] 
}
登入後複製

其中target_name可以設定為大家喜歡的任何內容。而sources數組當中包含該插件需要用到的所有來源檔案。在我們的實例中還包括addon.cc,它的作用在於容納編譯插件及stdstring.cc所必需的程式碼,外加我們的封裝類別。

STDStringWrapper類別

第一步,我們要做的是在stdstring.h檔案當中定義自己的類別。如果大家對於C 程式比較熟悉,那麼一定不會對以下兩行程式碼感到陌生。

#ifndef STDSTRING_H 
#define STDSTRING_H
登入後複製

這屬於標準的include guard。接下來,我們需要將以下兩個header納入include範疇:

#include 
#include
第一個面向的是std::string類,而第二個include則作用於全部與Node以及V8相關的內容。

這一步驟完成之後,我們可以對自己的類別進行聲明:

class STDStringWrapper : public node::ObjectWrap {
對於所有我們打算包含在插件當中的類別來說,我們必須擴展node::ObjectWrap類別。

現在我們可以開始定義該類別的private屬性了:

private: 
  std::string* s_; 
  
  explicit STDStringWrapper(std::string s = ""); 
  ~STDStringWrapper();
登入後複製

除了建構子與解析函數,我們還要為std::string定義一個指標。這是該技術的核心所在,能夠被用於將C/C 程式碼庫與Node相對接——我們為該C/C 類別定義一個私有指針,並將在隨後的所有方法中利用該指標實作操作。

現在我們聲明的constructor靜態屬性,它將為我們在V8中創建的類別提供函數:

static v8::Persistent constructor;
有興趣的朋友可以點擊此處參閱範本說明方案以獲取更多細節資訊。

現在我們還需要一個New方法,它將被分配給前面提到的constructor,同時V8會對我們的類別進行初始化:

static v8::Handle New(const v8::Arguments& args);
作用於V8的每一個函數都應該遵循以下要求:它將接受指向v8::Arguments物件的引用,並傳回一個v8::Handle>v8::Value>-這正是我們在使用強型別C 編碼時,V8處理弱型別JavaScript的一貫方式。

在此之後,我們還需要將另外兩個方法插入到物件的原型當中:

static v8::Handle add(const v8::Arguments& args); 
static v8::Handle toString(const v8::Arguments& args);
登入後複製

其中toString()方法允許我們在將其與普通JavaScript字串共同使用時獲得s_的值而非[Object object]的值。

最後,我們將引入初始化方法(此方法將由V8呼叫並指派給constructor函數)並關閉include guard:

public: 
    static void Init(v8::Handle exports); 
}; 
  
#endif
登入後複製

其中exports对象在JavaScript模块中的作用等同于module.exports。

stdstring.cc文件、构造函数与解析函数

现在来创建stdstring.cc文件。我们首先需要include我们的header:

#include "stdstring.h" 
登入後複製

下面为constructor定义属性(因为它属于静态函数):

v8::Persistent STDStringWrapper::constructor; 
登入後複製

这个为类服务的构造函数将分配s_属性:

STDStringWrapper::STDStringWrapper(std::string s) { 
  s_ = new std::string(s); 
}
登入後複製

而解析函数将对其进行delete,从而避免内存溢出:

STDStringWrapper::~STDStringWrapper() { 
  delete s_; 
}
登入後複製

再有,大家必须delete掉所有与new一同分配的内容,因为每一次此类情况都有可能造成异常,因此请牢牢记住上述操作或者使用共享指针。

Init方法

该方法将由V8加以调用,旨在对我们的类进行初始化(分配constructor,将我们所有打算在JavaScript当中使用的内容安置在exports对象当中):

void STDStringWrapper::Init(v8::Handle exports) {
首先,我们需要为自己的New方法创建一个函数模板:

v8::Local tpl = v8::FunctionTemplate::New(New);
这有点类似于JavaScipt当中的new Function——它允许我们准备好自己的JavaScript类。

现在我们可以根据实际需要为该函数设定名称了(如果大家漏掉了这一步,那么构造函数将处于匿名状态,即名称为function someName() {}或者function () {}):

tpl->SetClassName(v8::String::NewSymbol("STDString"));
我们利用v8::String::NewSymbol()来创建一个用于属性名称的特殊类型字符串——这能为引擎的运作节约一点点时间。

在此之后,我们需要设定我们的类实例当中包含多少个字段:

tpl->InstanceTemplate()->SetInternalFieldCount(2);
我们拥有两个方法——add()与toString(),因此我们将数量设置为2。现在我们可以将自己的方法添加到函数原型当中了:

tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("add"), v8::FunctionTemplate::New(add)->GetFunction());
tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("toString"), v8::FunctionTemplate::New(toString)->GetFunction());
这部分代码量看起来比较大,但只要认真观察大家就会发现其中的规律:我们利用tpl->PrototypeTemplate()->Set()来添加每一个方法。我们还利用v8::String::NewSymbol()为它们提供名称与FunctionTemplate。

最后,我们可以将该构造函数安置于我们的constructor类属性内的exports对象中:

constructor = v8::Persistent::New(tpl->GetFunction()); 
  exports->Set(v8::String::NewSymbol("STDString"), constructor); 
}
登入後複製

New方法

现在我们要做的是定义一个与JavaScript Object.prototype.constructor运作效果相同的方法:

v8::Handle STDStringWrapper::New(const v8::Arguments& args) {
登入後複製

我们首先需要为其创建一个范围:

v8::HandleScope scope; 
登入後複製

在此之后,我们可以利用args对象的.IsConstructCall()方法来检查该构造函数是否能够利用new关键词加以调用:

if (args.IsConstructCall()) { 
登入後複製

如果可以,我们首先如下所示将参数传递至std::string处:

v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str);
登入後複製

……这样我们就能将它传递到我们封装类的构造函数当中了:

STDStringWrapper* obj = new STDStringWrapper(s);
登入後複製

在此之后,我们可以利用之前创建的该对象的.Wrap()方法(继承自node::ObjectWrap)来将它分配给this变量:

obj->Wrap(args.This()); 
登入後複製

最后,我们可以返回这个新创建的对象:

return args.This(); 
登入後複製

如果该函数无法利用new进行调用,我们也可以直接调用构造函数。接下来,我们要做的是为参数计数设置一个常数:

} else { 
  const int argc = 1; 
登入後複製

现在我们需要利用自己的参数创建一个数组:

v8::Local argv[argc] = { args[0] }; 
登入後複製

然后将constructor->NewInstance方法的结果传递至scope.Close,这样该对象就能在随后发挥作用(scope.Close基本上允许大家通过将对象处理句柄移动至更高范围的方式对其加以维持——这也是函数的起效方式):

    return scope.Close(constructor->NewInstance(argc, argv)); 
  } 
} 
登入後複製

add方法

现在让我们创建add方法,它的作用是允许大家向对象的内部std::string添加内容:

v8::Handle STDStringWrapper::add(const v8::Arguments& args) { 
登入後複製

首先,我们需要为我们的函数创建一个范围,并像之前那样把该参数转换到std::string当中:

v8::HandleScope scope; 
  
v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str); 
登入後複製

现在我们需要对该对象进行拆包。我们之前也进行过这种反向封装操作——这一次我们是要从this变量当中获取指向对象的指针。

STDStringWrapper* obj = ObjectWrap::Unwrap(args.This()); 
登入後複製

接着我们可以访问s_属性并使用其.append()方法:

obj->s_->append(s); 
登入後複製

最后,我们返回s_属性的当前值(需要再次使用scope.Close):

return scope.Close(v8::String::New(obj->s_->c_str())); 
登入後複製

由于v8::String::New()方法只能将char pointer作为值来接受,因此我们需要使用obj->s_->c_str()来加以获取。

这时大家的插件文件夹中还应该创建出一个build目录。

测试

现在我们可以对自己的插件进行测试了。在我们的插件目录中创建一个test.js文件以及必要的编译库(大家可以直接略过.node扩展):

var addon = require('./build/Release/addon'); 
登入後複製

下一步,为我们的对象创建一个新实例:

var test = new addon.STDString('test'); 
登入後複製

下面再对其进行操作,例如添加或者将其转化为字符串:

test.add('!'); 
console.log('test\'s contents: %s', test); 
登入後複製

在运行之后,大家应该在控制台中看到以下执行结果:

结论

我希望大家能在阅读了本教程之后打消顾虑,将创建与测试以C/C++库为基础的定制化Node.js插件视为一项无甚难度的任务。大家可以利用这种技术轻松将几乎任何C/C++库引入Node.js当中。如果大家愿意,还可以根据实际需求为插件添加更多功能。std::string当中提供大量方法,我们可以将它们作为练习素材。

实用链接

感兴趣的朋友可以查看以下链接以获取更多与Node.js插件开发、V8以及C事件循环库相关的资源与详细信息。

• Node.js插件说明文档

• V8说明文档

• libuv (C事件循环库),来自GitHub

英文:http://code.tutsplus.com/tutorials/writing-nodejs-addons--cms-21771

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

nodejs和vuejs區別 nodejs和vuejs區別 Apr 21, 2024 am 04:17 AM

Node.js 是一種伺服器端 JavaScript 執行時,而 Vue.js 是一個客戶端 JavaScript 框架,用於建立互動式使用者介面。 Node.js 用於伺服器端開發,如後端服務 API 開發和資料處理,而 Vue.js 用於用戶端開發,如單一頁面應用程式和響應式使用者介面。

nodejs是後端框架嗎 nodejs是後端框架嗎 Apr 21, 2024 am 05:09 AM

Node.js 可作為後端框架使用,因為它提供高效能、可擴展性、跨平台支援、豐富的生態系統和易於開發等功能。

nodejs怎麼連接mysql資料庫 nodejs怎麼連接mysql資料庫 Apr 21, 2024 am 06:13 AM

要連接 MySQL 資料庫,需要遵循以下步驟:安裝 mysql2 驅動程式。使用 mysql2.createConnection() 建立連接對象,其中包含主機位址、連接埠、使用者名稱、密碼和資料庫名稱。使用 connection.query() 執行查詢。最後使用 connection.end() 結束連線。

nodejs中的全域變數有哪些 nodejs中的全域變數有哪些 Apr 21, 2024 am 04:54 AM

Node.js 中存在以下全域變數:全域物件:global核心模組:process、console、require執行階段環境變數:__dirname、__filename、__line、__column常數:undefined、null、NaN、Infinity、-Infinity

nodejs安裝目錄裡的npm與npm.cmd檔有什麼差別 nodejs安裝目錄裡的npm與npm.cmd檔有什麼差別 Apr 21, 2024 am 05:18 AM

Node.js 安裝目錄中有兩個與 npm 相關的文件:npm 和 npm.cmd,區別如下:擴展名不同:npm 是可執行文件,npm.cmd 是命令視窗快捷方式。 Windows 使用者:npm.cmd 可以在命令提示字元中使用,npm 只能從命令列執行。相容性:npm.cmd 特定於 Windows 系統,npm 跨平台可用。使用建議:Windows 使用者使用 npm.cmd,其他作業系統使用 npm。

nodejs和java的差別大嗎 nodejs和java的差別大嗎 Apr 21, 2024 am 06:12 AM

Node.js 和 Java 的主要差異在於設計和特性:事件驅動與執行緒驅動:Node.js 基於事件驅動,Java 基於執行緒驅動。單執行緒與多執行緒:Node.js 使用單執行緒事件循環,Java 使用多執行緒架構。執行時間環境:Node.js 在 V8 JavaScript 引擎上運行,而 Java 在 JVM 上運行。語法:Node.js 使用 JavaScript 語法,而 Java 使用 Java 語法。用途:Node.js 適用於 I/O 密集型任務,而 Java 適用於大型企業應用程式。

nodejs是後端開發語言嗎 nodejs是後端開發語言嗎 Apr 21, 2024 am 05:09 AM

是的,Node.js 是一種後端開發語言。它用於後端開發,包括處理伺服器端業務邏輯、管理資料庫連接和提供 API。

nodejs和java選哪個 nodejs和java選哪個 Apr 21, 2024 am 04:40 AM

Node.js 和 Java 在 Web 開發中各有優劣,因此選擇取決於專案需求。 Node.js 擅長即時應用程式、快速開發和微服務架構,而 Java 則在企業級支援、效能和安全性方面佔優。

See all articles