用类与原型写一个组件(二)——学习笔记

  上一篇里,我们为smartList组件写了一个简单的setData方法,运用此原型方法创建了member list和group list两个列表,今天我们继续完善插件功能。

  上一篇里,最终效果是这样:

  生成了2个一模一样的列表,都有编辑和删除的功能。所以现在我们要改进一下插件,使用户可以定制出不一样的列表(选择性的拥有编辑和删除功能)。

  我们想在创建对象的时候就能定制。像下面这样:

1   // 定义一个SmartList类
2   function SmartList(selector, isDeletable) {
3     this.list = [];
4     this.isDeletable = isDeletable;
5     this.element = $(selector)
6       .addClass('panel-group xx-smartlist');
7     console.log('----创建了一个SmartList对象----');
8   }
1   $(function () {
2     // 初始化一个SmartList的实例对象
3     var memberList = new xx.SmartList('#memberList',false);
4     var groupList = new xx.SmartList('#groupList',true);
5     // 调用原型方法更新数据
6     memberList.setData(members);
7     groupList.setData(groups);
8 
9   });

  由于我们是个字符串,所以可以判断一下,不能删除时就返回一个空字符串。

1 this.isDeletable ? '<i data-act="del" class="glyphicon glyphicon-remove pull-right"></i>' : '',

  到这里就处理完毕了,看一下效果:

  同理可定制编辑,再加一个参数就行。完整代码如下:

smartList.js

 1 /**
 2  * SmartList
 3  *
 4  * 数据源:id为必须项
 5  */
 6 
 7 + function (Flexx) {
 8   'use strict';
 9 
10   // 定义一个SmartList类
11   function SmartList(selector, isDeletable,isEditable) {
12     this.list = [];
13     this.isDeletable = isDeletable;
14     this.isEditable = isEditable;
15     this.element = $(selector)
16       .addClass('panel-group xx-smartlist');
17     console.log('----创建了一个SmartList对象----');
18   }
19 
20   // 更新列表
21   SmartList.prototype.setData = function (list) {
22     var self = this;
23     // 更新list数据
24     this.list = list;
25     // 清空dom容器
26     this.element.html('');
27     // 渲染元素
28     $.each(this.list, function (i, data) {
29       self._genItem(data).appendTo(self.element);
30 
31     });
32 
33     console.log('-> setData done', this.element);
34   };
35 
36 
37   // 生成一个条目
38   SmartList.prototype._genItem = function (data) {
39     var heading = 'heading_' + data.id;
40     var collapse = 'collapse_' + data.id;
41     var html = [
42       '<div class="panel panel-default" data-id="' + data.id + '">',
43       '<div class="panel-heading" role="tab" id="' + heading + '">',
44       '<h4 class="panel-title">',
45       '<a role="button" data-toggle="collapse" data-parent="#memberList" href="#' +
46       collapse + '" aria-expanded="true" aria-controls="' + collapse + '">',
47       data.title,
48       '</a>',
49       this.isDeletable ? '<i data-act="del" class="glyphicon glyphicon-remove pull-right"></i>' : '',
50       this.isEditable?'<i data-act="edit" class="glyphicon glyphicon-pencil pull-right"></i>':'',
51       '</h4>',
52       '</div>',
53       '<div id="' + collapse + '" class="panel-collapse collapse" role="tabpanel" aria-labelledby="' + heading + '">',
54       '<div class="panel-body">',
55       data.content,
56       '</div>',
57       '</div>',
58       '</div>'
59     ].join('');
60 
61     return $(html);
62   };
63 
64   Flexx.SmartList = SmartList;
65 
66 }(window.xx = window.xx || {});
View Code

在main.js中调用:

 1 + function () {
 2   'use strict';
 3 
 4   // 创建一个用来测试的模拟数据
 5   var members = [{
 6     id: 0,
 7     title: 'Brandon',
 8     content: 'hello'
 9   }, {
10     id: 1,
11     title: 'Kim',
12     content: 'hi'
13   }, {
14     id: 2,
15     title: 'Bunny',
16     content: 'hi'
17   }, {
18     id: 3,
19     title: 'Lovelyun',
20     content: 'hi'
21   }];
22 
23   var groups = [{
24     id: 1,
25     title: 'Web Dev',
26     content: 'hello again'
27   }];
28 
29   $(function () {
30     // 初始化一个SmartList的实例对象
31     var memberList = new xx.SmartList('#memberList',false,true);
32     var groupList = new xx.SmartList('#groupList',true,false);
33     // 调用原型方法更新数据
34     memberList.setData(members);
35     groupList.setData(groups);
36 
37   });
38 
39 }();
View Code

效果图如下:

  但是大家有没有注意到,为了这2个功能的定制,使得创建对象的参数变多了,而且参数还要一一对应,如果前面某个参数不传,还得写个null。

  如果参数再多一点呢?谁用谁知道!所以我们把重要的参数selector留着,其他定制的参数全放在一个options中。

