一、前言
上兩篇博客筆者對 JavaScript Module 模式,閉包等知識點(diǎn)做了簡單介紹之后,我們今天開始正式開發(fā)一款屬于自己的 JavaScript 插件。由于最近項(xiàng)目剛好用到地區(qū)選擇這一塊的功能。網(wǎng)上有許多類似插件,但是有些需求還是有些出入,所以就自己動(dòng)手寫了一個(gè)。思路是共通的但是實(shí)現(xiàn)和細(xì)節(jié)肯定會(huì)有所不同,我們重點(diǎn)放在代碼介紹上。筆者已經(jīng)將其上傳到 github,大家可以下載使用,也可以把源碼拷下來參考,路過的朋友順手 star 哦。
二、補(bǔ)充知識
當(dāng)前插件版本為1.0.1,能滿足最常見的使用方式,后續(xù)筆者將會(huì)繼續(xù)完善該插件。包括優(yōu)化或者功能拓展,也希望使用過程中發(fā)現(xiàn)問題,或者有改進(jìn)意見的朋友,可以幫忙指出。
源碼淺析
我們先來看下核心代碼(部分偽代碼)
;
(function(){
'use strict';
var CJPCD = function (provinceId,cityId,districtId){
if (!(this instanceof CJPCD)) return new CJPCD(provinceId,cityId,districtId);
var c = this;
//code...
return {
setUp:c.set_value,//設(shè)置值
reSet:c.builder_init,//重置
getValue:c.get_value//獲取值
};
};
CJPCD.prototype={
cj_areas_json: xxx(省市區(qū)json),
cj_municipalities: xxx(直轄市數(shù)組)
};
window.CJPCD = CJPCD;
}());
可以看出我們采用的是閉包 + 面向?qū)ο蟮乃季S模式來進(jìn)行開發(fā)的。這里所涉及的重復(fù)的知識點(diǎn)筆者將不再介紹,如果有不是很了解的朋友請移步上兩篇文章《淺析 JavaScript 中的閉包(Closures)》和《如何開發(fā)原生的 JavaScript 插件(知識點(diǎn)+寫法)》。我們把討論的重點(diǎn)放在前兩篇文章中沒有介紹到的知識點(diǎn)上。
我們先來看下下面這句代碼:
'use strict';
字面上的意義來理解就是:“使用嚴(yán)格模式”,顧名思義這行代碼開啟了 Javascript 的嚴(yán)格模式。開啟后有下面幾個(gè)作用:
1.消除一些不嚴(yán)謹(jǐn)?shù)恼Z法,例如:
num = 1;//報(bào)錯(cuò)
不開啟嚴(yán)格模式前上面的代碼相當(dāng)于隱式聲明了一個(gè)全局變量,但是開啟之后,全局變量只能顯示聲明,所以代碼報(bào)錯(cuò)
2.提編譯效率,增加運(yùn)行速度
該模式在 ECMAscript 5 添加,包括IE 10及更高版本的IE,以及其他主流瀏覽器都支持該模式。而老版本的瀏覽器會(huì)將其識別為普通字符串,而進(jìn)行忽略。
調(diào)用方式(兩種):
第一種是放在腳本文件的第一行,表示整個(gè)腳本以嚴(yán)格模式運(yùn)行。
第二種是放在函數(shù)內(nèi)部第一行,表示整個(gè)函數(shù)以嚴(yán)格模式運(yùn)行。
由此可以看出我們的插件(組件)是使用嚴(yán)格模式運(yùn)行的。
3.注意點(diǎn): 使用嚴(yán)格模式之后 this 關(guān)鍵字不允許指向全局對象。
我們通過代碼來看可能會(huì)直觀點(diǎn)。我們先來看下非嚴(yán)格模式下的代碼
function test(){
console.log(this);
}
test();
運(yùn)行結(jié)果如下

我們將代碼加上嚴(yán)格模式的限制
function test(){
'use strict';
console.log(this);
}
test();//undefined

所以當(dāng)我們使用了構(gòu)造函數(shù)后,我們就應(yīng)當(dāng)注意需要使用 new 運(yùn)算
function test(){
'use strict';
console.log(this);
}
var t = new test();

