openn2o文档 neko vm对象补充

简介:

vobject

neko 中对象是一种优化的hash表,在虚拟机中类型映射的值是 4
,数据内部在存储的时候和lua中的table结构上有些不同,不能共享解复用.对象的所有key被散列成整数叫做id类型(field ->int),表的键值可以通过id快速查找. 插入一个字段和数据的复杂度是O(n) 访问一个字段的复杂度是O(log n) .
原型:

typedef struct _vobject {
    val_type t;
    objtable table;
    struct _vobject *proto;
} vobject;

1 创建一个对象

在neko脚本中创建一个对象是非常容易的,这点和lua和js相似。

原型:

EXTERN value alloc_object( value cpy )

脚本使用的方式:

var obj = $new(null); //方式1

var o = { //方式2
    x => 0,
    y => -1,
    msg => "hello"
}

创建一个空的对象, 其实就是分配一个结构vobject的结构指针, 使用null的方式默认最小尺寸分配内存。方式2性能要优于方式1.

c里面使用的方式:

value obj= alloc_object(NULL);

令外一种情况是复制:

var obj2  = $new(obj); // makes a copy of o

如果有参数,初始化函数将执行深复制目标对象的字段和存储内容。当然参数必须为vobject类型否则会抛出异常。 复制对象行为通过内存拷贝来进行,并不直接引用地址。

c里面使用的方式:

value obj = alloc_object(value);

2 对象的CRUD

存储一个键值对我们使用:
原型:

EXTERN void alloc_field( value obj, field f, value v );

脚本方式:



 //方式1
var  o = $object(null);
o.x = 0;
o.y = 0;

$objset(o,$hash("z"),0); //方式2

在c中的使用方式

alloc_field(object, val_id("x"), 0 );

设置的属性时则会在运行时散列一个访问filed,点语法性能要好于objset函数的方式,但是objset方式是有类型安全校验。如果对象不存在某个filed时运行时会创建一个filed 追加值,如果存在则更新这个field和值。

获取一个键值对我们使用
原型:

EXTERN value val_field( value o, field f );

脚本方式:


$print(o.x); //方式1

$objget(o,$hash("z")); //方式2

在c中的使用方式

val_field (object, val_id("z") );

访问时如果对象没有某个field不存在则返回null。
要检查是否存在字段,可以使用$objfield内置函数检查对象是否具有给定字段,即使该字段设置为以下null值:

$objfield(o,$hash("field")); // true if o have "field"
$objfield(null,33); // false

可以使用$objremove内建删除对象的值
原型:

int otable_remove( objtable *t, field id)

$objremove(o,$hash("field")); // remove "field" from o

3 遍历属性

我们使用vobject的时候注意到voobject内部有一个objtable的结构
objtable的原型为


typedef struct _objtable
{
    int count;
    objcell *cells;
} objtable;

我们通过访问objtable可以拿到fields的数量和hashid的数组。并且获取数组的长度。

objtable *t  = &((vobject*)v)->table;
const int n = (const int)t->count ;
objcell *c = t->cells;

c就是所有id的数组。

使用脚本方式:

var a = $objfields(o); 获取属性数组

4 对象的方法

当使用点访问或内建$objcall函数调用函数时,该函数可以访问一个称为的特殊变量this,即“对象上下文”(调用该函数的对象):

o = $new(null);
o.x = 1;
o.add = function(y) { return this.x + y; }
$print(o.add(2)); // prints 3
$print( $objcall(o,$hash("add"),$array(2)) ); // prints 3
上下文在调用对象函数时设置,并且可以从任何子函数访问:

foo = function() {
    $print(this.x);
}
o = $new(null);
o.x = 3;
o.bar = function() { foo(); };
o.bar(); // prints 3

this只需将其分配给另一个值,即可修改运行时的值; 它可以设置为任何值,而不仅仅是对象。当从对象调用中返回时,上下文被恢复,所以任何修改都会丢失:

this = 1;
o.foo = function() {
    // here , we have this = o;
    this = 2; // modify
};
o.foo();
$print(this); // 1

5 对象的原型链

原型
每个对象都可以有一个也是对象的原型。当一个字段被读取并且在一个对象中没有被发现时,它会在其原型中被递归地搜索。

原型可以使用$objgetproto和访问$objsetproto:

var proto = $new(null);
proto.foo = function() { $print(this.msg) }

var o = $new(null);
o.msg = "hello";
$objsetproto(o,proto);
o.foo(); // print "hello"

$objsetproto(o,null); // remove proto
o.foo(); // exception

6 对象运算符重载

几个运算符可以重载,所以当它们应用于对象时,它们实际上是调用方法。以下是可重载操作符和相应方法名称的列表:

  1. 字符串转换:%%__string%%在没有参数的情况下调用对象的方法。应该返回一个字符串
  2. 对象比较:对于两个不同对象之间的任何比较,%%__compare%%将以第二个对象作为参数在第一个对象上调用该方法
  3. 加法:a + b如果a是一个对象,a.%%__add%%(b)则被调用,否则如果b是一个对象,b.%%__radd%%(a)则被调用
  4. 减法:与加法相同但与%%__sub%%和%%__rsub%%
  5. 乘法:与加法相同,但与%%__mult%%和%%__rmult%%
  6. 除法:除了%%__div%%和之外,还有一个加法%%__rdiv%%
  7. 取模:与加法相同但与%%__mod%%和%%__rmod%%
  8. 数组读取:当一个对象作为一个读取数组被访问时,使用a[i]实际的调用a.%%__get%%(i)
  9. 数组写入:当一个对象作为一个数组进行写入时,使用a[i] = v实际的调用a.%%__set%%(i,v)

如果在操作发生时未定义重载字段,则会引发异常。

相关文章
|
7月前
|
安全 Java 编译器
JDK21更新内容:字符串模板
JDK21更新内容:字符串模板
|
C# C++
创建目标类型对象在C#7.3中不可用,请使用9.0或更高的语言版本
创建目标类型对象在C#7.3中不可用,请使用9.0或更高的语言版本
1098 0
创建目标类型对象在C#7.3中不可用,请使用9.0或更高的语言版本
|
8月前
|
存储 数据挖掘 数据库
data的含义与作用及使用方法
data的含义与作用及使用方法
6069 0
|
11月前
|
前端开发
【React工作记录三十八】对象的属性和值转换
【React工作记录三十八】对象的属性和值转换
49 0
定义一个事件需要单独新建一个文件吗?底层原理是什么?
定义一个事件需要单独新建一个文件吗?底层原理是什么?
|
前端开发
#yyds干货盘点# 【React工作记录三十八】对象的属性和值转换
#yyds干货盘点# 【React工作记录三十八】对象的属性和值转换
122 0
#yyds干货盘点# 【React工作记录三十八】对象的属性和值转换