几个名词
鸭子模型:通俗的讲就是如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。
鸭子模型主要是指导我们只关注对象的行为,而不是关注对象本身
多态:同一操作作用于不同对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送不同的消息,这些对象会根据这个消息分别给出不同的反馈
多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来
老生常谈之this,apply,call
this指向
普通的方法定义之后,方法中的this指向,是取决于方法执行的环境动态绑定的,而非方法被声明时的环境。在es6中,使用箭头函数,可以将this固定下来,this永远指向的箭头函数声明时的this的指向。但是箭头函数不能作为构造函数使用,不能使用arguments对象
作为对象的方法调用
作为普通函数调用
var obj = { name: 'javascript', getName: function(){ return this.name } } obj.getName() // javascript 作为对象的方法进行调用,指向该对象 var getName = obj.getName getName() // undefined // 将对象的方法重新复制给新的变量, // 然后执行,getName作为普通函数调用,指向全局的name<html> <body> <div id="div1"> 我是一个div </div> </body> <script> window.id = 'window'; document.getElementIdById('div1').onclick = function(){ alert(this.id) // div1 var callback = function(){ alert(this.id) } callback() // window 作为普通方法进行调用,指向this全局的window对象 } </script> </html>
构造器调用
使用
new关键字生成一个对象,并将其构造函数的this都指向这个生成的对象Function.prototype.call和Function.prototype.apply调用动态改变this的指向
“高逼格”的call,apply
call和applay的几个用法和场景
改变this的指向
var obj1 = { name: 'obj1' } var obj2 = { name: 'obj2', getName: function(){ return this.name } } obj2.getName() // obj2 obj2.getName.apply(obj1) // obj1
对不支持bind的低版本浏览器提供兼容方式
Function.prototype.bind(content){ var _self = this; return function(){ _self.apply(content,arguments) } }
借用其他对象的方法
最常用的就是
arguments借用Array的方法(function(){ Array.ptototype.push(arguments,4) console.log(arguments) // [1,2,3,4] })(1,2,3)
又是闭包,还有高阶函数
通俗易懂的闭包
没搞懂闭包肯定是因为没太理解js的变量声明周期。
- 全局变量:变量的生命周期一直存在,不会被销毁,除非我们主动去销毁该变量
- 局部变量:局部变量一般存在在函数体内(es6有块级作用域),当局部变量的函数执行直接之后,该变量也就被销毁了。
但是局部变量存在一种情况,在函数执行完之后不会被销毁,那就是该局部变量还存在着引用(其他地方还在用这个变量),此时就出现了闭包。该局部变量有留下来的理由了,所以也就延续下来了。
说到闭包,必会说到闭包的缺点——造成内存泄漏,当然这个内存泄漏时在使用闭包不当的时候才会出现这个问题,这个问题在本书的3.1.6章节中,作做了比较详细的解释(作者漂亮的甩了“一波锅”)。有兴趣的可以自己翻阅。
举一个简单的闭包的例子
function count(){
var a = 0;
return function(){
return a++;
}
}
var conutNum = count()
conutNum() // 0
conutNum() // 1
conutNum() // 2没那么神秘的高阶函数
满足了一下条件之一的就是高阶函数:
- 函数可以作为参数被传递
- 函数可以作为返回值输出
函数作为参数被传递
平时使用的回调函数都算。
再举个例子:
// 从小到大排序 [1,3,2,5,4].sort(function(a,b){ return a-b }) // 从大到小排序 [1,3,2,5,4].sort(function(a,b){ return b-a })函数作为返回值输出
大部分的闭包都算。
再举个单例模式的例子:
var getSingle = function(fn){ var ret; return function(){ return ret || ret = fn.apply(this,arguments) } } var createScript = getSingle(function(){ document.createElemet('script') }) var script1 = createScript() var script2 = createScript() script1 === script2 // true
高级函数的其他的一些应用
函数柯里化(function currying)
函数柯里化又称部分求值,一个柯里化的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值
函数反柯里化(uncurrying)
函数节流
函数被频繁调用(
resize,mousemove等事件),影响性能时,需要人为的限制函数的执行频率,这种函数一般被称为throttle函数var throttle = function(fn, interval){ var _self = fn, timer, firstTime = true; return function(){ var args = arguments, _me = this; if(firstTime){ _self.apply(_me, args); retutn firstTime = false; } if(timer){ return false } timer = setTimeout(function(){ clearTimeout(timer) timer = null _self.apply(_me, args) }, interval || 500) } } window.resize = throttle(function(){ console.log(1); }, 1000)分时函数
用于解决短时间内向内面插入大量的DOM节点,导致页面渲染卡顿、假死的问题。
/** * @params ary 用于渲染dom节点的数据 * @parmas fn 渲染dom节点的业务逻辑 * @params count 一次渲染dom节点的数量 */ var timeChunk = function(ary, fn, count){ var obj, t; var start = function(){ for(var i = 0; i<Matn.min(count || 1, ary.length); i++){ var obj = ary.shift(); fn(obj) } } return function(){ t = setInterval(function(){ if(ary.length === 0){ return clearInterval(t) } start() }, 200) } }惰性加载函数
在函数执行时,需要通过if语句判断函数的执行分支,为提高效率,可以使用惰性加载函数