限于篇幅,關(guān)于嚴(yán)格模式的介紹先到這了,有興趣深入了解的朋友可以參考下面文章:
三、代碼部分
基本代碼
;
(function(){
'use strict';
var CJPCD = function (provinceId,cityId,districtId) {
if (!(this instanceof CJPCD)) return new CJPCD(provinceId,cityId,districtId);
var c = this;
//初始化
c.province = document.getElementById(provinceId);
c.city = document.getElementById(cityId);
c.district = document.getElementById(districtId);
//創(chuàng)建省市區(qū)數(shù)據(jù)
c.builder_init = function(){
//初始化先創(chuàng)建省份數(shù)據(jù)
c.province_builder();
c.bindChangeEvent();
//初始化觸發(fā)
c.province.onchange();
};
//設(shè)置自定義值
c.set_value = function(province,city,district){
//增強(qiáng)容錯(cuò)性-這里有點(diǎn)不嚴(yán)謹(jǐn),后續(xù)完善
if(province.indexOf("省")== -1)
province = _type_format(province,"省");
if (city.indexOf("市")== -1)
city += "市";
//初始化先創(chuàng)建省份數(shù)據(jù)
c.province_builder();
c.province.value = province;
c.city_builder();
c.city.value = city;
c.district_builder();
c.district.value = district;
//綁定事件
c.bindChangeEvent();
};
//綁定事件
c.bindChangeEvent = function(){
c.province.onchange=function(){
c.city_builder();
c.city.onchange();
};
c.city.onchange=function(){
c.district_builder();
};
};
//生成省數(shù)據(jù)
c.province_builder = function(){
c.province.innerHTML="";
//創(chuàng)建子元素
c.optionFactory(c.cj_areas_json,c.province,"prov_index","省");
};
//生成市數(shù)據(jù)
c.city_builder = function(){
c.city.innerHTML = "";
//獲取當(dāng)前選中省份的索引
var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index");
var city_obj = c.cj_areas_json[pro_Id].city;
//創(chuàng)建子元素
c.optionFactory(city_obj,c.city,"city_index","市");
};
//生成區(qū)數(shù)據(jù)
c.district_builder = function(){
c.district.innerHTML = "";
var pro_Id = c.province.options[c.province.selectedIndex].getAttribute("prov_index");
var city_Id = c.city.options[c.city.selectedIndex].getAttribute("city_index");
var district_obj = c.cj_areas_json[pro_Id].city[city_Id].area;
//創(chuàng)建子元素
c.optionFactory(district_obj,c.district,"");
};
//通用創(chuàng)建子函數(shù)方法
c.optionFactory = function(json_data,parent_obj,attr_name,type){
/*
數(shù)據(jù)源沒有 "省","市"(節(jié)省空間)
我們需要自己加(排除四個(gè)直轄市)
區(qū),縣沒有規(guī)律可循,所以數(shù)據(jù)源是完整的
*/
var option;
for (var i = 0; i < json_data.length; i++) {
option = document.createElement("option");
if(attr_name)
option.setAttribute(attr_name,i);
var name = !json_data[i].name ? json_data[i] : json_data[i].name;
//加上后綴之前要排除直轄市和自治區(qū)
option.innerHTML = c.type_format(name,type);
parent_obj.appendChild(option);
};
option = null;
};
//加上后綴之前要排除直轄市,還有數(shù)據(jù)源上的特殊元素"省"
c.type_format = function(name,type){
if (!type)
return name;
for(var item in c.cj_municipalities){
if(c.cj_municipalities[item] == name){
type = "";
break
};
};
return name + type;
};
c.get_value = function(valueType = "json"){
var province_val = c.province.options[c.province.selectedIndex].value;
var city_val = c.city.options[c.city.selectedIndex].value;
var district_val = c.district.options[c.district.selectedIndex].value;
if(province_val == "省"||city_val == "市"||district_val == "區(qū)")
return null;
if(valueType == "json")
return {"province":province_val,"city":city_val,"district":district_val};
else
return province_val + valueType + city_val + valueType + district_val;
};
//內(nèi)部初始化
c.builder_init();
//提供API給外部使用
return {
setUp:c.set_value,//設(shè)置值
reSet:c.builder_init,//重置
getValue:c.get_value//獲取值
};
};
//屬性
CJPCD.prototype={
//篇幅限制只貼出部分(北京和天津)數(shù)據(jù)
cj_areas_json : [{ "name": "省", "city": [{ "name": "市", "area": ["區(qū)"] }] },{ "name": "北京", "city": [{ "name": "北京", "area": ["東城區(qū)", "西城區(qū)", "崇文區(qū)", "宣武區(qū)", "朝陽區(qū)", "豐臺(tái)區(qū)", "石景山區(qū)", "海淀區(qū)", "門頭溝區(qū)", "房山區(qū)", "通州區(qū)", "順義區(qū)", "昌平區(qū)", "大興區(qū)", "平谷區(qū)", "懷柔區(qū)", "密云縣", "延慶縣"] }] }, { "name": "天津", "city": [{ "name": "天津", "area": ["和平區(qū)", "河?xùn)|區(qū)", "河西區(qū)", "南開區(qū)", "河北區(qū)", "紅橋區(qū)", "塘沽區(qū)", "漢沽區(qū)", "大港區(qū)", "東麗區(qū)", "西青區(qū)", "津南區(qū)", "北辰區(qū)", "武清區(qū)", "寶坻區(qū)", "寧河縣", "靜??h", "薊 縣"] }] }],
cj_municipalities : ["北京","上海","天津","重慶","內(nèi)蒙古","新疆","寧夏","西藏","廣西","澳門","香港","臺(tái)灣","釣魚島","省","市"]
};
window.CJPCD = CJPCD;
}());
源碼已經(jīng)基本都有加上注釋了,在這里我就不累述了。我們來看下如何使它。
四、CJPCD的使用
如果你是從 github 下載完整的文件包的話,你可以看到介紹文檔。內(nèi)容同下:
最簡單的使用只需要兩個(gè)步驟:
-
放置容器,引入依賴
<!--存放省市區(qū)數(shù)據(jù)的三個(gè) select--> <select id="province"></select> <select id="city"></select> <select id="district"></select> <!--引入插件--> <script src="./cj-pcd-1.0.1.min.js"></script> -
調(diào)用方法
var pcd = new CJPCD('province','city','district');效果如下圖

