作用域与命名解析
QML 属性绑定、内联函数和导入的 JavaScript 文件均在 JavaScript 作用域中运行。作用域控制表达式可以访问哪些变量,以及当两个或多个名称发生冲突时,哪个变量具有优先级。
由于 JavaScript 的内置作用域机制非常简单,QML 对它进行了增强,使其能更自然地与 QML 语言扩展相融合。
JavaScript 作用域
QML 的作用域扩展不会干扰 JavaScript 的自然作用域。JavaScript 程序员在 QML 中编写函数、属性绑定或导入的 JavaScript 文件时,可以复用现有的知识。
在下面的示例中,addConstant() 方法会将13加到传入的参数上,这完全符合程序员的预期,且与QML对象的a 和b 属性的值无关。
QtObject {
property int a: 3
property int b: 9
function addConstant(b) {
var a = 13;
return b + a;
}
}QML 遵循 JavaScript 常规作用域规则的特性甚至在绑定中也适用。这个极其糟糕、令人发指的绑定会将 12 赋值给 QML 对象的a 属性。
QML 中的每个 JavaScript 表达式、函数或文件都有其独特的变量对象。在一个变量对象中声明的局部变量绝不会与在另一个变量对象中声明的局部变量发生冲突。
类型名称与导入的 JavaScript 文件
QML 文档包含 import 语句,用于定义文档中可见的类型名称和 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引入了一种更结构化、面向对象的方法,因此不需要使用JavaScript的this 属性。
由于附加属性与作用域对象之间的交互关系,从绑定中访问附加属性时必须格外小心。从概念上讲,附加属性存在于所有对象上,即使它们仅对其中一部分对象产生影响。因此,未加修饰的附加属性读取操作将始终解析为作用域对象上的附加属性,这并不总是程序员所期望的结果。
例如,PathView 类型会根据其路径中的位置,将插值后的值属性附加到其委托上。由于PathView 仅将这些属性有意义地附加到委托的根对象上,因此任何访问这些属性的子对象都必须显式限定根对象,如下所示。
如果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意外地具有一个titletype 属性,titletype 这个 ID 仍将具有优先级。
组件实例层次结构
在 QML 中,组件实例将各自的组件作用域相互连接,从而形成作用域层次结构。组件实例可以直接访问其父组件的作用域。
最简单的方法是使用内联子组件,其组件作用域被隐式限定为外部组件的子项。
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 组件引用根对象id
请避免在定义该根对象的 QML 文件外部访问其id 。id 的值仅在同一文档内有效,若从其他组件依赖这些值,将破坏封装和组件边界。
// Main.qml
import QtQuick
Item {
id: root
CustomItem { }
}
// CustomItem.qml
import QtQuick
Rectangle {
width: root.width
height: root.height / 2
color: "red"
}在上例中,root 并未在CustomItem.qml 中声明。代码之所以仍能运行,是因为CustomItem 是在root 存在的上下文中实例化的,这符合 QML 的动态作用域规则。 然而,这会产生对外部作用域的隐式依赖,导致组件在动态加载或用于不同上下文时变得不可靠且难以复用。这种模式还可能导致难以察觉的错误。例如,如果作用域链中的另一个对象使用了相同的id ,它可能会遮蔽预期中的那个,从而导致意外行为。
为了使组件可靠且可复用,请仅通过显式属性、属性别名或信号访问外部状态。避免依赖外层作用域的id 值。
以下示例用显式的组件 API 替换了动态作用域。
// Main.qml
import QtQuick
import QtQuick.Window
Window {
id: root
width: 400
height: 300
visible: true
CustomItem {
width: root.width
height: root.height
boxColor: "blue"
}
}
// CustomItem.qml
import QtQuick
Item {
id: container
property alias boxColor: rect.color
Rectangle {
id: rect
width: container.width
height: container.height / 2
color: "red"
}
}将同一规则应用于组件内部所有使用id 声明的内部对象。
// MyItem.qml
import QtQuick
Item {
Item {
id: internalCounter
property int count: 5
}
}
// main.qml
import QtQuick
MyItem {
Component.onCompleted: {
console.log(internalCounter.count);
}
}在上例中,从另一个 QML 文件访问id internalCounter 会导致ReferenceError ,因为id 是文档局部的,无法在声明它的 QML 文件之外访问。
如果内部对象需要公开状态或行为,请通过属性、属性别名或信号显式地公开它们。
以下示例通过显式的组件 API 公开内部状态,而不是依赖于直接访问文档局部的id 。
// MyItem.qml
import QtQuick
Item {
Item {
id: internalCounter
property int count: 5
}
property alias internalCount: internalCounter.count
}
// main.qml
import QtQuick
MyItem {
Component.onCompleted: {
console.log(internalCount);
}
}这有助于保持清晰的组件边界,并支持更好的可扩展性和可测试性。
被覆盖的属性
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 对象的text 属性的绑定,都将指向在 Displayable 组件中声明的title 属性。
尽管名称相同,这两个属性却是完全独立的。其中一个属性的 onChanged 信号处理程序不会因另一个同名属性的变化而被触发。对任一属性的别名将指向其中一个,但不会同时指向两个。
JavaScript 全局对象
为避免混淆,QML 不允许类型、ID 和属性名称与全局对象上的属性发生冲突。程序员可以确信,Math.min(10, 9) 始终会按预期工作!
有关更多信息,请参阅JavaScript 宿主环境。
© 2026 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.