前言: Chromium瀏覽器采用的是V8引擎解析javascript代碼, V8引擎相對(duì)于傳統(tǒng)的JS引擎效率上有很大的提高,主要是因?yàn)樗鼘?/span>js代碼直接編譯成了目標(biāo)機(jī)器代碼. V8引擎的編譯過(guò)程主要是 js代碼->抽象代碼樹(shù)->目標(biāo)機(jī)器代碼. 而傳統(tǒng)的js編譯過(guò)程是 js代碼->抽象代碼樹(shù)->中間代碼->解釋執(zhí)行. V8引擎在執(zhí)行需要重復(fù)調(diào)用函數(shù)的js代碼中效率有顯著提升,但是在執(zhí)行代碼量龐大,函數(shù)一般是單次調(diào)用的JS代碼效率上卻并不明顯,原因是重復(fù)調(diào)用的函數(shù)只需要被編譯成一次機(jī)器碼就能重復(fù)執(zhí)行,而傳統(tǒng)的解釋執(zhí)行方式卻需要每次都去解釋執(zhí)行.
在做Chromium瀏覽器定制化需求時(shí)常常需要擴(kuò)展JS對(duì)象以增加js的功能. 在javascript這門編程語(yǔ)言的概念里,一切皆為對(duì)象,變量,函數(shù)等等一切皆為對(duì)象,沒(méi)有類的概念,javascript是一門動(dòng)態(tài)語(yǔ)言,它的主要特點(diǎn)是對(duì)象的類型和內(nèi)容是在運(yùn)行時(shí)決定的,是可以不斷變化的. 在javascript的世界里,根對(duì)象是global對(duì)象,所有的一切對(duì)象皆為global的子孫對(duì)象.在瀏覽器中,這個(gè)global對(duì)象的表現(xiàn)為window對(duì)象. 在webkit中已實(shí)現(xiàn)的對(duì)象稱為javascript內(nèi)部對(duì)象,內(nèi)部對(duì)象又分為本地對(duì)象和內(nèi)置對(duì)象. 本地對(duì)象需要由用戶在js代碼中使用new方法初化始對(duì)象之后才能使用,內(nèi)置對(duì)象是在webkit中已初始化好了可以直接使用. 例如
Test為js本地對(duì)象,使用方法為: Test test = new Test(); alert(test.value);
Test為js內(nèi)置對(duì)象,使用方法為: alert(Test.value);
一: 現(xiàn)在以擴(kuò)展一個(gè)javascript Test本地對(duì)象為例子說(shuō)明如何擴(kuò)展webkit js對(duì)象(本方法試驗(yàn)的chorium版本為53):
實(shí)現(xiàn)目標(biāo): 以下js代碼能夠順利執(zhí)行
var test = new Test(“aaa”,”bbb”,”ccc”);
test.setValue(“value”);
alert(“ip = “ + test.address + “,mask = “ + test.mask + “,gateway =”+test.gateway + “,value = “ + test.value);
實(shí)現(xiàn)步驟:
1.在webkit 的core目錄(瀏覽器根目錄/src/wibkit/Source/core)相應(yīng)的位置分別新建Test.idl,Test.h,Test.cpp文件.并在core.gypi中相應(yīng)的位置將.idl和c文件包含進(jìn)去.
2..idl文件是連接js對(duì)象和c++對(duì)象的媒介,首先根據(jù)你要設(shè)計(jì)的js對(duì)象的屬性和方法來(lái)確定.idl對(duì)象,再根據(jù).idl文件來(lái)寫相應(yīng)的.h 和 .cpp文件.
現(xiàn)在要新增一個(gè) 本地js對(duì)象,對(duì)象名稱為Test,它的構(gòu)造方法是傳入三個(gè)字符串參數(shù),它有三個(gè)可讀寫字符串屬性adress,mask,gateway,一個(gè)只讀字符串屬性value,一個(gè)方法為setValue.
那么Test.idl文件的內(nèi)容為.
[
Constructor(DOMString ip,DOMString mask,DOMString gateway),
] interface Test {
readonly attribute DOMString value;
attribute DOMString address;
attribute DOMString mask;
attribute DOMString gateway;
void setValue(DOMString value);
};
在window.idl中加上:
attribute TestConstructor Test; //注意這里的Test對(duì)應(yīng)的javascript中的Test,如果這里是myTest,那么js代碼就是 new myTest(...)
現(xiàn)在寫Test 對(duì)應(yīng)的c++類,它需要實(shí)現(xiàn) static Test * create(String & str1,String &str2,String& str3)供外部調(diào)用. 只讀屬性 value 對(duì)應(yīng)的方法 String value() const; 可讀寫屬性對(duì)應(yīng)的方法 String address() const; void setAddress(const String& ip); //(注意這里的函數(shù)名要與前面.idl文件聲明的屬性名稱一致,設(shè)置屬性的函數(shù)需要在前面加set并將第一個(gè)字母大寫,如setAdress函數(shù)名一致),mask , gateway屬性如上. .idl中聲明的方法在c++類中的方法一致,只需要照舊聲明一個(gè)方法 void setValue(String &value);就行. 好了,只需要把這些接口實(shí)現(xiàn)所有工作就完成了. 接下來(lái)就可以編譯運(yùn)行測(cè)試了.
類的實(shí)現(xiàn)代碼如下所示:
#ifndef TEST_H
#define TEST_H
#include "wtf/PassRefPtr.h"
#include "core/CoreExport.h"
#include "bindings/core/v8/ScriptWrappable.h"
#include "wtf/RefCounted.h"
#include "wtf/text/WTFString.h"
namespace blink {
class Test : public RefCountedWillBeGarbageCollectedFinalized<Test>,public ScriptWrappable{
DEFINE_WRAPPERTYPEINFO();
public:
virtual ~Test();
static PassRefPtrWillBeRawPtr<Test> create(){
return create( String(""), String(""), String(""));
}
static PassRefPtrWillBeRawPtr<Test> create(String ip,String mask,String gateway );
String address() const;
String mask() const;
String gateway() const;
void setAddress(const String& ip){mIp=ip;}
void setMask(const String& mask){mMask=mask;}
void setGateway(const String& gateway){mGateWay=gateway;}
String value() const;
void setValue(const String& value){mValue=value;}
private:
explicit Test(String,String,String);
String mValue;
String mIp;
String mMask;
String mGateWay;
};
}
#endif
二 webkit擴(kuò)展js內(nèi)置對(duì)象的方法
還是以上面的 Test為例:
1.在Test.idl中將[
Constructor(DOMString ip,DOMString mask,DOMString gateway),
] 去除.
2. 在 window.idl 將原來(lái)的聲明改為 [Replaceable] readonly attribute Test Test;
3. DomWindow.h中增加Test的虛函數(shù)構(gòu)造方法聲明 virtual Test* test() const {return NULL;}
4.在LocalDOMWindow.h中聲明方法 Test* test() const override,新增變量mutable PersistentWillBeMember<Test> m_test;
5.在LocalDomWindow.cpp中對(duì) test方法做實(shí)現(xiàn)
Test* LocalDOMWindow::test() const
{
if (!m_test)
m_test = Test::create();
return m_test.get();
}
三: 實(shí)現(xiàn)原理
Chromium編譯系統(tǒng)會(huì)首先去解析test.idl,通過(guò)python腳本解析.idl文件生成相應(yīng)的v8test.cpp,v8test.h. v8test對(duì)象 會(huì)調(diào)用到我們手動(dòng)寫的test對(duì)象. v8test.cpp對(duì)象綁定了js對(duì)象相應(yīng)的屬性和方法,所以在運(yùn)行時(shí)V8解析javascript代碼時(shí)能夠找到找到關(guān)聯(lián)的c++對(duì)象上. 運(yùn)行Test 的js代碼相應(yīng)的調(diào)用步驟是: V8引擎解析js代碼-> v8Test類->Test類.
四: 總結(jié)
在webkit中擴(kuò)展js對(duì)象的方法比較簡(jiǎn)單,實(shí)現(xiàn)過(guò)程中出現(xiàn)出錯(cuò)一般也能通過(guò)參照原有的代碼找到解決方法. 本文是對(duì)chorium瀏覽器如何擴(kuò)展js對(duì)象作了大體概述,但是對(duì)于 python腳本如何解析.idl文件生成對(duì)應(yīng)代碼,v8引擎如何解析javascript代碼這些內(nèi)部原理本文未涉及.