对于大名鼎鼎的 ZenCoding 来说,做前端的同学没有没听过,没用过的,如果你没用过的话,那么你就太 Out 了,不过以后如果没有用过的话,那也不算什么。

以为 ZenCoding 宣布停止更新了。

话说 ZenCoding 确实挺好用的,自从换了 SubLime Text 2 之后,加上这个 ZenCoding 写前端代码如飞一般的流畅,但是今天像往常一样打开 S 之后,却多出了一个关于 Package 的标签页,说的就是 ZenCoding 停止更新的事情。

不过不用担心,在 ZenCoding 停止更新之后,还有另一个项目上线了,貌似其实只是 ZenCoding 改了个名字的项目,那就是 Emmet。

在这个标签页中,ZenCoding 说了不但换了名字,而且功能有了一定的增强。反正就是比之前的强大了不少。

对于 Sublime Text 2 来说,直接在安装包管理器中安装就可以了。不过要先卸载掉 ZenCoding。

1. Ctrl + Shift + P

2. Remove Package

3. ZenCoding

4. 重复 1

5. Install Package

6. Emmet

回车,就会自动安装好的。使用方式我还没有试过呢,应该和 ZenCoding 一样一样的。

对了,新项目主页:http://docs.emmet.io

貌似我也应该弄一个这个个性的域名?!

比onload更快获取图片尺寸

lightbox类效果为了让图片居中显示而使用预加载,需要等待完全加载完毕才能显示,体验不佳(如filick相册的全屏效果)。javascript无法获取img文件头数据,真的是这样吗?本文通过一个巧妙的方法让javascript获取它。

这是大部分人使用预加载获取图片大小的例子:

var imgLoad = function (url, callback) {
	var img = new Image();

	img.src = url;
	if (img.complete) {
		callback(img.width, img.height);
	} else {
		img.onload = function () {
			callback(img.width, img.height);
			img.onload = null;
		};
	};

};

可以看到上面必须等待图片加载完毕才能获取尺寸,其速度不敢恭维,我们需要改进。

web应用程序区别于桌面应用程序,响应速度才是最好的用户体验。如果想要速度与优雅兼得,那就必须提前获得图片尺寸,如何在图片没有加载完毕就能获取图片尺寸?

十多年的上网经验告诉我:浏览器在加载图片的时候你会看到图片会先占用一块地然后才慢慢加载完毕,并且不需要预设width与height属性,因为浏览器能够获取图片的头部数据。基于此,只需要使用javascript定时侦测图片的尺寸状态便可得知图片尺寸就绪的状态。

当然实际中会有一些兼容陷阱,如width与height检测各个浏览器的不一致,还有webkit new Image()建立的图片会受以处在加载进程中同url图片影响,经过反复测试后的最佳处理方式:

// 更新:
// 05.27: 1、保证回调执行顺序:error > ready > load;2、回调函数this指向img本身
// 04-02: 1、增加图片完全加载后的回调 2、提高性能

/**
 * 图片头数据加载就绪事件 - 更快获取图片尺寸
 * @version	2011.05.27
 * @author	TangBin
 * @see		http://www.planeart.cn/?p=1121
 * @param	{String}	图片路径
 * @param	{Function}	尺寸就绪
 * @param	{Function}	加载完毕 (可选)
 * @param	{Function}	加载错误 (可选)
 * @example imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {
		alert('size ready: width=' + this.width + '; height=' + this.height);
	});
 */
var imgReady = (function () {
	var list = [], intervalId = null,

	// 用来执行队列
	tick = function () {
		var i = 0;
		for (; i < list.length; i++) {
			list[i].end ? list.splice(i--, 1) : list[i]();
		};
		!list.length && stop();
	},

	// 停止所有定时器队列
	stop = function () {
		clearInterval(intervalId);
		intervalId = null;
	};

	return function (url, ready, load, error) {
		var onready, width, height, newWidth, newHeight,
			img = new Image();
		
		img.src = url;

		// 如果图片被缓存,则直接返回缓存数据
		if (img.complete) {
			ready.call(img);
			load && load.call(img);
			return;
		};
		
		width = img.width;
		height = img.height;
		
		// 加载错误后的事件
		img.onerror = function () {
			error && error.call(img);
			onready.end = true;
			img = img.onload = img.onerror = null;
		};
		
		// 图片尺寸就绪
		onready = function () {
			newWidth = img.width;
			newHeight = img.height;
			if (newWidth !== width || newHeight !== height ||
				// 如果图片已经在其他地方加载可使用面积检测
				newWidth * newHeight > 1024
			) {
				ready.call(img);
				onready.end = true;
			};
		};
		onready();
		
		// 完全加载完毕的事件
		img.onload = function () {
			// onload在定时器时间差范围内可能比onready快
			// 这里进行检查并保证onready优先执行
			!onready.end && onready();
		
			load && load.call(img);
			
			// IE gif动画会循环执行onload,置空onload即可
			img = img.onload = img.onerror = null;
		};

		// 加入队列中定期执行
		if (!onready.end) {
			list.push(onready);
			// 无论何时只允许出现一个定时器,减少浏览器性能损耗
			if (intervalId === null) intervalId = setInterval(tick, 40);
		};
	};
})();