API
-
初始化
/** provinceId :省份 select id cityId :城市 select id districtId :縣區(qū) select id */ var pcd = new CJPCD(provinceId,cityId,districtId); -
設(shè)置初始值
/** provinceName :省份名稱 cityName :城市名稱 districtName :縣區(qū)名稱 注意:前提是必須初始化 */ pcd.setUp('provinceName','cityName','districtName');eg:
var pcd = new CJPCD('provinceId','cityId','districtId');//先初始化 pcd.setUp("廣東省","廣州市","越秀區(qū)");//設(shè)置值使用場景:設(shè)置值一般用于地址信息的修改
-
重置
pcd.reSet(); -
獲取值
pcd.getValue('json');//指定以json對象返回 pcd.getValue();//默認(rèn)以json對象返回,效果同上json 對象返回如下:
{"province":"廣東省","city":"廣州市","district":"越秀區(qū)"};指定拼接成一定格式的字符串進(jìn)行返回,以逗號為例(當(dāng)然也可以使用其他字符串)
pcd.getValue(',');//結(jié)果使用逗號拼接成字符串返回值如下
"廣東省,廣州市,越秀區(qū)" -
補(bǔ)充說明
如果業(yè)務(wù)中涉及多組地址選擇,請創(chuàng)建多個(gè) CJPCD 實(shí)例 如
var pcd1 = new CJPCD(provinceId1,cityId1,districtId1); var pcd2 = new CJPCD(provinceId2,cityId2,districtId2); ... var pcdn = new CJPCD(provinceIdn,cityIdn,districtIdn);使用案例如下:

限于筆者技術(shù),文章觀點(diǎn)難免有不當(dāng)之處,希望發(fā)現(xiàn)問題的朋友幫忙指正,筆者將會(huì)及時(shí)更新。也請轉(zhuǎn)載的朋友注明文章出處并附上原文鏈接,以便讀者能及時(shí)獲取到文章更新后的內(nèi)容,以免誤導(dǎo)讀者。筆者力求避免寫些晦澀難懂的文章(雖然也有人說這樣顯得高逼格,專業(yè)),盡量使用簡單的用詞和例子來幫助理解。如果表達(dá)上有好的建議的話也希望朋友們在評論處指出。
本文為作者原創(chuàng),轉(zhuǎn)載請注明出處! Cboyce