JavaScript Module Loader 实现原理

11年前

前段时间我开始基于SeaJS开发2.0版本的JRaiser,主要目的就是把这个库模块化。结合实际开发过程中遇到的问题,我重新写了一个更符合自身需求的JRaiser Loader以代替SeaJS(另一方面也是为了亲手写一个Loader)。与SeaJS一样,JRaiser Loader也是Wrappings规范(关于AMD与Wrappings的区别,这篇文章有详细说明)的实现,主要接口也与SeaJS保持一致(但功能暂时比SeaJS少)。下面以JRaiser Loader的实现为例介绍一下Loader的实现原理。

先介绍几个术语:

  • 模块:模块化开发中的一个功能单元。它有一个唯一的Id作为标识,并可以依赖于其他模块。
  • 任务:全局require函数的回调函数。在JRaiser Loader的内部处理中,任务也是模块。
  • 就绪:当某个模块已经加载完成,并且不依赖于任何模块或者它依赖的所有模块已经就绪,这个模块就是就绪状态。

在模块化开发中,一个模块可以依赖于任意个模块,而被它依赖的模块又可以依赖于任意个其他模块。这就要求加载模块时必须一层一层把所有依赖的模块都加载进来,类似于树的遍历。由于前端动态加载JS的过程是异步的,也就是说,这是异步的遍历。在算法上,JRaiser Loader采取自顶向下的遍历方式以及自底向上的通知方式。假设有A、B、C三个模块,A依赖于B,B依赖于C。当通过加载A模块时,其加载过程如下(红色箭头部分为异步流程):

加载流程

2822次阅读,7条评论

使用JavaScript获取当前目录的绝对路径

11年前

一谈到路径相关的问题,大家都会往window.location上想,确实这个对象提供了相当多的路径信息,其中常用的就包括:

  • location.href:当前页面的完整URL
  • location.pathname:当前URL中的路径名
  • location.hash:当前URL中的锚点
  • location.search:当前URL中的查询参数