调用例子:

imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {
	alert('size ready: width=' + this.width + '; height=' + this.height);
});

是不是很简单?这样的方式获取摄影级别照片尺寸的速度往往是onload方式的几十多倍,而对于web普通(800×600内)浏览级别的图片能达到秒杀效果。看了这个再回忆一下你见过的web相册,是否绝大部分都可以重构一下呢?好了,请观赏令人愉悦的 DEMO :

http://www.planeart.cn/demo/imgReady/

(通过测试的浏览器:Chrome、Firefox、Safari、Opera、IE6、IE7、IE8)

转载自:planeArt

常用的 JavaScript 检测浏览器为 IE 是哪个版本的代码。包括是否是最人极端厌恶的 ie6 识别与检测。

var isIE=!!window.ActiveXObject;
var isIE6=isIE&&!window.XMLHttpRequest;
var isIE8=isIE&&!!document.documentMode;
var isIE7=isIE&&!isIE6&&!isIE8;
if (isIE){
    if (isIE6){
        alert("ie6");
    }else if (isIE8){
        alert("ie8");
    }else if (isIE7){
        alert("ie7");
    }
}

然后是一个稍微短的 js 判断是否 ie 的方法:
这个貌似是利用 IE 中 JScript所特有的条件编译(或曰条件注释)来区分 IE 和非 IE(这里的IE/非IE均是针对内核而言,以IE为内核的浏览器将视为IE)

var ie = 0/*@cc_on+1@*/;

一个最短的 js 判断 ie 或非 ie 的代码,大小仅仅 7bytes:

var ie = !+'\v1';

2010年1月,一位俄国人利用了IE与标准浏览器在处理数组的toString方法的差异,仅6bytes就完美完成了是否IE浏览器的检测:

var ie = !-[1,];

利用这些发现,我们可以使代码写的更短。如今检测是否为 IE6 其实可以写成:

var ie6=!-[1,]&&!window.XMLHttpRequest;

之前那大段累赘的分析 navigator,然后正则比较的方式,后面的 js 方法是不是效率更高了!

来源:
1. http://biancheng.dnbcw.info/javascript/101495.html
2. http://dean.edwards.name/weblog/2007/03/sniff/
3. http://feilong.org/shortest-ie6-judge-means

目前为止,我们的例子中提到过很多次 this 以及函数了。例如 Function.prototype.apply 和 Function.prototype.call 他们都有各自的优势,但是,在讨论这门语言的其他方面之前,先来看看 this 关键字和执行上下文。

方法和函数

方法在ECMAScript规范中被定义如下:

……函数是属性的值。

注:当一个函数被当做对象的方法调用的时候,此时,函数的值就会被当做对象的值。

当被当做对象的方法调用的时候,也可以将函数当做单独的子程序,这在社区中,也是被普遍使用的。

执行上下文(Execution Context)

ECMAScript 5.1 规定,执行中的程序形成执行上下文。此执行上下文支持此语言的某些结构,而这些结构就是需要管理的范围–来自于其他部分的可见变量和函数声明。

此执行上下文包含一个引用到各种可用状态元素的处于当前逻辑范围内的被管理的程序。更多详细信息,请访问 执行上下文

全局上下文和构造函数 (this)

运行下面的代码:

function add(x, y) {
  console.log(this);
  return x + y;
}

add(1, 1);

第二行代码将会是一个全局变脸被现实:window ,但是,如果在第一行之后增加 use strict 的话,将会返回 undefined .

现在思考一下,当构造函数被执行的时候,发生了神马?

当作为 new Shape() 的结果,进入 Shape 构造函数的时候,this 的值就会使当前对象,也就是此处的 shape 。请记住下面的话,如果想写面向对象的程序的话:如果一个函数被作为一个对象的方法调用,或者使用 new 关键字,此时, this 的值就是对象的当前实例,否则就是全局上下文 window 或者 undefined 。但是此处要讨论的是,this 的值,取决于如何调用函数。

