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

  前两篇博文里学习了类与原型的相关知识,现在就理论结合实际,看看如何用类和原型来写一个插件。

  首先写好html页面如下,主要是引入jquery和bs,页面中放了一个id为memberList的div,用来放我们待会使用类和原型生成的item。

 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>
22   </div>
23 </body>
24 </html>

  下面我们就创建一个原型,并为其初始化一个实例。

  下面我们复习一下上一篇中说过的set方法,用来更新列表。

  console可以看到,数据更新了,但还只是在console中看到,要怎么反应到页面中呢?

  为此,我们需要写一个原型方法用于生成节点(我们还会用到bs3的Collapse 插件),将生成的节点添加到DOM树上,之所以要写成原型的方法,是因为这个方法重用性高。

 1   // 生成一个条目
 2   SmartList.prototype._genItem = function (data) {
 3     var heading = 'heading_' + data.id;
 4     var collapse = 'collapse_' + data.id;
 5     var html = [
 6       '<div class="panel panel-default" data-id="' + data.id + '">',
 7       '<div class="panel-heading" role="tab" id="' + heading + '">',
 8       '<h4 class="panel-title">',
 9       '<a role="button" data-toggle="collapse" data-parent="#memberList" href="#' +
10       collapse + '" aria-expanded="true" aria-controls="' + collapse + '">',
11       data.title,
12       '</a>',
13       '<i data-act="del" class="glyphicon glyphicon-remove pull-right"></i>',
14       '<i data-act="edit" class="glyphicon glyphicon-pencil pull-right"></i>',
15       '</h4>',
16       '</div>',
17       '<div id="' + collapse + '" class="panel-collapse collapse" role="tabpanel" aria-labelledby="' + heading + '">',
18       '<div class="panel-body">',
19       data.content,
20       '</div>',
21       '</div>',
22       '</div>'
23     ].join('');
24 
25     return $(html);
26   };

  大家可能注意到了,这个方法名称是以下划线开头,为什么和其它原型方法不同呢?是因为这个方法是打算封装在插件内部,不打算对用户可见的。详细缘由后文再说。

  那么,现在就可以使用原型的_genItem方法来生成节点,从而向页面添加节点了。

  但是页面报错了,是为什么呢?我们注意到渲染元素的时候用了this,是不是这个this导致的呢?不用猜,看不出来的话,可以打印看看this到底是什么。

  现在可以清楚的看到2个this的不同,而我们要用_genItem方法的对象应该是SmartList对象,怎么把each里面的this变一下呢?当然,变是变不了的,但是我们可以想办法在each里面用SmartList对象。方法如下,将SmartList对象用变量存起来,在each里面要用的时候就用这个变量。

 1   // 更新列表
 2   SmartList.prototype.setData = function (list) {
 3     var self = this;
 4     // 更新list数据
 5     this.list = list;
 6     // 渲染元素
 7     $.each(this.list, function (i, data) {
 8       self._genItem(data).appendTo(self.element);
 9     });
10   };

  现在我们setData方法也有了,循环传进来的list数据,一个一个生成节点后添加到DOM里(SmartList的element属性用来表示要添加到那个DOM节点里)。那么现在代码和效果如下:

  效果图看起来是不是和bs的Collapse 插件示范的有点不一样呢?可以去参考一下bs的官方文档,原来是我们的id="memberList"的div少了一个class,但是我们又想让别人使用插件的时候页面尽可能简单,所以不把这个class写进Html里,于是,可以在原型里面加上。

1   // 定义一个SmartList类
2   function SmartList(selector) {
3     this.list = [];
4     this.element = $(selector).addClass('panel-group xx-smartlist');
5 
6     console.log('----创建了一个SmartList对象----');
7   }

  同时,还可以加上我们插件的自定义样式xx-smartlist。

  现在,已经可以通过原型来生成一个member list 了,那么同样,可以利用该原型生成很多很多类似的List,比如现在我们就生成一个group list。

  现在html里放一个id="groupList"的div,再在main.js里面初始化一个groupList的实例,使它可以调用原型里的方法,接下来调用一下原型里setData方法,就可以批量生产list了。

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

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     var groupList = new xx.SmartList('#groupList');
33     memberList.setData(members);
34     groupList.setData(groups);
35   });
36 
37 }();
View Code

  smartList.js不需要做任何改动。是不是方便快捷,实现同样的效果,避免了大段的重复代码?现在插件虽然还只有获取列表的功能,但是看了这一个功能的实现,是不是对其它功能的实现有所启发了呢?

  现在做个总结:

1、注意this指向。

  就近原则,this指向它作用域的方法(函数)所属的对象,一个对象的方法上所有的函数中的this,都指向这个对象,即这个方法的主人。一个全局函数的this指向window,因为所有全局函数都是window上的一个方法。

2、注意性能优化。

  选择器从dom中把一个元素选出来,是有成本的,有个查找的过程。所以要把使用次数较多的节点用变量存起来。

3、注意js执行顺序。

  js中涉及DOM操作的,要等到document ready后再执行,否则在执行的时候DOM还没有加载。

4、一个js潜规则。

  一般已下划线开头的方法,表示仅在类的方法内部使用的方法,不对外暴露接口。即带_开头的方法,不允许也不建议在对象上使用,仅是类内部使用,私有方法。(因为逻辑要重用,所以必须把一段段逻辑拆分成一个个方法,通过名称很清晰的能了解方法干什么用的,我又需要提供什么方法出去,哪些不提供,仅内部调用。你若不区分,会造成别人使用上的困惑,所以养成习惯)

5、注意提前退出for循环的方法。

  原生js用 break退出for循环,jq里面用return false来退出循环(这个要记着哟,光return是没用的,要return false才行)。

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

原文地址:https://www.cnblogs.com/lovelyun/p/5088413.html