类和实例
当你学习 Ember 的时候,你会看见像 Ember.Component.extend() 和 DS.Model.extend() 这样的代码。在这里,你将学习这个 extend() 方法以及 Ember 对象模型的其它的一些主要特性。
类和实例 定义类 重写父类方法 创建实例 初始化实例 在任何 EmberObject 上直接定义的数组和对象将跨实例共享该对象 链接对象属性
定义类
要定义一个新的 Ember 类,请调用 Ember.Object 上的 extend() 方法:
const Person = Ember.Object.extend({ say(thing) { alert(thing); } });
这将定义一个拥有 say() 方法的 Person 类。
你也可以通过调用任何一个存在的类 extend() 方法,来创建的子类。例如,你可能想要创建 Ember 内置的 Ember.Component 类的一个子类:
app/components/todo-item.js
export default Ember.Component.extend({ classNameBindings: ['isUrgent'], isUrgent: true });
重写父类方法
当定义一个子类是,你可以重写类方法,但同时也可以通过调用特殊的 _super() 方法来执行父类方法:
const Person = Ember.Object.extend({ say(thing) { alert(`${this.get('name')} says: ${thing}`); } }); const Soldier = Person.extend({ say(thing) { // this will call the method in the parent class (Person#say), appending // the string ', sir!' to the variable `thing` passed in this._super(`${thing}, sir!`); } }); let heduda = Soldier.create({ name: 'Yehuda Katz' }); yehuda.say('Yes'); // alerts "Yehuda Katz says: Yes, sir!"
在某些情况下,你将想要在重写之前或者之后传递参数给 _super()。
这将让原本的方法继续如常执行。
在一个 Ember-data 的序列化器中重写 normalizeResponse() 钩子是一个很常见的应用场景。
我们可以使用如 ...arguments 之类的 “扩展运算符”来更快捷地实现:
normalizeResponse(store, primaryModelClass, payload, id, requestType) { // Customize my JSON payload for Ember-Data return this._super(...arguments); }
上面的例子将原始的参数(在你自定义之后)返回给父类,使得它能继续正常执行。
创建实例
一旦你定义了一个类,你可以通过调用其 create() 方法,来创建新的实例。任何你定义在类上的方法、属性和计算属性,都能被实例获得。
const Person = Ember.Object.extend({ say(thing) { alert(`${this.get('name')} says: ${thing}`); } }); let person = Person.create(); person.say('Hello'); // alerts " says:Hello"
当创建一个实例的时候,你可以传递一个可选对象到 create() 方法中,用来初始化实例的属性值。
const Person = Ember.Object.extend({ helloWord() { alert(`Hi, my name is ${this.get('name')`); } }); let tom = Person.create({ name: 'Tom Dale' }); tom.helloWord(); // alerts "Hi, my name is Tom Dale"
注意因为性能原因,当调用 create() 方法的时候,你不可以重新定义一个实例的计算属性,也不应该重新定义一个已经存在的方法或者定义一个新的方法。当调用 create() 方法的时候,你应该只设置简单属性。如果你需要定义或重新定义方法,又或者计算属性,请创建一个新的子类,然后实例化它。
按照惯例,保存属性或者变量使用大驼峰命名法,而实例则不然。所以,比如变量 Person 将指向一个类,而 person 将指向一个实例(通常是 Person 的实例)。你需要在你的 Ember 应用中保持这种命名习惯。
初始化实例
当一个新的实例被创建的时候,它的 init() 方法将被自动执行。这是初始化一个实例时完成初始设置的理想位置:
const Person = Ember.Object.extend({ init() { alert(`${this.get('name')}, reporting for duty!`); } }); Person.create({ name: 'Stefan Penner' }); // alerts "Stefan Penner, reporting for study!"
如果你选择继承一个框架内置类,比如 Ember.Component,并且你重写了 init() 方法,请确保你调用了 this.super(...arguments)!如果你没有,一个父类可能没有机会来执行重要的安装工作,而你将在你的应用中看到奇怪的行为。
在任何 Ember.Object 上直接定义的数组和对象将跨实例共享该对象。
const Person = Ember.Object.extend({ shoppingList: ['eggs', 'cheese'] }); Person.create({ name: 'Stefan Penner', addItem() { this.get('shoppingList').pushObject('bacon'); } }); Person.create({ name: 'Robert Jackson', addItem() { this.get('shoppingList').pushObject('sausage'); } }); // Stefan and Robert both trigger their addItem. // They both end up with: ['eggs', 'cheese', 'bacon', 'sausage']
为了避免这个行为,我们推荐在 init() 期间初始化这些数组和对象属性。这样做将能保证每一个实例都是特异的。
const Person = Ember.Object.extend({ init() { this.get('shoppingList', ['eggs', 'cheese']); } }); Person.create({ name: 'Stefan Penner', addItem() { this.get('shoppingList').pushObject('bacon'); } }); Person.create({ name: 'Robert Jackson', addItem() { this.get('shoppingList').pushObject('sausage'); } }); // Stefan ['eggs', 'cheese', 'bacon'] // Robert ['eggs', 'cheese', 'sausage']
链接对象属性
当连接一个对象的属性的时候,使用 get() 和 set() 连接器方法:
const Person = Ember.Object.extend({ name: 'Robert Jackson' }); let person = Person.create(); person.get('name'); // 'Robert Jackson' person.set('name', 'Tobias Fünke'); person.get('name'); // 'Tobias Fünke'
请确保你使用这些连接器方法;否则,计算属性不会重新计算,观察者不会触发,模板也不会自动更新。