1   // 定义一个SmartList类
2   function SmartList(selector, options) {
3     this.list = [];
4     this.options = options;
5     this.element = $(selector)
6       .addClass('panel-group xx-smartlist');
7     console.log('----创建了一个SmartList对象----');
8   }
1 this.options.isDeletable ? '<i data-act="del" class="glyphicon glyphicon-remove pull-right"></i>' : '',
2 this.options.isEditable?'<i data-act="edit" class="glyphicon glyphicon-pencil pull-right"></i>':'',

  改动以上2处代码后这样调用:

 1   $(function () {
 2     // 初始化一个SmartList的实例对象
 3     var memberList = new xx.SmartList('#memberList',{
 4       isDeletable:false,
 5       isEditable:true
 6     });
 7     var groupList = new xx.SmartList('#groupList',{
 8       isDeletable:true,
 9       isEditable:false
10     });
11     // 调用原型方法更新数据
12     memberList.setData(members);
13     groupList.setData(groups);
14 
15   });

  可以达到同样的效果。但是这样有一个问题,不知道大家发现没有:如果传入参数,就能定制,但不传参数就报错了。

  那么,不传第二个参数的时候 options 是什么?当然是undefined啦!所以,为了解决这个问题,我们可以给这2个参数设一个默认值,在使用的时候,如果没有参数传进来,就使用默认值。

  怎么设置这个默认值呢?方法有很多,我们从易到难一一道来(代码截图注意看行号,就知道改动了哪里)。

  一:判断options里面有没有对应属性:

  二:判断对应的属性是否被赋值:

  三:判断对应的属性值是否是布尔值(判断更精确):

  但以上方法代码还是太多了,下面用三目来判断:

  上面的两个感叹号,用来将某个值转化为bool,因为isDeletable和isEditable有可能未定义,一个!是取反,只要有值就是false,再来一个,只要有值就是true了。

   到这里,得到最佳解决方案了吗?

  NO,我们虽然给options设置了默认值,但是,如果属性再继续增加呢?所以我们要把属性追加到options,而不是赋值给options。故最终解决办法如下:

  如果没有传options,也不会未定义,如果传入了,则覆盖默认属性。这还有一个好处就是让属性名显示出来,让用户知道有哪些属性可以设置。

  现在可以控制相关功能的按钮是否在页面显示了,下一步就是写一个remove事件并绑定。

 1   SmartList.prototype.remove = function(id){
 2     var self = this;
 3 
 4     $.each(this.list, function (i, val) {
 5       if (val.id === id) {
 6         self.list.splice(i, 1);
 7         self.element.find('[data-id="' + id + '"]').remove();
 8         return false;
 9       }
10     });
11   }
 1   // 生成一个条目
 2   SmartList.prototype._genItem = function (data) {
 3     var self = this;
 4     var heading = 'heading_' + data.id;
 5     var collapse = 'collapse_' + data.id;
 6     var html = [
 7       '<div class="panel panel-default" data-id="' + data.id + '">',
 8       '<div class="panel-heading" role="tab" id="' + heading + '">',
 9       '<h4 class="panel-title">',
10       '<a role="button" data-toggle="collapse" data-parent="#memberList" href="#' +
11       collapse + '" aria-expanded="true" aria-controls="' + collapse + '">',
12       data.title,
13       '</a>',
14       this.options.isDeletable ? '<i data-act="del" class="glyphicon glyphicon-remove pull-right"></i>' : '',
15       this.options.isEditable?'<i data-act="edit" class="glyphicon glyphicon-pencil pull-right"></i>':'',
16       '</h4>',
17       '</div>',
18       '<div id="' + collapse + '" class="panel-collapse collapse" role="tabpanel" aria-labelledby="' + heading + '">',
19       '<div class="panel-body">',
20       data.content,
21       '</div>',
22       '</div>',
23       '</div>'
24     ].join('');
25     var item = $(html);
26 
27     if(this.options.isDeletable){
28       item.find('[data-act="del"]').click(function(){
29         self.remove(data.id);
30       });
31     }
32       
33     return item;
34   };

  现在,我们可以让用户定制列表是否拥有编辑或删除功能了。完整代码如下。

index.html:

 1 <html>
 2 <head>
 3   <meta charset="UTF-8">
 4   <title>Flexx</title>
 5   <link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
 6   <link rel="stylesheet" href="./css/main.css">
 7   <script src="http://apps.bdimg.com/libs/jquery/2.0.0/jquery.min.js"></script>
 8   <script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
 9   <script src="./js/flexx.js"></script>
10   <script src="./js/smartList.js"></script>
11   <script src="./js/main.js"></script>
12 </head>
13 <body>
14   <div class="container">
15     <h1>Flexx Library</h1>
16     <hr>
17     <div class="row">
18       <div class="col-xs-6">
19         <div id="memberList"></div>
20       </div>
21       <div class="col-xs-6">
22         <div id="groupList"></div>
23       </div>
24     </div>
25   </div>
26 </body>
27 </html>
View Code

