博客单页化实践

发表于

半年前,因为VPS未续费导致所有数据丢失,直至今日终于重新恢复了所有的文章数据(虽然丢失了全部的评论),并且借此机会对所有文章进行了一次重新审视,修改了部分问题,并将所有示例迁移到jsfiddlejsperf上,总算造一段落。

新的博客完全独立建设,不使用任何第三方的CMS系统,后端使用ASP.NET MVC实现,数据库使用MySQL,通过Mono部署于Ubuntu Server之上,前端使用nginx作为静态服务器。

也正因为完全独立构建,不受任何系统出于安全、简便等奇怪理由而附加的限制,这个博客系统也成了自己练手的娱乐场。就比如本篇要介绍的OPOA化实践。

概念

OPOA,全称One Page One Application,中文可以称之为单页应用

顾名思义,在OPOA下,一个页面组成一个应用,不再以传统的超链接跳转导航的方式,而是通过javascript以XMLHttpRequest加载数据,通过DOM操作展现数据。

作为一个单页应用,其优势主要有:

  • 多个页面拥有相同的结构时,一些相同的内容(如侧边栏、LOGO等)不需要重复加载,节省流量(及一定的数据库查询)。
  • 没有浏览器跳转地址导致的短暂空白页面状态,提升用户体验。
  • 可以增加过渡效果(如渐隐、渐显等),进一步提升体验。

浅谈eval的影响

发表于

听闻最近由于老赵在其Wind.js关于eval这一函数使用上与BYVoid进行了一系列的争论,甚至为此写了一篇博客来证明eval对性能的影响可以忽略这一观点。

而不幸由于在这一次讨论中,起于Ericpoon_智eval性能影响的疑问,也不知不觉参与了一些讨论,随后_Franky提出要有数据上的事实来说明这一问题,于是在好久没有新的博客出产的时候,决定对这一问题进行一下非常浅薄的挖掘,提供一些客观的数据。

首先在此文中,需要声明的是:

  • 个人倾向于不要计较eval产生的影响。
  • 此文会从eval有负面影响这一角度进行表述和举例,与老赵的博客属于完全相反的观点,但并不是一种对其的驳斥或者反对,仅仅是希望可以提供一些客观的理论依据。
  • 本文只谈V8引擎,由于这一问题是不同引擎的表现会大不相同,不可能完全覆盖,因此数据不可作为权威参考。
  • 本人不懂C或C++,看不懂V8,因此完全是黑盒的推断,各结论也不具权威意义,完全可能是臆测,当然数据保证真实。

以下是本次测试使用的Chrome版本详细信息:

  • Google Chrome: 21.0.1180.79 (Official Build 151411) m
  • OS: Windows
  • WebKit: 537.1 (@124502)
  • JavaScript: V8 3.11.10.18
  • Flash: 11.3.31.227
  • User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.79 Safari/537.1

所谓闭包的PPT

发表于

这是近期一次内部分享的PPT,从函数的相关概念上,主要内容有:

  • 变量的概念。
  • 闭包的表象。
  • 核心概念,包括可执行代码、执行环境、词法环境、变量环境、环境数据、绑定对象等。
  • 函数的相关过程,包括创建函数、进入函数、定义绑定初始化、变量查找等。
  • 闭包对垃圾回收的影响的探究。

PPT权当对新事物的一种普及性展示,不会有深入的内容,也不会有详细的例子,如果有任何疑问,欢迎随时交流。

如需观看或下载,请点击此处

关于闭包及变量回收问题

发表于

本文的诞生,源自近期打算做的一个关于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 outer() {
    var largeObject = LargeObject.fromSize('100MB');
    var anotherLargeObject = LargeObject.fromSize('100MB');

    return function() {
        largeObject.work();
        console.log('inner');
    };
}
var inner = outer();

首先一个显然的概念是largeObject肯定不能被回收,因为inner确实地需要使用它。但是anotherLargeObject又能不能被回收呢?它将跟随largeObject一起始终存在,还是和largeObject分离,独立地被回收呢?

另类MVC模式 - 优势及实现

发表于

继续大逆不道系列……

上一篇中,提出了一个另类的MVC模型,与经典MVC模型有一些不同,那么自然需要描述这样的另类模型有什么优势,又能在怎么样的场景中使用。

逻辑划分

正如上一篇所说,这种模式下,最大的优势莫过于逻辑的清晰划分。在该模式的作用下,每一个Action都只要处理真正与自己有关的逻辑及数据,而不需要关心一些“通用”的内容,因为这些通用内容也成了独立的Action

例如,继续引用上一篇中的页面设计,根据经典的MVC模式,我们不得不在一个Action中准备所有数据:

