スコープと名前解決
QMLのプロパティバインディング、インライン関数、およびインポートされたJavaScriptファイルは、すべてJavaScriptのスコープ内で実行されます。スコープは、式がどの変数にアクセスできるかを制御し、2つ以上の名前が競合する場合にどの変数が優先されるかを決定します。
JavaScript の組み込みスコープメカニズムは非常に単純であるため、QML では、QML 言語の拡張機能により自然に適合するよう、これを拡張しています。
JavaScript のスコープ
QML のスコープ拡張は、JavaScript の自然なスコープの仕組みに干渉することはありません。JavaScript プログラマは、QML で関数、プロパティバインディング、またはインポートされた JavaScript ファイルをプログラミングする際に、既存の知識を再利用できます。
次の例では、addConstant() メソッドは、QMLオブジェクトのa およびb プロパティの値にかかわらず、プログラマが期待する通りに、渡されたパラメータに13を加算します。
QtObject {
property int a: 3
property int b: 9
function addConstant(b) {
var a = 13;
return b + a;
}
}QMLがJavaScriptの通常のスコープ規則を尊重するという点は、バインディングにおいても同様です。この全くもって悪質で忌まわしいバインディングは、QMLオブジェクトのa プロパティに12を代入します。
QML内のすべてのJavaScript式、関数、ファイルには、それぞれ固有の変数オブジェクトがあります。ある場所で宣言されたローカル変数は、別の場所で宣言されたローカル変数と決して競合することはありません。
型名とインポートされた JavaScript ファイル
QMLドキュメントには、そのドキュメントから参照可能な型名やJavaScriptファイルを定義するimport文が含まれます。型名は、QML宣言自体での使用に加え、JavaScriptコードがアタッチされたプロパティや列挙値にアクセスする際にも使用されます。
インポートの効果は、QMLドキュメント内のすべてのプロパティバインディングおよびJavaScript関数に適用されます。これは、ネストされたインラインコンポーネント内のものにも同様です。次の例は、いくつかの列挙値にアクセスし、インポートされたJavaScript関数を呼び出す単純なQMLファイルを示しています。
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コンポーネントは、論理的なスコープを定義します。各ドキュメントには少なくとも1つのルートコンポーネントがありますが、その他のインラインサブコンポーネントを含むこともできます。コンポーネントのスコープとは、そのコンポーネント内のオブジェクト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 プロパティに直接アクセスしています。ルート型のプロパティに直接アクセスできるため、コンポーネント全体へのデータ配信が容易になります。
2つ目のText 型は、IDを使用して1つ目のテキストに直接アクセスしています。IDはQMLプログラマによって明示的に指定されるため、他のプロパティ名(JavaScriptスコープ内のものを除く)よりも常に優先されます。 たとえば、前の例で、バインディングのスコープオブジェクトに titletype プロパティがあったという(ありそうもない)場合でも、titletype というIDが優先されます。
コンポーネントインスタンスの階層
QML では、コンポーネントインスタンスはそれぞれのコンポーネントスコープを相互に接続して、スコープ階層を形成します。コンポーネントインスタンスは、その親コンポーネントのコンポーネントスコープに直接アクセスできます。
これを示す最も簡単な方法は、コンポーネントスコープが外側のコンポーネントの子として暗黙的にスコープ設定されるインラインサブコンポーネントを使用することです。
Item {
property color defaultColor: "blue"
ListView {
delegate: Component {
Rectangle {
color: defaultColor
}
}
}
}このコンポーネントインスタンス階層により、デリゲートコンポーネントのインスタンスは、Item 型のdefaultColor プロパティにアクセスできます。もちろん、もしデリゲートコンポーネントにdefaultColor というプロパティがあった場合、そちらが優先されます。
コンポーネント・インスタンスのスコープ階層は、インライン外のコンポーネントにも適用されます。 次の例では、TitlePage.qml コンポーネントが2つの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コードの挙動が予測しづらくなるのを防ぐため、慎重に使用する必要があります。一般的には、2つのコンポーネントがすでに別の方法で密接に結合されている場合にのみ使用すべきです。再利用可能なコンポーネントを構築する際は、次のようにプロパティインターフェースを使用することが推奨されます:
// 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 ` を参照しないでください
ルートオブジェクトの `id ` には、それが定義されている QML ファイルの外側からアクセスしないようにしてください。`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 ファイルの外からはアクセスできないためです。
内部オブジェクトが状態や挙動を公開する必要がある場合は、プロパティ、プロパティエイリアス、またはシグナルを通じて明示的に公開してください。
次の例では、ドキュメントローカルの `id` への直接アクセスに頼るのではなく、明示的なコンポーネント API を通じて内部状態を公開しています。
// 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 プロパティを指します。
同じ名前を共有しているにもかかわらず、これら2つのプロパティは完全に別個のものです。一方のプロパティに対する 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.