10/20
2015

##underscore学习笔记

这里主要记录一些underscore.js-1.8.3源码阅读的时候,感觉比较好的地方。

###1. restArgs

代码:

//生成一个能接受不定参数的函数,并将不定参数转换成一个数组。
var restArgs = function(func, startIndex) {
	startIndex = startIndex == null ? func.length - 1 : +startIndex;
	return function() {
		var length = Math.max(arguments.length - startIndex, 0);
		var rest = Array(length);
		for (var index = 0; index < length; index++) {
			rest[index] = arguments[index + startIndex];
		}
		switch (startIndex) {
			case 0:
				return func.call(this, rest);
			case 1:
				return func.call(this, arguments[0], rest);
			case 2:
				return func.call(this, arguments[0], arguments[1], rest);
		}
		var args = Array(startIndex + 1);
		for (index = 0; index < startIndex; index++) {
			args[index] = arguments[index];
		}
		args[startIndex] = rest;
		return func.apply(this, args);
	};
};

//使用
_.delay = restArgs(function(func, wait, args) {
	return setTimeout(function() {
		return func.apply(null, args);
	}, wait);
});

分析:

利用func.length获取函数的形参数量,由此判断arguments的后面的哪几位是被设为不定参数。

###2. baseCreate

代码:

var Ctor = function(){};
// An internal function for creating a new object that inherits from another.
var baseCreate = function(prototype) {
	if (!_.isObject(prototype)) return {};
	if (nativeCreate) return nativeCreate(prototype);
	Ctor.prototype = prototype;
	var result = new Ctor;
	Ctor.prototype = null;
	return result;
};

分析:

  1. 这个方法里面如果存在nativeCreate即Obejct.create,则使用Obeject.create。
  2. 创建一个临时的函数,设置这个临时函数的prototype为被继承的prototype.(包含constructor);
  3. 这种方式只能继承prototype的方法和变量。

###3 NaN

NaN是js里特殊的一个特殊值,它有一个特性:NaN与任何值总是不相等,包括它自己。也有全局的方法isNaN()来判断一个值是否是NaN。

NaN !== NaN //true 可以用这个特性来判断是否是NaN

###4 +b可以将b转化为数值类型.

+"1" + 1; // 2
1 + "1" //"11"

###5 判断是否是Array

var a = [];
a instanceof Array; //true
Object.prototype.toString.call(a) === [object Array]; //true

利用Object.prototype.toString不仅仅可以判断一个对象是否是Array,String、Number、Date等均可。可以用作偏函数生成器。

###6 html转义

// List of HTML entities for escaping.
var escapeMap = {
	'&': '&amp;',
	'<': '&lt;',
	'>': '&gt;',
	'"': '&quot;',
	"'": '&#x27;',
	'`': '&#x60;'
};
var unescapeMap = _.invert(escapeMap);

// Functions for escaping and unescaping strings to/from HTML interpolation.
var createEscaper = function(map) {
	var escaper = function(match) {
		return map[match];
	};
	// Regexes for identifying a key that needs to be escaped
	var source = '(?:' + _.keys(map).join('|') + ')';
	var testRegexp = RegExp(source);
	var replaceRegexp = RegExp(source, 'g');
	return function(string) {
		string = string == null ? '' : '' + string;
		return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
	};
};
_.escape = createEscaper(escapeMap);
_.unescape = createEscaper(unescapeMap);

###7 template模板

//underscore支持三种模板
_.templateSettings = {
	evaluate: /<%([\s\S]+?)%>/g, //运行js代码
	interpolate: /<%=([\s\S]+?)%>/g, //插入数值
	escape: /<%-([\s\S]+?)%>/g  //转义
};

var noMatch = /(.)^/;

// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
	"'": "'",
	'\\': '\\',
	'\r': 'r', //换行符
	'\n': 'n', //换行符
	'\u2028': 'u2028', //行分隔符
	'\u2029': 'u2029' //行结束符
};

var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;

var escapeChar = function(match) {
	return '\\' + escapes[match];
};

