Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

更多设置对象属性的方法

在之前的绑定中,我们基本上是使用JS_SetPrototypeStr进行代码绑定。但还有其他的方法。

JS_SetProperty系列

JS_SetPrototype系列有如下函数:

int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, const char *prop, JSValue val)
  • this_obj:要设置到的值
  • prop:设置的成员名称
  • val:要设置的值
int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val)

JS_SetPropertyStr的区别是第三个参数是JSAtom。这其实是更底层的函数。JSAtom就是一个字符串。只是如果你用JS_NewAtom创建之后你可以复用他,这样QuickJS就不用将字符串字面量在底层到处拷贝(比如使用JS_SetPropertyStr时)

JS_DefineProperty系列

这个系列函数给了你更加精细的控制:

int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags)
int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags)
int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, const char *prop, JSValue val, int flags)
int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags)

同样的,有带有JSAtom和直接使用字符串的。但都多出了int flags这个参数。取值如下:

#define JS_PROP_CONFIGURABLE  (1 << 0)
#define JS_PROP_WRITABLE      (1 << 1)
#define JS_PROP_ENUMERABLE    (1 << 2)
#define JS_PROP_C_W_E         (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)
#define JS_PROP_LENGTH        (1 << 3) /* used internally in Arrays */
#define JS_PROP_TMASK         (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */
#define JS_PROP_NORMAL         (0 << 4)
#define JS_PROP_GETSET         (1 << 4)
#define JS_PROP_VARREF         (2 << 4) /* used internally */
#define JS_PROP_AUTOINIT       (3 << 4) /* used internally */
/* throw an exception if false would be returned
   (JS_DefineProperty/JS_SetProperty) */
#define JS_PROP_THROW            (1 << 14)
/* throw an exception if false would be returned in strict mode
   (JS_SetProperty) */
#define JS_PROP_THROW_STRICT     (1 << 15)
  • JS_PROP_NORMAL:默认值,无任何属性
  • JS_PROP_ENUMERABLE:属性可被枚举(可被for...in遍历到)
  • JS_PROP_WRITABLE:可写入
  • JS_PROP_CONFIGURABLE:属性的配置可被修改。如果第一次使用JS_DefinePropertyXXX未指定这个值,那之后使用JS_DefinePropertyXXX不允许修改属性的PROP。
  • JS_PROP_C_W_EJS_PROP_CONFIGURABLE,JS_PROP_WRITABLE,JS_PROP_ENUMERABLE的组合
  • JS_PROP_GETSET:标识属性是getter/setter(一般和JS_DefineProperty配合使用)。注意:
    1. 使用这个枚举的时候还需要配合JS_PROP_HAS_GET,JS_PROP_HAS_SET来告诉QuickJS是否有getter/setter
    2. 传入的getter/setter相关JSValue需要被JS_FreeValue,因为底层调用了js_dup
  • JS_PROP_THROW:如果JS_SetPropertyXXXJS_DefinePropertyXXX产生了非法行为,抛出一个异常。
  • JS_PROP_THROW_STRICT:在严格模式下判断是否非法并抛出异常

JS_SetPropertyFunctionList

可以通过这个函数一次性绑定多个属性:

const JSCFunctionListEntry entries[] = {
    // bind member function
    JS_CFUNC_DEF("introduce", 0, IntroduceBinding),
	
    // bind getter/settrer
    JS_CGETSET_DEF("name", NameGetter, NameSetter),
    JS_CGETSET_DEF("bmi", BMIBinding, nullptr),
};

JS_SetPropertyFunctionList(ctx, proto, entries, std::size(entries));

首先构造一个JSCFunctionListEntry数组,然后使用一些方便的宏就可以进行绑定。

只需要将我们的绑定函数的函数指针传给宏即可,不需要担心函数类型不一致,宏会自动帮你处理(也就不需要JSCFunctionType

最后使用JS_SetPropertyFunctionList即可完成。

缺点:JSCFunctionListEntry[]是传指针进去的。也就是说你需要保证此数组在JS_Eval之前是有效的。

删除属性

使用JS_DeleteProperty函数。

JS_SetProperty和JS_DefineProperty的区别

JS_SetPropertyXXX相当于JS中的复制。当没有这个属性的时候,赋值时会生成一份属性。但不能给const属性赋值:

JS_SetPropertyStr(ctx, global_this, "const_global_var2", new_obj); // error!

JS_DefinePropertyXXX则是定义属性。他等于是完全重新创建属性。