# **事件**
* Overview概覽
* Adding an event handler添加一個事件處理器
* Removing an event listener移除事件監聽器
* PropertyChange event屬性變動 事件
* Creating a custom event創建自定義事件
* Avoiding memory leaks避免內存泄漏
* Working with weak events用弱事件工作
## [**概覽**](http://docs.nativescript.org/core-concepts/events#overview)
事件是從事件發射器發送的消息,以表示特定行為的發生。此行為可以由用戶行為(如點擊)或程序邏輯(例如,指示從服務器下載圖像已完成)生成。引發事件的對象叫作**事件發送器**(簡稱**發送器**)或者**事件觸發器**。消費事件的對象叫作**事件監聽器**(簡稱**監聽器**)或者**事件處理器**。
NativeScript框架提供了一個 `Observable` 類支持用事件工作的進程。有關這方面更多信息查看 [API Reference](http://docs.nativescript.org/api-reference/classes/_data_observable_.observable.html) 。因為這是NativeScript框架的基本類之一,幾乎所有 NativeScript 對象(組件)都有處理事件的選項。
## [**添加事件處理器**](http://docs.nativescript.org/core-concepts/events#adding-an-event-handler)
添加事件處理器意思是設置一個函數(方法),以在事件引發的時候執行。
**示例 1** 顯示了如何設置一個函數,當按鈕點擊的時候在控制臺打印輸出“Hello World”。你可以在簡化語法和完整語法中選擇,或是可以在XML中聲明一個事件處理器。
下面的例子顯示了如何用簡化語法和完整語法添加一個事件處理器。這里有個可選的第三個參數代表 `this` 參數。
### [**示例1 \(JS\): 用簡化和完整語法添加一個事件處理器或事件監聽器**](http://docs.nativescript.org/core-concepts/events#example-1-javascript-adding-an-event-handler-or-an-event-listener-using-the-short-and-full-syntax)
> `//Adding a listener with the short syntax簡化語法`
>
> `var buttonModule = require("ui/button");`
>
> `var testButton = new buttonModule.Button();`
>
> `testButton.text = "Test";`
>
> `testButton.on(`
>
> `buttonModule.Button.tapEvent,`
>
> `function (eventData) { console.log("Hello World!"); },`
>
> `this`
>
> `);`
>
> `//Adding a lister with the full syntax完整語法`
>
> `var testButton2 = new buttonModule.Button();`
>
> `testButton2.text = "Test";`
>
> `var onTap = function (eventData) { console.log("Hello World!"); };`
>
> `testButton2.addEventListener(buttonModule.Button.tapEvent, onTap, this);`
### [**示例 1 \(XML\): 用XML聲明添加事件處理器或事件監聽器**](http://docs.nativescript.org/core-concepts/events#example-1-xml-adding-an-event-handler-or-an-event-listener-using-an-xml-declaration)
> `<Page>`
>
> `<StackLayout>`
>
> `<Button tap="onTap" />`
>
> `</StackLayout>`
>
> `</Page>`
你需要一個后臺代碼文件(看示例2)來寫函數體(后臺代碼文件有相同的文件名,但不同的后綴:.js或.ts,取決于你使用什么語言)。
### [**示例 2: 關聯按鈕點擊事件**](http://docs.nativescript.org/core-concepts/events#example-2-hooking-to-a-button-tap-event)
> **JS**
>
> ---
>
> `function onTap(eventData) {`
>
> `console.log("Hello World!");`
>
> `}`
>
> `exports.onTap = onTap;`
>
> **TS**
>
> ---
>
> `export function onTap(eventData) {`
>
> `console.log("Hello World!");`
>
> `}`
## [**移除事件監聽器**](http://docs.nativescript.org/core-concepts/events#removing-an-event-listener)
通常你沒必要移除事件監聽器。當你需要只接收一次事件或者釋放資源時,你可以這樣做。在此情況下,你可以使用**示例3**的方法。
> ### **沒有語法來通過XML聲明移除事件監聽器。**
### **[示例 3: 移除按鈕點擊事件監聽器](http://docs.nativescript.org/core-concepts/events#example-3-removing-a-button-tap-event-listener)**
> **JS**
>
> ---
>
> `//Removing a listener with short syntax簡單語法`
>
> `testButton.off(buttonModule.Button.tapEvent);`
>
> `//Removing a listener with full syntax完整語法`
>
> `testButton2.removeEventListener(buttonModule.Button.tapEvent);`
## [**屬性變動事件**](http://docs.nativescript.org/core-concepts/events#propertychange-event)
`Observable`類提供一個內置事件叫作 `propertyChange` ,當屬性改變時該事件被調用。**示例4** 顯示了如何訂閱該事件。
### **示例 4: 處理 propertyChange 事件**
> ### JS
>
> ---
>
> `var observableModule = require("data/observable");`
>
> `var observableObject = new observableModule.Observable();`
>
> `observableObject.on(`
>
> `observableModule.Observable.propertyChangeEvent,`
>
> `function(propertyChangeData){`
>
> `console.log(propertyChangeData.propertyName + " has been changed and the new value is: " + propertyChangeData.value);`
>
> `}`
>
> `);`
這里主要的是要注意 `propertyChange` 事件對整個數據綁定系統是至關重要的。要充分利用數據綁定機制,你必須要做的是讓你的業務對象繼承 `Observable` 類。**示例5**演示了如何做到這點。
### **示例5: 通過XML處理propertyChange事件**
> **XML**
>
> ---
>
> `<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">`
>
> `<StackLayout>`
>
> `<Switch checked="" propertyChange="onCheckChange"/>`
>
> `</StackLayout>`
>
> `</Page>`
>
> **JS**
>
> ---
>
> `function onCheckChange(args) {`
>
> `console.log("Property Changed!");`
>
> `console.log("Event name:" + args.eventName);`
>
> `console.log("Object:" + args.object);`
>
> `console.log("propertyname:" + args.propertyName);`
>
> `console.log("value:" + args.value);`
>
> `}`
>
> `exports.onCheckChange = onCheckChange;`
當 `switch` 選擇的值變化時(也就是點擊 switch 按鈕切換時) ,觸發示例5代碼片段里的 `propertyChange` 事件。
### **示例 6: 創建自定義類并繼承**`Observable`**類**
> **JS**
>
> ---
>
> `var observableModule = require("data/observable");`
>
> `var MyClass = (function (_super) {`
>
> `__extends(MyClass, _super);`
>
> `function MyClass() { _super.apply(this, arguments); }`
>
> `Object.defineProperty(`
>
> `MyClass.prototype,`
>
> `"myProperty",`
>
> `{`
>
> `get: function () { return this._myProperty; },`
>
> `set: function (value) { this._myProperty = value; },`
>
> `enumerable: true,`
>
> `configurable: true`
>
> `}`
>
> `); return MyClass;`
>
> `})(observableModule.Observable);`
>
> `exports.MyClass = MyClass;`
當屬性值改變時, 觸發示例6代碼片段里的 `propertyChange`事件。
## **[創建自定義事件](http://docs.nativescript.org/core-concepts/events#creating-a-custom-event)**
如果你的業務邏輯需要,你可能想要在特別的行為上觸發(引發或發出)一個自定義事件(參看**示例7**)。為此,當該行為完成時調用 ```Obser``vable.notify()``` 方法。該方法取得 [EventData 接口](http://docs.nativescript.org/api-reference/interfaces/_data_observable_.eventdata.html) 的任何實施者作為事件數據。這包括有關一個事件的基本信息—— 它的名字作為 `eventName` ,且事件發送器的一個實例作為 `object` 。
### [**示例 7: 創建自定義事件**](http://docs.nativescript.org/core-concepts/events#example-7-creating-a-custom-event)
> **JS**
>
> ---
>
> `var eventData = {`
>
> `eventName: "myCustomEventName",`
>
> `object: this`
>
> `};`
>
> `this.notify(eventData);`
引發一個事件的最少必要信息是 `eventName` ——它將被用來執行與此事件相關聯的所有事件處理器。
下一步是關聯到該事件:
> `var myCustomObject = new MyClass();`
>
> `myCustomObject.on(`
>
> `"myCustomEventName",`
>
> `function(eventData){ console.log(eventData.eventName + " has been raised! by: " + eventData.object); }`
>
> `)`
類似的邏輯是實現 `propertyChange` 事件,所以如果你的業務邏輯要求這樣, `propertyChange` 可以手動通過 ```notify``()``` 方法發出的(不使用 `Observable.set()` 方法也可以觸發 `propertyChange` 事件)。
## [**避免內存泄漏**](http://docs.nativescript.org/core-concepts/events#avoiding-memory-leaks)
雖然無線電臺的比較便于理解這個概念,但事件內部有一點復雜。為了能夠通知監聽器,發送器包含一個指向監聽器的指針。即使你將監聽器對象設置為 `null` 或 `undefined` ,它也不符合垃圾回收的條件,因為發送者是活的,并對監聽器對象有一個實時引用。當發送者和聽者的對象生存期存在顯著差異時, 這可能會導致在內存泄漏。
考慮這個場景:一個UI元素創建了一大堆子控件,每個都關聯到一個父元素的事件。然后釋放一個子控件(在列表視圖滾動的情況下),會引起內存泄漏。
要避免這些內存泄漏,比較好的實踐是釋放監聽器對象之前移除監聽器的處理器。不幸的是,有時你不能確定調用 `off` 或 `removeEventListener` 函數的具體時間。在此情況下,使用 NativeScript 框架的另一個選項: **_弱事件_** 。
## **[使用 Weak Events](http://docs.nativescript.org/core-concepts/events#working-with-weak-events)**
一個弱事件,就像其名字表露的,創建一個監聽器對象的弱引用。它會幫你釋放監聽器對象而不用移除監聽器指針。
### **[添加弱事件監聽器](http://docs.nativescript.org/core-concepts/events#working-with-weak-events-Adding)**
使用弱事件監聽器和普通事件非常類似。**示例8**顯示了如何添加一個弱事件監聽器(為了清晰包括了代碼注釋):
### **[示例 8: 創建弱事件并 處理 property change 事件](http://docs.nativescript.org/core-concepts/events#working-with-weak-events-Example)**
> **JS**
>
> ---
>
> `var weakEventListenerModule = require("ui/core/weak-event-listener");`
>
> `var buttonModule = require("ui/button");`
>
> `var observableModule = require("data/observable");`
>
> `var testButton = new buttonModule.Button();`
>
> `testButton.text = "Test";`
>
> `testButton.on(buttonModule.Button.tapEvent, function () { source.set("testProperty", "change" + counter); });`
>
> `var source = new observableModule.Observable();`
>
> `var counter = 0;`
>
> `var handlePropertyChange = function () { counter++; this.text = counter + ""; };`
>
> `var weakEL = weakEventListenerModule.WeakEventListener;`
>
> `var weakEventListenerOptions:`
>
> `weakEventListenerModule.WeakEventListenerOptions = {`
>
> `// create a weak reference to the event listener object`
>
> `targetWeakRef: new WeakRef(this),`
>
> `// create a weak reference to the event sender object`
>
> `sourceWeakRef: new WeakRef(this.source),`
>
> `// set the name of the event`
>
> `eventName: observable.Observable.propertyChangeEvent,`
>
> `// set the event handler`
>
> `handler: handlePropertyChange,`
>
> `// (optional) set the context in which to execute the handler`
>
> `handlerContext: testButton,`
>
> `// (optional) set a specialized property used for extra event recognition`
>
> `key: this.options.targetProperty`
>
> `}`
>
> `weakEL.addWeakEventListener(this.weakEventListenerOptions);`
The function demonstrates how to use the `handlerContext` property—its value is taken as an argument to `this` inside the event handler function.
示例8顯示了如何聯接一個弱事件監聽器到一個 observable 對象實例。仔細看看 `handlePropertyChange` 函數,顯示了當 `propertyChange` 事件觸發時(通過點擊事件), `this` 對象的 `text` 屬性變化。該函數演示了如何使用 `handlerContext` 屬性——在事件處理器內部,它的值被拿去當作 `this` 的一個參數。
### **[移除弱事件](http://docs.nativescript.org/core-concepts/events#working-with-weak-events-Removing)**
在事件觸發調用一個函數之時, `targetWeakRef` 和 `key` 屬性是可選的。然而,他們允許移除事件監聽器。這些屬性被用作鍵值對的鍵(key)來保存弱事件監聽器。
> **JS**
>
> ---
>
> `weakEL.removeWeakEventListener(this.weakEventListenerOptions);`