左银右煌 2014-03-24T07:35:33-07:00 yinhuangle@gmail.com jQuery技术内幕试读 2014-03-24T00:00:00-07:00 左银右煌 http://grahamle.github.io/2014/03/jQuery技术内幕试读 缘起

因为这本书的作者是阿里的大拿,所以一直有关注,这次51CTO竟然祭出近200页的试读版,虽然之前在高云大拿的博客园看过系列文章,为了学习先进的设计理念、学习各种实现技巧以及巩固Js基础,我特来拜读一下这个版本(尼玛,其实就是为了赠书,邪恶的笑ing,当然,真心为了巩固Js基础以及limit-->阿里牛人)。废话不多说,以下是我试读过程的一些note。

总体架构部分

寥寥5页纸,我是打印出来的,足够清晰明了的直接引领我进入了读jQuery源码的世界。为什么这么说呢?首先高云大拿一开始就很清晰地上了张图,将总体架构很清晰地展现出来了,我是这么理解的:

  • Step1:入口模块,构造jQuery全局对象,而调用jQuery都会调用这个入口,然后通过底层支持模块的选择器模块进入
  • Step2:功能模块,我个人理解它是API模块,需要底层模块的支持,总共是有9个
  • Step3:底层模块,这是jQuery的基础核心所在,其中工具方法模块和浏览器功能测试模块是API模块都会用到的

不仅如此,后面贴了一个源码的总体结构清单,更是简单地不能再简单了,整个对外输出一个jQuery全局对象作为入口,其他总共16个模块在自调用函数的上下文中是私有的,证明了jQuery源码是相当清晰和有条理的。

另外,书中还提及了很多代码细节,亮点在于全书一开始就定下基调是采用问答的形式进行源码解读,这个非常符合我的胃口,到后来还有“例1、例2”这样勾起童年的细节存在,简直是乐high了好么,难道你们没有想起高中数学或物理课本的例1等么?越是这样,我觉得也是有兴趣往下看了。

当然,也有觉得牵强的地方,比如,在解释源码声明时传进windowundefined两个参数时竟然说压缩时优化,这个真的看不懂是什么梗啊,可能自己还没到那境界,还得再google学习下,或者作者可以考虑把这也讲清楚。最后,个人一直不喜欢书本有什么总结呀,之类的,还是章节的总结,这个觉得应该是留给读者去总结的,可以留个白的板块,让读者自己去总结,做自己的总结,岂不惬意?

入口模块:jQuery对象

这个接口是给用户调用用的,所以用户传入的参数不同,调用效果不同,之前用jQuery只知道有这样的情况,但是不知道其具体是如何实现,这一章就彻底解开了我的疑惑。通过对7种不同调用方式逐一详细的深入源码剖析,又是从整体到细节,最后又回到整体总结的方式,非常好,具体到哪几行代码都干了些什么,这才是我想要的技术内幕,当然,这是高云大拿自己的理解,读者自己看,会根据自己对jQuery的使用情况,会有不同的理解,不过这样的理解只会是更深入,更有利的,互相促进的,我就是这样的,非常感谢这几个样章,而且同时又是问答形式,真是太棒了,perfect!

每种方式来调用方法都从实现原理 --> 源码分析 --> 如何使用 --> UML图似的小结,这里,每一种都有小结,而且是类图方式或者说是思维导图似的给出小结对我这种看了前面忘了的人非常有益处,点32个赞。

另外,讲到静态属性和方法这一小结时,因为非常多,所以以列表形式,一个个细讲,读起来竟然一点都不乏味,真乃有点神了,我自己都不相信自己竟然看下来了。这个小结我也是打印出来看了,因为太多属性和方法了,我喜欢纸质的,所以,求小编给力呀,赠书呀,有木有,虽然厚一点。高云大拿把底层模块中的工具方法以及一部分的浏览器功能测试的方法都放到了这里来讲,我刚开始以为不妥,后来细看,其实,应该讲也是合理的,但是第一章画架构图时,这些就应该分属于入口模块了。但是呢,回过头想,这些也是底层的支持方法,只不过,其他模块都会用到他们就是了。所以,应该讲是一个交集。

