1# getTarget接口:获取状态管理框架代理前的原始对象
2
3为了获取状态管理框架代理前的原始对象,开发者可以使用[getTarget接口](../reference/apis-arkui/js-apis-StateManagement.md#gettarget)。
4
5在阅读本文档前,建议提前阅读:[\@Observed](./arkts-observed-and-objectlink.md),[\@ObservedV2](./arkts-new-observedV2-and-trace.md)。
6
7>**说明:**
8>
9>从API version 12开始,开发者可以使用UIUtils中的getTarget接口获取状态管理框架代理前的原始对象。
10
11## 概述
12
13状态管理框架会对Class、Date、Map、Set、Array类型的原始对象添加代理,用于观测属性变化与API调用。这一层代理会使得变量类型改变,在类型判断、NAPI调用等场景,会由于类型并非原始对象的类型产生预料之外的结果。
14
15- 使用getTarget接口需要导入UIUtils工具。
16
17  ```ts
18  import { UIUtils } from '@kit.ArkUI';
19  ```
20
21- 状态管理V1中,会给\@Observed装饰的类对象以及使用状态变量装饰器如\@State装饰的Class、Date、Map、Set、Array添加一层代理用于观测一层属性或API调用产生的变化。
22- 状态管理V2中,会给使用状态变量装饰器如\@Trace、\@Local装饰的Date、Map、Set、Array添加一层代理用于观测API调用产生的变化。
23
24使用getTarget接口可以获取这些代理对象的原始对象。
25
26## 限制条件
27
28- getTarget仅支持对象类型传参
29
30  ```ts
31  import { UIUtils } from '@kit.ArkUI';
32  let res = UIUtils.getTarget(2); // 非对象类型入参,错误用法
33  @Observed
34  class Info {
35    name: string = "Tom";
36  }
37  let info: Info = new Info();
38  let rawInfo: Info = UIUtils.getTarget(info); // 正确用法
39  ```
40
41- 更改getTarget获取的原始对象中的内容不会被观察到变化,也不会触发UI刷新。
42
43  ```ts
44  import { UIUtils } from '@kit.ArkUI';
45  @Observed
46  class Info {
47    name: string = "Tom";
48  }
49  @Entry
50  @Component
51  struct Index {
52    @State info: Info = new Info();
53
54    build() {
55      Column() {
56        Text(`info.name: ${this.info.name}`)
57        Button(`更改代理对象的属性`)
58          .onClick(() => {
59            this.info.name = "Alice"; // Text组件能够刷新
60          })
61        Button(`更改原始对象的属性`)
62          .onClick(() => {
63            let rawInfo: Info = UIUtils.getTarget(this.info);
64            rawInfo.name = "Bob"; // Text组件不能刷新
65          })
66      }
67    }
68  }
69  ```
70
71## 使用场景
72
73### 获取状态管理V1代理前的原始对象
74
75状态管理V1有两种场景会给对象增加代理:
76
77【1】\@Observed装饰的类实例。在创建\@Observed装饰的类实例时,会给该实例添加代理。该过程发生在new对象的过程中,没有经过new操作符创建的对象是不被代理的。
78
79```ts
80@Observed
81class ObservedClass {
82  name: string = "Tom";
83}
84class NonObservedClass {
85  name: string = "Tom";
86}
87let observedClass: ObservedClass = new ObservedClass(); // 被代理
88let nonObservedClass: NonObservedClass = new NonObservedClass(); // 不被代理
89```
90
91【2】状态变量装饰器装饰的复杂类型对象。使用\@State、\@Prop等状态变量装饰器装饰Class、Map、Set、Date、Array时,会添加代理。若该对象已经是代理对象,则不会重复创建代理。
92
93```ts
94@Observed
95class ObservedClass {
96  name: string = "Tom";
97}
98class NonObservedClass {
99  name: string = "Tom";
100}
101let observedClass: ObservedClass = new ObservedClass(); // 被代理
102let nonObservedClass: NonObservedClass = new NonObservedClass(); // 不被代理
103@Entry
104@Component
105struct Index {
106  @State observedObject: ObservedClass = observedClass; // 已被代理数据不会重复创建代理
107  @State nonObservedObject: NonObservedClass = nonObservedClass; // 创建代理
108  @State numberList: number[] = [1, 2, 3]; // Array类型创建代理
109  @State sampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // Map类型创建代理
110  @State sampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // Set类型创建代理
111  @State sampleDate: Date = new Date(); // Date类型创建代理
112
113  build() {
114    Column() {
115      Text(`this.observedObject === observedClass: ${this.observedObject === observedClass}`) // true
116      Text(`this.nonObservedObject === nonObservedClass: ${this.nonObservedObject === nonObservedClass}`) // false
117    }
118  }
119}
120```
121
122使用UIUtils.getTarget接口可以获取代理前的原始对象。
123
124```ts
125import { UIUtils } from '@kit.ArkUI';
126@Observed
127class ObservedClass {
128  name: string = "Tom";
129}
130class NonObservedClass {
131  name: string = "Tom";
132}
133let observedClass: ObservedClass = new ObservedClass(); // 被代理
134let nonObservedClass: NonObservedClass = new NonObservedClass(); // 不被代理
135let globalNumberList: number[] = [1, 2, 3]; // 不被代理
136let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理
137let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理
138let globalSampleDate: Date = new Date(); // 不被代理
139@Entry
140@Component
141struct Index {
142  @State observedObject: ObservedClass = observedClass; // 已被代理数据不会重复创建代理
143  @State nonObservedObject: NonObservedClass = nonObservedClass; // 创建代理
144  @State numberList: number[] = globalNumberList; // Array类型创建代理
145  @State sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理
146  @State sampleSet: Set<number> = globalSampleSet; // Set类型创建代理
147  @State sampleDate: Date = globalSampleDate; // Date类型创建代理
148
149  build() {
150    Column() {
151      Text(`this.observedObject === observedClass: ${this.observedObject ===
152           observedClass}`) // true
153      Text(`UIUtils.getTarget(this.nonObservedObject) === nonObservedClass: ${UIUtils.getTarget(this.nonObservedObject) ===
154           nonObservedClass}`) // true
155      Text(`UIUtils.getTarget(this.numberList) === globalNumberList: ${UIUtils.getTarget(this.numberList) ===
156           globalNumberList}`) // true
157      Text(`UIUtils.getTarget(this.sampleMap) === globalSampleMap: ${UIUtils.getTarget(this.sampleMap) ===
158           globalSampleMap}`) // true
159      Text(`UIUtils.getTarget(this.sampleSet) === globalSampleSet: ${UIUtils.getTarget(this.sampleSet) ===
160           globalSampleSet}`) // true
161      Text(`UIUtils.getTarget(this.sampleDate) === globalSampleDate: ${UIUtils.getTarget(this.sampleDate) ===
162           globalSampleDate}`) // true
163    }
164  }
165}
166```
167
168### 获取状态管理V2代理前的原始对象
169
170状态管理V2会给状态变量装饰器如\@Trace、\@Local装饰的Map、Set、Date、Array添加一层代理。和V1不同的是,状态管理V2不会对类对象实例进行代理。
171
172```ts
173@ObservedV2
174class ObservedClass {
175  @Trace name: string = "Tom";
176}
177let globalObservedObject: ObservedClass = new ObservedClass(); // 不被代理
178let globalNumberList: number[] = [1, 2, 3]; // 不被代理
179let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理
180let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理
181let globalSampleDate: Date = new Date(); // 不被代理
182@Entry
183@ComponentV2
184struct Index {
185  @Local observedObject: ObservedClass = globalObservedObject; // V2中对象不被代理
186  @Local numberList: number[] = globalNumberList; // Array类型创建代理
187  @Local sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理
188  @Local sampleSet: Set<number> = globalSampleSet; // Set类型创建代理
189  @Local sampleDate: Date = globalSampleDate; // Date类型创建代理
190
191  build() {
192    Column() {
193      Text(`this.observedObject === globalObservedObject ${this.observedObject === globalObservedObject}`) // true
194      Text(`this.numberList === globalNumberList ${this.numberList === globalNumberList}`) // false
195    }
196  }
197}
198```
199
200使用UIUtils.getTarget接口可以获取代理前的原始对象。
201
202```ts
203import { UIUtils } from '@kit.ArkUI';
204@ObservedV2
205class ObservedClass {
206  @Trace name: string = "Tom";
207}
208let globalObservedObject: ObservedClass = new ObservedClass(); // 不被代理
209let globalNumberList: number[] = [1, 2, 3]; // 不被代理
210let globalSampleMap: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]); // 不被代理
211let globalSampleSet: Set<number> = new Set([0, 1, 2, 3, 4]); // 不被代理
212let globalSampleDate: Date = new Date(); // 不被代理
213@Entry
214@ComponentV2
215struct Index {
216  @Local observedObject: ObservedClass = globalObservedObject; // V2中对象不被代理
217  @Local numberList: number[] = globalNumberList; // Array类型创建代理
218  @Local sampleMap: Map<number, string> = globalSampleMap; // Map类型创建代理
219  @Local sampleSet: Set<number> = globalSampleSet; // Set类型创建代理
220  @Local sampleDate: Date = globalSampleDate; // Date类型创建代理
221
222  build() {
223    Column() {
224      Text(`this.observedObject === globalObservedObject ${this.observedObject ===
225           globalObservedObject}`) // true
226      Text(`UIUtils.getTarget(this.numberList) === globalNumberList: ${UIUtils.getTarget(this.numberList) ===
227           globalNumberList}`) // true
228      Text(`UIUtils.getTarget(this.sampleMap) === globalSampleMAP: ${UIUtils.getTarget(this.sampleMap) ===
229           globalSampleMap}`) // true
230      Text(`UIUtils.getTarget(this.sampleSet) === globalSampleSet: ${UIUtils.getTarget(this.sampleSet) ===
231           globalSampleSet}`) // true
232      Text(`UIUtils.getTarget(this.sampleDate) === globalSampleDate: ${UIUtils.getTarget(this.sampleDate) ===
233           globalSampleDate}`) // true
234    }
235  }
236}
237```
238
239状态管理V2装饰器会为装饰的变量生成getter和setter方法,同时为原有变量名添加"\_\_ob\_"的前缀。出于性能考虑,getTarget接口不会对V2装饰器生成的前缀进行处理,因此向getTarget接口传入\@ObservedV2装饰的类对象实例时,返回的对象依旧为对象本身,且被\@Trace装饰的属性名仍有"\_\_ob\_"前缀。
240
241该前缀会导致某些NAPI接口无法按预期处理对象的属性,以下面的对象为例,目前已知影响的NAPI接口如下:
242
243```ts
244// ObservedV2装饰的类
245@ObservedV2
246class Info {
247  @Trace name: string = "Tom";
248  @Trace age: number = 24;
249}
250let info: Info = new Info(); // NAPI接口传入info实例
251```
252
253| 影响接口名              | 影响结果                                       |
254| ----------------------- | ---------------------------------------------- |
255| napi_get_property_names | 返回值为"\_\_ob\_name","\_\_ob\_age"。        |
256| napi_set_property       | 使用"name","\_\_ob\_name"均能赋值成功。       |
257| napi_get_property       | 使用"name","\_\_ob\_name"均能获取到值。       |
258| napi_has_property       | 使用"name","\_\_ob\_name"均返回true。         |
259| napi_delete_property    | 删除属性时需要加上"\_\_ob\_"前缀才能删除成功。 |
260| napi_has_own_property   | 使用"name","\_\_ob\_name"均返回true。         |
261| napi_set_named_property | 使用"name","\_\_ob\_name"均能赋值成功。       |
262| napi_get_named_property | 使用"name","\_\_ob\_name"均能获取到值。       |
263| napi_has_named_property | 使用"name","\_\_ob\_name"均返回true。         |