作用域和命名解析
QML 属性绑定、内联函数和导入的 JavaScript 文件都在 JavaScript 范围内运行。作用域控制表达式能访问哪些变量,以及当两个或多个名称冲突时哪个变量优先。
由于 JavaScript 内置的作用域机制非常简单,QML 对其进行了增强,使其更自然地与 QML 语言扩展相匹配。
JavaScript 作用域
QML 的作用域扩展不会干扰 JavaScript 的自然作用域。JavaScript 程序员在 QML 中编写函数、属性绑定或导入 JavaScript 文件时,可以重复使用他们已有的知识。
在下面的示例中,无论 QML 对象的a
和b
属性的值如何,addConstant()
方法都会像程序员期望的那样,将 13 添加到传递的参数中。
QtObject { property int a: 3 property int b: 9 function addConstant(b) { var a = 13; return b + a; } }
QML 尊重 JavaScript 的正常范围规则,甚至在绑定中也是如此。这个完全邪恶、可憎的绑定会给 QML 对象的a
属性赋值 12。
QtObject { property int a a: { var a = 12; a; } }
QML 中的每个 JavaScript 表达式、函数或文件都有自己唯一的变量对象。一个对象中声明的局部变量绝不会与另一个对象中声明的局部变量冲突。
类型名称和导入的 JavaScript 文件
QML 文档包含导入语句,可定义文档可见的类型名称和 JavaScript 文件。除了在 QML 声明本身中使用外,JavaScript 代码在访问附加属性和枚举值时也会使用类型名。
导入的效果适用于 QML 文档中的每个属性绑定和 JavaScript 函数,即使是嵌套内联组件中的也不例外。下面的例子显示了一个简单的 QML 文件,它访问了一些枚举值,并调用了一个导入的 JavaScript 函数。
import QtQuick 2.0 import "code.js" as Code ListView { snapMode: ListView.SnapToItem delegate: Component { Text { elide: Text.ElideMiddle text: "A really, really long string that will require eliding." color: Code.defaultColor() } } }
绑定范围对象
具有属性绑定的对象称为绑定的作用域对象。在下面的例子中,Item 对象就是绑定的作用域对象。
Item { anchors.left: parent.left }
绑定可以访问作用域对象的属性,无需限定条件。在上例中,绑定直接访问Item 的parent
属性,不需要任何形式的对象前缀。QML 为 JavaScript 引入了一种更结构化、更面向对象的方法,因此不需要使用 JavaScriptthis
属性。
从绑定访问附加属性时必须小心,因为它们会与作用域对象交互。从概念上讲,附加属性存在于所有对象中,即使它们只对其中的一个子集产生影响。因此,未限定的附加属性读取总是会解析为作用域对象上的附加属性,而这并不总是程序员所希望的。
例如,PathView 类型会根据委托在路径中的位置将内插值属性附加到委托上。由于PathView 只将这些属性有意义地附加到委托中的根对象上,因此访问这些属性的任何子对象都必须明确限定根对象,如下所示。
PathView { delegate: Component { Rectangle { id: root Image { scale: root.PathView.scale } } } }
如果Image 对象省略了root
前缀,就会无意中访问自身上未设置的PathView.scale
附加属性。
组件范围
QML 文档中的每个 QML 组件都定义了一个逻辑范围。每个文档至少有一个根组件,但也可以有其他内联子组件。组件范围是组件内对象 id 和组件根对象属性的结合。
Item { property string title Text { id: titletype text: "<b>" + title + "</b>" font.pixelSize: 22 anchors.top: parent.top } Text { text: titletype.text font.pixelSize: 18 anchors.bottom: parent.bottom } }
上面的例子显示了一个简单的 QML 组件,它在顶部显示富文本标题字符串,在底部显示同一文本的较小副本。在形成要显示的文本时,第一个Text
类型直接访问组件的title
属性。根类型的属性可以直接访问,这使得在整个组件中分发数据变得轻而易举。
第二个Text
类型使用 ID 直接访问第一个类型的文本。ID 由 QML 程序员明确指定,因此总是优先于其他属性名称(JavaScript Scope 中的除外)。例如,在前面的例子中,万一绑定的scope 对象有titletype
属性,titletype
id 仍将优先。
组件实例层次
在 QML 中,组件实例把它们的组件作用域连接在一起,形成一个作用域层次结构。组件实例可以直接访问其祖先的组件作用域。
最简单的方法是使用内联子组件(inline sub-components),其组件作用域隐式地作为外层组件的子作用域。
Item { property color defaultColor: "blue" ListView { delegate: Component { Rectangle { color: defaultColor } } } }
组件实例层次结构允许委托组件的实例访问Item
类型的defaultColor
属性。当然,如果委托组件有一个名为defaultColor
的属性,它将优先于该属性。
组件实例作用域层次结构也可扩展到行外组件。在下面的示例中,TitlePage.qml
组件创建了两个TitleText
实例。尽管TitleText
类型在一个单独的文件中,但当它在TitlePage
中使用时,仍可访问title
属性。 QML 是一种动态作用域语言--根据它的使用位置,title
属性可能会有不同的解析。
// TitlePage.qml import QtQuick 2.0 Item { property string title TitleText { size: 22 anchors.top: parent.top } TitleText { size: 18 anchors.bottom: parent.bottom } } // TitleText.qml import QtQuick 2.0 Text { property int size text: "<b>" + title + "</b>" font.pixelSize: size }
动态作用域非常强大,但必须谨慎使用,以防止 QML 代码的行为变得难以预测。一般来说,它只能在两个组件已经以另一种方式紧密耦合的情况下使用。在构建可重用的组件时,最好使用属性接口,如下面这样:
// TitlePage.qml import QtQuick 2.0 Item { id: root property string title TitleText { title: root.title size: 22 anchors.top: parent.top } TitleText { title: root.title size: 18 anchors.bottom: parent.bottom } } // TitleText.qml import QtQuick 2.0 Text { property string title property int size text: "<b>" + title + "</b>" font.pixelSize: size }
重载属性
QML 允许一个对象声明中定义的属性名被另一个对象声明中声明的属性重载,而另一个对象声明是对第一个对象声明的扩展。例如
// Displayable.qml import QtQuick 2.0 Item { property string title property string detail Text { text: "<b>" + title + "</b><br>" + detail } function getTitle() { return title } function setTitle(newTitle) { title = newTitle } } // Person.qml import QtQuick 2.0 Displayable { property string title property string firstName property string lastName function fullName() { return title + " " + firstName + " " + lastName } }
title
既是 Displayable 输出文本的标题,也是 Person 对象的尊称。
重载属性根据其引用的作用域进行解析。在 Person 组件的作用域内,或从外部作用域引用 Person 组件的实例时,title
会解析到 Person.qml 中声明的属性。fullName
函数将引用 Person 内部声明的title
属性。
而在 Displayable 组件内部,title
将引用 Displayable.qml 中声明的属性。getTitle() 和 setTitle() 函数以及文本对象text
属性的绑定都将引用在 Displayable 组件中声明的title
属性。
尽管名称相同,但这两个属性是完全独立的。其中一个属性的 onChanged 信号处理程序不会因另一个同名属性的变化而触发。任何一个属性的别名都会指向其中一个属性,但不会同时指向两个属性。
JavaScript 全局对象
QML 不允许类型、id 和属性名称与全局对象上的属性相冲突,以防止任何混淆。程序员可以确信Math.min(10, 9)
将始终按预期工作!
更多信息,请参阅JavaScript 主机环境。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.