最近在看一些博客的时候,内容经常是来自javascript忍者秘籍,最近虽然一直在面试实习,但是不能把注意力一直放在应付面试上,提升自己的能力也很重要,所以一直抽空看看忍者秘籍。总体来说,这本书,初学者就不用看了,有一定基础之后来看,感悟会比较多。我在这一遍阅读3-5章的过程中,其实并没有很难,有些概念,之前就懂了,这次的阅读,其实更多程度上是对js一个系统的梳理过程。
第三章 函数
函数为什么这么重要?
- 函数可以被调用,通常是异步的。
传统的gui编程界面,通常要做以下几件事:- 创建用户界面
- 进入轮询,等待事件触发
- 调用事件的处理程序(也成为监听器,listener)
而浏览器不同,代码不需要负责事件的轮询和事件派发,而是浏览器帮忙处理,而我们的职责是为浏览器发生的各种事件建立处理的程序handler,这些事件的调用被放置在一个事件队列,然后浏览器调用这些handler
- 函数声明和函数表达式的异同之一:勘误,已经改变。函数表达式也是有name的,但是匿名函数的name是空
function test(){}; var test2 = function(){}; console.log(test2.name);//test2 console.log(test.name);//test (function(){console.log(arguments.callee.name)})();//""
- 函数作用域:
- 变量声明的作用域开始于声明的地方,结束于所在函数的结尾,与代码的嵌套无关
- 函数声明的作用域是指声明该函数的整个函数范围,与代码嵌套无关,
- 对于作用域声明,全局上下文就像一个包含页面所有代码的超大型函数
- this依赖于函数的调用方式
- 作为函数调用
- 作为方法调用
- 作为构造器调用
构造器的特点:- 创建一个空对象
- 构造函数的原型赋值给空对象的原型
- 如果没有显示返回值,新创建的对象将作为构造器的返回值进行返回
- call,apply,bind
可以在回调函数中强制指定上下文,也就是this
第四章 挥舞函数
- 递归的时候,匿名函数起到很大的作用
- 做为对象的属性定义的函数,在递归的时候可以通过this.属性名来避免别的对象的引用递归失败的问题,但是又限制了新对象的属性名必须与之前相同的问题
- 通过内联函数可以很好的解决这个问题。
- 当然,arguments.callee也能很好的解决这个问题,但是callee在严格模式下不能使用,这一点不是很清楚为什么。
- 函数也是对象
很好用的一个特点就是函数也可以像对象那样拥有属性和属性值函数的存储
将函数存储起来是一个常见的场景,通过数组来存储也行,但是并不优雅,而且相同函数的判断也是很尴尬,采取对象存储的方法更为直观高效。- 新建一个对象store,中间的属性有id,cache以及add方法。
- add(someFunc)的时候通过检测someFunc.id是否存在,不存在就增加id属性,同时将其存入cache中。
- 通过对id的判断就可以知道函数有没有添加到cache中
自记忆函数,这种函数能够记住之前的结果,来避免已经执行过的不必要的计算,原理就是暗中为函数增加属性
缓存之前的计算结果- 计算之前检测是否之前计算过,计算过,直接输出结果。没有则进行计算,并将结果进行缓存
优点: - 性能好
- 发生在幕后,开发人员不需要进行相关的特殊操作。
缺点: - 必然牺牲掉内存
- 有些纯粹主义者可能认为一个函数做一件事就可以了,这样容易造成混乱
- 很难测试一个算法的性能
缓存记忆dom元素
通过元素的标签查询dom元素是很常见的操作,但是性能并不是特别好,可以利用缓存来保存已经匹配到的元素,性能提升很大。
function getElements(name){ if(!getElements.cache) getElements.cache = {}; return getElements.cache[name] = getElements.cache[name] || document.getElementsByTagName(name); }
这两个实例可以看出,作为函数的属性来对一些信息进行存储,可以提高性能,同时又不会对别的代码或者全局环境造成干扰,是一个很好的特性。
- 计算之前检测是否之前计算过,计算过,直接输出结果。没有则进行计算,并将结果进行缓存
伪造数组方法,并没有看懂有什么好处。以后再读
首先,数组也是对象,是可以添加属性的。
- 可变长度的参数列表
js一个灵活的特性就是接收任意长度的参数- Math.max,Math.min方法不支持数组形式的参数,怎么办?通过Math.max.apply(null,arr)就可以。apply的一个灵活运用。太简单。
- 函数重载
js没有函数重载,但是通过参数的长度其实是可以实现一些类似重载的功能的。
函数的功能,可以根据输入参数的数目以及类型来进行判断,进而实现意义上的函数重载- 函数的length属性,表明了函数定义的时候的形参个数,而不是arguments.length
通过length属性,可以知道函数定义的形参个数,arguments.length可以知道函数调用的时候的实际参数个数 - addmethod方法的写法,经典,牛逼,闭包的经典应用
- 函数的length属性,表明了函数定义的时候的形参个数,而不是arguments.length
- 判断一个对象是不是函数?
正常情况下:typeof 可以解决问题。但是存在某些浏览器不兼容的问题
可以采用 Object.prototype.toString.call(func) === “[object Function]”来判断,也可以用来验证别的如reg,math等
第五章 闭包
闭包其实不难,重点在于,当函数声明的时候,不仅声明了函数,还确定了它的作用域链,这个作用域链和函数在什么时候执行无关,只和函数定义或者声明的地方有关。这个是理解闭包的关键。当函数执行的时候,根据函数内部定义的变量确定了它内部最优先访问到的变量,如果不存在,会依次向上查找。同时,闭包保存的是变量的引用,而不是变量的值。
闭包的作用
- 私有变量:封装一些信息作为私有变量,只能通过实例的方法对其进行访问。
- 回调和计时器:回调函数通常希望访问一些外部变量,所以可以在回调的外部定义变量,回调的函数因为声明的时候确定外部作用域,自然就将那些变量包含进去,从而在回调中访问到这些变量,计时器的道理与之相同
- 绑定函数上下文:这本书写的时候应该是没有bind函数,所以作者直接写了一个bind,其实也并不难,返回值是一个函数,外部定义了私有变量保留了上下文而已。
- 偏函数应用:就是函数的柯里化,预先填充几个原函数的参数值,返回一个已经包含了这几个参数的新函数。
- 函数记忆:之前的文章其实提到了,就是函数本身也是一个对象,可以有属性,利用这个特性可以缓存之前计算的结果。这里给出了在函数的原型上定义一个memorize的方法,使用函数的时候,调用memorize就可以实现缓存。
这里提出的改进是利用闭包来实现函数的记忆功能:采用立即执行函数将原函数进行打包,从而使用者不必每次调用memorize来进行缓存。
立即执行函数
- 创建临时作用域,将变量名做为参数传递,换成一个短的参数,可以让代码更加简洁
- 循环绑定:利用立即执行函数创建一个闭包从而达到循环绑定想要的效果
- 类库包装:
(function(){ var jQuery = window.jQuery = function(){ // } // }) //方式2 var jQuery = function(){ function jQuery(){ //init } //yewu code return jQuery; }