Ember 計(jì)算屬性

2018-12-11 14:49 更新

簡單的計(jì)算屬性

簡單地來說,計(jì)算屬性就是將函數(shù)聲明為屬性,就類似于調(diào)用了一個(gè)函數(shù),Ember會自動調(diào)用這個(gè)函數(shù)。計(jì)算屬性最大的特點(diǎn)就是能自動檢測變化,及時(shí)更新數(shù)據(jù)。

  1. Person = Ember.Object.extend({
  2. firstName: null,
  3. lastName: null,
  4. // fullName 就是一個(gè)計(jì)算屬性
  5. fullName: Ember.computed('firstName', 'lastName', function() {
  6. return this.get('firstName') + ", " + this.get('lastName');
  7. })
  8. });
  9. // 實(shí)例化同時(shí)傳入?yún)?shù)
  10. var piter = Person.create({
  11. firstName: 'chen',
  12. lastName: 'ubuntuvim'
  13. });
  14. console.log(piter.get('fullName')); // output >> chen, ubuntuvim

計(jì)算屬性其實(shí)就是一個(gè)函數(shù),如果你接觸過就jQuery、Extjs相信你會非常熟悉,在這兩個(gè)框架中函數(shù)就是這么定義的。只不過在Ember中,把這種函數(shù)當(dāng)做屬性來處理,并且可以通過get獲取函數(shù)的返回值。

計(jì)算屬性鏈

Ember程序中,計(jì)算屬性還能調(diào)用另外一個(gè)計(jì)算屬性,形成計(jì)算屬性鏈,也可以用于擴(kuò)展某個(gè)方法。在上一實(shí)例的基礎(chǔ)上增加一個(gè)description()方法。

  1. Person = Ember.Object.extend({
  2. firstName: null,
  3. lastName: null,
  4. age: null,
  5. county: null,
  6. // fullName 就是一個(gè)計(jì)算屬性
  7. fullName: Ember.computed('firstName', 'lastName', function() {
  8. return this.get('firstName') + ", " + this.get('lastName');
  9. }),
  10. description: Ember.computed('fullName', 'age', 'county', function() {
  11. return this.get('fullName') + " age " + this.get('age') + " county " + this.get('county');
  12. })
  13. });
  14. // 實(shí)例化同時(shí)傳入?yún)?shù)
  15. var piter = Person.create({
  16. firstName: 'chen',
  17. lastName: 'ubuntuvim',
  18. age: 25,
  19. county: 'china'
  20. });
  21. console.log(piter.get('description')); // output >> chen, ubuntuvim

當(dāng)用戶使用set方法改變firstName的值,然后再調(diào)用get('description')得到的值也是更新后的值。

重寫計(jì)算屬性的get、set方法

注意要把重寫的屬性作為參數(shù)傳入computed方法,要區(qū)別計(jì)算屬性的定義方法,定義的時(shí)候computed方法的最后一個(gè)參數(shù)是一個(gè)function,而重寫的時(shí)候最后一個(gè)參數(shù)是一個(gè)hash。

  1. // 重寫計(jì)算屬性的get、set方法
  2. Person = Ember.Object.extend({
  3. firstName: null,
  4. lastName: null,
  5. // 重寫計(jì)算屬性fullName的get、set方法
  6. fullName: Ember.computed('firstName', 'lastName', {
  7. get(key) {
  8. return this.get('firstName') + "," + this.get('lastName');
  9. },
  10. set(key, value) {
  11. // 這個(gè)官方文檔使用的代碼,但是我運(yùn)行的時(shí)候出現(xiàn) Uncaught SyntaxError: Unexpected token [ 這個(gè)錯誤,不知道是否是缺少某個(gè)文件,后續(xù)會補(bǔ)上;
  12. // console.log("value = " + value);
  13. // var [ firstName, lastName ] = value.split(/\s+/);
  14. var firstName = value.split(/\s+/)[0];
  15. var lastName = value.split(/\s+/)[1];
  16. this.set('firstName', firstName);
  17. this.set('lastName', lastName);
  18. }
  19. }),
  20. // 對于普通的屬性無法重寫get、set方法
  21. // firstName: Ember.computed('firstName', {
  22. // get(key) {
  23. // return this.get('firstName') + "@@";
  24. // },
  25. // set(key, value) {
  26. // this.set('firstName', value);
  27. // }
  28. // })
  29. });
  30. var jack = Person.create();
  31. jack.set('fullName', "james kobe");
  32. console.log(jack.get('firstName'));
  33. console.log(jack.get('lastName'));

運(yùn)行結(jié)果

計(jì)算屬性值的統(tǒng)計(jì)

