《JavaScript设计模式与开发实践》读书笔记之基础部分

393 阅读5分钟

几个名词

  • 鸭子模型:通俗的讲就是如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。

    鸭子模型主要是指导我们只关注对象的行为,而不是关注对象本身

  • 多态:同一操作作用于不同对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送不同的消息,这些对象会根据这个消息分别给出不同的反馈

    多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来

老生常谈之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

callapplay的几个用法和场景

  1. 改变this的指向

    var obj1 = {
      name: 'obj1'
    }
    
    var obj2 = {
      name: 'obj2',
      getName: function(){
        return this.name
      }
    }
    
    obj2.getName() // obj2
    obj2.getName.apply(obj1) // obj1

  2. 对不支持bind的低版本浏览器提供兼容方式

    Function.prototype.bind(content){
      var _self = this;
      return function(){
        _self.apply(content,arguments)
      }
    }

  3. 借用其他对象的方法

    最常用的就是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. 函数可以作为参数被传递
  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

高级函数的其他的一些应用

  1. 函数柯里化(function currying)

    函数柯里化又称部分求值,一个柯里化的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值

  2. 函数反柯里化(uncurrying)

  3. 函数节流

    函数被频繁调用(resizemousemove等事件),影响性能时,需要人为的限制函数的执行频率,这种函数一般被称为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)
  4. 分时函数

    用于解决短时间内向内面插入大量的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)
      }
    }
  5. 惰性加载函数

    在函数执行时,需要通过if语句判断函数的执行分支,为提高效率,可以使用惰性加载函数

文章连接