日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
使用Node.js Addon實(shí)現(xiàn)類繼承

本文轉(zhuǎn)載自微信公眾號「編程雜技」,作者theanarkh。轉(zhuǎn)載本文請聯(lián)系編程雜技公眾號。

我們提供的服務(wù)有:網(wǎng)站制作、成都網(wǎng)站建設(shè)、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、北屯ssl等。為1000+企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的北屯網(wǎng)站制作公司

前言:昨天有個同學(xué)問怎么通過NAPI把C++類的繼承關(guān)系映射到JS,很遺憾,NAPI貌似還不支持,但是V8支持,因?yàn)閂8在頭文件里導(dǎo)出了這些API,并Node.js里也依賴這些API,所以可以說是比較穩(wěn)定的。本文介紹一下如何實(shí)現(xiàn)這種映射(不確定是否能滿足這位同學(xué)的需求)。

下面我們看一下Addon的實(shí)現(xiàn)。會涉及到V8的一些使用,可以先閱讀該文章《一段js理解nodejs中js調(diào)用c++/c的過程》。首先看一下基類的實(shí)現(xiàn)。

 
 
 
  1. #ifndef BASE_H 
  2. #define BASE_H 
  3. #include  
  4. #include  
  5. #include  
  6.  
  7. using namespace node; 
  8. using namespace v8; 
  9. class Base: public ObjectWrap { 
  10.     public: 
  11.         static void New(const FunctionCallbackInfo& info) { 
  12.             // 新建一個對象,然后包裹到info.This()中,后面會解包出來使用 
  13.             Base* base =  new Base(); 
  14.             base->Wrap(info.This()); 
  15.         } 
  16.  
  17.         static void Print(const FunctionCallbackInfo& info) { 
  18.             // 解包出來使用 
  19.             Base* base = ObjectWrap::Unwrap(info.This()); 
  20.             base->print(); 
  21.         } 
  22.  
  23.         void print() { 
  24.             printf("base print\n"); 
  25.         } 
  26.  
  27.         void hello() { 
  28.             printf("base hello\n"); 
  29.         } 
  30. }; 
  31.  
  32. #endif 

Node.js提供的ObjectWrap類實(shí)現(xiàn)了Wrap和UnWrap的功能,所以我們可以繼承它簡化封包解包的邏輯。Base類定義了兩個功能函數(shù)hello和print,同時定義了兩個類靜態(tài)函數(shù)New和Print。New函數(shù)是核心邏輯,該函數(shù)在js層執(zhí)行new Base的時候會執(zhí)行并傳入一個對象,這時候我們首先創(chuàng)建一個真正的有用的對象,并且通過Wrap把該對象包裹到傳進(jìn)來的對象里。我們繼續(xù)看一下子類。

 
 
 
  1. #ifndef DERIVED_H 
  2. #define DERIVED_H 
  3. #include  
  4. #include  
  5. #include"Base.h" 
  6.  
  7. using namespace node; 
  8. using namespace v8; 
  9. class Derived: public Base { 
  10.     public: 
  11.         static void New(const FunctionCallbackInfo& info) { 
  12.             Derived* derived =  new Derived(); 
  13.             derived->Wrap(info.This()); 
  14.         } 
  15.  
  16.         static void Hello(const FunctionCallbackInfo& info) { 
  17.             Derived* derived = ObjectWrap::Unwrap(info.This()); 
  18.             // 調(diào)用基類的函數(shù) 
  19.             derived->hello(); 
  20.         } 
  21. }; 
  22.  
  23. #endif 

