js语言精粹读书笔记
程序员文章站
2022-07-14 20:45:21
...
- 全书贯穿一个method方法定义新方法:
Function.prototype.method = function(name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
};
- js只有一种数字类型,表示为64位浮点数,没有分离出整数类型,1和1.0的值相同
- Infinity表示所有大于1.79769313486231570e+308的值
- 运算符优先级
- 运算汇总取余向0靠近取整,取模向负无穷靠近取整, 所以js中的模运算符号实际上是取余运算
- js对象属性名字可以为空字符串
- || 一般用来填充默认值,&&可以防止取值undefined的typeError错误
- 原型连接只有在检索的时候才会被用到
- 当函数中有return没有返回一个对象,且这个函数被当做构造函数使用,则return返回this
- 经典递归
// 定义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方法链接起来通常比+运算符链接要快(古老浏览器,现代浏览器已经优化)
未完待续~