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!"});
相关文章
相关标签/搜索