Qt 5.5 中Qt Script翻译 (七)

接着上一章

基于原型的继承

Qt Script原型对象的目的是定义其他Qt Script对象集应该共享的行为。我们说,共享相同原型对象的对象属于同一类(同样,在技术方面,这不应该与C++和Java等语言的类结构混淆,ECMAScript没有这样的构造)。

基本的基于原型的继承机制工作如下:每个QT脚本对象都有另一个对象的内部链接,即它的原型。当在对象中查找属性时,当在对象中查找属性时,对象本身不具有该属性,而是在原型对象中查找该属性;如果原型具有该属性,则返回该属性。否则,在原型对象的原型中查找属性,等等;该对象链构成了原型链。遵循原型对象链,直到找到属性或到达链的末尾。

例如,当通过表达式new Object()创建新对象时,结果对象将具有标准Object原型Object.prototype作为其原型;通过该原型关系,新对象继承一组属性,包括hasOwnProperty()函数和toString()函数:

var o = new Object();
  o.foo = 123;
  print(o.hasOwnProperty('foo')); // true
  print(o.hasOwnProperty('bar')); // false
  print(o); // calls o.toString(), which returns "[object Object]"

toString()函数本身没有在o中定义(因为我们没有向o.toString分配任何内容),因此调用标准Object原型中的toString()函数,它返回o(“[object Object]”)的高度通用的字符串表示。

注意,原型对象的属性不复制到新对象;只维护从新对象到原型对象的链接。这意味着对原型对象所做的更改将立即反映在具有修改对象作为原型的所有对象的行为中。

定义基于原型的类

在Qt脚本中,没有明确定义一个类;没有类关键字。相反,您可以用两个步骤定义一个新类:

  • 定义一个初始化新对象的构造函数
  • 建立定义类接口的prototype对象,并将此对象分配给构造函数中的公共prototype属性。

通过这种安排,构造函数的公共prototype属性将自动设置为通过将new运算符应用到构造函数中创建的对象的原型;例如,由new Foo()创建的对象的原型将是Foo.prototype的值。

不对this对象进行操作的函数(“静态”方法)通常作为构造函数属性存储,而不是作为原型对象的属性存储。常数也一样,例如枚举值。

下面的代码为一个名为“Person”的类定义了一个简单的构造函数。

  function Person(name)
  {
    this.name = name;
  }

接下来,您需要将Person.prototype设置为原型对象;即,定义应该对所有Person对象都通用的接口。Qt Script自动为每个脚本函数创建一个默认的原型对象(通过表达式new Object());您可以向该对象添加属性,或者您可以分配自己的自定义对象。(一般来说,任何Qt脚本对象都可以作为任何其他对象的原型。)

下面是一个示例,说明如何重写Person.prototype从Object.prototype继承的toString()函数,以便为您的Person对象提供更合适的字符串表示:

Person.prototype.toString = function() { return "Person(name: " + this.name + ")"; }

这类似于在C++中重新实现虚拟函数的过程。此后,当在Person对象中查找名为toString的属性时,它将在Person.prototype中解析,而不是像以前那样在Object.prototype中解析:

var p1 = new Person("John Doe");
  var p2 = new Person("G.I. Jane");
  print(p1); // "Person(name: John Doe)"
  print(p2); // "Person(name: G.I. Jane)"

也有一些其他有趣的事情我们可以了解Person对象:

print(p1.hasOwnProperty('name')); // 'name' is an instance variable, so this returns true
  print(p1.hasOwnProperty('toString')); // returns false; inherited from prototype
  print(p1 instanceof Person); // true
  print(p1 instanceof Object); // true

hasOwnProperty()函数不是从Person.prototype继承的,而是从Object.prototype继承的,Object.prototype是Person.prototype本身的原型;即,Person对象的原型链是Person.prototype后跟Object.prototype。该原型链建立类层次结构,如应用instanceof操作符所演示的;instanceof通过跟踪左侧对象的原型链来检查右侧构造函数公共原型属性的值是否达到。