底层支持模块

刷了一下样章,总页数其实只给到了底层支持模块的一个模块,也是入口模块会最先调用到的:选择器模块。之前就知道jQuery用的是sizzle选择器,而且在面试的时候以及笔试的时候都遇到过,这次算是可以较为系统地进行学习了。

这一章还是依循大拿的前两章的风格,总分总有木有,这种行文结构从小学写作文就开始锻炼了,这是我第一次在技术书籍中看到如此好的应用和实践,亲切感倍增呀,怎么可能会读不进去呢,果断是更能让我深入了解为什么sizzle的从右到左的解析是怎么个回事了,一般人我不告诉他,自己去看这一章,效果会更好,总体沿袭第二章的风格,到这一章代码一段,我又作死地想要如果纸质书上代码能够有高亮,就好了,所以自己就配合着抓了一份jQuery1.7.1源码下来,开在sublime里面高亮配合着读,效果更佳,这个就告诉你们了哈,受用无穷。

试读总结

三章试读下来,利用一周零零星星的时间,有的地方还没读透,回过头希望能够拿到赠书(要是没有自己买咯只好,当然别是这种情况呀),然后继续研读。哦啦,希望自己在Js的学习道路上,能够通过这次第一次读源码(间接)更进一步,能够渐渐企及前辈们的高度。

最后,祝愿此书大卖。

]]>
Node.js重修之道 2014-03-21T00:00:00-07:00 左银右煌 http://grahamle.github.io/2014/03/Node.js重修 缘起

14年以来,做完项目之余,还是没想开始写论文的我,就开始了Node.js学习,显然已经很晚了,但希望,还赶得上。首先就是BYVoid写的《Node.js开发指南》这本书,以及《JavaScript异步编程》这本书,让我非常想要去揭开异步编程到底是怎么样的一个面目。由于自己在项目中使用了ng的$q的promise,几次使用都不是很顺手,所以会来学这个。
简言之,就是好奇吧。

过程

看指南这本书时,其中有一句讲到http这个核心模块的时候,说是内部是由C++开发,外部由Js封装,我就不大理解了,于是google之。这边说几点开始的时候一定要认清的。

第一步:弄清是什么

第二步:开始用来干点什么吧

]]>
动态网站访问过程详解 2014-01-11T00:00:00-08:00 左银右煌 http://grahamle.github.io/2014/01/动态网站访问过程详解 以Apache服务器、php为例详解动态网站访问过程

目前来说,网站页面主要分为静态页面和动态页面,纯静态页面组成的网站现在相对比较少见,大型网站一般使用的是动态网站建站技术,还有一部分网站是静态网页与动态网页共存,本文以Apache服务器、php语言为例,详解动态网站的访问过程,下面直接切入本文主题。

图解

(1) 用户端访问服务器端的html文件

  • S1:通过本机配置好的DNS域名服务器地址寻找DNS服务器,将网站URL中的Web主机域名解析为Web服务器所在的Linux操作系统(Apache通常与Linux操作系统组合使用)中对应的IP地址。
  • S2:通过HTTP协议(超文本传输协议)去连接上述IP地址的服务器系统,通过默认80端口(默认的端口是80,也有其他端口,输入URL时一般不用输入端口)请求Apache服务器上相应目录下的html文件(如index.htm)。

  • S3:Apache服务器收到用户的访问请求后,在它管理的文档目录中找到并打开相应的html文件(如index.htm),将文件内容响应给客户端浏览器(即用户)。

  • S4:浏览器收到Web服务器的响应后,接收并下载服务器端的html静态代码,然后浏览器解读代码,最终将网页呈现出来(由于不同的浏览器对于代码的解读规则会不一样,所以不同浏览器对于相同的网页呈现的最终页面效果会有所差异)。

