• 闭包模式


    1.调用ajax的一种套路

    调用ajax的一种套路
    //exports.emit("pick","failure")和exports.emit("pick","timeout")
    C.failure = C.timeout = function() {
            if (!m)
                m = setTimeout(function() {
                    exports.emit("error", {
                        type: "offline"
                    }),
                    s()
                }
                , I)   //I为13S
        }
    //exports.emit("error", "stop");
    S.stop = function() {
        exports.emit("error", {
            type: "offline"
        }),
        s()
    }
    function n(e) {
        if (exports.emit("pick", "success"),!A) {
            if (e.fields && Array.isArray(e.fields))
                S.ack = e.ack || S.ack,
                e.fields.forEach(function(e) {
                    if (w[e.command])
                        w[e.command](e)
                }
                );
            if (!A)
                exports.emit("pick", "response", {
                    status: 0,
                    bid: S.bid,
                    session: S.session,
                    seq: S.seq,
                    ack: S.ack
                })
        }
    }
    function r(e) {
        if (exports.emit("pick", "failure"),"offline" == e || "kicked" == e)
            exports.emit("error", "stop");
        else if (!A)
            exports.emit("pick", "response", {
                status: 1,
                bid: S.bid,
                session: S.session,
                seq: S.seq,
                ack: S.ack
            })
    }
    function o() {
        //超时操作
        exports.emit("pick", "timeout"),
        clearTimeout(y);
        var e = v;
        //  e为发出去的XMLHttpRequest对象。撤销aja请求,轮询a();重新去取数据
        if (v = null ,e.abort(),!v)
            a();
    }
    //为什么要通过i封装一次,原因是提供统一接口。
    function i(e, t, i, n, r) {
        if ("function" == typeof i)
            r = n,
            n = i;
        if (e == g.TYPE_HI)
            i = i || {},
            i.session = S.session,
            i.seq = S.seq++;
        //调用ajax,也是对ajax的封装
        return g.get(e, t, i, n, r)
    }
    function s(e, t) {
        return function() {
            var i = Array.prototype.slice.call(arguments);
            //闭包来判断私有静态变量b与返回的数据进行对比,e,t闭包起来。
            if (e === b) {
                //有返回,清除y。
                if (v = null ,clearTimeout(y),"success" == t)
                    //调用成功
                    n.apply(null , i);
                else
                    //调用失败
                    r.apply(null , i);
                //A初始时为false,立即重新调用a(), 什么时候A为true;error为stop,或者二次调用失败。
                if (!A)
                    setTimeout(function() {
                        a()       
                    }
                    , 0)
            }
        }
    }
    function a() {
        if (!v)                            //v全局变量,初始为NULL。
            b++,                           //b初始为0
            v = i(g.TYPE_HI, "pick", {
                    imuss: S.bid,
                    ucid: S.ucid,
                    ack: S.ack || ""
                }, 
                //这里的b传递到回调函数中。
                s(b, "success"), s(b, "failure")),
            //对发出的请求超时监控C.PICK_TIMEOUT=4e4
            y = setTimeout(o, C.PICK_TIMEOUT)
    }
     
    //程序入口
    a();

    2.函数包装器

         在看express的源码中,经常看到函数包装的写法,有点难理解,函数包装器的作为是对一个函数进行包装,返回另外一个函数,在包装的过程中,对初始传递参数和目前传递的参数进行改造加工。一般模式是:

    // oldF  旧函数, newF 新函数,callback回调函数 options 参数
    newF = function(oldF,callback, options) {
        return function(){
            // 在调用newF是,获取newF的参数
            var args = Array.prototype.slice(arguments);
            // this为newF运行的环境
            callback.apply(this,args);
        }  
    }
    newF(x1,x2)

    这里非常类似函数的函数柯里化,函数柯里化就是接受多个参数的函数变换成接受一个单个参数的函数。如:

    //针对第一重函数和第二重函数通过第一重函数fn进行处理。
    function curry(fn){
        // 1...n,
        var args = Array.prototype.slice.apply(arguments,1);
        return function(){
            var innerArgs = Array.prototype.slice.apply(arguments);
            var allArgs = args.concat(innerArgs);
            fn.apply(null, allArgs);
        }
    }

    在express源码中,有如下函数进行包装了:

    // restore obj props after function
    function restore(fn, obj) {
      var props = new Array(arguments.length - 2);
      var vals = new Array(arguments.length - 2);
      for (var i = 0; i < props.length; i++) {
        props[i] = arguments[i + 2];
        //通过闭包把fn,obj之后的参数存储在一个数组中,vals数组存储obj(res)对象中以参数为key的value。后续调用返回的函数时,该数组不会消失。
        vals[i] = obj[props[i]];
      }
      return function(err){
        // restore vals,还原闭包遍历vals,也就是obj中的值还原到以前的状态。
        for (var i = 0; i < props.length; i++) {
          obj[props[i]] = vals[i];
        }
        //执行fn,和不用restore来包装一样。
        return fn.apply(this, arguments);
      };
    }

    还有:

    //done和参数done区别在于回调函数第一个参数是参数done,执行done(),实现一个函数代理过程。
    done = wrap(done, function(old, err) {
          if (err || options.length === 0) return old(err);
          sendOptionsResponse(res, options, old);
    });
    // wrap a function
    function wrap(old, fn) {
      //新done函数会接受一些参数,这些参数和wrap函数的参数fn组成新的数组,供apply来调用。
      return function proxy() {
        var args = new Array(arguments.length + 1);
        args[0] = old;
        for (var i = 0, len = arguments.length; i < len; i++) {
          args[i + 1] = arguments[i];
        }
       //在express中this为undefined,args[0] = old,也就是done,
        fn.apply(this, args);   //这里的数组转到回调函数时,参数被打散。
      };
    }
    // send an OPTIONS response
    function sendOptionsResponse(res, options, next) {
      try {
        var body = options.join(',');
        res.set('Allow', body);
        res.send(body);
      } catch (err) {
        next(err);
      }
    }

     JS对于这个闭包简直是无处不在,看下插件finalHandler的源码:

    function finalhandler (req, res, options) {
      var opts = options || {}
    
      // get environment
      var env = opts.env || process.env.NODE_ENV || 'development'
    
      // get error callback
      var onerror = opts.onerror
      //返回的函数,这个函数保持了定义时的成员变量,req、res.
      return function (err) {
        var headers = Object.create(null)
        var status
    
        // ignore 404 on in-flight response
        if (!err && res._header) {
          debug('cannot 404 after headers sent')
          return
        }
    
        // unhandled error
        if (err) {
          // respect status code from error
          status = getErrorStatusCode(err) || res.statusCode
    
          // default status code to 500 if outside valid range
          if (typeof status !== 'number' || status < 400 || status > 599) {
            status = 500
          }
    
          // respect err.headers
          if (err.headers && (err.status === status || err.statusCode === status)) {
            var keys = Object.keys(err.headers)
            for (var i = 0; i < keys.length; i++) {
              var key = keys[i]
              headers[key] = err.headers[key]
            }
          }
    
          // production gets a basic error message
          var msg = env === 'production'
            ? statuses[status]
            : err.stack || err.toString()
          msg = escapeHtml(msg)
            .replace(/
    /g, '<br>')
            .replace(/x20{2}/g, ' &nbsp;') + '
    '
        } else {
          status = 404
          msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '
    '
        }
    
        debug('default %s', status)
    
        // schedule onerror callback
        if (err && onerror) {
          defer(onerror, err, req, res)
        }
    
        // cannot actually respond
        if (res._header) {
          debug('cannot %d after headers sent', status)
          req.socket.destroy()
          return
        }
    
        // send response
        send(req, res, status, headers, msg)
      }
    }

     3.函数的静态私有变量

    var x = (function(){
        function e(e){
            this.id = e.toUpperCase();
        }
        var t;
        //特權函數,給對象x的私有變量賦值,也是函数e的公有静态方法
        e.init = function(x){
            t = x;
        }
        e.prototype.key = function(){
            return t +"_" + this.id;
        }
        return e;
    })();
    //初始化函数X的静态私有变量
    x.init("123456");
    var _ = new x("core");
    console.log(_.key());
    //123456_CORE

     又如:

    var f1 = function(){
        var i = 0;  //f1的静态私有变量
        return {
            getI:function(){
                return i;
            },
            setI:function(){
                i++;
            }
        }
    }
    var obj = f1();
    obj.setI();
    console.log(obj.getI());       //1
    obj.setI();
    console.log(obj.getI());       //2

     3.once函数(来源于vue-router)

    function once (fn) {
      let called = false
      return function (...args) {
        if (called) return
        called = true
        return fn.apply(this, args)
      }
    }
  • 相关阅读:
    位运算及其妙用
    Ubuntu 下的Python开发 mysqlclient安装失败问题解决,亲测有效
    Ubuntu "sudo apt update"失败的问题可以这样解决
    青魔法-驭虫术(不定时更新)
    白魔法安全课(持续更新)
    空间魔法-Mysql(持续更新)
    时间魔法-Git(持续更新)
    仪式魔法——区块链(持续更新)
    Web圣堂幻术VUE不定时更新)
    影魔法 Shell 与 Dos(持续更新)
  • 原文地址:https://www.cnblogs.com/liuyinlei/p/6752634.html
Copyright © 2020-2023  润新知