定义子类时,有一个通用的模式可以使用。下面的示例演示如何创建一个名为“Employee”的子类:

function Employee(name, salary)
  {
    Person.call(this, name); // call base constructor

    this.salary = salary;
  }

  // set the prototype to be an instance of the base class
  Employee.prototype = new Person();

  // initialize prototype
  Employee.prototype.toString = function() {
      // ...
  }

同样,可以使用实例验证Employee和Person之间的类关系已经正确建立:

var e = new Employee("Johnny Bravo", 5000000);
  print(e instanceof Employee); // true
  print(e instanceof Person);   // true
  print(e instanceof Object);   // true
  print(e instanceof Array);    // false

这表明Employee对象的原型链与Person对象的原型链相同,但是将Employee.prototype添加到了链的前面。

 

基于Qt脚本C++原型的程序设计

可以使用QScriptEngine::newFunction()来封装本地函数。在实现构造函数时,将原型对象作为参数传递给QScriptEngine::newFunction()。您可以调用QScriptValue::construct()来调用构造函数函数,如果需要调用基类构造函数,则可以在本机构造函数中使用QScriptValue::call()。

QScript类提供了一种方便的方法来实现原型对象的C++插槽和属性。查看默认原型示例,看看这是如何完成的。或者,原型功能可以通过使用QScriptEngine::newFunction()包装的独立本地函数实现,并通过调用QScriptValue::setProperty()设置为原型对象的属性。

在实现原型函数时,您使用QScriptable::thisObject()(或 QScriptContext::thisObject())来获得对正在运行的QScriptValue的引用;然后调用qscriptvalue_cast() 将其转换为C++类型,并使用通常的C++ API来执行相关操作。

通过调用QScriptEngine::setDefaultPrototype()将原型对象与C++类型相关联。一旦建立了映射,当一个类型的值被包装在QScriptValue中时,Qt脚本将自动分配正确的原型;当您显式调用Engine::toScriptValue()时,或者当这种类型的值从C++时隙返回并由引擎内部传递回脚本代码时。这意味着,如果使用这种方法,就不必实现包装类。

作为示例,让我们考虑如何根据Qt Script API实现前一节中的Person类。我们从本地构造函数开始:

 QScriptValue Person_ctor(QScriptContext *context, QScriptEngine *engine)
  {
    QString name = context->argument(0).toString();
    context->thisObject().setProperty("name", name);
    return engine->undefinedValue();
  }

这里是我们以前见过的Person.prototype.toString 函数的本地等价物:

 QScriptValue Person_prototype_toString(QScriptContext *context, QScriptEngine *engine)
  {
    QString name = context->thisObject().property("name").toString();
    QString result = QString::fromLatin1("Person(name: %0)").arg(name);
    return result;
  }

然后可以初始化如下类:

QScriptEngine engine;
  QScriptValue ctor = engine.newFunction(Person_ctor);
  ctor.property("prototype").setProperty("toString", engine.newFunction(Person_prototype_toString));
  QScriptValue global = engine.globalObject();
  global.setProperty("Person", ctor);

Employee子类的实现类似。我们使用QScriptValue::call()调用超级类(Person)构造函数:

 QScriptValue Employee_ctor(QScriptContext *context, QScriptEngine *engine)
  {
    QScriptValue super = context->callee().property("prototype").property("constructor");
    super.call(context->thisObject(), QScriptValueList() << context->argument(0));
    context->thisObject().setProperty("salary", context->argument(1));
    return engine->undefinedValue();
  }

然后可以初始化Employee 类如下:

 QScriptValue empCtor = engine.newFunction(Employee_ctor);
  empCtor.setProperty("prototype", global.property("Person").construct());
  global.setProperty("Employee", empCtor);

在实现类的原型对象时,您可能希望使用QScriptable类,因为它允许您根据Qt属性、信号和槽定义脚本类的API,并自动处理Qt脚本和C++侧之间的值转换。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值