_.template = function(text, settings, oldSettings) {
	if (!settings && oldSettings) settings = oldSettings;
	settings = _.defaults({}, settings, _.templateSettings);
	
	//模板匹配正则表达式
	var matcher = RegExp([
		(settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source
	].join('|') + '|$', 'g');
	//matcher = /<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$/g;

	var index = 0;
	var source = "__p+='"; //__p是最终的要返回页面文本
	//match:整个匹配字符串
	//escape、interpolate、evaluate:子串
	//offset:匹配字符串的开始相对于整个字符串的偏移
	text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
		//非匹配串之外的文本,转换成一行,以免构造函数体时出错。
		source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
		index = offset + match.length; //调整非匹配文本的位置。
		
		if (escape) {
			//把需要转义的字符串赋值给__t
			source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
		} else if (interpolate) {
			//把需要插入的值赋值给__t
			source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
		} else if (evaluate) {
			//直接将匹配的js代码插入source之中
			source += "';\n" + evaluate + "\n__p+='";
		}

		// Adobe VMs need the match returned to produce the correct offset.
		return match;
	});
	source += "';\n";

	// If a variable is not specified, place data values in local scope.
	// 这里有两种方式,指定`setting.variable: 'data'`,在渲染模板中使用`data.prop`;
	// 另外一种方式,默认使用with(obj),在渲染模板中直接使用obj的属性名来调用变量。
	if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';

	//添加render函数的变量声明、返回值。
	source = "var __t,__p='',__j=Array.prototype.join," +
		"print=function(){__p+=__j.call(arguments,'');};\n" +
		source + 'return __p;\n';

	var render;
	try {
		render = new Function(settings.variable || 'obj', '_', source);
	} catch (e) {
		e.source = source;
		throw e;
	}
	
	//构建模板函数的返回值,是render函数。
	var template = function(data) {
		return render.call(this, data, _);
	};

	// Provide the compiled source as a convenience for precompilation.
	var argument = settings.variable || 'obj';
	template.source = 'function(' + argument + '){\n' + source + '}';

	return template;
};
  1. 在evalute匹配串中,其实就是js代码,可以使用任何js语句,只要控制好结构就好。
  2. 非匹配串之外的文本串,要讲其中的换行符转义,转换成一行,以免new Function('obj', source)出错。

以下是一个模板的和它中间产生的render函数:

//模板
<%_.each(datas, function(item){ %>
	<p>index: <%=item.index%></p>
<%});%>

//render函数
function(obj, _ /**/ ) {
	var __t,
		__p = '',
		__j = Array.prototype.join,
		print = function() {
			__p += __j.call(arguments, '');
		};
	with(obj || {}) {
		__p += '\n		';
		_.each(datas, function(item) {
			__p += '\n			<p>index: ' +
				((__t = (item.index)) == null ? '' : __t) +
				'</p>\n		';
		});
		__p += '\n	';
	}
	return __p;
}

其中source在匹配的各个阶段的值:

var source = "__p+='";
匹配三次
第一次:
	 source += text.slice(index, offset).replace(escapeRegExp, escapeChar);;
	 	source = "__p+='";
	 source += "';\n" + evaluate + "\n__p+='"		
	 	source = "__p+='';\n_.each(datas, function(item){\n__p+='";

第二次:
	source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
		source = "__p+='';\n_.each(datas, function(item){\n__p+='<p>index: ";
	source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
		source = "__p+='';\n_.each(datas, function(item){\n__p+='<p>index: '+\n((__t=(item.index))==null?'':__t)+\n'";
第三次:
	source += text.slice(index, offset).replace(escapeRegExp, escapeChar);;
	 	source = "__p+='';\n_.each(datas, function(item){\n__p+='<p>index: '+\n((__t=(item.index))==null?'':__t)+\n'</p>";
	source += "';\n" + evaluate + "\n__p+='"		
	 	source = "__p+='';\n_.each(datas, function(item){\n__p+='<p>index: '+\n((__t=(item.index))==null?'':__t)+\n'</p>';\n});\n__p+='";

source += "';\n";
	source="__p+='';\n_.each(datas, function(item){\n__p+='<p>index: '+\n((__t=(item.index))==null?'':__t)+\n'</p>';\n});\n__p+='';\n"

source = 'with(obj||{}){\n' + source + '}\n'
	source="with(obj||{}){\n__p+='';\n_.each(datas, function(item){\n__p+='<p>index: '+\n((__t=(item.index))==null?'':__t)+\n'</p>';\n});\n__p+='';\n}\n"

source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + 'return __p;\n';
    source="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\nwith(obj||{}){\n__p+='';\n_.each(datas, function(item){\n__p+='<p>index: '+\n((__t=(item.index))==null?'':__t)+\n'</p>';\n});\n__p+='';\n}\nreturn __p;\n"

var render = new Function('obj', source);
作者:teazean 文章地址: