类和实例
当你学习 Ember 的时候,你会看见像 Ember.Component.extend()
和 DS.Model.extend()
这样的代码。在这里,你将学习这个 extend()
方法以及 Ember 对象模型的其它的一些主要特性。
定义类
要定义一个新的 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'
请确保你使用这些连接器方法;否则,计算属性不会重新计算,观察者不会触发,模板也不会自动更新。