public ActionResult ViewPost(int id) {
    ViewBag.Friends = FriendsRepository.ByUser(CurrentUser);
    ViewBag.RecentVisitors = VisitorsRepository.ByPost(id, TimeSpan.FromMinutes(30));

    Post post = PostRepository.ById(id);
    return View(post);
}

即便你按上一篇提到的方案使用Action Filter,依旧没有办法彻底地将这些数据从当前Action产生的数据模型中消除,当模块越来越多时,各种“通用”数据一起扔在ViewBag中,造成臃肿、冲突,没有清晰的文档更加大了维护的难度。

而如果使用修改后的MVC模型,每一个Action都只照顾自己相关的逻辑:

另类MVC模式 - 思考和雏形

发表于

这是一篇大逆不道的文章,其作用就是供大家娱乐以及批斗,因为此文所提及的思想,试图改变现有的著名模式MVC的结构,因此如果认为MVC优秀甚至完美的话,还请直接忽略此文,以免影响心情。

本文将提出一种类似MVC但又不完全是现有的经典MVC的模式,该模式仅基于HTTP的Web系统中对经典的MVC模式进行改造,其特点是将View前置,通过View的切分来切分逻辑,形成多次M-V-C交互,最终生成响应

经典MVC模式

对于经典的MVC模式,虽然从表面上看完全是个“不需要解释”的问题,但是每个人的理解又不尽相同。在我的理解中,MVC模式可以用下面这张图来表达:

经典MVC模式

基于HTTP的Web系统里,在经典的MVC模式中,一个请求的处理过程大致分为:

  1. Controller处理原始请求,根据请求的数据与系统的配置,寻找到真正处理该请求的逻辑,称之为Action。
  2. Action处理请求提交的数据,与Model进行交互,获取需要反馈的数据,并将这些数据按View的要求组装后交给View。
  3. View根据Action递交的数据,组装为用户可识别的视图模板,并通过响应传递到客户端进行渲染。

遭遇问题

这个模式一直工作得非常好,我也完全没有理由反驳他存在的意义和优势,直到有一天我发现我的页面有了些奇怪的需求:

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

发表于

时隔N久,继续生产,年底实在忙……

上一篇发表之后,有网友指出这个沙箱过于复杂,事实上一些更简单的代码也能完成同样的效果,例如:

var proxy = {
    document: new DocumentProxy()
};
function(window) {
    // 开发者提交的代码
}.call(proxy, proxy);

通过call和函数参数来劫持thiswindow这两个指向全局对象的变量,以达到屏蔽全局对象的目的。

但是这个方式虽然简单易懂,却有一个非常重要的环节无法照顾到,即全局环境下LexicalEnvironment和VariableEnvironment和ThisBinding均是全局对象,这一特性意味着在全局环境下执行的代码将同时满足以下4个条件:

  1. 通过var x = 3;定义的变量,可以使用console.log(x);输出。
  2. 通过this.x = 3;定义的属性,可以使用console.log(this.x);输出。
  3. 通过this.x = 3;定义的属性,可以使用console.log(x);输出。
  4. 通过var x = 3;定义的变量,可以使用console.log(this.x);输出。

其中第1点和第2点是任何环境下的代码都可以满足的,因此通过call和函数参数来支持全局对象没有问题。但是第3和第4点正是全局环境的特征,并不能简单地通过单层的函数代码来控制。而通过一个with语句则可以得到满足第3点,而第4点则需要一些tricky的手段来实现了,本文将会重点从标准出发来阐述这2点的推理过程。

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

发表于

上一章中,经过对标准的查询、翻阅、解析,终于在绕了一个大圈之后,明白了全局对象的特征,并明确了模拟一个全局对象需要的内容,还为此编写了简单的测试用例。本文将从第1个测试用例着手,放眼与全局作用域的VariableEnvironmentThisBinding以及window别名这三者的一致性,来推导出一个沙箱的原型。

所谓纰漏

在开始这一章以前,首先不得在这里道个歉,原因很简单,我对标准进行了误读,且在没有测试的情况下就开始了这一系列的博客。事实上,以我的思路,是无法制作出一个理想中的沙箱的,虽然可以控制住第三方代码的作用区域,但是无法做到完全的透明,其主要问题出在这里:

var x = 3;
console.log(this.x); // 打印出3

事实上,仅仅依靠Javascript本身,是没有办法在非全局环境下使上面这段代码得到正确的结果的。

因此,在这里,不得不先把标准给改上一改,放弃绝对透明性这一要求,事实上也确实很少有开发者会通过var声明变量并使用this.xwindow.x来访问,不得以之下只能放弃这个需求,将var声明和this.x声明隔离开来。

