绑定全局变量
我们首先从最简单的全局变量绑定开始。
QuickJS的类型
QuickJS中使用JSValue
包装所有C/C++类型。支持的类型如下:
- Number:数字类型
- String:字符串类型(是C风格字符串不是
std::string
) - Boolean:布尔值
- Class:类
- Module:模块
- Exception:异常
- Function:函数
- Array:数组
例子
现在希望绑定一个数值类型的变量:
|
|
做法如下:
|
|
C++和JS的所有数据交换都是通过JSValue
进行的。这里步骤如下:
- 首先使用
JS_NewInt32
创建一个整数类型的JSValue
- 做异常检查
- 得到全局对象
JS_GetGlobalObject
- 使用
JS_SetPropertyStr
将我们的变量注册到全局对象中 - 清理内存
重点是注册的函数JS_SetPropertyStr
。其原型如下:
|
|
this_object
:要注册到的对象prop
:要注册的对象名称(在JS中使用的名称)val
:要注册的对象
注册完之后就可以在JS中使用了:
|
|
绑定基础类型变量的规则
绑定对象的步骤如下:
- 使用
JS_NewXXX
来创建一个JS对象 - 使用
JS_SetPropertyStr
来将对象绑定在另一个JS对象中 - 清理内存
可创建的JS对象
- 通用的数值类创建使用
JS_NewNumber
。细分如下:JS_NewInt32
:创建32位的整数JS_NewInt64
:创建64位整数(底层规则是:如果是32位,调用JS_NewInt32
,否则直接调用JS_NewFloat64
存在64位浮点数中)JS_NewUint32
:创建无符号32位整数(规则通JS_NewInt64
)JS_NewFloat64
:创建double类型(JS中没有float类型都是double)
- 大数类型:
JS_NewBigInt64
JS_NewBigUInt64
JS_NewBool
:创建布尔值JS_NewClass
:创建类JS_NewObject
:创建对象- 字符串类:
JS_NewString
JS_NewStringLen
:可指定字符串长度
何时释放JSValue?
JSValue
的释放也是有讲究的。其底层实现为:
|
|
是先判断是否有引用计数,如果有的话,当计数降为0释放内存。
有引用计数的一般是类和对象类型。像基础的数值类型是直接值拷贝入JSValue
的,也不会有内存分配/释放:
|
|
tag
:即JS_TAG_XXX
类型,标识JSValue
的类型u
:如果是数值类型,就记录在非ptr
中并且没有内存分配。否则进行内存分配,将指针记录在ptr
中
而JS_VALUE_HAS_REF_COUNT
也让我们知道哪些是会进行内存分配的:
|
|
那么所有时候,只要使用了JS_NewXXX
就一定要调用JS_FreeValue
吗?答案是否定的。具体是否需要释放要看之后的函数是否影响了其引用计数。比如上面HelloWorld中的代码我们就没有释放new_obj
。因为new_obj
被创建出来时引用计数是1。而JS_SetPropertyStr
传入new_obj
后是不会改变其引用计数的。这个时候如果调用了JS_FreeValue
则会将new_obj
的引用计数降为0,进而释放内存。但此时其已被注册在global_this
中了。在JSRuntime
释放的时候会尝试释放所有被引用的节点,这个时候会再次释放new_obj
造成程序崩溃。
所以是否要释放JSValue
,关键在于之后使用的函数是否增加了JSValue
的引用计数。如果增加了就需要释放。
或者说的更严谨一点,当其他函数内部使用了js_dup()
时就是增加了引用计数,这个时候我们就得手动释放。
绑定类对象
如果想要绑定类对象,需要使用JS_NewClassObject
并且将类对象设置进去:
|
|
这里需要一个class_id
,class_id
是类的唯一标识,在你注册类的时候会生成一个(等待后面绑定类的时候会说到)。
然后使用JS_SetOpaque
函数将我们的类对象塞给JSValue
即可。
绑定空对象
使用JS_NewObject
创建一个空对象(即创建一个JS中的Object
实例)。这个对象不和任何类相关联,所以也不需要一个class_id
。
内置JSValue常量
JS_UNDEFINED
JS_NULL
这两个是内置的字面常量,可以直接使用无需JS_NewXXX
。