浏览器前端编程的面貌自2005年以来已经发生了深刻的变化,这并不简单的意味着出现了大量功能丰富的基础库,使得我们可以更加方便的编写业务代码,更重
要的是我们看待前端技术的观念发生了重大转变,明确意识到了如何以前端特有的方式释放程序员的生产力。本文将结合jQuery源码的实现原理,对
javascript中涌现出的编程范式和常用技巧作一简单介绍。
1. AJAX: 状态驻留,异步更新
首先来看一点历史。
A. 1995年Netscape公司的Brendan Eich开发了javacript语言,这是一种动态(dynamic)、弱类型(weakly typed)、基于原型(prototype-based)的脚本语言。
B. 1999年微软IE5发布,其中包含了XMLHTTP ActiveX控件。
C. 2001年微软IE6发布,部分支持DOM level 1和CSS 2标准。
D. 2002年Douglas Crockford发明JSON格式。
至
此,可以说Web2.0所依赖的技术元素已经基本成形,但是并没有立刻在整个业界产生重大的影响。尽管一些“页面异步局部刷新”的技巧在程序员中间秘密的
流传,甚至催生了bindows这样庞大臃肿的类库,但总的来说,前端被看作是贫瘠而又肮脏的沼泽地,只有后台技术才是王道。到底还缺少些什么呢?
当我们站在今天的角度去回顾2005年之前的js代码,包括那些当时的牛人所写的代码,可以明显的感受到它们在程序控制力上的孱弱。并不是说2005年之
前的js技术本身存在问题,只是它们在概念层面上是一盘散沙,缺乏统一的观念,或者说缺少自己独特的风格,
自己的灵魂。当时大多数的人,大多数的技术都试图在模拟传统的面向对象语言,利用传统的面向对象技术,去实现传统的GUI模型的仿制品。
2005年是变革的一年,也是创造概念的一年。伴随着Google一系列让人耳目一新的交互式应用的发布,Jesse James
Garrett的一篇文章《Ajax: A New Approach to Web
Applications》被广为传播。Ajax这一前端特有的概念迅速将众多分散的实践统一在同一口号之下,引发了Web编程范式的转换。所谓名不正则
言不顺,这下无名群众可找到组织了。在未有Ajax之前,人们早已认识到了B/S架构的本质特征在于浏览器和服务器的状态空间是分离的,但是一般的解决方
案都是隐藏这一区分,将前台状态同步到后台,由后台统一进行逻辑处理,例如ASP.NET。因为缺乏成熟的设计模式支持前台状态驻留,在换页的时候,已经
装载的js对象将被迫被丢弃,这样谁还能指望它去完成什么复杂的工作吗?
Ajax明确提出界面是局部刷新的,前台驻留了状态,这就促成了一种需要:需要js对象在前台存在更长的时间。这也就意味着需要将这些对象和功能有效的管理起来,意味着更复杂的代码组织技术,意味着对模块化,对公共代码基的渴求。
jQuery现有的代码中真正与Ajax相关(使用XMLHTTP控件异步访问后台返回数据)的部分其实很少,但是如果没有Ajax, jQuery作为公共代码基也就缺乏存在的理由。
2. 模块化:管理名字空间
当大量的代码产生出来以后,我们所需要的最基础的概念就是模块化,也就是对工作进行分解和复用。工作得以分解的关键在于各人独立工作的成果可以集成在一
起。这意味着各个模块必须基于一致的底层概念,可以实现交互,也就是说应该基于一套公共代码基,屏蔽底层浏览器的不一致性,并实现统一的抽象层,例如统一
的事件管理机制等。比统一代码基更重要的是,各个模块之间必须没有名字冲突。否则,即使两个模块之间没有任何交互,也无法共同工作。
jQuery目前鼓吹的主要卖点之一就是对名字空间的良好控制。这甚至比提供更多更完善的功能点都重要的多。良好的模块化允许我们复用任何来源的代码,所
有人的工作得以积累叠加。而功能实现仅仅是一时的工作量的问题。jQuery使用module
pattern的一个变种来减少对全局名字空间的影响,仅仅在window对象上增加了一个jQuery对象(也就是$函数)。
所谓的module pattern代码如下,它的关键是利用匿名函数限制临时变量的作用域。
var feature =(function() { // 私有变量和函数 var privateThing = 'secret', publicThing = 'not secret', changePrivateThing = function() { privateThing = 'super secret'; }, sayPrivateThing = function() { console.log(privateThing); changePrivateThing(); }; // 返回对外公开的API return { publicThing : publicThing, sayPrivateThing : sayPrivateThing } })();
require(["jquery", "jquery.my"], function() { //当jquery.js和jquery.my.js都成功装载之后执行 $(function(){ $('#my').myFunc(); }); });
require.def("my/shirt", ["my/cart", "my/inventory"], function(cart, inventory) { // 这里使用module pattern来返回my/shirt模块对外暴露的API return { color: "blue", size: "large" addToCart: function() { // decrement是my/inventory对外暴露的API inventory.decrement(this); cart.add(this); } } } );
var $ = function (id) { return "string" == typeof id ? document.getElementById(id) : id; };
var ea = docuement.getElementById('a'); var eb = docuement.getElementById('b'); ea.style....
$('header_'+id).style... $('body_'+id)....
function $() { var elements = new Array(); for (var i = 0; i < arguments.length; i++) { var element = arguments[i]; if (typeof element == 'string') element = document.getElementById(element); if (arguments.length == 1) return element; elements.push(element); } return elements; }
$('#content') // 找到content元素 .find('h3') // 选择所有后代h3节点 .eq(2) // 过滤集合, 保留第三个元素 .html('改变第三个h3的文本') .end() // 返回上一级的h3集合 .eq(0) .html('改变第一个h3的文本');
$.fn['someFunc'] = function(){ return this.each(function(){ jQuery.someFunc(this,...); } }
$('.switch, .clapper').click(function() { var $light = $(this).parent().find('.lightbulb'); if ($light.hasClass('on')) { $light.removeClass('on').addClass('off'); } else { $light.removeClass('off').addClass('on'); } });
$('.switch, .clapper').click(function() { $(this).parent().find('.lightbulb').trigger('changeState'); });
$('input') .animate({left:'+=200px',top:'300'},2000) .animate({left:'-=200px',top:20},1000) .queue(function(){ // 这里dequeue将首先执行队列中的后一个函数,因此alert("y") $(this).dequeue(); alert('x'); }) .queue(function(){ alert("y"); // 如果不主动dequeue, 队列执行就中断了,不会自动继续下去. $(this).dequeue(); });
function getData(){ return $.get('/foo/').done(function(){ console.log('Fires after the AJAX request succeeds'); }).fail(function(){ console.log('Fires after the AJAX request fails'); }); } function showDiv(){ var dfd = $.Deferred(); $('#foo').fadeIn( 1000, dfd.resolve ); return dfd.promise(); } $.when( getData(), showDiv() ) .then(function( ajaxResult, ignoreResultFromShowDiv ){ console.log('Fires after BOTH showDiv() AND the AJAX request succeed!'); // 'ajaxResult' is the server’s response });
Object.extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; }
if(elm.tagName == 'OPTION'){ return ...; }else if(elm.tagName == 'TEXTAREA'){ return ...; }
jQuery.fn.myWidth = function(){ return parseInt(this.style.width,10) + 10; } jQuery.fn.myHeight = function(){ return parseInt(this.style.height,10) + 10; } 而可以选择动态生成 jQuery.each(['Width','Height'],function(name){ jQuery.fn['my'+name] = function(){ return parseInt(this.style[name.toLowerCase()],10) + 10; } });
(function(window,undefined){ // 内部又有一个包装 var jQuery = (function() { var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); } .... // fn实际就是prototype的简写 jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) {... } } // 调用jQuery()就是相当于new init(), 而init的prototype就是jQuery的prototype jQuery.fn.init.prototype = jQuery.fn; // 这里返回的jQuery对象只具备最基本的功能, 下面就是一系列的extend return jQuery; })(); ... // 将jQuery暴露为全局对象 window.jQuery = window.$ = jQuery; })(window); 显然, $.fn其实就是jQuery.prototype的简写. 无状态的插件仅仅就是一个函数, 非常简单. // 定义插件 (function($){ $.fn.hoverClass = function(c) { return this.hover( function() { $(this).toggleClass(c); } ); }; })(jQuery); // 使用插件 $('li').hoverClass('hover');
$.widget("ui.dialog", { options: { autoOpen: true,... }, _create: function(){ ... }, _init: function() { if ( this.options.autoOpen ) { this.open(); } }, _setOption: function(key, value){ ... } destroy: function(){ ... } });
this.each(function() { var instance = $.data( this, "dialog" ); if ( instance ) { instance.option( options || {} )._init(); } else { $.data( this, "dialog", new $.ui.dialog( options, this ) ); } }
jQuery.browser = { version:(userAgent.match(/.+(?:rv|it|ra|ie)[/: ]([d.]+)/) || [0,'0'])[1], safari:/webkit/.test(userAgent), opera:/opera/.test(userAgent), msie:/msie/.test(userAgent) && !/opera/.test(userAgent), mozilla:/mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent) };
if($.browser.msie) { // do something } else if($.browser.opera) { // ... }
jQuery.support = { // IE strips leading whitespace when .innerHTML is used leadingWhitespace: ( div.firstChild.nodeType === 3 ), ... }
共有0个评论 我要评论»
网友回复/评论仅代表其个人看法,并不表明本社区同意其观点或证实其描述。
1.不欢迎无意义的回复/评论和类似“顶”、“沙发”之类没有营养的文字
如果只是想简单的表个态,请点 有用无用支持反对 等按钮
2.发言之前请再仔细看一遍文章,或许是您遗漏、误解了,理性讨论、切莫乱喷
3.严禁发布违法、违规的信息,请勿到处招贴广告、发布软文;
4.如果您发现自己的回复/评论不见了,请参考以上3条
5.不停制造违规、垃圾信息的,账户将被禁止