smartList.js:

 1 /**
 2  * SmartList
 3  *
 4  * 数据源:id为必须项
 5  */
 6 
 7 + function (Flexx) {
 8   'use strict';
 9 
10   // 定义一个SmartList类
11   function SmartList(selector, options) {
12     this.list = [];
13     //覆盖默认属性
14     this.options = $.extend({
15       //默认属性
16       isDeletable: true,// 设置是否有行删除按钮
17       isEditable: true// 设置是否有行编辑按钮
18     }, options);
19    
20     this.element = $(selector)
21       .addClass('panel-group xx-smartlist');
22     console.log('----创建了一个SmartList对象----');
23   }
24 
25   // 更新列表
26   SmartList.prototype.setData = function (list) {
27     var self = this;
28     // 更新list数据
29     this.list = list;
30     // 清空dom容器
31     this.element.html('');
32     // 渲染元素
33     $.each(this.list, function (i, data) {
34       self._genItem(data).appendTo(self.element);
35 
36     });
37 
38     console.log('-> setData done', this.element);
39   };
40 
41   SmartList.prototype.remove = function(id){
42     var self = this;
43 
44     $.each(this.list, function (i, val) {
45       if (val.id === id) {
46         self.list.splice(i, 1);
47         self.element.find('[data-id="' + id + '"]').remove();
48         return false;
49       }
50     });
51   }
52 
53   // 生成一个条目
54   SmartList.prototype._genItem = function (data) {
55     var self = this;
56     var heading = 'heading_' + data.id;
57     var collapse = 'collapse_' + data.id;
58     var html = [
59       '<div class="panel panel-default" data-id="' + data.id + '">',
60       '<div class="panel-heading" role="tab" id="' + heading + '">',
61       '<h4 class="panel-title">',
62       '<a role="button" data-toggle="collapse" data-parent="#memberList" href="#' +
63       collapse + '" aria-expanded="true" aria-controls="' + collapse + '">',
64       data.title,
65       '</a>',
66       this.options.isDeletable ? '<i data-act="del" class="glyphicon glyphicon-remove pull-right"></i>' : '',
67       this.options.isEditable?'<i data-act="edit" class="glyphicon glyphicon-pencil pull-right"></i>':'',
68       '</h4>',
69       '</div>',
70       '<div id="' + collapse + '" class="panel-collapse collapse" role="tabpanel" aria-labelledby="' + heading + '">',
71       '<div class="panel-body">',
72       data.content,
73       '</div>',
74       '</div>',
75       '</div>'
76     ].join('');
77     var item = $(html);
78 
79     if(this.options.isDeletable){
80       item.find('[data-act="del"]').click(function(){
81         self.remove(data.id);
82       });
83     }
84       
85     return item;
86   };
87 
88   Flexx.SmartList = SmartList;
89 
90 }(window.xx = window.xx || {});
View Code

main.js:

 1 + function () {
 2   'use strict';
 3 
 4   // 创建一个用来测试的模拟数据
 5   var members = [{
 6     id: 0,
 7     title: 'Brandon',
 8     content: 'hello'
 9   }, {
10     id: 1,
11     title: 'Kim',
12     content: 'hi'
13   }, {
14     id: 2,
15     title: 'Bunny',
16     content: 'hi'
17   }, {
18     id: 3,
19     title: 'Lovelyun',
20     content: 'hi'
21   }];
22 
23   var groups = [{
24     id: 1,
25     title: 'Web Dev',
26     content: 'hello again'
27   }];
28 
29   $(function () {
30     // 初始化一个SmartList的实例对象
31     var memberList = new xx.SmartList('#memberList',{
32       isDeletable:false,
33       isEditable:true
34     });
35     var groupList = new xx.SmartList('#groupList');
36     // 调用原型方法更新数据
37     memberList.setData(members);
38     groupList.setData(groups);
39 
40   });
41 
42 }();
43 aaa
View Code

  现在做个总结:

1、注意性能优化。

  最后一步绑定事件为什么还需要判断?为false的时候根本不会显示在页面呀!但是,不显示,还去选择干什么?记住:性能优化就是从代码的点点滴滴累积出来的。

2、两个js潜规则。

  一般只要是bool值我们都用is开头(比如smartList.js中的isDeletable和isEditable);

  数组我们习惯用复数(比如main.js中的members),但如果你叫xxxList 就不用了。

潜规则能让你快速读懂别人的代码,反之亦然,这些都是一些良好习惯,从字面上让人理解作者意图。

 

最后,感谢大神CX的讲解。

posted @ 2015-12-31 14:36  lovelyun  阅读(560)  评论(0编辑  收藏  举报