然而,location没有一个属性能直接获得当前目录(不含文件名的绝对路径。通过Google我发现了一些错误的方法,比如说把URL通过“/”分离成数组,把数组的最后一项去掉以后再连接成字符串。但如果URL中没有指定文件名,结果就大错特错了。

根据以往编码的经验,a元素的href属性总是会返回绝对路径,也就是说它具有把相对路径转成绝对路径的能力。使用下面的代码尝试了一下,果然成了:

var a = document.createElement('a');
a.href = './';
alert(a.href);
a = null;
6602次阅读,4条评论

[译]原生全屏JavaScript API

12年前

HTML 5的<video>是一个相当不错的标签,但是它刚发布的时间,最大的问题是它不能像Flash那样实现真正的全屏。幸好,几个月后,大部分浏览器已经原生地支持全屏。

全屏API简史

  1. 第一个原生的全屏接口是在Safari 5.0(和iOS)中添加的 webkitEnterFullScreen() 函数。不过,它只能用于<video>标签。
  2. 在Safari 5.1中,苹果修改了这个API使它更接近于Mozilla的全屏API草案(比苹果的实现更早)。现在,所有DOM元素都可以调用 webkitRequestFullScreen() 方法。
  3. Firefox和Chome表示它们将会添加原生全屏API支持,而且这个特性已经在Chome 15+以及Firefox Nightly中实现。

在2011年10月15日,W3C发布了一份全屏API草案(由Opera团队的一名成员编写),它跟Mozilla的草案有两个主要的不同点:

  1. Mozilla/Webkit使用大写字母'S'(FullScreen),但W3C则不是(Fullscreen);
  2. Mozilla/Webkit使用cancelFullScreen,W3C使用exitFullscreen
9835次阅读,2条评论

使用canvas绘制时钟

12年前

准备工作

在HTML中指定一个区域放置时钟:

<div id="clock" style="position: relative;"></div>

时钟的一些外观设定:

var width = 260; // 桌布宽度
var height= 260; // 桌布高度
var dot = {
	x : width / 2,
	y : height / 2,
	radius : 6
}; // 圆点位置、半径
var radius = 120; // 圆半径
var borderWidth = 6; // 圆边框宽度
9236次阅读,4条评论

[译文]ECMAScript 5严格模式(Strict Mode)

12年前

严格模式(Strict Mode)是ECMAScript 5的新特性,它允许你把整个程序,或者某个函数,放置在“严格”的操作语境中。这种严格的语境会防止某些特定的操作并抛出更多的异常。

虽然ECMAScript 5对ECMAScript 3是向下兼容的,但是在严格模式下,所有在ECMAScript 3中不赞成使用的特性都被禁用(或抛出错误)而不是兼容。

启用严格模式有以下好处:

  • 捕获一些编程错误,并抛出异常。
  • 阻止进行一些相对“不安全”的操作(例如访问全局变量),抛出异常。
  • 禁用一些让人迷惑的特性。

关于严格模式的大多数信息都可以在《ES5规范》[PDF]的第223页找到。

(注意:ECMAScript 5的严格模式跟Firefox的严格模式是不同的)

如何启用严格模式

在程序的开头添加这条语句即可对整段脚本启用严格模式:

'use strict';
2136次阅读,2条评论

GO比分开发总结

12年前

最近一直在忙公司的第一款类客户端产品——GO比分

GO比分

网页具有强大的跨平台特性,HTML+CSS比任何其他界面制作方式都要强大和灵活。然而,网页无法实现Web范围外的一些功能(例如手机通知栏、铃声、震动等)。因此,还需要通过一个代理去调用,也就是客户端。所谓类客户端,就是穿了客户端这件“马甲”的网页

1802次阅读,12条评论

多余的逗号

13年前

JSON

在JSON格式中,逗号是多个属性键值对间的分隔符,例如:

var json = { id: 1, name: 'heero' };

但在编程的时候,很容易会画蛇添足,在最后一对键值对后也加上了逗号

var json = { id: 1, name: 'heero', };

在这种情况下,IE6、7会报错,但IE8以及其他浏览器则没有问题。

1779次阅读,2条评论

Array的push与unshift方法性能分析

13年前

Array的pushunshift方法都能给当前数组添加元素,不同的是,push是在末尾添加,而unshift则是在开头添加。从原理就可以知道,unshift的效率是较低的。原因是,它每添加一个元素,都要把现有元素往下移一个位置。但到底效率差异有多大呢?下面来测试一下。

测试环境的主要硬件:CPU T7100(1.8G);内存4G DDR2 667;硬盘5400转。主要软件:操作系统为Windows 7;浏览器为Firefox 3.6.9。测试代码:

var arr = [ ], s = +new Date;

// push性能测试
for (var i = 0; i < 50000; i++) {
  arr.push(i);
}

console.log(+new Date - s);

s = +new Date;
arr = [ ];

// unshift性能测试
for (var i = 0; i < 50000; i++) {
  arr.unshift(i);
}

console.log(+new Date - s);
2191次阅读,2条评论

前端JavaScript模板引擎

13年前

说起模板,很多人会认为这是后台的东西(如PHP的Smarty、Java的Velocity),跟前端没有关系。然而,随着前端的逻辑变得越来越复杂,引入模板引擎已经是非常必要了。

模板引擎的主要功能就是把变化的数据融入到不变的模板中,并生成最终结果。目前,前端的主要数据格式无非是XMLJSON

如果选择XML作为数据格式,XSLT就是最佳的模板语言,但XML+XSLT的缺点非常明显:

  • 兼容性问题。XML+XSLT在不同浏览器下的转换方式有所不同。
  • XML、XSLT的语法都是极其冗余的,数据量相对较大。

如果选择JSON作为数据格式,就没有原生的模板语言可用了,只能生拼字符串。例如,把下面代码中的data转换成一个表格:

var data = [
	{ id : 1, name : 'Google', url : "google.com" },
	{ id : 2, name : '百度', url : "baidu.com" },
	{ id : 3, name : '有道', url : "youdao.com" }
], html = [ ];

html.push('<table>');
for (var i = 0; i < data.length; i++) {
	html.push('<tr>');
	html.push('<td>', data[i].id, '</td>');
	html.push('<td><a href="http://', data[i].url, '" target="_blank">', data[i].name, '</a></td>');
	html.push('</tr>');
}
html.push('</table>');

document.write(html.join('\r\n'));
5181次阅读,7条评论

再论JavaScript的类继承

13年前

说到JavaScript的类继承,就必然离不开原型链,但只通过原型链实现的继承有着不少缺陷。

无参数类继承的问题

先看一段示例代码,实现B继承于A:

function A() {
}
A.prototype.a1 = function() { };

function B() {
}
B.prototype = new A();
B.prototype.b1 = function() { };

var b = new B();
alert(b.constructor == A); // true
alert(b.constructor == B); // false

这段代码的主要问题是:

  • 需要实例化A作为B的原型,此时就执行了A的构造函数。但按照面向对象的规则,实例化B之前,B及其父类A的构造函数都不应该执行
  • 更改了B的prototype,导致b.constructor不是B而是A
856次阅读,2条评论