• 读javascript高级程序设计16-几条函数小技巧


    内容概要

    • 作用域安全的构造函数
    • 惰性载入函数
    • 函数绑定
    • 函数节流

    一、作用域安全的构造函数

    我们知道,当使用new操作符调用构造函数时,构造函数内部的this会指向新创建对象的实例。

    function Person(name){
    this.name=name;
    }
    var p=new Person('peter');
    console.log(p.name);//结果:perter

    但是,如果没有使用new操作符,而是将构造函数当作普通函数调用时,this会指向window对象。

    var p1=Person('peter');
    console.log(p1.name);//报错
    console.log(window.name);//peter

    因此,在构造函数中应该首先检查this是否为正确的类型实例,这种方式就是作用域安全的构造函数。

    function Person(name) {
    if (this instanceof
    Person) { this.name = name; } else { return new
     Person(name);
      }
    }
    var p = new Person('peter');
    console.log(p.name); //perter
    var p1 = Person('peter');
    console.log(p1.name); //perter
    console.log(window.name); //报错

    在该构造函数基础上,结合原型链的实现如下:

    function Person(name) {
      if (this instanceof Person) {
        this.name = name;
      }
      else {
        return new Person(name);
      }
    }
    function Student(name,sno){
    Person.call(this,name);
      this.sno=sno;
    }
    Student.prototype
    =new
     Person();
    var s=new Student('peter','N0015');
    console.log(s.name);

    通过作用域安全的构造函数,可以保证在缺少new操作符调用构造函数的时候在正确的执行环境中进行。

    二、惰性载入函数

    有些函数包含很多分支,每次调用时都会执行一遍if分支判断,但实际上可能每次走的都是相同分支。看下这个创建CORS对象的方法:

    function createCORSRequest(method,url){
                  //创建XHR对象   
            var xhr = new XMLHttpRequest();
            //启动请求   
            if("withCredentials" in xhr){
                 xhr.open(method,url,true);
                 }else if(typeof XDomainRequest!='undefined'){
                      xhr=new XDomainRequest();
                      xhr.open(method,url);
                      }else{
                           xhr=null;
                           }                 
            return xhr;
                  }
    
    var xhr1 = createCORSRequest('get', 'http://www.othersite.com/weather.ashx');
    var xhr2 = createCORSRequest('get', 'http://www.othersite.com/articles.ashx');

    实际上对于同一款浏览器每次创建对象时都会执行相同的分支,因此每次都进行if判断是多余的,使用惰性载入函数可以实现函数执行分支只发生一次。有两种常见方案:

    方案一:在第一次调用函数时,根据分支结果,将该函数替换为另一个按合适的方式执行的函数。我们改进上面的函数如下:

    function createCORSRequest(method, url) {
      var xhr0 = new XMLHttpRequest();
      if ('withCredentials' in xhr0) {
        var xhr = new XMLHttpRequest();
    createCORSRequest = function (method, url) {
          xhr.open(method, url, true);
          return xhr;
        }
      } else if (typeof XDomainRequest != 'undefined') {
    createCORSRequest = function (method, url) {
          var xhr = new XMLHttpRequest();
          xhr = new XDomainRequest();
          xhr.open(method, url);
          return xhr;
        }
      } else {
    createCORSRequest = function (method, url) {
          return null;
        }
      }
      return createCORSRequest(method, url);
    }

    这样改进后,每次执行时不必再执行多个if分支判断,而是直接执行替换后的简洁函数。它只会在第一次调用时有一些性能损失。

    方案二:在声明函数时,就指定适当的函数。同样改进上面的例子:

    var createCORSRequest=(function(method, url) {
      var xhr0 = new XMLHttpRequest();
      if ('withCredentials' in xhr0) {
    return function (method, url) {
             var xhr = new XMLHttpRequest();
          xhr.open(method, url, true);
          return xhr;
        }
      } else if (typeof XDomainRequest != 'undefined') {
    return function (method, url) {
          var xhr = new XMLHttpRequest();
          xhr = new XDomainRequest();
          xhr.open(method, url);
          return xhr;
        }
      } else {
    return function (method, url) {
          return null;
        }
      }
    })();

    这种方案不同的是使用var声明函数,并为每个分支指定了匿名且自动执行的函数。这样,函数第一次加载的时候就确定了执行哪个分支的函数。

    三、函数绑定

    在执行回调函数或者事件处理程序时,常常需要把函数作为变量传递,此时我们需要保存函数的代码执行环境。函数绑定就是要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数

    var demo = {
      message: 'hello world',
      show: function () {
        console.log(this.message);
      }
    }
    var obj = document.getElementById('my-btn');
    EventUtil.addHandler(obj, 'click', demo.show);//点击按钮结果undefined

    其实我们可以利用闭包写法修复这个问题:

    EventUtil.addHandler(obj, 'click', function (event) {
      demo.show();
    });

    但是用太多的闭包看上去代码不够简洁。在很多js库中,都会定义bind()函数来实现函数绑定。

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

    这个bind函数实现比较简单,两个参数分别为要调用的函数和函数执行环境,执行结果返回一个在指定执行环境调用函数的函数,它把其内部函数的arguments参数全部传递过去。调用方法如下:

    EventUtil.addHandler(obj, 'click',bind(demo.show,demo));

    四、函数节流

    对于周期性执行的代码,应该进行函数节流。

    例如下面的滚动事件:

    window.onscroll = function(){
           throttle(demo,window);
           }

    当页面滚动的时候,会持续的输出结果。这种高频率的响应,如果方法复杂一些的话会耗用还多的性能。对于这种情况我们可以进行如下优化:

    //设置和消除定时器
    function throttle(method,context){
         clearTimeout(method.id);
         method.id=setTimeout(function(){
              method.call(context)
              },100);
         }
    function demo(){
         console.log(1);
         }
      window.onscroll = function(){
           throttle(demo,window);
           }

    throttle方法有两个参数:要执行的方法和作用域。第一次调用时会创建定时器,后续调用时都是先消除已有的定时器,然后重新创建定时器。这样一来只有最后一次调用之后100ms后才会将相应的方法加入执行队列。

  • 相关阅读:
    条码的开发使用介绍文档
    identity server4
    IUrlHelper ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
    netcore 版本 切换 sdk
    深层目录文件复制,C# 递归,录音录像图片文件过多,用于测试程序
    C# int uint long ulong byte sbyte float double decimal 范围,及类型!
    sqlserver 数据类型 C# clr 数据类型 映射
    Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'xxxxx.Controllers.xxxxController'.
    identity 基础表没有创建 aspnetuserclaims aspnetuserlogins
    DatabaseGeneratedOption
  • 原文地址:https://www.cnblogs.com/janes/p/3976934.html
Copyright © 2020-2023  润新知