在第一篇中我们已经实现了学校选择器的基本功能,但是当其他页面也需要同样的功能的时候,我们当然不希望大段的复制代码,我们希望能够降低js和页面的耦合,提供一种更简单的初始化和调用方式。
目标
我们希望在页面上只需要定义一个父元素,然后直接new
一个选择器出来即可。
1 | var schoolBox = new SchoolBox({ |
并且SchoolBox
只暴露一些必要的供外使用的API,如schoolBox.show()
、schoolBox.hide()
等。
封装设计模式
为了实现“简单”的目标,我们需要先了解下“封装”。其实js提供了非常弱的语法能力,它是弱类型语言,没有class的概念,没有public
和private
,也没有函数重载。但另一方面,正是它的弱语法,它提供了更高的自由度,利用它本身的closure和prototype的机制,完全可以模拟出“类”和公有/私有属性。
1 | var Book = (function(){ |
特权方法能够访问私有属性和方法,但是必须声明在this
中。任何不需要直接访问私有属性的方法都可以在prototype
中声明。prototype
中的方法可以通过访问特权方法来间接访问私有属性。只有那些需要直接访问私有成员的方法才应该被设计为特权方法,但是每个对象实例都会包含所有特权方法的新副本,容易占内存。
SchoolBox重构
根据上面的封装模式,我们先搭出SchoolBox
的框子。
1 | (function($){ |
1.将目标元素和元素copy定义成私有静态属性
1 | var $schoolBoxCopy = $( |
2.将初始化province和school定义成私有静态方法
1 | var getProvinceById = function(pid){ |
注意这里与先前不一样的是,需要操作元素的方法中得把目标元素作为参数传进去,而实例化的缓存(当前选中的province)也需要作为对象指针传入。
3.初始化方法及构造函数
1 | var init = function(instance){ |
4.在prototype
中添加对外API
1 | SchoolBox.prototype = { |
5.添加学校click
事件的外部回调,将这个回调放在构造函数的options
中
1 | var init = function(instance){ |
6.点缀下,初始化SchoolBox
时默认选中第一个province,并对外提供init
方法
1 | var SchoolBox = (function(){ |
7.应用层调用
1 | // 目标元素 |
到此为止?
到这里我们已经将学校选择器的基本功能封装成了一个“类”,具体页面使用时,只需要定义它被包裹的父元素,可以直接new
一个对象出来,并在构造时的配置变量里定义事件回调。虽然大体上实现了本文一开始的目标,但是仅仅实现了基本的级联功能,而且只能定义一个事件回调。如果页面有多个元素都需要根据选中的学校进行一些改变,那么这些代码都得写在schoolClickCallback
中,这部分代码可能操作着来自页面不同部分的元素(甚至是其他组件),这样就会造成一些耦合。