一般可能犯得错误

小心的对待嵌套在方法中的函数,例如下面的例子:

function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype = {
  move: function(x, y) {
    this.x += x;
    this.y += y;

    function checkBounds() {
      if (this.x > 100) {
        console.error('Warning: Shape out of bounds');
      }
    }

    checkBounds();
  }
};

var shape = new Shape();
shape.move(101, 1);

checkBounds 函数被定义在 move 方法内部,但是 console.error 此行代码,却永远不会被运行到,这是因为 checkBounds 函数中的 this 的值是全局上下文 window 而不是此处的 shape。因为虽然函数在 move 方法内部,但执行函数的时候,函数是单独的子程序,所以 this 还是指向 window。而 window.x 没有定义,所以永远执行不到。但是此时就可能混淆,所以带来错误。

但是,我们可以将 this 赋值给其他变量,从而可以正常使用。请注意有 self 的两行代码。如何修改的。

function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype = {
  move: function(x, y) {

    var self = this;

    this.x += x;
    this.y += y;

    function checkBounds() {
      if (self.x > 100) {
        console.error('Warning: Shape out of bounds');
      }
    }

    checkBounds();
  }
};

var shape = new Shape();
shape.move(101, 1);

现在,当执行 move 方法的时候,self 代替了 this,从而可以正常的执行了。

总结

使用 this 的时候,请记住以下三点:

1. 取决于函数被以何种方式调用,new MyConstructor 和 myObject.method() 将被指向其实例,而当 this 在函数中的话,将会指向 window 。
2. 严格模式下,this 将会是 undefined 而不是 window 。
3. this 可以赋值给任意合法变量,但是一般 self 用的比较多。

ECMAScript 5.1 规范定义了调用内建构造函数时候的行为,例如,不仅 new Array(1,2) 是正确的,直接使用 Array(1,2) 也是正确的。也就是说这两种方式都可以用,但是结果却不同,第一种创建并初始化一个新的数组对象,而第二种则返回一个具有那两个参数的表达式,但是其构造函数必须是函数才行。

例如,Object() 可以进行数据类型的转换,而 String() 也可以,例如:String();

var a = new String(1);
console.log(a);
// { '0': '1' }
var b = String(1);
console.log(b);
// '1'

Date() 构造函数也可以进行类型转换,但是这样有时候有比较混乱,尤其是对那些只想让日期可读的初学者。例如,

var a = new Date(2012, 0, 1);
// Sun, 01 Jan 2012 00:00:00 GMT
Date(2012, 0, 1)
// 'Sun Jun 10 2012 11:28:03 GMT+0100 (BST)'

第一段代码返回来日期对象,而第二段代码返回了原始的日期字符串。

从构造函数返回的对象

在规范的 13.2.2 节,从构造函数返回的对象的行为被定义为:如果 使用 Type(result) 来检测返回的是 object 的话,则直接返回 result。

这使得构造函数返回另一个对象而不是一个实例。

function Shape() {
  return {
    x: 1, y: 1
  };
}

Shape.prototype = {
  move: function() {}
};

var shape = new Shape();
shape.move();
// TypeError: Object #<Object> has no method 'move'

返回没有此方法的结果,因为 shape 返回 {x: 1, y: 1} ,而其没有 move() 方法,可以使用 instanceof 来测试是否构造函数是作为构造函数来调用的。

function Shape() {
  if (this instanceof Shape) {
    // An object is being instantiated
  } else {
    return {
      a: 'b'
    };
  }
}

Shape.prototype = {
  move: function() {}
};

var shape = new Shape();
shape.move(); //instance nothing returned

Shape(); // Returns { a: 'b' }

jQuery 也使用了类似的方法来实例化 jQuery.Event 对象,而不需要 new 关键字。