(2) 用户端访问服务器端的php文件

  • S1:该步与上面访问html静态网页是一样的,通过DNS服务器解析出相应的Web服务器的IP地址。

  • S2:与上面访问html静态页面相似,不过最后请求的是Apache服务器上相应目录下的php文件,如index.php。

  • S3:Apache服务器本身不能处理php动态语言脚本文件,就寻找并委托PHP应用服务器来处理(服务器端事先得安装PHP应用服务器),Apache服务器将用户请求访问的php文件(如index.php)文件交给PHP应用服务器。

  • S4:PHP应用服务器接收php文件(如index.php),打开并解释php文件,最终翻译成html静态代码,再将html静态代码交还给Apache服务器,Apache服务器将接收到的html静态代码输出到客户端浏览器(即用户)。

  • S5:与上面访问html静态页面相同,浏览器收到Web服务器的响应后,接收并下载服务器端的html静态代码,然后浏览器解读代码,最终将网页呈现出来。

(3) 用户端访问服务器端的MySQL数据库

如果用户需要对MySQL数据库中的数据进行操作,那么就需要在服务器端安装数据库管理软件MySQL服务器,用来存储和管理网站数据。由于Apache服务器是无法连接和操作MySQL服务器,所以还需要安装php应用服务器,这样Apache服务器就委托php应用服务器去连接和操作数据库,在对数据库中的数据进行管理的时候,一般都需要用到结构化查询语句,即SQL语句。

  • S1:该步与上面访问php文件一样,通过DNS服务器解析出相应的Web服务器的IP地址。

  • S2:与上面访问php文件一样,请求访问Apache服务器上相应目录下的php文件。

  • S3:与上面访问php文件一样,PHP应用服务器接收Apache服务器的委托,收到相应的php文件。

  • S4:PHP应用服务器打开php文件,在php文件中通过对数据库连接的代码来连接本机或者网络上其他机器上的MySQL数据库,并在php程序中通过执行标准的SQL查询语句来获取数据库中的数据,再通过PHP应用服务器将数据生成html静态代码。

  • S5:浏览器收到Web服务器的响应后,接收并下载服务器端的html静态代码,然后浏览器解读代码,最终将网页呈现出来。

需要注意的是,文中(2)与(3)的区别是,一个访问了数据库,另一个没有访问数据库,所以在过程中有点差别。

web访问过程

]]>
前后端MVC思考 2013-12-24T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/12/前后端MVC思考 问题的提出

Q:看了Zakas写的前后端架构文章之后,就结合自己的项目思考MVC的问题,前端MVC和后端MVC到底有什么区别?

A:应该讲,问题其实是什么是Js的MVCJs的MVC只是后端MVC中的View里面再去分出来的MVC,跟后端MVC没太大关系。前端的MVC是为了解决复杂前端情况下模块化 Js 的问题。最典型的应用,单页面的 Js 应用,如豆瓣说。

基于RESTful接口的前端MVC架构

原文地址

Q:为什么会有前端MVC?
A:前端处理的逻辑越来越复杂,不仅有交互体验需要编写大量 js 代码,甚至很多业务逻辑都被搬到前端。基于此,如何组织这大量的 js 代码成为亟待解决的问题,所以前端MVC也就应运而生了。

Q:RESTful接口是什么? A:前端的逻辑增多,实际上是把后端的View剔除,然后让后端只负责输出JSON数据给前端,前端收到后将JSON对象当作Model根据业务需要进行各种操作。于是在前端眼里,后端便退化为了数据源,实现完全的接口化。

]]>
Node.js 24小时速战 2013-12-06T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/12/Node.js24小时 callback

Node.js 使用回调

var http = require('http');
http.get({host: 'shapeshed.com'}, function(res) {
  // 这个回调时发生在远程服务器发回响应之后发生的
  console.log('Got response: '+res.statusCode);
}).on('error', function(e) {
  console.log('Got error: '+e.message);
});

同步和异步

同步和阻塞时可以互用的两个概念,指的是代码(脚本)的执行会在函数返回之前停止,如下面的代码,在 fetchPage() 运行时,它阻塞了整个脚本的运行。其他接下来的操作必须等待。

// blocking code
function sleep(milliseconds) {
  var start = new Date().getTime();
  while((new Date().getTime() - start) < milliseconds) {
    // 超过给定时间才从该循环跳出,模拟了 setTimeout
  }
}

function fetchPage() {
  console.log('fetching page');
  sleep(2000);
  console.log('page fetched');
}

function fetchApi() {
  console.log('fetching api');
  sleep(2000);
  console.log('api fetched');
}

fetchPage();
fetchApi();

异步和非阻塞是可以互用的概念,指的是基于回调的、允许脚本并行(并发)执行操作的方法。脚本无需等待某个操作的结果,因为操作结果会在事件发生时由回调函数来处理。

// non-blocking code
var http = require('http');
function fetchPage() {
  console.log('fetching page');
  http.get({host: 'baidu.com', path: '/?delay=2000'}, function(res) {
    console.log('page fetched');
  }).on('error', function(e) {
    console.log('error occured'+e);
  });
}
function fetchApi() {
  console.log('fetching api');
  http.get({hots: 'baidu.com', path: '/?delay=2000'}, function(res) {
    console.log('api fetched');
  }).on('error', function(e) {
    console.log('error occured'+e);
  });
}

fetchPage();
fetchApi();

事件循环

该机制是确保 Node.js 可以使用回调进行工作的基石。事件循环使得系统可以将回调函数先保存起来,而后当事件发生时再运行。而事件可以是数据库返回数据,也可以是http请求返回数据等。控制流在事件发生后需要执行回调时会回到需要的位置上。
使用基于事件循环进行编程,实际上关键思想是,将代码围绕着事件来构架而不是按照期待中的输入顺序来构架

由于Js中的事件循环以单一进程为基础,所以为了确保高性能,需要遵循以下规则:

  • 函数必须快速返回
  • 函数不得阻塞
  • 长时间运行的操作必须移到另一个进程中

由于以上的第三点,如果程序或函数需要长时间运行才能完成处理,那么事件循环就不是个好选择。Node.js 所不适合的地方包括处理大量数据或者长时间运行计算等。Node.js 旨在在网络中推送数据并瞬间完成

]]>
现代web应用架构 2013-12-01T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/12/现代web应用架构 这篇文章,我主要是收集一些有关现代web应用开发的架构,包括前后端以及和数据库通信的。由于时间限制,还没细读。先放上一概链接,以供后查。

]]>
ng中的$apply() 2013-11-30T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/11/ng-$apply 絮叨

这篇就来总结一下遇到 $apply() 的情况吧。
大致目前有这样几种:

  • 在PouchDB中各种存取数据库的方法中,数据改变后需要通过 $rootScope.$apply(fn) 更新
  • 其他异步操作或是别的库的操作对数据进行了改变,那也需要通过 scope.$apply() 来更新
  • 所有跑出了ng的digest循环进入到原生js的API操作对数据的改变也需要

正题

想要真正了解什么时候是用 $apply(),就需要理解为什么我们需要用它,下面是看这篇文章时的一些笔记与心得。
AngularJS and scope.$apply

原因:Js是按顺序执行的

原文标题是:Js is Turn Based,很简洁,我刚开始还把它翻译成转回Js为基础,尼玛,把turn当动词用了,后来想想,语法错了,2B了,turn这里是名词。
我们都知道,我们写的所有的Js代码并不是一次就全部执行完的,它们是按顺序执行的,而且是单线程的。
当有一个需要消耗一定时间(ms级)的任务需要执行时,比如一次ajax请求,等待一个单击事件,或者是设置一个timeout,我们总是把这样的任务丢到一个回调中,然后结束这一轮。之后,如果ajax请求完成,单击事件检测到,或者是定时器超时,这时候,新一轮Js执行开始,回调也就完成了它的使命注意,这之间的时间间隔,是可以有新的Js代码被执行的,至于执行多少轮,那要看回调返回的时间了,回调返回总是强行插入的。看下面一个例子:

var  button = document.getElementById('clickMe');

function buttonClicked() {
    console.log('the button was clicked');
}

button.addEventListener('click', buttonClicked);

function timerComplete() {
    console.log('timer complete');
}

setTimeout(timerComplete, 2000);

上面的代码其实有三轮执行,并非 completed in one turn

  1. 第一轮:找到那个按钮,安上一个click监听器,设置一个定时器,结束本轮,然后浏览器根据需要看是否更新页面,然后开始等待接受用户的单击输入
  2. 第二轮:如果浏览器检测到一个单击事件在id为 clickMe 上的按钮发生了,那么Js引擎开启第二轮,这时候执行 buttonClicked 函数,当这个函数返回时,这一轮也就结束了
  3. 第三轮:从第一轮开始算起,2000毫秒之后,浏览器会开启新一轮执行,执行 timerComplete 方法

注意:事实上,上面第二轮或者第三轮的先后可能是反过来的,这样看在每一轮执行的间隔中发生了什么。

如何更新绑定的数据

一般情况下,有两种策略:

  • 通过特殊对象,这些对象里面存储的数据是通过方法存取的,get/set那一套,采用这种的有:EmberJS, KnockoutJS
  • 采用普通对象,就不用通过方法来设置对象中的字段、数据等,要做的就是等Js每轮执行最后检查是否有数据改变

AngularJS采用的就是上面所说的后一种策略,表面上看起来不高效,不能马上更新,但其实有一些策略可以提高这个性能的。

等到每轮执行的最后来检测数据是否有变更,在ng中是用 $scope.$digest() 方法,但其实我们从来很少直接调用这个,我们往往是通过一个切入点:$scope.$apply() 来让Js进入ng的digest loop,这样来更新所有的绑定还有监听器等。

尽量用$apply(fn)

带参数的这个方法不仅仅可以及时更新数据,而且它其实内置了 try...catch 来处理异常。

好了,今天就到这了,GN

]]>
ng中的promise 2013-11-29T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/11/ng-promise 引子

其实一直知道 promise 是属于 Js 的非同步操作的一种模式,ng中的也一直有在用,就是一直没有整太明白到底它和 deferred 有什么区别。今天是在整合 PouchDB 和 AngularJS 的一个应用时,又遇到了这个问题,通过 ng 的 $q.defer 返回的 promise 到底是什么呢?又再一次勾起了我的困惑,所以,这次,我决心搞懂它来。
Sync Multiple AngularJS Apps Without Server via PouchDB

Js中的promise

首先我们得来从Js中的promise说起。你要知道,我们所说的 promise 其实就是一个 POJO = Plain Old JavaScript Object 。而这个 promise object 又是被好多库用到了,包括我们熟知的 jQuery, AngularJS, Dojo, 还有 WinJS 等。其实呢,promise模式可以看作是对回调模式的一种简化,通过在返回的promise对象上调用其 then() 方法,把在异步操作结束后需要进行的回调(操作)丢进去,这样看起来像是很顺畅的流程

关于 promise 的两种实现,请看 jQuery与ng实现promise的不同

如果你想直接看代码,直接看下面这两个fiddle吧:

对比两种进行学习,虽然个人觉得 jQuery 这种不需要中间多一个对象来的好理解很多,但貌似现在这些都提倡像ng这样的实现,whatever,反正现在算是理解了,明天画个图。

ng-promise进阶

说是进阶,其实说白了就是把东西细讲了,在之前的那篇的基础之上讲的更细一些了,适合阅读。因为这篇是有一个实际操作例子的,它的异步操作是数据库的存储/读写等,和上面的仅仅是个纯示例的 setTimeout() 的为了异步而异步完全是上了一个级别,真正的实例,让你爽。
Using AngularJS Promises

]]>
web应用中非同步操作的多种模式 2013-11-28T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/11/js-非同步操作 引子