事实上(开始狡辩),对标准的误读也是学习标准的一种过程,不会犯错的人也很难有太大的进步。我思考良久,最终的决定是不删除这一系列的博文,而是使之继续下去,在修改测试用例的情况下,继续来制作沙箱。当然这个难度要简单不少了。

这一系列已然变成了一个“边研究边写”的“研究笔记”,随着这一系列的进行,必然会出现越来越多的问题,事实上这个沙箱也不见得有可用性。还请以“标准对研究进行指导”这样的角度来看待这个问题吧,得出一个“无角”这样的结论,姑且也算一个结论……

理解VariableEnvironment和ThisBinding

依旧沿用上一章的思路,打开ECMAScript 5的HTML版本,通过CTRL+F组合键打开搜索框,查询VariableEnvironment,可以看到搜索的结果并不多,同样搜索ThisBinding,则发现多数内容与VariableBinding的搜索结果重叠,说明这两者经常放在一块描述。

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

发表于

上一章中对需求进行了分析,并对实现方案有了一定的探讨,最终得出从劫持全局对象这一环节入手的结论,而本文将讨论如何劫持这个全局对象。

全局对象,多数会进行Javascript编程的工程师都知道,在浏览器的执行环境中,叫做window,虽然事实上window和全局对象并不严格意义上是一个东西,不过这对我们的实现并不会造成什么障碍。

剖析全局对象

既然要去做劫持全局对象这样不人道的事,自然先要知道全局对象是个什么东西,有什么样的特点,以便针对目标,一刀致命。这个时候,标准就会成为我们最好的参考,首先打开ECMAScript 5的HTML版本,此后几乎所有的标准引用都会从这里产生。

所谓擒贼先擒王,经典理论告诉我们,不可以把整个洋洋洒洒数万字的标准给全看了,必须以快、狠、准的手段找到我们需要的东西。如果是纸质的书籍,对于一个不熟悉文档结构的人而言自然是无处下手,但好在现在是数字化的时代,我们有一个名为“搜索”的强大功能。

在浏览器中按下CTRL+F的组合键,随后输入Global,看看一共有多少内容。从目录来看,一共有7处内容,确实不多:

  1. 10.2.3 The Global Environment
  2. 10.4. Entering Global Code
  3. 15.1 The Global Object
  4. 15.1.1 Value Properties of the Global Object
  5. 15.1.2 Function Properties of the Global Object
  6. 15.1.4 Constructor Properties of the Global Object
  7. 15.1.5 Other Properties of the Global Object

而这8处内容又可以分为2部分,其中1-3分别介绍了全局对象本身的特点,而4-7则专注于介绍全局对象上的内置属性(即本地对象)的相关内容。仅仅以劫持全局对象为目的的我们,自然可以先不管全局对象上的其它属性,只针对全局对象自身入手,因此将目光放置在前3者之上。

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

发表于

目的

这将是一个系列的文章,大致由4篇博文组成,内容应该会是这样的:

  1. 目的、背景及分析
  2. 劫持全局对象
  3. 劫持全局对象属性
  4. 存在的问题及漏洞

起草这一系列博文的原因有两点,第一自然是因为最近突然有这样的需求,而群里正好也在某个深夜(技术宅基本深夜活动)讨论到了一个类似的话题,给了我一些灵感,所以决定将近期“研究”的成果展示一下。

而第二个目的,也是我想将这么一个简单的东西(其中最终就10几行代码)用整整5个博文的篇幅来说的原因,是因为近期有不少朋友向我强烈地表达了标准的存在意义极其微弱,我能把代码写好就足够的想法,而且现在更是催生了一种叫做jQuery工程师的职位,大有星星之火的样子。

对于这样的态度,作为一个只会理论不懂实践的2B工程师,个人自然是坚持地抱以反击态度的,因此,这一系列的博文,将会从“构建一个沙箱”这样的场景出发,将对于这一话题的研究过程展现于众,期间将频繁涉及到对标准的参阅,以展现标准如何引导问题的解决这一事实。

在阅读这一系列的博文前,还请确认几件事:

  • 你懂英文,因为我不是翻译专家,没能力也没篇幅将标准相关的内容都翻译一下。对于标准,我会将相关的原文引用于此,但肯定是英文的。
  • 你得抱着标准无用论来看待问题,如果你也觉得标准很好用,那还是看最后的成品代码吧,因为其中的过程于你已经失去了大部分的意义。

最后,还是先声明一件事,其实我研究这个问题并没有这么麻烦,因为本系列博文中引用标准的内容几乎全在我的脑子里。我只是将这个思考、查找、得出结论的过程放大、放慢,进行分解,一点一点地通过文字表达出来而已。至于类似“根本不知道标准又怎么会往这方面想”的问题如果出现在你的大脑中,那么恭喜你似乎已经意识到了标准学习的些许价值。