javascript引擎在c,c+中調用

JavaScript是一種普遍用於Web客戶端開發的腳本語言,經常使用來控制瀏覽器的DOM樹,給HTML網頁添加動態功能。目前JavaScript遵循的web標準的是ECMAScript262。因爲JavaScript提供了豐富的內置函數、良好的對象機制。因此JavaScript還能夠嵌入到某一種宿主語言中,彌補宿主語言的表現力,從而實現快速、靈活、可定製的開發。 軟件程序應用javascript 現有的主流瀏覽器基本上都實現了一個本身的JavaScript引擎。這些JavaScript引擎能夠分析、編譯和執行JavaScript腳本。這些JavaScript引擎都是用C或者C++語言寫的,都對外提供了API接口。因此在C、C++語言中使用這些JavaScript引擎,嵌入JavaScript是很是方便的。有一些著名的開源項目都使用了這一種方式,來進行混合的編程,好比Node.js, K-3D等。javascript

已知著名的JavaScript引擎有Google的V8引擎、IE的Trident引擎、Firefox的SpiderMonkey引擎、Webkit的JavaScriptCore引擎、Opera的Carakan引擎(非開源的,本文沒有分析)等。這些JavaScript引擎對外提供的API接口在細節上各不相同,可是這些API的一個基本的設計思路都相似。C、C++要使用這些引擎,首先要得到一個全局的Global對象。這個全局的Global對象有屬性、方法、事件。好比在JavaScript環境中有一個window窗口對象。它描述的是一個瀏覽器窗口。通常JavaScript要引用它的屬性和方法時,不須要用「window.xxx」這種形式,而直接使用「xxx」。 它是JavaScript中最大的對象,全部的其餘JavaScript對象、函數或者是它的子對象,或者是子對象的子對象。C、C++經過對這個最大的Global對象調用get、set操做就能夠實現與JavaScript進行雙向交互了。 V8對外的API接口是C++的接口。V8的API定義了幾個基本概念:句柄(handle),做用域(scope),上下文環境(Context)。模板(Templates),瞭解這些基本的概念纔可使用V8。java

l 上下文環境Context就是腳本的運行環境,JavaScript的變量、函數等都存在於上下文環境Context中。Context能夠嵌套,即當前函數有一個Context,調用其它函數時若是又有一個Context,則在被調用的函數中javascript是以最近的Context爲準的,當退出這個函數時,又恢復到了原來的Context。web

l 句柄(handle)就是一個指向V8對象的指針,有點像C++的智能指針。全部的v8對象必須使用句柄來操做。沒有句柄指向的V8對象,很快會被垃圾回收器回收了。編程

l 做用域(scope)是句柄的容器,一個做用域(scope)能夠有不少句柄(handle)。當離開一個做用域(scope)時,全部在做用域(scope)裏的句柄(handle)都會被釋放了。瀏覽器

l 模板(Templates)分爲函數模板和對象模板,是V8對JavaScript的函數和對象的封裝。方便C++語言操做JavaScript的函數和對象。ide

l V8 API定義了一組類或者模板,用來與JavaScript的語言概念一一對應。好比:函數

V8的 Function模板與JavaScript的函數對應設計

V8的Object類與JavaScript的對象對應指針

V8的String類與JavaScript的字符對應code

V8的Script類與JavaScript的腳本文本對應,它能夠編譯並執行一段腳本。

2.2. C++調用JavaScript

使用V8,在C++中訪問Javascript腳本中的內容,首先要調用Context::GetCurrent()->Global()獲取到Global全局對象,再經過Global全局對象的Get函數來提取Javascript的全局變量、全局函數、全局複雜對象。C++代碼示例以下:

//獲取Global對象

Handle<Object>globalObj = Context::GetCurrent()->Global();

//獲取Javascrip全局變量

Handle<Value>value = globalObj->Get(String::New("JavaScript變量名"));

intn = value ->ToInt32()->Value();

//獲取Javascrip全局函數,並調用全局函數

Handle<Value>value = globalObj->Get(String::New("JavaScript函數名"));

Handle<Function> func = Handle<Function>::Cast(value) ;//轉換爲函數

Local<Value> v1 = Int32::New(0); Local<Value> v2 = Int32::New(1);

Handle<Value> args[2] = { v1, v2 }; //函數參數

func->Call(globalObj, 2, args);

//獲取Javascrip全局對象,並調用對象的函數

Handle<Value>value = globalObj->Get(String::New("JavaScript複雜對象名"));

Handle<Object> obj = Handle<Object>::Cast(value);//轉換爲複雜對象

Handle<Value> objFunc = obj ->Get(String::New("JavaScript對象函數名"));

Handle<Value> args[] = {String::New("callobject function ")};//函數參數

objFunc->Call(globalObj, 1, args);

2.3. JavaScript調用C++

使用V8,在Javascript腳本中想要訪問C++中的內容,必須先將C++的變量、函數、類注入到Javacsript中。注入時,首先要調用Context::GetCurrent()->Global()獲取到Global對象,再經過Global對象的Set函數來注入全局變量、全局函數、類對象。