我們經(jīng)常會遇到這種情況:某個(gè)計(jì)算屬性值是依賴某個(gè)數(shù)組或者其他對象的,比如在Embertodos這個(gè)例子中有這樣的一段代碼。

  1. export default Ember.Controller.extend({
  2. todos: [
  3. Ember.Object.create({ isDone: true }),
  4. Ember.Object.create({ isDone: false }),
  5. Ember.Object.create({ isDone: true })
  6. ],
  7. remaining: Ember.computed('todos.@each.isDone', function() {
  8. var todos = this.get('todos');
  9. return todos.filterBy('isDone', false).get('length');
  10. })
  11. });

計(jì)算屬性remaining的值于依賴數(shù)組todos。在這里還有個(gè)知識點(diǎn):在上述代碼computed()方法里有一個(gè)todos.@each.isDone這樣的鍵,里面包含了一個(gè)特別的鍵@each(后面還會看到更特別的鍵[])。需要注意的是這種鍵不能嵌套并且是只能獲取一個(gè)層次的屬性。比如todos.@each.foo.name(獲取多層次屬性,這里是先得到foo再獲取name)或者todos.@each.owner.@each.name(嵌套)這兩種方式都是不允許的。

在如下4種情況Ember會自動更新綁定的計(jì)算屬性值:<br> 1.在todos數(shù)組中任意一個(gè)對象的isDone屬性值發(fā)生變化的時(shí)候; 2.往todos數(shù)組新增元素的時(shí)候; 3.從todos數(shù)組刪除元素的時(shí)候; 4.在控制器中todos數(shù)組被改變?yōu)槠渌臄?shù)組的時(shí)候;

比如下面代碼演示的結(jié)果;

  1. Task = Ember.Object.extend({
  2. isDone: false // 默認(rèn)為false
  3. });
  4. WorkerLists = Ember.Object.extend({
  5. // 定義一個(gè)Task對象數(shù)組
  6. lists: [
  7. Task.create({ isDone: false }),
  8. Task.create({ isDone: true }),
  9. Task.create(),
  10. Task.create({ isDone: true }),
  11. Task.create({ isDone: true }),
  12. Task.create({ isDone: true }),
  13. Task.create({ isDone: false }),
  14. Task.create({ isDone: true })
  15. ],
  16. remaining: Ember.computed('lists.@each.isDone', function() {
  17. var lists = this.get('lists');
  18. // 先查詢屬性isDone值為false的對象,再返回其數(shù)量
  19. return lists.filterBy('isDone', false).get('length');
  20. })
  21. });
  22. // 如下代碼使用到的API請查看:http://emberjs.com/api/classes/Ember.MutableArray.html
  23. var wl = WorkerLists.create();
  24. // 所有isDone屬性值未做任何修改
  25. console.log('1,>> Not complete lenght is ' + wl.get('remaining')); // output 3
  26. var lists = wl.get('lists'); // 得到對象內(nèi)的數(shù)組
  27. // ----- 演示第一種情況: 1. 在todos數(shù)組中任意一個(gè)對象的isDone屬性值發(fā)生變化的時(shí)候;
  28. // 修改數(shù)組一個(gè)元素的isDone的 值
  29. var item1 = lists.objectAt(3); // 得到第4個(gè)元素 objectAt()方法是Ember為我們提供的
  30. // console.log('item1 = ' + item1);
  31. item1.set('isDone', false);
  32. console.log('2,>> Not complete lenght is ' + wl.get('remaining')); // output 4
  33. // --------- 2. 往todos數(shù)組新增元素的時(shí)候;
  34. lists.pushObject(Task.create({ isDone: false })); //新增一個(gè)isDone為false的對象
  35. console.log('3,>> Not complete lenght is ' + wl.get('remaining')); // output 5
  36. // --------- 3. 從todos數(shù)組刪除元素的時(shí)候;
  37. lists.removeObject(item1); // 刪除了一個(gè)元素
  38. console.log('4,>> Not complete lenght is ' + wl.get('remaining')); // output 4
  39. // --------- 4. 在控制器中todos數(shù)組被改變?yōu)槠渌臄?shù)組的時(shí)候;
  40. // 創(chuàng)建一個(gè)Controller
  41. TodosController = Ember.Controller.extend({
  42. // 在控制器內(nèi)定義另外一個(gè)Task對象數(shù)組
  43. todosInController: [
  44. Task.create({ isDone: false }),
  45. Task.create({ isDone: true })
  46. ],
  47. // 使用鍵”@each.isDone“遍歷得到的filterBy()方法過濾后的對象的isDone屬性
  48. remaining: function() {
  49. // remaining()方法返回的是控制器內(nèi)的數(shù)組
  50. return this.get('todosInController').filterBy('isDone', false).get('length');
  51. }.property('@each.isDone') // 指定遍歷的屬性
  52. });
  53. todosController = TodosController.create();
  54. var count = todosController.get('remaining');
  55. console.log('5,>> Not complete lenght is ' + count); // output 1

