【js引擎】quickjs 中的两个 proto

打印 上一主题 下一主题

主题 802|帖子 802|积分 2406

别被 quickjs 的接口定名骗了,接口 JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
中的 proto 并不是 obj.__proto__,而是筹划模式中的原型模式。
了解 javascript 语言的面向对象编程模式的同砚都知道,javascript 中的继承和多态是利用原型链来实现的。见官方描述: ECMAScript® 2023 Language Specification 4.3.1
javascript 的原型链( prototype chain)

总结来说,每一个构造函数都有一个prototype属性(property)。当利用 new 表达式实行构造函数时,返回的对象会包含一个指向这个构造函数的prototype属性的引用,即:__proto__。所以 prototype 属性是共享的,所有通过该构造函数创建的对象,都会指向同一个 prototype 属性。
prototype 属性本身也是一个对象,也有 __proto__ 引用,这个逐次向上的引用链,叫做原型链。
mdn 和 阮一峰的日记中,都具体描述过该机制,并且配有样例,这里不再多说。
quickjs 中的原型链(prototype chain) 和 原型模式(Prototype Pattern)

原型模式

quickjs 是开发 javascript 运行时最经常利用的 js 引擎之一。quickjs 引擎利用 c 语言开发。在利用 quickjs 引擎的时候,此中创建对象的接口,很容易让人疑惑:
  1. JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
复制代码
我们会想当然的认为 proto 就是 javascript 中的 __proto__引用。我们会认为
  1. JSValue obj = JS_NewObjectProto(ctx, my_proto);
复制代码
创建的 object 满足 obj.__proto__ is my_proto。而现实上并不是。要说清楚这个问题,我们还必要结合另外几个接口来看
  1. JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id);JSValue JS_NewObjectClass(JSContext *ctx, int class_id);JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
  2. JSValue JS_NewObject(JSContext *ctx);
复制代码

结合这张这四个函数的调用关系图,可以得到以下结论:

  • 创建一个对象与两件事有关,proto 对象 和 class id
  • 当没有class id 时,默认class id 为 JS_CLASS_OBJECT
  • 当没有 proto 时,会从 context->class_proto 获取一个 proto。
那这两个变量到底有什么作用呢?继承向下追溯发现,proto 的作用是找到其背后的 shape 对象。然后传递给 JS_NewObjectFromShape 函数,而 class_id 的作用临时还没提现出来。
  1. JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
  2.                                JSClassID class_id)
  3. {
  4.     JSShape *sh;
  5.     JSObject *proto;
  6.     proto = get_proto_obj(proto_val);
  7.     sh = find_hashed_shape_proto(ctx->rt, proto);
  8.     if (likely(sh)) {
  9.         sh = js_dup_shape(sh);
  10.     } else {
  11.         sh = js_new_shape(ctx, proto);
  12.         if (!sh)
  13.             return JS_EXCEPTION;
  14.     }
  15.     return JS_NewObjectFromShape(ctx, sh, class_id);
  16. }
复制代码
再继承看JS_NewObjectFromShape 函数,会发现shape在这里只是被设置进去了,而 class_id 的作用是,用来实行一些范例独有的设置。比方 ARRAY、C_FUNCTION 等。而如果没有预界说的特殊设置,则只会关注该对象是否是 exotic 的。
  1. static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
  2. {
  3.     JSObject *p;
  4.     js_trigger_gc(ctx->rt, sizeof(JSObject));
  5.     p = js_malloc(ctx, sizeof(JSObject));
  6.         ....
  7.     p->shape = sh;
  8.         ....
  9.     switch(class_id) {
  10.     .....
  11.     case JS_CLASS_ARRAY:
  12.         {
  13.             JSProperty *pr;
  14.             p->is_exotic = 1;
  15.             p->fast_array = 1;
  16.             p->u.array.u.values = NULL;
  17.             p->u.array.count = 0;
  18.             p->u.array.u1.size = 0;
  19.             /* the length property is always the first one */
  20.             if (likely(sh == ctx->array_shape)) {
  21.                 pr = &p->prop[0];
  22.             } else {
  23.                 /* only used for the first array */
  24.                 /* cannot fail */
  25.                 pr = add_property(ctx, p, JS_ATOM_length,
  26.                                   JS_PROP_WRITABLE | JS_PROP_LENGTH);
  27.             }
  28.             pr->u.value = JS_NewInt32(ctx, 0);
  29.         }
  30.         break;
  31.                 .....
  32.     default:
  33.     set_exotic:
  34.         if (ctx->rt->class_array[class_id].exotic) {
  35.             p->is_exotic = 1;
  36.         }
  37.         break;
  38.     }
  39.         ....
  40.     return JS_MKPTR(JS_TAG_OBJECT, p);
  41. }
复制代码
所以总结一下,proto 的作用,是 copy 其背后的 shape,而 class_id 的作用,是做一些特殊范例的独有配置。因此这里的 proto 的含义其实是筹划模式中的原型模式。通过已有的原型对象,快速生成与原型对象相同的实例。而具体shape是什么作用,以后有机会再单说。
多说一句,class_id 并不是用来判断 object 的范例的。有些同砚看到switch case 中判断范例的操作,就以为 class_Id 是用来判断范例的。现实上,只要有class_id 的对象,都是 Object。至于javascript界说的 7 种值范例:Undefined、Null、Boolean、String、Symbol、Numeric、Object,是靠JSValue 中的 Tag 来区分的。
原型链

那么原型链是如何实现的呢?我们只必要找一下 quickjs 是如何查找 __proto__ 属性的就好了。
__proto__属性是object类都有的属性,统一界说在 js_object_proto_funcs 中,可以看到 __proto__在quickjs中,现实上是 shape 中的一个字段
  1. static const JSCFunctionListEntry js_object_proto_funcs[] = {
  2.         ....
  3.     JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
  4.         ....
  5. };
  6. static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
  7. {
  8.         ...
  9.     ret = JS_GetPrototype(ctx, val);
  10.     ...
  11.     return ret;
  12. }
  13. JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
  14. {
  15.     JSValue val;
  16.         .....
  17.             p = p->shape->proto;
  18.             if (!p)
  19.                 val = JS_NULL;
  20.             else
  21.                 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
  22.         }
  23.         .....
  24.     return val;
  25. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

火影

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表