JS學習筆記11_高級技巧

1.類型檢測

typeof有時返回值不合理,好比RegExp對象返回object,測試代碼:javascript

var regex = /^what$/i;
regex = new RegExp('^what$');
alert(typeof regex);

instanceof在頁面有多個frame時用不了,來自不一樣frame的對象instanceof返回falsejava

能夠用Object.prototype.toString.call(value) === ‘[object Array/Function...]’來作類型檢查,也能夠用來區分原生對象和自定義對象,例如:數組

[object JSON]//原生JSON對象
[object Object]//自定義JSON對象

注意:IE中以COM對象形式實現的函數對象toString不會返回[object Function]瀏覽器

2.做用域安全的構造函數

用new操做符調用構造函數會給新建立的對象添加屬性,而直接調用構造函數會給全局對象window添加屬性安全

爲了不污染全局做用域,能夠用以下構造函數:app

/* 可能會污染全局做用域
function Student(name){
  this.name = name;
}
*/
//做用域安全的構造函數
function Student(name){
  if(this instanceof Student){
    this.name = name;
  }
  else{
    return new Student(name);
  }
}

上面的構造函數可以避免直接調用構造函數給全局對象意外添加屬性,但用這種方式實現繼承可能會出現問題,類型檢查可能致使繼承失敗函數

3.惰性載入(避免重複分支檢測)

  1. 在第一次執行分支檢測時,覆蓋原有函數,例如:性能

    function detect(){
      if(...){
        detect = function(){
          //
        }
      }
      else if(...){
        detect = function(){
          //
        }
      }
      else...
    }
  2. 能夠用匿名函數當即執行並返回匿名函數來實現惰性載入,例如:測試

    var detect = (function(){
      if(...){
        return function(){
          //
        }
      }
      else if(...){
        return function(){
          //
        }
      }
      else...
    })();

第一種方式第一次調用時損失性能,之後調用不會損失性能;第二種方式在第一次調用時也不會損失性能,由於把時耗放到了第一次載入代碼時this

4.函數綁定(指定執行環境)

能夠用下面的函數給函數指定執行環境並創造新函數:

function bind(fun, context){
  return function(){
    return fun.apply(context, arguments);
  }
}

能夠方便地根據已有函數生成新函數,[IE9+]有原生的bind方法,例如var newFun = fun.bind(obj);

注意:函數綁定存在內存消耗多,執行慢的缺點

5.函數柯里化(也叫函數套用,容許指定一些參數)

建立柯里化函數的通用方式:

function curry(fun){
  var args = Array.prototype.slice.call(arguments, 1);//去掉第一個參數fun,獲得給定的參數值
  return function(){
    var innerArgs = Array.prototype.slice.call(arguments);//把內部arguments對象轉換爲數組(爲了用concat方法)
    var finalArgs = args.concat(innerArgs);//拼接參數列表
    return fun.apply(null, finalArgs);//把拼接的參數列表傳給fun
  }
}

或者加強bind方法實現柯里化:

function bind(fun, context){
  var args = Array.prototype.slice.call(arguments, 2);//去掉前2個參數
  return function(){
    var innerArgs = Array.prototype.slice.call(arguments);//同curry
    var finalArgs = args.concat(innerArgs);//同curry
    return fun.apply(context, finalArgs);//指定執行環境和參數
  }
}

注意:柯里化和bind都存在額外開銷,不要濫用

6.防篡改對象

  1. 不可擴展對象(不能添加新屬性)

    var obj = {a : 1, b : 2};
    alert(Object.isExtensible(obj));//true
    Object.preventExtensions(obj);//把obj設置爲不可擴展
    alert(Object.isExtensible(obj));//false
    obj.c = 3;
    alert(obj.c);//undefined

    注意:設置不可擴展操做沒法撤銷(改不回來),設置以後沒法添加新屬性,但能夠修改/刪除原有屬性

  2. 密封對象(只能修改現有屬性,沒法刪除或添加)

    var obj = {a : 1, b : 2};
    Object.seal(obj);//設置密封對象
    alert(Object.isSealed(obj));//true
    obj.c = 3;
    alert(obj.c);//undefined
    delete obj.a;//嚴格模式下報錯
    alert(obj.a);//1
  3. 凍結對象(只讀,訪問器屬性可寫)

    var obj = {a : 1, b : 2};
    Object.freeze(obj);//設置密封對象
    alert(Object.isFrozen(obj));//true
    obj.a = 3;
    alert(obj.a);//1

上面的實現都是ES5新增的部分,瀏覽器支持性未知,本機測試[IE8-]不支持,Chrome和FF支持

7.函數節流

把耗時的大任務分割成小塊,用setTimeout控制執行

優勢:提升了頁面響應速度

缺點:邏輯連貫性沒了,實現難度增大,並且不易實現事務控制,由於完整事務被拆開了

8.觀察者模式

用自定義事件能夠實現觀察者模式:

function EventTarget(){
  this.handlers = {};    
}

EventTarget.prototype = {
  constructor: EventTarget,

  addHandler: function(type, handler){
    if (typeof this.handlers[type] == "undefined"){
      this.handlers[type] = [];
    }

    this.handlers[type].push(handler);
  },

  fire: function(event){
    if (!event.target){
      event.target = this;
    }
    if (this.handlers[event.type] instanceof Array){
      var handlers = this.handlers[event.type];
      for (var i=0, len=handlers.length; i < len; i++){
        handlers[i](event);
      }
    }
  },

  removeHandler: function(type, handler){
    if (this.handlers[type] instanceof Array){
      var handlers = this.handlers[type];
        for (var i=0, len=handlers.length; i < len; i++){
          if (handlers[i] === handler){
            break;
          }
        }

        handlers.splice(i, 1);
      }
  }
};

用法以下:

function handleMessage(event){
  alert("Message received: " + event.message);
}
//建立新對象
var target = new EventTarget();
//添加事件處理器
target.addHandler("message", handleMessage);
//觸發事件
target.fire({ type: "message", message: "Hello world!"});
//刪除事件處理器
target.removeHandler("message", handleMessage);
//再次觸發事件,應該沒有事件處理器
target.fire({ type: "message", message: "Hello world!"});
相關文章
相關標籤/搜索