在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 傀儡構(gòu)造函數(shù)
cocos2d-x for js 中的繼承
JS 與 C++ 的交互 1——JS 代碼調(diào)用 C++ 代碼
迎接腳本時代的到來
解決在 vs 中修改 js 源文件無效
JS 腳本語言的優(yōu)勢與一些問題
注冊函數(shù)
回調(diào)函數(shù) 2
cxx-generator JS 綁定工具
使用 cocos2d-console 工具轉(zhuǎn)換腳本為字節(jié)碼
hybrid 開發(fā)模式
JS 與 C++ 的交互 2——JS 與 C++ 的“函數(shù)重載”問題
回調(diào)函數(shù)1——按鍵回調(diào)
Google 的繼承寫法解析
John Resiq 的繼承寫法解析
JS 與 C++ 的交互 3——C++ 和 JS 類型轉(zhuǎn)換
傀儡構(gòu)造函數(shù)

傀儡構(gòu)造函數(shù)

上篇我們以 Sprite 為例,分析了注冊函數(shù)。但其中我們似乎遺漏了一個地方,那就是構(gòu)造函數(shù)。因為 Cocos2d-x 在 C++ 層使用的是工場函數(shù)來生成對象,而不是構(gòu)造函數(shù)。所以在 JS 層代碼中,也需要有相應(yīng)的對應(yīng)機(jī)制來處理這件事。

看一下 jsb_cocos2dx_auto.hpp

extern JSClass  *jsb_cocos2d_Sprite_class;
extern JSObject *jsb_cocos2d_Sprite_prototype;
JSBool js_cocos2dx_Sprite_constructor(JSContext *cx, uint32_t argc, jsval *vp);
void js_cocos2dx_Sprite_finalize(JSContext *cx, JSObject *obj);
void js_register_cocos2dx_Sprite(JSContext *cx, JSObject *global);
void register_all_cocos2dx(JSContext* cx, JSObject* obj);

這聲明了幾個重要的對象和函數(shù)。JSClass 對象和原型對象、注冊函數(shù)、自己實現(xiàn)的 finalize 的 Stub 等。但是我們發(fā)現(xiàn)js_cocos2dx_Sprite_constructor 構(gòu)造函數(shù)并沒有對應(yīng)的實現(xiàn)代碼,僅僅是一個聲明而已。

需要注意的是,根據(jù) JS 的原型繼承,我們在生成 jsb_cocos2d_Sprite_prototype 原型時,需要傳入一個構(gòu)造函數(shù),而構(gòu)造函數(shù) js_cocos2dx_Sprite_constructor 又是未實現(xiàn)的,那么他是如何做到的呢?

在 js_register_cocos2dx_Sprite 函數(shù)中查看生成 jsb_cocos2d_Sprite_prototype 原型的代碼:

jsb_cocos2d_Sprite_prototype = JS_InitClass(
    cx, global,
    jsb_cocos2d_Node_prototype,
    jsb_cocos2d_Sprite_class,
    dummy_constructor<cocos2d::Sprite>, 0, // no constructor
    properties,
    funcs,
    NULL, // no static properties
    st_funcs);

注意到第五個參數(shù)是一個模板函數(shù) dummy_constructor,字面意思是傀儡構(gòu)造函數(shù)。

看一下這個模板函數(shù)的定義

template<class T>
static JSBool dummy_constructor(JSContext *cx, uint32_t argc, jsval *vp) {
    JS::RootedValue initializing(cx);
    JSBool isNewValid = JS_TRUE;
    JSObject* global = ScriptingCore::getInstance()->getGlobalObject();
    isNewValid = JS_GetProperty(cx, global, "initializing", &initializing) && JSVAL_TO_BOOLEAN(initializing);
    if (isNewValid)
    {
        TypeTest<T> t;
        js_type_class_t *typeClass = nullptr;
        std::string typeName = t.s_name();
        auto typeMapIter = _js_global_type_map.find(typeName);
        CCASSERT(typeMapIter != _js_global_type_map.end(), "Can't find the class type!");
        typeClass = typeMapIter->second;
        CCASSERT(typeClass, "The value is null.");
        JSObject *_tmp = JS_NewObject(cx, typeClass->jsclass, typeClass->proto, typeClass->parentProto);
        JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(_tmp));
        return JS_TRUE;
    }
    JS_ReportError(cx, "Don't use `new cc.XXX`, please use `cc.XXX.create` instead! ");
    return JS_FALSE;
}

這個函數(shù)首先使用了 JS::RootedValue 類型的量來判斷 GlobalObject 對象是否初始化完畢。JS::RootedValue 具體的原理暫時不用深究,你只需要知道這是 SpiderMonkey 引擎的一種內(nèi)存管理方式即可。

然后使用了一個非常有趣的技巧,用一個模板類 TypeTest t,取出對應(yīng)的類型名。這是一個很不錯的寫法,能夠不破壞函數(shù)簽名,使得函數(shù)能夠匹配 JS_InitClass 的參數(shù)類型,又能夠在不同的上下文中里面獲得需要的信息。我們看一下 TypeTest 的實現(xiàn),這種寫法在很多時候有很大的借鑒意義!

template< typename DERIVED >
class TypeTest
{
public:
    static const char* s_name()
    {
        // return id unique for DERIVED
        // ALWAYS VALID BUT STRING, NOT INT - BUT VALID AND CROSS-PLATFORM/CROSS-VERSION COMPATBLE
        // AS FAR AS YOU KEEP THE CLASS NAME
        return typeid( DERIVED ).name();
    }
};

最后我們在 _js_global_type_map 里查詢對應(yīng)的類型,取出相應(yīng)的參數(shù)來調(diào)用 JS_NewObject 函數(shù),生成對應(yīng)的對象并設(shè)置為返回值。