明天花了些时日又收拾了下以前公布过的《,但如此叁个不难易行的事例有助于有2个主导的CP理解

 

写在日前

  好长期没有写博客了,前几天花了些日子又收拾了下从前发布过的《Ember.js之computed
Property
》小说,并创建了二个测试代码库,花了些日子,希望能运用测试代码的艺术,来演示怎么着行使Ember.js同时能防止升级Ember版本后,一些意义上的变动所拉动的隐含Bug。

 

  假设大家对Ember.js有趣味想一起切磋以来,欢迎大家一块儿爱抚测试代码库 
:)

  本文首就算针对Ember.Array中的[]和@each,数组方法开展详细分析。

 

   计算属性,以下简称CP。不难回顾来讲,正是在须要属性值的时候总计2个Function,并将Function重返的值保存在属性中,当第②遍获得属性值时,固然发现属性并未改变则直接读取属性,假诺属性正视的东西产生变更,则重复计算该属性值。

小说索引

 

JS前端框架之Ember.js体系

 

下面用C#福寿年高基本的CP原理(CP其实更扑朔迷离一些,但如此八个简练的事例有助于有1个基本的CP明白):

壹 、如何行使[] & @each

  Ember中二者均可用于总计属性的绑定,使用特别有利于,能够像如下形式定义重视关系:

1 totalArea: Ember.computed('sizeArray.[]', function () {
2       return this.get('sizeArray').reduce(function (prev, cur) {
3         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
4       }, 0);
5 }),

  或者:

1 totalArea: Ember.computed(‘sizeArray.@each', function () {
2       return this.get('sizeArray').reduce(function (prev, cur) {
3         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
4       }, 0);
5 }),

  那样就定义了1个借助于数组sizeArray的计算属性,建立二个绑定关系,只要sizeArray产生变化,totalArea就会被重新总结,使用尤其简练,只要在function前边罗列出重视的质量即可。尽管使用简单,可是在行使上依然某个细节的,下一节将用测试代码来上课使用上的底细。

注:Ember总结属性有三种写法,那里给出了Ember推荐写法,越多具体细节请参考小说《Ember.js之computed
Property
-1.
What is CP》

 

private object _myCar;                                    // 内部缓存变量
public bool IsDirty;                                      // MyCar属性依赖的对象

public object MyCar {
    get {
          if(_isDirty) {                                           
               _myCar = 4S.BuyANewCar('Audio A4');        // 重新计算属性值并保存
          }
          return _myCar;
    }
}                                        

二、Array.[] & Array.@each天性总结

(Ember v1.13.7) 

 

1. Array.@each.property 特性

当CP属性正视于.property(‘columns.@each.isLoaded’)时:

  -
columns里面其余3个要素的isLoaded属性产生变化。

  -
columns增英镑素大概去除子成分。

  -
columns数组本人被再次赋值。

  -
不可能凭借@each.owner.@each.name 。

 1 test('computed property depend on @each.height', function (assert) {
 2   var Size = Ember.Object.extend({height: 0, width: 0});
 3   var rectangle = Ember.Object.extend({
 4     totalArea: Ember.computed('sizeArray.@each.height', function () {
 5       return this.get('sizeArray').reduce(function (prev, cur) {
 6         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
 7       }, 0);
 8     }),
 9     sizeArray: [
10       Size.create({height: 10, width: 10}),
11       Size.create({height: 10, width: 10})
12     ]
13   }).create();
14   var sizeArray = rectangle.get('sizeArray');
15 
16   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200");
17 
18   sizeArray.pushObject(Size.create({height: 10, width: 10}));
19   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added");
20 
21   sizeArray.removeAt(0);
22   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed");
23 
24   sizeArray[0].set('height', 20);
25   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after 'height' changed");
26 
27   sizeArray[0].set('width', 20);
28   assert.equal(rectangle.get('totalArea'), 300, "the total area should not be changed after 'width' changed");
29 
30   sizeArray.clear();
31   assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
32 });

