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 对象运算符重载
几个运算符可以重载,所以当它们应用于对象时,它们实际上是调用方法。以下是可重载操作符和相应方法名称的列表:
- 字符串转换:%%__string%%在没有参数的情况下调用对象的方法。应该返回一个字符串
- 对象比较:对于两个不同对象之间的任何比较,%%__compare%%将以第二个对象作为参数在第一个对象上调用该方法
- 加法:a + b如果a是一个对象,a.%%__add%%(b)则被调用,否则如果b是一个对象,b.%%__radd%%(a)则被调用
- 减法:与加法相同但与%%__sub%%和%%__rsub%%
- 乘法:与加法相同,但与%%__mult%%和%%__rmult%%
- 除法:除了%%__div%%和之外,还有一个加法%%__rdiv%%
- 取模:与加法相同但与%%__mod%%和%%__rmod%%
- 数组读取:当一个对象作为一个读取数组被访问时,使用a[i]实际的调用a.%%__get%%(i)
- 数组写入:当一个对象作为一个数组进行写入时,使用a[i] = v实际的调用a.%%__set%%(i,v)
如果在操作发生时未定义重载字段,则会引发异常。