子類的邏輯類似,New函數(shù)和基類的邏輯一樣,除了繼承基類的方法外,額外定義了一個Hello函數(shù),但是我們看到這只是個殼子,底層還是調(diào)用了基類的函數(shù)。定義完基類和子類后,我們把這兩個類導(dǎo)出到JS。

 
 
 
  1. #include  
  2. #include "Base.h" 
  3. #include "Derived.h" 
  4.  
  5. namespace demo { 
  6.  
  7. using v8::FunctionCallbackInfo; 
  8. using v8::Isolate; 
  9. using v8::Local; 
  10. using v8::Object; 
  11. using v8::String; 
  12. using v8::Value; 
  13. using v8::FunctionTemplate; 
  14. using v8::Function; 
  15. using v8::Number; 
  16. using v8::MaybeLocal; 
  17. using v8::Context; 
  18. using v8::Int32; 
  19. using v8::NewStringType; 
  20.  
  21. void Initialize( 
  22.   Local exports, 
  23.   Local module, 
  24.   Local context 
  25. ) { 
  26.   Isolate * isolate = context->GetIsolate(); 
  27.   // 新建兩個函數(shù)模板,基類和子類,js層New導(dǎo)出的函數(shù)時,V8會執(zhí)行New函數(shù)并傳入一個對象 
  28.   Local base = FunctionTemplate::New(isolate, Base::New); 
  29.   Local derived = FunctionTemplate::New(isolate, Derived::New); 
  30.  
  31.   // js層使用的類名 
  32.   NewStringType type = NewStringType::kNormal; 
  33.   Local base_string = String::NewFromUtf8(isolate, "Base", type).ToLocalChecked(); 
  34.   Local derived_string = String::NewFromUtf8(isolate, "Derived", type).ToLocalChecked(); 
  35.  
  36.   // 預(yù)留一個指針空間 
  37.   base->InstanceTemplate()->SetInternalFieldCount(1); 
  38.   derived->InstanceTemplate()->SetInternalFieldCount(1); 
  39.  
  40.   // 定義兩個函數(shù)模板,用于屬性的值 
  41.   Local BasePrint = FunctionTemplate::New(isolate, Base::Print); 
  42.   Local Hello = FunctionTemplate::New(isolate, Derived::Hello); 
  43.  
  44.   // 給基類定義一個print函數(shù) 
  45.   base->PrototypeTemplate()->Set(isolate, "print", BasePrint); 
  46.   // 給子類定義一個hello函數(shù) 
  47.   derived->PrototypeTemplate()->Set(isolate, "hello", Hello); 
  48.   // 建立繼承關(guān)系 
  49.   derived->Inherit(base); 
  50.   // 導(dǎo)出兩個函數(shù)給js層 
  51.   exports->Set(context, base_string, base->GetFunction(context).ToLocalChecked()).Check(); 
  52.   exports->Set(context, derived_string, derived->GetFunction(context).ToLocalChecked()).Check(); 
  53.  
  54. NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, Initialize) 
  55. 我們看到給基類原型定義了一個print函數(shù),給子類定義了hello函數(shù)。最后我們看看如何在JS層使用。

     
     
     
    1. const { Base, Derived } = require('./build/Release/test.node'); 
    2. const base = new Base(); 
    3. const derived = new Derived(); 
    4. base.print(); 
    5. derived.hello(); 
    6. derived.print(); 
    7. console.log(derived instanceof Base, derived instanceof Derived) 

    下面是具體的輸出

     
     
     
    1. base print 
    2. base hello 
    3. base print 
    4. true true 

    我們逐句分析

    1 base.print()比較簡單,就是調(diào)用基類定義的Print函數(shù)。

    2 derived.hello()看起來是調(diào)用了子類的Hello函數(shù),但是Hello函數(shù)里調(diào)用了基類的hello函數(shù),實(shí)現(xiàn)了邏輯的復(fù)用。

    3 derived.print()子類沒有實(shí)現(xiàn)print函數(shù),這里調(diào)用的是基類的print函數(shù),和1一樣。

    4 derived instanceof Base, derived instanceof Derived。根據(jù)我們的定義,derived不僅是Derived的實(shí)例,也是Base的實(shí)例。

    實(shí)現(xiàn)代碼分析完了,我們看到把C++類映射到JS的方式有兩種,第一種就是兩個C++ 類沒有繼承關(guān)系,通過V8的繼承API實(shí)現(xiàn)兩個JS層存在繼承關(guān)系的類(函數(shù)),比如print函數(shù)的實(shí)現(xiàn),我們看到子類沒有實(shí)現(xiàn)print,但是可以調(diào)用print,因?yàn)榛惗x了,Node.js就是這樣處理的。第二種就是兩個存在繼承關(guān)系的C++類,同樣先通過V8的API實(shí)現(xiàn)兩個繼承的類導(dǎo)出到JS使用,因?yàn)镴S層使用的只是殼子,具體執(zhí)行到C++代碼的時候,我們再體現(xiàn)出這種繼承關(guān)系。比如Hello函數(shù)的實(shí)現(xiàn),雖然我們是在子類里導(dǎo)出了hello函數(shù),并且JS執(zhí)行hello的時候的確執(zhí)行到了子類的C++代碼,但是最后會調(diào)用基類的hello函數(shù)。

    最后我們通過Nodej.js看看是如何做這種映射的,我們通過PipeWrap.cc的實(shí)現(xiàn)進(jìn)行分析。

     
     
     
    1. // 新建一個函數(shù)模板 
    2. Local t = env->NewFunctionTemplate(New);// 繼承兩個函數(shù)模板 
    3. t->Inherit(LibuvStreamWrap::GetConstructorTemplate(env));// 導(dǎo)出給JS使用 
    4. exports->Set(env->context(), 
    5.               pipeString, 
    6.               t->GetFunction(env->context()).ToLocalChecked()).Check(); 

    上面代碼實(shí)現(xiàn)了繼承,我們看看GetConstructorTemplate的實(shí)現(xiàn)。

     
     
     
    1. tmpl = env->NewFunctionTemplate(nullptr); 
    2. env->SetProtoMethod(tmpl, "setBlocking", SetBlocking); 
    3. env->SetProtoMethod(tmpl, "readStart", JSMethod<&StreamBase::ReadStartJS>); 
    4. env->SetProtoMethod(t, "readStop", JSMethod<&StreamBase::ReadStopJS>);// ... 

    上面代碼新建了一個新的函數(shù)模板并且設(shè)置了一系列的原型屬性,那么模板t就繼承了這些屬性。我們看看Node.js里怎么使用的。

     
     
     
    1. function createHandle(fd, is_server) { 
    2.   // ... 
    3.   return new Pipe( 
    4.       is_server ? PipeConstants.SERVER : PipeConstants.SOCKET 
    5.   );} 
    6.  
    7. this._handle = createHandle(fd, false); 
    8. err = this._handle.setBlocking(true); 

    上面的代碼首先會創(chuàng)建一個Pipe對象,然后調(diào)用它的setBlocking方法。我們發(fā)現(xiàn)Pipe(pipe_wrap.cc)是沒有實(shí)現(xiàn)setBlocking函數(shù)的,但是好為什么他可以調(diào)用setBlocking呢?答案就是它的基類實(shí)現(xiàn)了。

    后記:在JS里實(shí)現(xiàn)繼承是簡單的,但是在底層實(shí)現(xiàn)起來還是比較復(fù)雜的,但是從代碼設(shè)計(jì)的角度來看是非常有必要的。

    代碼可以在倉庫獲取:

    https://github.com/theanarkh/learn-to-write-nodejs-addons。


    網(wǎng)站題目:使用Node.js Addon實(shí)現(xiàn)類繼承
    文章位置:http://www.5511xx.com/article/djeehgd.html