jQuery.Event = function( src, props ) {
  // Allow instantiation without the 'new' keyword
  if ( !(this instanceof jQuery.Event) ) {
    return new jQuery.Event( src, props );
  }

此工厂式的行为也可能适合某些API – Dojo 在 NodeList 也使用了它。

结论

调用内置构造函数时,如果不使用关键字,则是类型转换,但是某些构造函数的行为又好像使用了 new 关键字,为了避免因为丢掉 new 关键字而造成的 bug ,所以,还是要牢记,加 new 关键字,和不加,是不一样的。

所以,当编写构造函数的时候,这个行为可以被用来创建类工厂式的 API,这是因为一个构造函数被当作函数调用的话,其行为可以被检测得到。

想了解更多这方面的知识,请在 ECMAScript 规范中搜索 called as a function 。

这一系列文章都讲述的是关于使用 JavaScript 操作文件相关的知识,其中最重要的是 File 对象,而实际上 file 对象只是 blob 对象的一个更具体的版本,blob 存储着大量的二进制数据,并且 blob 的 size 和 type 属性,都会被 file 对象所继承。

所以,可以说,在大多数情况下,blob 对象和 file 对象可以用在同一个地方,例如,可以使用 FileReader 借口从 blob 读取数据,也可以使用 URL.createObjectURL() 从 blob 创建一个新的 URL 对象。

Slicing (分割)

通过 Blob 对象可以做的一件有趣的事情就是可以创建一个子 Blob 对象,其实就是可以将其分割(file 对象也可以)。由于每个 Blob 对象都是通过指针指向数据的而不是指向数据本身,因此可以快速的创建指向其他子部分的新的 Blob 对象,这里就需要使用 slice() 方法了。是不是和 JavaScript 的 slice() 方法很象,其实差不多。

此方法接受三个参数,起始偏移量,结束偏移量,还有可选的 mime 类型。如果 mime 类型,没有设置,那么新的 Blob 对象的 mime 类型和父级一样。

不过目前浏览器实现此方法还没有统一,火狐使用的是 mozSlice() ,Chrome 使用的是 webkitSlice() ,其他浏览器则正常的方式 slice() 。重写的兼容各个浏览器的例子如下:

function sliceBlob(blob, start, end, type) {

    type = type || blob.type;

    if (blob.mozSlice) {
        return blob.mozSlice(start, end, type);
    } else if (blob.webkitSlice) {
        return blob.webkitSlice(start, end type);
    } else {
        throw new Error("This doesn't work!");
    }
}

当要上传大文件的时候,此方法非常有用,可以将大文件分割分段,然后各自上传,因为分割之后的 Blob 对象和原始的是独立存在的。例如,Flickr 的工程师就使用此方法将照片中的需要使用的 exif 信息截取出来,而不是等到其传到服务器上之后,才处理的,并且一旦选择要上传照片,则同时传输文件数据和 Exif 数据,这样几乎就可以在上传照片的时候同时显示照片的信息了。

使用旧方法创建 Blob 对象

当 File 对象开始出现在浏览器中的时候,开发者们意识到 Blob 对象太强大了,都想可以在用户不干预的情况下,创建 Blob 对象,毕竟任何数据都可以用 Blob 对象表示,不用非要和文件产生关系。通过 BlobBuilder 创建一个包含有特定数据的 Blob 对象,然后浏览器快速响应即可。(不过目前其还不统一,Firefox:MozBlobBuilder, Internet Explorer 10:MSBlobBuilder,Chrome:WebKitBlobBuilder)。

例子:

var builder = new BlobBuilder();
builder.append("Hello world!");
var blob = builder.getBlob("text/plain");

BlobBuilder() 创建一个新实例,并且使用一个 append() 方法,将字符串(或者 ArrayBuffer 或者 Blob,此处用 string 举例)插入,一旦数据插入成功,就可以使用 getBlob() 方法设置一个 mime 。

并且 BlobBuilder() 还可以处理动态数据,例如 worker 中的数据等,这里就不翻译了。

使用新方法创建 Blob 对象

因为开发者一直想要能够直接创建 Blob 对象,因此浏览器实现了 BlobBuilder(); Blob 作为构造函数而存在,而且,此构造函数也已经被作为标准了,其也是今后创建 Blob 对象的方式。

Blob()–构造函数,接受两个参数,第一个为一个数据序列,可以是任意格式的值,例如,任意数量的字符串,Blobs 以及 ArrayBuffers。第二个参数,是一个包含了两个属性的对象,其两个属性分别是:

type — MIME 的类型。

endings — 决定 append() 的数据格式,(数据中的 \n 如何被转换)可以取值为 “transparent” 或者 “native”(t* 的话不变,n* 的话按操作系统转换;t* 为默认) 。

例子:

var blob = new Blob(["Hello world!"], { type: "text/plain" });

可以看到,此方法比 BlobBuilder() 简单多了。

但是,此 Blob 构造函数还没有被一些浏览器实现,目前只有某些版本的 Chrome 和 Firefox 实现了。而且剩余的浏览器也没有确定什么时候实现,但是其现在作为 File API 的一部分,将来应该会被统一实现的。

全文完。