[参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js\#L164-Lundefined)

 

  1. Array.@each特性

当CP属性重视于.property(‘columns.@each’)时:

  -
columns扩大或删除成分。

  -
columns本身被替换或重新赋值。

 1 test('computed property depend on @each', function (assert) {
 2   var Size = Ember.Object.extend({height: 0, width: 0});
 3   var rectangle = Ember.Object.extend({
 4     totalArea: Ember.computed('sizeArray.@each', function () {
 5       return this.get('sizeArray').reduce(function (prev, cur) {
 6         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
 7       }, 0);
 8     }),
 9     sizeArray: [
10       Size.create({height: 10, width: 10}),
11       Size.create({height: 10, width: 10})
12     ]
13   }).create();
14   var sizeArray = rectangle.get('sizeArray');
15 
16   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200");
17 
18   sizeArray.pushObject(Size.create({height: 10, width: 10}));
19   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added");
20 
21   sizeArray.removeAt(0);
22   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed");
23 
24   sizeArray[0].set('height', 20);
25   assert.equal(rectangle.get('totalArea'), 200, "the total area should not be changed");
26 
27   sizeArray.clear();
28   assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
29 });

[参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js\#L104)

 

  1. Array.[]特性

当CP属性信赖于.property(‘columns.[]’)时:

  -
与绑定.property(‘columns.@each’) 行为同样。

 1 test('computed property depend on []', function (assert) {
 2   var Size = Ember.Object.extend({height: 0, width: 0});
 3   var rectangle = Ember.Object.extend({
 4     totalArea: Ember.computed('sizeArray.[]', function () {
 5       return this.get('sizeArray').reduce(function (prev, cur) {
 6         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
 7       }, 0);
 8     }),
 9     sizeArray: [
10       Size.create({height: 10, width: 10}),
11       Size.create({height: 10, width: 10})
12     ]
13   }).create();
14   var sizeArray = rectangle.get('sizeArray');
15 
16   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200");
17 
18   sizeArray.pushObject(Size.create({height: 10, width: 10}));
19   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added");
20 
21   sizeArray.removeAt(0);
22   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed");
23 
24   sizeArray[0].set('height', 20);
25   assert.equal(rectangle.get('totalArea'), 200, "the total area should not be changed");
26 
27   sizeArray.clear();
28   assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
29 });

[参照代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js\#L134)

 

(Ember v2.0.0)

 

二零一五-08-17
除提出用[]取而代之@each,暂未察觉其余变化。

[参照代码](https://github.com/emberjs/ember.js/blob/v2.0.0/packages/ember-metal/lib/computed.js\#L224)

注: 越来越多关于总计属性难题,请参见小说《Ember.js之computed
Property
-3.CP首要标准》

 

小说索引

JS前端框架之Ember.js体系

 

叁 、源码分析Array.[] & Array.@each

Array.@each重返的是一个出奇类型,那几个类型便于达成绑定。

 1   '@each': computed(function() {
 2     if (!this.__each) {
 3       // ES6TODO: GRRRRR
 4       var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy'];
 5 
 6       this.__each = new EachProxy(this);
 7     }
 8 
 9     return this.__each;
10   })

[参照那里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/mixins/array.js\#L516)

 

Array.[]继承自Ember.Enumerable.[]再者重临this。

1   '[]': computed({
2     get(key) {
3       return this;
4     },
5     set(key, value) {
6       this.replace(0, get(this, 'length'), value);
7       return this;
8     }
9   }),

[参照那里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/mixins/array.js\#L164)

 

二者之间唯一的分别是Array.[]重返的是一指标自作者(普通对象组成的数组),
而Array.@each再次来到的是EachProxy目的,
针对普通对象组成的数组而言,仅仅能检查和测试到数高管度的转移和对象自小编的生成,对数组内容爆发变化则不得而知,而EachProxy对每个成分扩充observerListener监听器,当数组内容产生变化时,通告数组爆发变动,达成了数组成分这顶尖其余监听。

 1 var EachProxy = EmberObject.extend({
 2 
 3   init(content) {
 4     this._super(...arguments);
 5     this._content = content;
 6     content.addArrayObserver(this);
 7 
 8     // in case someone is already observing some keys make sure they are
 9     // added
10     forEach(watchedEvents(this), function(eventName) {
11       this.didAddListener(eventName);
12     }, this);
13   },
14 
15 ...

[参照那里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/system/each\_proxy.js\#L109)

 

1. What is CP?

  In a nutshell, computed properties let you declare functions as
properties. You create one by defining a computed property as a
function, which Ember will automatically call when you ask for the
property. 

  一句话来说,计算属性让你能够把函数方法评释为属性,你通过定义多少个盘算属性作为函数方法时,Ember将电动调用当访问属性时。

  1. 率先种写法:

    App.Person = Ember.Object.extend({
    firstName: null,
    lastName: null,

    fullName: function() {

     return this.get('firstName') + ' ' + this.get('lastName');
    

    }.property(‘firstName’, ‘lastName’) // fullName是贰个凭借属性,当firstName或然lastName爆发变更时,并且当访问fullName是,重新履行function,不然重回已缓存的fullName值。
    });

    var ironMan = App.Person.create({
    firstName: “Tony”,
    lastName: “Stark”
    });

    ironMan.get(‘fullName’); // “Tony Stark”

  2. 其次种写法:(推荐写法

  CP也足以如此写,提议如下写法,制止“禁止使用”function的property扩充带来的标题。

  [Prototype Extensions](http://emberjs.com/guides/configuring-ember/disabling-prototype-extensions/)

 fullName: Ember.computed('firstName', 'lastName', function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  })

  Ember.js v1.12 更新带有Set和get方法的CP:

  fullName: Ember.computed("firstName", "lastName", {
    get: function() {
      return this.get("firstName") + " " + this.get("lastName");
    },
    set: function(key, newName) {
      var parts = newName.split(" ");
      this.setProperties({ firstName: parts[0], lastName: parts[1] });
      return newName;
    }
  });

 

  1. 其三种写法:

    // An array of Ember.Table.Row computed based on content
    bodyContent: Ember.computed(function() {

     return RowArrayController.create({
       target: this,
       parentController: this,
       container: this.get('container'),
       itemController: Row,
       content: this.get('content')
     });
    

    }).property(‘content.[]’, ‘_reloadBody’),

 

④ 、相关引用

[Ember-@each](http://emberjs.com/api/classes/Ember.Array.html\#property\_\_each)

[Emer-EachProxy](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/system/each\_proxy.js)

[Ember-study](https://github.com/Cuiyansong/ember-table-learnning)

 

2. CP中名词描述定义 

2.1 CP:Computed Property

2.2 CP属性:上文例子中的‘fullName’。

2.3 CP所依靠的源属性:上文例子中的‘firstName’、‘lastName’。

2.4 CP的回调方法:上文例子中的function(){……}方法。

2.5 CP属性的Setter/Getter

 

3. CP主要尺度(天性)

3.二头有当获得或安装CP属性时,才大概会触发CP的回调方法,也便是说CP属性是属于‘懒加载’的法子(利用时总括行为)。

3.2 当CP属性信赖于.property(‘person.name’,
‘person.age’)时,当person.name、person.age或person本人发生改变时改变(链式正视)。

3.3 当CP属性信赖于.property(‘columns.@each.isLoaded’)时:

  • columns里面别的三个因素的isLoaded属性爆发变化。
  • columns增法郎素或许去除子成分。
  • columns数组自己被再度赋值。
  • 永利会娱乐,不可能凭借@each.owner.@each.name

3.4 当CP属性信赖于.property(‘columns.@each’)时:

  • 当columns扩充或删除成分。
  • 当columns自己被沟通或重复赋值。

3.5 当CP属性依赖于.property(‘columns.[]’)时:

  与3.4 绑定.property(‘columns.@each’) 行为同一。

3.6
当通过set方法设置CP属性时,然后调用get方法获得CP属性,则不调用CP回调,set时值被缓存。(CP属性被覆盖)

3.7 当存在八个互相信赖的CP属性时,仅仅爆发二遍属性别变化更。

3.8 原则上无须将CP的借助属性附着在另一个CP属性上。

3.9
当CP属性正视于对象列表时,例如.property(‘a.b.c.d’)上时,节点上无限制对象发生变化时,均会再一次计算属性。(链式依赖

4. CP宏定义

Ember.computed.empty: empty(属性名)返回bool

Ember.computed.not:
not(属性名)返回bool

Ember.computed.alias:alias(属性名),双向绑定, alias不要借助于多个CP.

Ember.computed.defaultTo:
假使CP属性为null,则读取依赖属性值一回

Ember.computed.match(属性名, 匹配字符串)

Ember.computed.gt(属性名,数字),大于再次来到bool

Ember.computed.gte(属性名,数字),大于或等于bool

Ember.computed.and(属性名,属性名), 并集

Ember.computed.or(属性名, 属性名),
交集

Ember.computed.collect(
数组 ) ,匹配全部项,没有相则为null

Ember.computed.oneWay(属性名) ,单方向从源到PC属性.
CP能够被安装,但不会潜移默化到CP正视的习性。

Ember.computed.readOnly(属性名) ,CP属性区别意被设置,但CP所依赖的源属性更新CP值。

 更加多宏定义请参考那里:http://emberjs.com/api/classes/Ember.computed.html#method_alias

5. CP使用景况

5.1
在我们采用的靶子上,希望利用2个属性值监听2个或八个属性的变更,恐怕CP属性强依赖于有些质量,而且还是能够缓存CP属性值,缩小品质损耗。(CP本性请参见3.1)

5.2 CP能够依靠在三个目的的多少个属性上,
越发是绑定在集合元素上甚至监听集合成分内部某一性能,但层次有限定。例如.property(‘person{name,age}’)或.property(‘pencilBox.[]’,
penBox.@each.color’, penBox.@each)。(CP性格请参考3.二 、3.三 、3.4)

5.3 Ember.computed.alias作用于两个强涉嫌对象的双向绑定,并提供缓存机制。

5.4 通过CP来构成属性,CP属性回调中不可能有境界效应等循环、异步方法。

6. Q & A

6.1 当总括属性(CP)设置为getter和setter时,其CP回调函数触发的场地:

  • 安装(set)CP属性时,无论几时都触发CP回调方法。
  • 当得到CP属性时,若CP依赖属性未爆发变化,则不实施CP回调方法。

 

6.2 Ember.Computed(function(){}).property(‘xxxx’) 与
function(){}.property(‘xxxx’)区别:

  • 前端能够用{get:function(){}, set: function(key,
    newName){}}的艺术,不必在认清Argument.length,完结了get、set的语法糖。

    fullName: Ember.computed(“firstName”, “lastName”, {

    get: function() {
      return this.get("firstName") + " " + this.get("lastName");
    },
    set: function(key, newName) {
      var parts = newName.split(" ");
      this.setProperties({ firstName: parts[0], lastName: parts[1] });
      return newName;
    }
    

    });

 

6.3 CP之间的正视性行为(cp1->cp2->x):

  • cp2重新计算后,并不会主动触发cp1总结。
  • 假如x更新后,直接获得cp1属性,则cp2也被触发总括。

 

6.4 Person.get(‘firstName’)和Person.firstName之间的区分:

  当firstName为(非计算属性)普通属性时,行为一点差别也没有于。

  当firstName为CP属性时,前者能触发CP回调方法,后者不可能。

 

7. 示范代码

[github示例代码](https://github.com/Cuiyansong/ember-table-learnning/tree/master/ember-test/tests/unit)

 

Ember更新日志:http://emberjs.com/deprecations/v1.x/\#toc\_deprecate-using-the-same-function-as-getter-and-setter-in-computed-properties 

Ember-Data:
http://guides.emberjs.com/v1.13.0/object-model/computed-properties-and-aggregate-data/

 

相关文章