全局變量、全局函數的注入過程與上一節的代碼相似,這裏就省略不寫了。比較麻煩的是將C++的類、類對象注入到Javascript中。其基本的過程以下:

  1. 首先定義一個C++類,定義一個類對象指針

  2. 定義一組C++全局函數,封裝V8對C++類的調用,提供給V8進行CALLBACK回調。

  3. 最後調用V8 API,將定義的C++類和C++函數注入到Javascript中

在V8的API接口中,還提供了一個「內部數據」(Internal Field)的概念,「內部數據」就是容許V8對象保存一個C++的void指針。當V8回調C++全局函數時,C++能夠設置或者獲取該void指針。

下面是一個將C++類、類變量注入到Javascript中的C++代碼示例。

//一個C++類test、

class test

{

public:

test(){number=0;};

voidfunc(){number++;}

int number;

};

//一個全局對象g_test

//目的是:在Javascript中能夠直接使用這個對象,例如g_test.func()

test g_test;

//封裝V8調用test類構造函數

//在Javascript中若是執行:var t = new test;V8就會調用這個C++函數

//在C++中執行NewInstance函數注入對象時,也會調用這個函數

//默認的test類的構造函數沒有參數,C++注入對象時,提供一個額外的參數

v8::Handlev8::Value testConstructor(constv8::Arguments& args)

{

v8::Local<v8::Object>self = args.Holder();

//這裏假定有兩個「內部數據」(Internal Field)

//第一個「內部數據」保存test對象的指針

//第二個「內部數據」爲1 就表示這個對象是由C++注入的

//第二個「內部數據」爲0 就表示這個對象是JS中本身創建的

if(args.Length())

{

    //默認爲0,當C++注入對象時,會填充這個「內部數據」

    self->SetInternalField(0,v8::External::New(0));

    self->SetInternalField(1,v8::Int32::New(1));

}

else

{

    self->SetInternalField(0,v8::External::New(new test));

    self->SetInternalField(1,v8::Int32::New(0));

}

return self;

}

//封裝V8調用test類func方法

//在Javascript中若是執行:t.func();V8就會調用這個C++函數

v8::Handlev8::Value testFunc(constv8::Arguments& args)

{

//獲取構造函數testConstructor時,設置的對象指針

v8::Local<v8::Object>self = args.Holder();

v8::Localv8::External wrap =v8::Localv8::External::Cast(self->GetInternalField(0));

void* ptr =wrap->Value();

//調用類方法

static_cast<test*>(ptr)->func();

returnv8::Undefined();

}

//封裝V8調用test類成員變量number

//在Javascript中若是執行:t.number;V8就會調用這個C++函數

v8::Handlev8::ValuegetTestNumber(v8::Localv8::String property, const v8::AccessorInfo& info)

{

//獲取構造函數testConstructor時,設置的對象指針

v8::Local<v8::Object>self = info.Holder();

v8::Localv8::External wrap =v8::Localv8::External::Cast(self->GetInternalField(0));

void* ptr =wrap->Value();

//返回類變量

returnv8::Int32::New(static_cast<test*>(ptr)->number);

}

//C++類和全局的函數定義好之後,就能夠開始將類、變量注入V8 Javascript中了

//獲取global對象

v8::Handlev8::Object globalObj =context->Global();

//新建一個函數模板,testConstructor是上面定義的全局函數

v8::Handlev8::FunctionTemplate test_templ =v8::FunctionTemplate::New(testConstructor);

//設置類名稱

test_templ->SetClassName(v8::String::New("test"));

//獲取Prototype,

v8::Handlev8::ObjectTemplate test_proto =test_templ->PrototypeTemplate();

//增長類成員函數,testFunc是上面定義的全局函數

test_proto->Set("func",v8::FunctionTemplate::New(testFunc));

//設置兩個內部數據,用於構造函數testConstructor時,存放類對象的指針。

v8::Handlev8::ObjectTemplate test_inst =test_templ->InstanceTemplate();

test_inst->SetInternalFieldCount(2)

//增長類成員變量

//getTestNumber是上面定義的全局函數

//只提供了成員變量的get操做,最後一個參數是成員變量的set操做,這裏省略了

test_inst->SetAccessor(v8::String::New("number"),getTestNumber, 0);

//將test類的定義注入到Javascript中

v8::Handlev8::Function point_ctor =test_templ->GetFunction();

globalObj->Set(v8::String::New("test"),point_ctor);

//新建一個test對象,並使得g_test綁定新建的對象

v8::Handlev8::Value flag = v8::Int32::New(1);

v8::Handlev8::Object obj =point_ctor->NewInstance(1, &flag);

obj->SetInternalField(0, v8::External::New(&g_test));

globalObj->Set(v8::String::New("g_test"), obj);

將C++類和類指針注入到V8 JavaScript後,在JavaScript中就能夠這樣使用了:

g_test.func();

var n = g_test.number;

var t = new test; end.

相關文章
相關標籤/搜索