接着上一章
构造函数
一些脚本函数是构造函数;它们期望初始化 new 对象。下面的片段是一个小例子:
function Book(isbn) {
this.isbn = isbn;
}
var coolBook1 = new Book("978-0131872493");
var coolBook2 = new Book("978-1593271473");
构造函数没有什么特别之处。事实上,任何脚本函数都可以充当构造函数(即,任何函数都可以充当new的操作数)。有些函数根据它们是否作为 new 表达式的一部分被调用而具有不同的行为;例如,表达式new Number(1)将创建一个Number对象,而Number(“123”)将执行类型转换。其他函数,如Array(),将始终创建和初始化新对象(例如,new Array()和Array()具有相同的效果)。
本地Qt Script函数可以调用QScriptContext::isCalledAsConstructor()函数来确定它是作为构造函数调用还是作为常规函数调用。当函数被调用为构造函数时(即,它是new表达式中的操作数),这具有两个重要的含义:
- 这个对象QScriptContext::thisObject()包含要初始化的新对象;引擎在调用函数之前自动创建这个新对象。这意味着本地构造函数在被调用为构造函数时通常不必(也不应该)创建新对象,因为引擎已经准备了一个新对象。相反,您的函数应该对提供的对象进行操作。
- 如果将构造函数当做一个普通函数调用,构造函数应该返回一个未定义的值QScriptEngine::.undefinedValue(),告诉引擎这个对象应该是new操作符的最终结果。如果通过new表达式操作,则函数可以返回这个对象本身。
当QScriptContext::isCalledAsConstructor()返回false,你的构造函数如何处理这种情况取决于你想要做什么。如果与内置Number()函数一样,普通函数调用应该执行其参数的类型转换,则执行转换并返回结果。另一方面,如果希望构造函数的行为像调用构造函数(带有new)一样,则必须显式地创建新对象(即,忽略this对象),初始化该对象并返回它。
下面的示例实现了一个构造函数,该函数总是创建和初始化新对象:
QScriptValue Person_ctor(QScriptContext *ctx, QScriptEngine *eng)
{
QScriptValue object;
if (ctx->isCalledAsConstructor()) {
object = ctx->thisObject();
} else {
object = eng->newObject();
object.setPrototype(ctx->callee().property("prototype")); //???????
}
object.setProperty("name", ctx->argument(0));
return object;
}
给定此构造函数,脚本能够使用表达式new Person("Bob")或Person("Bob")来创建新的Person对象;二者的行为方式相同。
对于在脚本代码中定义的函数,没有等效的方法来确定它是否被作为构造函数调用。
注意,即使它不被认为是好的方式,当函数被调用为构造函数并创建自己的对象时,也没有什么可以阻止您选择忽略默认构造的(this)对象;只要让构造函数返回该对象即可。对象将“override”引擎构建的默认对象(即,默认对象将简单地在内部丢弃)。
将数据绑定到函数
即使函数是全局的,也就是说,不与任何特定的(类型)对象相关联,您可能仍然希望将一些数据与它关联起来,从而使其成为自包含的;例如,函数可以有一个指向它需要访问的C++资源的指针。如果您的应用程序只使用一个脚本引擎,或者在所有脚本引擎之间都可以共享相同的C++资源,您可以简单地使用静态C++变量,并从原生QT脚本函数中访问它。
在这种情况下,使用静态C++变量或Singleton类不合适的,可以在函数对象上调用QScriptValue::setProperty(),但是要注意,脚本代码也可以访问这些属性。
另一种方法是使用QScriptValue::setData();这个数据不是脚本可访问的。实现可以通过QScriptContext::callee()函数访问这个内部数据,该函数返回被调用的函数对象。下面的示例演示了如何使用:
QScriptValue rectifier(QScriptContext *ctx, QScriptEngine *eng)
{
QRectF magicRect = qscriptvalue_cast<QRectF>(ctx->callee().data());
QRectF sourceRect = qscriptvalue_cast<QRectF>(ctx->argument(0));
return eng->toScriptValue(sourceRect.intersected(magicRect));
}
...
QScriptValue fun = eng.newFunction(rectifier);
QRectF magicRect = QRectF(10, 20, 30, 40);
fun.setData(eng.toScriptValue(magicRect));
eng.globalObject().setProperty("rectifier", fun);
将本地函数作为另一个函数的参数
如前所述,函数对象可以作为参数传递给另一个函数;当然,对于本机函数也是如此。举个例子,这里是一个本地比较函数,它比较了两个参数:
QScriptValue myCompare(QScriptContext *ctx, QScriptEngine *eng)
{
double first = ctx->argument(0).toNumber();
double second = ctx->argument(1).toNumber();
int result;
if (first == second)
result = 0;
else if (first < second)
result = -1;
else
result = 1;
return result;
}
上面的函数可以作为参数传递给Array.prototype.sort函数来对数组进行数值排序,因为下面的C++代码说明:
QScriptEngine eng;
QScriptValue comparefn = eng.newFunction(myCompare);
QScriptValue array = eng.evaluate("new Array(10, 5, 20, 15, 30)");
array.property("sort").call(array, QScriptValueList() << comparefn);
// prints "5,10,15,20,30"
qDebug() << array.toString();
注意,在本例中,我们真正地将本地函数对象作为值对待——即,我们不将其存储为脚本环境的属性——我们只是将其作为“匿名”参数传递给另一个脚本函数,然后忘记它。
激活对象
每个Qt Script函数调用都有一个与之关联的激活对象,这个对象可以通过QScriptContext::activationObject() 函数访问。激活对象是一个脚本对象,其属性是与调用相关联的本地变量(包括脚本函数具有相应形式参数名称的参数)。因此,从C++中获取、修改、创建和删除局部变量是使用常规QScriptValue::property()和QScriptValue::setProperty()函数完成的。激活对象本身不能直接从脚本代码访问(但是无论何时从本地变量读取或写入本地变量,都会隐式地访问它)。
对于C++代码,激活对象有两个主要的应用:
- 激活对象通过将其用作QScriptValueIterator的输入,提供了遍历与函数调用相关联的变量的标准方法。这对于调试目的非常有用。
- 激活对象可用于准备当内联评估脚本时应该可用的本地变量;这可以被视为向脚本本身传递参数的一种方式。此技术通常与QScriptEngine::pushContext()结合使用,如下面的示例所示:
QScriptContext *ctx = eng.pushContext();
QScriptValue act = ctx->activationObject();
act.setProperty("digit", 7);
qDebug() << eng.evaluate("digit + 1").toNumber(); // 8
eng.popContext();
我们创建临时执行上下文,为其创建本地变量,评估脚本,最后恢复旧上下文。
Property Getters and Setters
脚本对象属性可以定义为一个getter/setter函数,类似于Qt C++属性如何与它相关联的读写函数。这使得脚本可以使用object.x而不是object.getX()之类的表达式;每当访问属性时,就会隐式地调用x的getter/setter函数。对于脚本,属性看起来和行为就像一个常规对象属性。
单个Qt脚本函数可以作为属性的getter和setter。当它被当做getter调用时,参数计数为0。当它作为setter调用时,参数计数为1;参数是属性的新值。在下面的示例中,我们定义了一个本地组合的getter/setter,它
QScriptValue getSet(QScriptContext *ctx, QScriptEngine *eng)
{
QScriptValue obj = ctx->thisObject();
QScriptValue data = obj.data();
if (!data.isValid()) {
data = eng->newObject();
obj.setData(data);
}
QScriptValue result;
if (ctx->argumentCount() == 1) {
QString str = ctx->argument(0).toString();
str.replace("Roberta", "Ken");
result = str;
data.setProperty("x", result);
} else {
result = data.property("x");
}
return result;
}
?????????????????待续。。。。。???????????????????