《javascript语言精粹》读书笔记

245 阅读4分钟
  1. 全书贯穿一个method方法定义新方法:
Function.prototype.method = function(name, func) {
	if (!this.prototype[name]) {
		this.prototype[name] = func;
	}
	return this;
};
  1. js只有一种数字类型,表示为64位浮点数,没有分离出整数类型,1和1.0的值相同
  2. Infinity表示所有大于1.79769313486231570e+308的值
  3. 运算符优先级 这里写图片描述
  4. 运算汇总取余向0靠近取整,取模向负无穷靠近取整, 所以js中的模运算符号实际上是取余运算
  5. js对象属性名字可以为空字符串
  6. || 一般用来填充默认值,&&可以防止取值undefined的typeError错误
  7. 原型连接只有在检索的时候才会被用到
  8. 当函数中有return没有返回一个对象,且这个函数被当做构造函数使用,则return返回this
  9. 经典递归
// 定义walk_the_DOM函数,它从某个指定的节点开始,按照html源码中的顺序访问该树的每一个节点
// 它会调用一个函数,并依次传递每个节点给它,walk_the_DOM调用自身去处理每一个子节点

var walk_the_DOM = function walk(node, func) {
	func(node);
	node = node.firstChild;
	while (node) {
		walk(node);
		node = node.nextSibling;
	}
};

// 定义 getElementsByAttribute 函数。它以一个属性名称字符串
// 和一个可选的匹配值作为参数
// 它调用walk_the_DOM,传递一个用来查找节点属性名的函数作为参数。
// 匹配的节点会累加到一个数组中

var getElementsByAttribute = function(att, value) {
	var result  = [];
	walk_the_DOM(document.body, function(node) {
		var actual = node.nodeType === 1 && node.getAttribute(att);
		if (typeof actual === 'string' && (actual === value || typeof value !== 'string')) {
			result.push(node)
		}
	});
	return results;
};

11 一些语言提供了尾递归,如果一个函数返回资深的递归调用结果,调用过程会被替换成一个循环,可以显著提高速度,但是js并么有提供尾递归优化。 12 闭包

// 糟糕的例子
// 构造一个函数,用错误的方式给一个数组中的节点设置事件处理程序。
// 当点击一个节点时候,按照预期,应该弹出一个对话框显示一个节点的序号
// 但是它总会显示节点的个数
var add_the_handlers = function (nodes) {
	var i;
	for (i = 0; i < nodes.length; i++) {
		nodes[i].onclick = function() {
			alert(i);
		};
	}
}

用闭包改良后的例子,与之前的例子相比,这边使用了闭包返回了一个函数,在绑定事件时候执行helper函数保证事件函数变量i的值是函数构造时候的值。 其次,我们应该避免在循环中创建函数,引起无谓的计算,影响性能。

// 改良后的例子
// 构造一个函数,用正确的方式给一个数组中的节点设置事件处理程序
// 点击一个节点,将会弹出一个对话框显示节点的序号
var add_the_handlers = function (nodes) {
	var helper = function (i) {
		return function(e) {
			alert(i);
		};
	};
	var i;
	for (i = 0; i < nodes.length; i++) {
		nodes[i].onclick = helper(i);
	}
}

13 模块的一般形式:利用闭包可以创建可以访问私有变量和内部函数的特权函数,最后返回这个特权函数; 14 柯里化 允许我们把函数与传递给它的参数相结合产生一个新的函数

Function.method('curry', function() {
	var slice = Array.prototype.slice,
	that = this;
	return function () {
		return that.apply(null, args.concat(slice.apply(arguments)))
	};
})

15 函数记忆,理解为缓存

// 斐波那契数列,糟糕的写法
// 我们调用了11次,它自身调用442次计算
var fibonacci = function (n) {
	return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};
for (var i = 0; i <= 10; i += 1) {
	document.writeln('//' + i + ':' + fibonacci)
}
// 0: 0
// 1 : 1
// 2 : 1
// 3 : 2
// ...
// 10 : 55
// 改良后的写法, 利用闭包存储
var fibonacci = function () {
	var memo = [0, 1];
	var fib = function(n) {
		var result = memo[n];
		if (typeof result !== 'number') {
			result = fib(n - 1) + fib (n - 2);
			memo[n] = result;
		}
		return result;
	};
	return fib;
}();

提取记忆函数

var memoizer = function (memo, formula) {
	var recur = function() {
		var result = memo[n];
		if (typeof result !== 'number') {
			result = formula(recur, n);
			memo[n] = result;
		}
		return result;
	};
	return recur;
};

改良后的fibonacci:

var fibonacci = memoizer([1, 1], function(recur, n) {
	return recur(n - 1) * recur(n - 2)
})

阶乘:

var factorial = memoizer([1, 1], function(recur, n){
	return n * recur(n-1)
});

16 继承

Function.method('inherits', function(Parent) {
	this.prototype = new Parent();
	return this;
})

使用伪类的缺点:

  • 没有私有环境,所有属性都是公开的
  • 无法访问父类的方法
  • 使用构造器函数忘记new函数的时候,this就绑定在了全局变量上,污染全局,没有任何错误提示

17 纯粹的原型模式中,我们会摒弃嘞,转而专注对象。差异化继承:

var myMammal = {
	name: 'herb the name',
	says: function() {
		this.saying || '',
	},
};

var myCat = Object.create(myMammal);
myCat.name = 'Tom';
myCat.saying = 'meow';

17 判断是否为数组的方法:

var is_array = function () {
	return Object.prototype.toString.apply(value) === '[Object Array]'
}

18 如果想把大量的字符串片段组成一个字符串,将这些片段放到一个数组中并用join方法链接起来通常比+运算符链接要快(古老浏览器,现代浏览器已经优化)

未完待续~