ECMAScript6

强迫症的模块化

在ES2015发布后,JavaScript最终也有了一个标准的模块化方案,而同时从webpack开始,也带来了一波“一切皆模块”的潮流。整个2015-2016的前端发展中,除去在UI层不断的努力和突破外,几乎每一件事都和模块化脱不开关系。 本文也试图从几个方面简单地说一下模块化,并分析一些在模块化实施中产生的误区。 模块的本质 模块化顾名思义,指的必然是将程序拆分为多个“模块”,使用模块间通信的方式进行交互。这和其它对程序逻辑进行拆分的手段是一样的,无论是拆成类、拆成对象还是拆成啥,其目的无非是隔离内部的实现以及声明对外的接口。 事实上自远古时代起,JavaScript就在努力进行实现封装的尝试,无论是通过IIFE避免全局变量,还是基于Namespace进行管理,以至后期的AMD、CJS社区模块化方案,直到现在的ES Module标准模块化,无不以上为目的。 在模块化的思想里,一个模块应该是: 需要一系列的依赖。 涉及一个内部的实现。

JavaScript

使用高阶函数实现类的扩展设计

在不少框架中,都会对“扩展”这一概念有需求。所谓扩展,即一个可组合的组件,用于嵌入到目标的生命周期中,对目标的行为进行额外的处理使得目标拥有不同的表现。 一个非常简单的案例即日志的记录。通常框架自身并不会有业务相关的日志记录的功能,而业务代码也不希望混入并非业务逻辑的日志记录部分。那么使用一个扩展,在合适的点进行日志的收集和存储是很合适的设计。 在以往,比较流行的扩展通常有几种形式: Mixin形式。这种形式下扩展与目标形成完全的覆盖关系,属于暴力而简单的方法。 class Component { constructor({mixins}) { mixins.forEach(mixin => Object.assign(this, mixin)); } doWork() { // ... } } let logMixin

JavaScript

ES Decorators简介

我跟你说,我最讨厌“简介”这种文章了,要不是语文是体育老师教的,早就换标题了! Decorators是ECMAScript现在处于Stage 1的一个提案。当然ECMAScript会有很多新的特性,特地介绍这一个是因为它能够在实际的编程中提供很大的帮助,甚至于改变不少功能的设计。 先说说怎么回事 如果光从概念上来介绍的话,官方是这么说的: Decorators make it possible to annotate and modify classes and properties at design time. 我翻译一下: 装饰器让你可以在设计时对类和类的属性进行注解和修改。 什么鬼,说人话! 所以我们还是用一段代码来看一下好了:

JavaScript

PC端大型单页式商业内容管理系统的JS模块化构建探索

前提 为了不被喷得太惨,给标题加了这么多的限制定语也是相当不容易的了。此文讨论的是我所处的环境下对JavaScript构建的一些简单探索,因此有相当多的前提限制。 首先,何为大型。从我们的系统来看,20多个业务模块,近100个页面组成的单页系统,对应的业务源码代码量如下: 对应的依赖库,除underscore和moment外均为公司内部库,代码量为: 其次,所谓的“模块化”指我们使用AMD进行构建,使用符合社区AMD标准的Loader进行模块的加载。 而“PC端单页式商业内容管理系统”则代表着系统的不少特性: 使用是相对强制的,对用户来说这是一项工作,而不是爱用不用的用户产品。 商业公司通常拥有较好的网络环境,面向PC设计更使得带宽不是一个需要着重考虑的因素。 单页系统使得所有功能被包含在一个HTML页面内,不存在页间的跳转,因此资源不以页面为单位进行切分。 为何要构建 第一个问题是,AMD有自然的按需加载的属性,

JavaScript

博客单页化实践

这个单页化的博客现在也没在用了,Ghost平台挺好的……如果需要看代码的话,请参考GitHub上的仓库 半年前,因为VPS未续费导致所有数据丢失,直至今日终于重新恢复了所有的文章数据(虽然丢失了全部的评论),并且借此机会对所有文章进行了一次重新审视,修改了部分问题,并将所有示例迁移到jsfiddle和jsperf上,总算造一段落。 新的博客完全独立建设,不使用任何第三方的CMS系统,后端使用ASP.NET MVC实现,数据库使用MySQL,通过Mono部署于Ubuntu Server之上,前端使用nginx作为静态服务器。 也正因为完全独立构建,不受任何系统出于安全、简便等奇怪理由而附加的限制,这个博客系统也成了自己练手的娱乐场。就比如本篇要介绍的OPOA化实践。 概念 OPOA,全称One Page One Application,中文可以称之为单页应用。

JavaScript

浅谈eval的影响

听闻最近由于老赵在其Wind.js关于eval这一函数使用上与BYVoid进行了一系列的争论,甚至为此写了一篇博客来证明eval对性能的影响可以忽略这一观点。 而不幸由于在这一次讨论中,起于Ericpoon_智对eval性能影响的疑问,也不知不觉参与了一些讨论,随后_Franky提出要有数据上的事实来说明这一问题,于是在好久没有新的博客出产的时候,决定对这一问题进行一下非常浅薄的挖掘,提供一些客观的数据。 首先在此文中,需要声明的是: 个人倾向于不要计较eval产生的影响。 此文会从**eval有负面影响**这一角度进行表述和举例,与老赵的博客属于完全相反的观点,但并不是一种对其的驳斥或者反对,仅仅是希望可以提供一些客观的理论依据。 本文只谈V8引擎,由于这一问题是不同引擎的表现会大不相同,不可能完全覆盖,因此数据不可作为权威参考。 本人不懂C或C++,看不懂V8,因此完全是黑盒的推断,各结论也不具权威意义,完全可能是臆测,当然数据保证真实。 以下是本次测试使用的Chrome版本详细信息: Google

JavaScript

所谓闭包的PPT

这是近期一次内部分享的PPT,从函数的相关概念上,主要内容有: 变量的概念。 闭包的表象。 核心概念,包括可执行代码、执行环境、词法环境、变量环境、环境数据、绑定对象等。 函数的相关过程,包括创建函数、进入函数、定义绑定初始化、变量查找等。 闭包对垃圾回收的影响的探究。 PPT权当对新事物的一种普及性展示,不会有深入的内容,也不会有详细的例子,如果有任何疑问,欢迎随时交流。 如需观看或下载,请点击此处。

JavaScript

关于闭包及变量回收问题

本文的诞生,源自近期打算做的一个关于javascript中的闭包的专题,由于需要解析闭包对垃圾回收的影响,特此针对不同的javascript引擎,做了相关的测试。 为了能从本文中得到需要的知识,看本文前,请明确自己知道闭包的概念,并对垃圾回收的常用算法有一定的了解。 问题的提出 假设有如下的代码: function outer() { var largeObject = LargeObject.fromSize('100MB'); return function() { console.log('inner'); }; } var inner = outer(); 在这一段代码中,outer函数和inner函数间会形成一个闭包,致使inner函数能够访问到largeObject,但是显然inner并没有访问largeObject,那么在闭包中的largeObject对象是否能被回收呢? 如果引入更复杂的情况: function

JavaScript

不完美沙箱 - 变量定义及查找

时隔N久,继续生产,年底实在忙…… 在上一篇发表之后,有网友指出这个沙箱过于复杂,事实上一些更简单的代码也能完成同样的效果,例如: var proxy = { document: new DocumentProxy() }; function(window) { // 开发者提交的代码 }.call(proxy, proxy); 通过call和函数参数来劫持this及window这两个指向全局对象的变量,以达到屏蔽全局对象的目的。 但是这个方式虽然简单易懂,却有一个非常重要的环节无法照顾到,即全局环境下LexicalEnvironment和VariableEnvironment和ThisBinding均是全局对象,这一特性意味着在全局环境下执行的代码将同时满足以下4个条件: 通过var x = 3;定义的变量,可以使用console.log(x);输出。 通过this.

JavaScript

不完美沙箱 - 模拟全局对象

上一章中,经过对标准的查询、翻阅、解析,终于在绕了一个大圈之后,明白了全局对象的特征,并明确了模拟一个全局对象需要的内容,还为此编写了简单的测试用例。本文将从第1个测试用例着手,放眼与全局作用域的VariableEnvironment、ThisBinding以及window别名这三者的一致性,来推导出一个沙箱的原型。 所谓纰漏 在开始这一章以前,首先不得在这里道个歉,原因很简单,我对标准进行了误读,且在没有测试的情况下就开始了这一系列的博客。事实上,以我的思路,是无法制作出一个理想中的沙箱的,虽然可以控制住第三方代码的作用区域,但是无法做到完全的透明,其主要问题出在这里: var x = 3; console.log(this.x); // 打印出3 事实上,

JavaScript

不完美沙箱 - 劫持全局对象

上一章中对需求进行了分析,并对实现方案有了一定的探讨,最终得出从劫持全局对象这一环节入手的结论,而本文将讨论如何劫持这个全局对象。 全局对象,多数会进行Javascript编程的工程师都知道,在浏览器的执行环境中,叫做window,虽然事实上window和全局对象并不严格意义上是一个东西,不过这对我们的实现并不会造成什么障碍。 剖析全局对象 既然要去做劫持全局对象这样不人道的事,自然先要知道全局对象是个什么东西,有什么样的特点,以便针对目标,一刀致命。这个时候,标准就会成为我们最好的参考,首先打开ECMAScript 5的HTML版本,此后几乎所有的标准引用都会从这里产生。 所谓擒贼先擒王,经典理论告诉我们,不可以把整个洋洋洒洒数万字的标准给全看了,必须以快、狠、准的手段找到我们需要的东西。如果是纸质的书籍,对于一个不熟悉文档结构的人而言自然是无处下手,但好在现在是数字化的时代,我们有一个名为“搜索”的强大功能。

JavaScript

不完美沙箱 - 目的、背景及分析

目的 这将是一个系列的文章,大致由4篇博文组成,内容应该会是这样的: 目的、背景及分析 劫持全局对象 劫持全局对象属性 存在的问题及漏洞 起草这一系列博文的原因有两点,第一自然是因为最近突然有这样的需求,而群里正好也在某个深夜(技术宅基本深夜活动)讨论到了一个类似的话题,给了我一些灵感,所以决定将近期“研究”的成果展示一下。 而第二个目的,也是我想将这么一个简单的东西(其中最终就10几行代码)用整整5个博文的篇幅来说的原因,是因为近期有不少朋友向我强烈地表达了标准的存在意义极其微弱,我能把代码写好就足够的想法,而且现在更是催生了一种叫做jQuery工程师的职位,大有星星之火的样子。 对于这样的态度,作为一个只会理论不懂实践的2B工程师,个人自然是坚持地抱以反击态度的,因此,这一系列的博文,将会从“构建一个沙箱”

JavaScript

ECMAScript5小试PPT

这是近期一次内部分享的PPT,其中内容主要来自于Franky教主的相关材料,并加入了一些对ECMAScript Harmony的补充资料。同时可以在这里下载到教主的原版PPT。 对于ECMAScript,由于本人也不甚了解,其中一些诡异的问题和隐隐的矛盾(在Harmony中会体现得特别明显)暂时也没有精力去一一追究,PPT权当对新事物的一种普及性展示,不会有深入的内容,也不会有详细的例子。 如需观看或下载,请点击此处。

JavaScript

JavaScript中的对象查找

近期群里常有人提一些简单的问题,比如发一段代码乱七八糟的代码,然后说里面某个变量是什么,比如这里就有个很好的例子: function fn(arg) { console.log(this.arg); console.log(this); } fn(123); var o = { fn: fn }; o.fn(123); 然后就可能有这样的问题: 为什么this.arg是undefined?为什么2次调用fn的this是不一样的? 为此,我觉得自己作为一个虽然不成熟的前端,对于一些自己力所能及的事情,还是应该传道授业解惑的。所以,这篇文章,

JavaScript

引入Flash那些事

最近做的事和Flash打交道比较多,简单来说,就是要在第三方的页面引入一个Flash的广告,播放一会,让用户能够关闭。 起初总觉得,就引入一个Flash嘛,Adobe都给出了官方的使用方法,直接拿来用不就成了。顶多IE下不支持object元素的appendChild,大不了拼接字符串然后通过innerHTML来创建就好了。 但是真正做的时候,开始测试各个浏览器的时候,才发现情况远没有想的这么简单…… 关于如何引入Flash 这大概是最基本的问题,其他的一切问题都是基于Flash可以正确引入到DOM中这个前提的。 最先的方式自然是使用adobe给出的官方方式,即<object><embed /></object>的结构来嵌入一个Flash。根据各种资料,<embed>

JavaScript

记一次失败的jQuery优化尝试

我经常抱怨jQuery的DOM操作性能并不优秀,并且经常尝试用一些方法去进行优化,但是越是优化,越是沮丧地发现jQuery其实已经做得很好,从使用者的角度能够进行的优化实在有限(这并不意味着jQuery的性能是优秀的, 反之只能说它是一个相对封闭的库,无法从外部介入进行优化)。这篇文章就记录一次失败的优化经历。 优化思想 这一次优化的思想来自于数据库。在数据库优化的时候,我们常会说将大量的操作放在一个事务中一起提交,能有效提高效率。虽然对数据库不了解的我并不知道其原因,但是“事务”的思想却为我指明了方向(虽然是错的……)。 因此我尝试将“事务”这一概念引入到jQuery中,通过打开和提交事务,从外部对jQuery进行一些优化,其最重要的在于减少each函数的循环次数。 众所周知,jQuery的DOM操作,以get all, set first为标准,其中用于设置DOM属性/样式的操作,

JavaScript

单页系统前端MVC设计 – MVVM模式

在上一篇中,Action已经可以获取所有需要的数据,下一步就是将数据与视图中的控件树进行关联,使得View模块可以获取、修改数据,并通过与Action的进一步交互来完成整个业务逻辑。 MVVM模式 在介绍本文的ValueStack和ModelDescriptor这两个在整个框架中意义非凡的组件之前,需要先简单地介绍一下MVVM模式。 这个MVC框架引入了MVVM模式,MVVM模式的全称是Model-View-ViewModel模式。该模式顾名思义地有三个部分组成: Model:即数据,所有可持久化和不可持久化的,与业务有着关系的内容都可以被称为数据。Model的数据是扁平的,为了区别下面的ViewModel,这里可以称之为FlatModel。 例如,一个系统有用户,以及用户发表的文章,期间是1:n的关系,那么Model就存在两条数据。其一为user,其二为postList,两者之间虽然有着“从属”的关系,但是在FlatModel这样“扁平”的结构中,

JavaScript

单页系统前端MVC设计 – 获取数据

一个单页系统中,由于浏览器始终不能离开当前页面,从而无法利用服务器端的动态页面(ASP、JSP、PHP等)将数据与页面结合起来,数据与表现相比单独的页面请求分离得更为彻底。其中数据获取的部分,通常使用的是AJAX的技术,利用XMLHttpRequest向服务器请求数据。 我在很多篇文章中提到过,现有互联网的大部分应用是数据驱动的,数据是整个业务的核心和关键所在。在框架的执行过程中,Action同样需要数据才可以驱动整个界面的渲染及交互,因此当Action进入自身的执行流程时,第一件事就是将自己需要的所有数据获取过来。 Action流程 Action从execute开始,到renderView绘制视图,并在之后与视图进行交互,其执行流程如下所述: 调用execute函数开始流程。 调用prepareData函数,该函数中利用DataProxy组件获取数据。 当所有数据请求完成后,会调用buildModel函数,该函数通过ValueStack和ModelDescriptor两个组件,将第2步中获取的数据组装成为更加结构化、可用于视图中的控件树的模型。 完成了模型的组装后,调用getRoot函数,

JavaScript

单页系统前端MVC设计 – ActionMapping过程

上一篇讲到通过对外暴露LocationManager组件,提供接口进行redirect等操作,可以形成一个Request对象交给Controller(即调用Controller的process函数)。 本篇主要讲述Controller如何处理一个Request,包括通过Request查找到对应的Action,并准备好上下文对象,交进而进入到Action的执行流程中。 Request对象 类似服务器端的MVC框架,Request对象将提供对一次请求中所能接触到的所有内容的封装,其会包含以下内容: url、path、query属性,对应window.location的href、pathname和search属性,表示整个请求资源符或者其中的一部分。 getParameter(key)函数,获取query中某个键对应的值。 getSession(key)函数,获取Session或者Session中某个键对应的值。 getAppContext(key)函数,获取AppContext或者AppContext中某个键对应的值。 Session对象及AppContext对象 在服务器端,

JavaScript

单页系统前端MVC设计 – 入口

这一篇讲作为整个MVC框架的入口的LocationManager和HashListener组件。 LocationManager和HashListener作为框架最外层的组件,提供了整个框架的入口,其中HashListener依赖于LocationManager,并且通过LocationManager才能够进入到框架的运行流程中。 在设计中,HashListener将会被设计成一个内部的组件,不会对外暴露,其所有可能提供的接口都通过LocationManager作代理,以保证对外开放的对象尽可能地少,形成更简明的API。 LocationManager 在接触该组件之前,先解释一下hash这个名词的概念。 在Web中,浏览器的地址栏中的URL通常可以分成几个部分,比如有一个简单的地址: http://www.some-where.com/list.html?page=2&size=20#item-3 上面的URL就能分为下面几个部分: 橙色:protocol,此处表示使用http协议。

JavaScript

单页系统前端MVC设计 – 总览

这是近期可能会接触的设计,对应的需求大致是: 单页系统,所有与后端交互通过AJAX进行。 每个模块、功能有自己的独立界面。 不同模块、功能对应着不同的URL,要求能通过URL直接定位到相关的模块或功能上。 模块间相互穿插,一个功能可能会突然被另一个模块引入,比如放在Dialog中。 对于上面的需求,我们讨论认为,对应的设计至少要能满足以下条件: 基于URL的可导航性和可访问性。 能够非常清晰、明确地划分模块及功能,划分上模块、功能相互独立。 可以方便地引入另一模块或功能的相关界面、交互。 基于此,虽然有着非常严重的过渡设计的嫌疑,最后我们还是选择了MVC的模式。当然这也是因为已经有过一个项目使用类似的模式,并且需要重用其中的一部分功能。 这一篇主要介绍整个设计的总体结构,以及一个请求的流程,首先下面是总体设计的总览图: 图很复杂,组件很多,所以说这根本就是个过渡设计的东西。

JavaScript

一小段jQuery代码的分析与优化

今天刚回家,QQ群里就看到有人求助优化一段jQuery代码,简单看了一下,发现如果对jQuery这东西只停留在用的层面,而不知其具体实现的话,真的很容易用出问题来。这也是为什么近期我一直不怎么推崇用jQuery,这框架的API设定就有误导人们走上歧途之嫌。 需要优化的代码大致是这样的,也不方便直接把人家的代码复制过来,就大概地表达下意思: $.fn.beautifyTable = function(options) { // 定义默认配置项,再用options覆盖 return this.each(function() { var table = $(this), tbody = table.children('tbody'), tr = tbody.children('tr'), th

JavaScript

jQuery1.5的改进细节

jQuery 1.5 beta1出来了,从学习跟进上来说,这一次已经比较晚了(我竟然不知道1.5什么时候出的alpha,就这么beta了)。 这个1.5版本最大的更新是AJAX的完全重写,提供了更强的可扩展性。但是受制于精力和篇幅,对新的AJAX的分析还是放到下回,本篇先简单介绍一下细节方面的改进。 jQuery._Deferred和jQuery.Deferred 首先不得不说这两个新生事物,因为他们是作为基础设施存在,不把这两个东西讲明白了,有些问题根本没办法解释。 首先,jQuery.Deferred是jQuery._Deferred的增强版,因此对于这个问题,从jQuery._Deferred入手,就能说明一大半的问题。 什么是Deferred?从字面上看,我的第一反应是“

JavaScript

异步函数的依赖维护

写背景太麻烦了!总之随着AJAX理念的深入人心、随着一些纠结的问题在一个setTimeout之后奇迹般地被解决、随着未来更多的异步(WebWorker、WebSocket、message事件)功能的引入,编码人员变得必须熟悉“异步执行+回调”的编程模型。 但是随着项目、应用的规模扩展,复杂度上升,异步回调模型的理解和维护成本也使地逐渐攀升,毕竟人看东西总是从上往下的,没人喜欢看完一个函数的时候突然发现要去300行以后、甚至另一个文件里找一个回调函数。同时,为了更灵活地切分系统、更合理地设计接口,类似于“从多源取得数据并合并”之类的需求也被摆到了台面之上,这意味着多个异步调用之间开始存在依赖关系。在设计不那么理想的系统中,此类依赖关系也可能变得非常复杂,导致代码的逻辑不易看懂、回调函数四处传递等问题,维护性极剧下降。 为了处理以上提到的问题,就需要一个“