在网页应用程序(以下简称“web应用”)中进行非同步操作,是从 Ajax 重新火起来之后带来的 XMLHttpRequest 开始风靡的,现在,开发者可以运用各种相关的模式来辅助执行非同步操作。当然,web应用中除了部分需要非同步操作(如取数据等),大部分还仍然以同步操作为主。
近来,Node.js 袭来,因为它大规模采用了非同步操作,使得不少开发者留意到同步与非同步操作之间存在的显著差异,也开始重视非同步操作的各种模式。

回调模式

回调就是所谓的 callback,台湾译作“回呼”,当实现程序出现许多重复流程,仅小部分需要特定实现时,那么一般会有的处理情况是:把重复流程的实现作为样板,而特定实现则通过“呼叫者”提供回调对象或函数。
例如

有Js数组:[1,3,2,5,4],对其进行排序,我们会考虑如下实现:

[1, 3, 2, 5, 4].sort(function(a, b) { return a-b;});

再如
在浏览器使用非同步对象 XMLHttpRequest 时,由于判断环境、发出请求、回应等流程是可重复利用的,因此可封装,也就是说在进行非同步请求的同时,提供回调函数,以便在浏览器获得“远端”对“非同步请求”的回复时予以处理,可以看jQuery的实现:

$.get(url, options, function(responseText) { ... });  // 第三个就是回调函数

通过上面两个例子,我们可以看出回调模式的特点:

  • 封装了大部分流程
  • 仅要求呼叫者提供对应事件发生时相对应的回调
  • 这种模式对呼叫者友善,逻辑清晰

回调这种非同步操作模式有几点不好的:

  • 对开发者来说,如果用同步操作的习惯来对待回调函数的执行结果,将会产生不正确的执行结果
  • 如果非同步操作串连在一起的话,则会形成回调地狱(Callback hell)的问题,影响可读性

连续调用

英文名称是:Continuation-passing style,简称CPS,它是一种流程控制风格,如果用回调方式来实现,那么是通过函数将其执行结果传给呼叫者提供的回调函数,因而形成一种连续呼叫的风格。
这个模式目前看的还是比较晕,先放链接吧:非同步操作的多種模式

Promise模式

这种模式在不同语言中有不同的称呼,如 future,delay 或是 deferred 模式。
Promise 对戏那个基本上是作为一个代理对象,代表着一段可能长时间执行或延后执行的计算,并承诺在未来提供计算结果,无论那是成功、失败或其他可能的结果。
对于angular中的promise写法,项目中也是运用很多,比如通过 then() 解决回调地狱的问题,还可以使用 try ... catch 配合 rejectresolve 进行代码书写。

写在最后

最后说下这篇文章是想说,其实我们运用非同步操作很多,Js中,包括各种库和框架也都提供了这方面的支持,理解它其实是作非同步(异步)操作的,就是最重要的了。
最后这边有篇文章,希望有时间的可以看看:JavaScript异步编程

]]>
ng中的digest 2013-11-26T00:00:00-08:00 左银右煌 http://grahamle.github.io/2013/11/ng-digest 这篇主要就是很简单地来澄清一下angular中的 $apply() 到底是干什么用的,说的直接一点,现在遇到的是在你写自己的 directive 的时候,触发了一个事件,那么应用的控制权会转移到 Native JS 中去,并且你在事件的回调函数中改变了 注册在 scope 中的一些 model ,那么,为了让 angular 依然能够通过 $watch() 看到 model 的变化(虽然watch是隐形地进行的),那么就需要通过调用 scope.$apply() 让应用的控制权转移回去给 angular ,也就是说所谓地进入它的“消化周期”中,也就是 digest loop 中了。
具体可以看 Using scope.$watch and scope.$apply

如果上面stack overflow的文章还不够的话,可以看一下这篇咯AngularJS and scope.$apply

]]>