浅析 JavaScript 组件编写
之前因项目需要也编写过一些简单的JS组件,大多是基于JQuery库的,一直也没有过总结,导致再次写到的时候还去Google, 近日看到一个文章总结的挺好,拿过整理一下做个备忘。
此次同样是基于jquery库来开发,根据一个例子来循序渐进地说明。
了解AngularJs的同学知道,其最具魅力的特点莫过于双向绑定,那么我们今天也来实现一个双向绑定的组件,在一个text文本框后面显示输入的文本内容。
<大概长这个模样>
1. 常规实现思路
1 <input id="txt-con" type="text" /> 2 <script> 3 $(function(){ 4 var textInput = $("#txt-con"); 5 textInput.bind('keyup', function(){ 6 if (textInput.val()) { 7 appendText(this, textInput.val()); 8 } 9 }); 10 11 var appendText = function(target, content) { 12 var id = target.id + "-present"; 13 if ($("#" + id).length > 0) { 14 $("#" + id).text(content); 15 } else { 16 $(target).after("<h2 id='"+id+"'>"+content+"</h2>"); 17 } 18 } 19 }) 20 </script>
以上代码可以妥妥的跑,达到预期的目的。但是细心的你会发现,我们的完全没有考虑作用域的问题,appendText 方法被暴露在全局作用域里面,如果这一个页面中有几十上百个这样的全局变量,那维护起来会疯掉。
2. “隔离”作用域
我们现在对上面的代码进行一些修改,引入一个单独的命名空间。
1 var TextPresenter = { 2 init: function(id){ 3 var textInput = $(id); 4 var self = this; 5 textInput.bind("keyup", function() { 6 if(textInput.val()) { 7 self.appendText(this, textInput.val()); //注意这里的self变量,是将TextPresenter对象的引用赋给了self 8 } 9 }) 10 }, 11 12 appendText: function(target, content){ 13 var id = target.id + "-present"; 14 if ($("#" + id).length > 0) { 15 $("#" + id).text(content); 16 } else { 17 $(target).after("<h2 id='"+id+"'>"+content+"</h2>"); 18 } 19 }, 20 } //以上部分你可以写进一个单独的js文件了,如jquery.textpresenter.js, 别忘了在document ready后初始化
$(function(){
TextPresenter.init("#txt-con");
})
以上部分有点组件的雏形了不是,很多时候就这样被拿来用了,如果你再细细一想,好像有哪里不对,因为 TextPresenter 里面的所有成员变量都可以在外部被访问,而没有一点私有化的概念在里面。所以,说是隔离其实并没有真正隔离,只是看起来像是隔离了。
3. 真正隔离作用域
说到作用域的隔离,我们不得不引入闭包的概念了。曾经看了多少遍闭包的概念,然而实际用到的机会少之又少。借此也来实践一下闭包的基础知识。
1 var TextPresenter = (function(){ 2 var appendText = function(target, content) { //此方法成了私有化方法,外部不可直接访问 3 var id = target.id + "-present"; 4 if ($("#" + id).length > 0) { 5 $("#" + id).text(content); 6 } else { 7 $(target).after("<h2 id='"+id+"'>"+content+"</h2>"); 8 } 9 }; 10 11 var TextPresenterFun = function() { 12 this.init = function(id) { 13 var textInput = $(id); 14 textInput.bind("keyup", function() { 15 if(textInput.val()) { 16 appendText(this, textInput.val()); 17 } 18 }) 19 } 20 }; 21 22 return TextPresenterFun; //返回构造函数,在调用此组件时需要实例化,然后调用init方法 23 24 })();
new TextPresenter().init("#txt-con"); //调用的过程
这种方式可以创建必要的私有方法,对外开放的只有init方法,具体的内部逻辑并不对外开放,满足我所要求的隔离作用域的需求。事实上,大多数的jQuery组件都是这么实现的。
先写这么多吧,都是些基础,作为一个入门,要走的路还长着啊。