代碼演示的結(jié)果

上述的情況中,我們對數(shù)組對象的是關(guān)注點(diǎn)是在對象的屬性上,但是實(shí)際中往往很多情況我們并不關(guān)系對象內(nèi)的屬性是否變化了,而是把數(shù)組元素作為一個(gè)整體對象處理(比如數(shù)組元素個(gè)數(shù)的變化)。相比上述的代碼下面的代碼檢測的是數(shù)組對象元素的變化,而不是對象的isDone屬性的變化。在這種情況你可以看看下面例子,在例子中使用鍵[]代替鍵@each。從鍵的變化也可以看出他們的不同之處。

  1. Task = Ember.Object.extend({
  2. isDone: false, // 默認(rèn)為false
  3. name: 'taskName',
  4. // 為了顯示結(jié)果方便,重寫toString()方法
  5. toString: function() {
  6. return '[name = '+this.get('name')+', isDone = '+this.get('isDone')+']';
  7. }
  8. });
  9. WorkerLists = Ember.Object.extend({
  10. // 定義一個(gè)Task對象數(shù)組
  11. lists: [
  12. Task.create({ isDone: false, name: 'ibeginner.sinaapp.com' }),
  13. Task.create({ isDone: true, name: 'i2cao.xyz' }),
  14. Task.create(),
  15. Task.create({ isDone: true, name: 'ubuntuvim' }),
  16. Task.create({ isDone: true , name: '1527254027@qq.com'}),
  17. Task.create({ isDone: true })
  18. ],
  19. index: null,
  20. indexOfSelectedTodo: Ember.computed('index', 'lists.[]', function() {
  21. return this.get('lists').objectAt(this.get('index'));
  22. })
  23. });
  24. var wl = WorkerLists.create();
  25. // 所有isDone屬性值未做任何修改
  26. var index = 1;
  27. wl.set('index', index);
  28. console.log('Get '+wl.get('indexOfSelectedTodo').toString()+' by index ' + index);

代碼演示的結(jié)果

Ember.computed這個(gè)組件中有很多使用鍵[]實(shí)現(xiàn)的方法。當(dāng)你想創(chuàng)建一個(gè)計(jì)算屬性是數(shù)組的時(shí)候特別適用。你可以使用Ember.computed.map來構(gòu)建你的計(jì)算屬性。

  1. const Hamster = Ember.Object.extend({
  2. chores: null,
  3. excitingChores: Ember.computed('chores.[]', function() { //告訴Ember chores是一個(gè)數(shù)組
  4. return this.get('chores').map(function(chore, index) {
  5. //return `${index} --> ${chore.toUpperCase()}`; // 可以使用${}表達(dá)式,并且在表達(dá)式內(nèi)可以直接調(diào)用js方法
  6. return `${chore}`; //返回元素值
  7. });
  8. })
  9. });
  10. // 為數(shù)組賦值
  11. const hamster = Hamster.create({
  12. // 名字chores要與類Hamster定義指定數(shù)組的名字一致
  13. chores: ['First Value', 'write more unit tests']
  14. });
  15. console.log(hamster.get('excitingChores'));
  16. hamster.get('chores').pushObject("Add item test"); //add an item to chores array
  17. console.log(hamster.get('excitingChores'));

Ember還提供了另外一種方式去定義數(shù)組類型的計(jì)算屬性。

  1. const Hamster = Ember.Object.extend({
  2. chores: null,
  3. excitingChores: Ember.computed('chores.[]', function() {
  4. return this.get('chores').map(function(chore, index) {
  5. //return `${index} --> ${chore.toUpperCase()}`; // 可以使用${}表達(dá)式,并且在表達(dá)式內(nèi)可以直接調(diào)用js方法
  6. return `${chore}`; //返回元素值
  7. });
  8. })
  9. });
  10. // 為數(shù)組賦值
  11. const hamster = Hamster.create({
  12. // 名字chores要與類Hamster定義指定數(shù)組的名字一致
  13. chores: ['First Value', 'write more unit tests']
  14. });
  15. console.log(hamster.get('excitingChores'));
  16. hamster.get('chores').pushObject("Add item test"); //add an item to chores array
  17. console.log(hamster.get('excitingChores'));

<br> 博文完整代碼放在Github(博文經(jīng)過多次修改,博文上的代碼與github代碼可能又出入,不過影響不大?。?,如果你覺得博文對你有點(diǎn)用在github項(xiàng)目上給我個(gè)star吧。您的肯定對我來說是最大的動力?。?/p>

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號