zoukankan      html  css  js  c++  java
  • JavaScript中各种常用高级函数的实现

    1.call/apply
    Function.prototype.myCall = function (context, ...args{
        if (typeof this !== 'function'return;
        var fun = this;
        context._fn = fun; // TODO:确保_fn 键不存在
        var res = context._fn(args);
        return res;
    }

    // test
    function say (...args{
        return args + this.name
    }

    var obj = { name'rencoo' }

    say.myCall(obj, 'hello, '// "hello, rencoo"
    2.bind
    • 绑定环境
    • 如果以new调用的话,
    // var fn = func.bind(obj, ...args);
    // fn(...args);
    // new fn(...args); // 对 this 来说, new 的优先级高于 bind; 会忽视传入的 context
    Function.prototype.myBind = function (context, ...args{
        if (typeof this !== 'function'return;
        var fun = this;
        return function (...innerArgs{
            args = args.concat(innerArgs);
            var res = fun.apply(context, args);
            return res;
        }
    }

    考虑到 new 调用,在返回函数里做一层判断,如果函数是使用使用 new 进行调用的,那么绑定的环境就会失效

    var obj = { age25 };
    function Person(name{
        this.name = name;
        console.log(this); // (*)
    }
    Person.prototype.sayHi = function () {
        console.log('hi');
    }

    var fn = Person.bind(obj, 'Bob');

    // 调用后, (*)的打印内容
    // 普通调用
    fn();
    console: { age25name"Bob" }

    // 通过new调用
    var p = new fn();
    console.log(p); // Person { name: 'Bob' }
    console.log(p.sayHi); // f () { console.log('hi') }

    改进我们的 myBind

    Function.prototype.myBind = function (context, ...args{
        var fn = this;
        if (typeof fn !== 'function'return;

        var boundFn;
        var binder = function (...innerArgs{
            args = args.concat(innerArgs);

            // console.log(this instanceof boundFn); // 使用 new 调用时, this 即为构造函数的实例, 判断结果为 true (优先级高于 bind 传入的 context)
            if(this instanceof boundFn) { // new 调用; 不使用bind传入的 context
                var res = fn.apply(this, args);
                if (typeof res === 'object') {
                    return res;
                }
                return this;
            } else { // 普通调用
                return fn.apply(context, args);
            }
        }

        boundFn = Function('binder''return function (){ return binder.apply(this,arguments); }')(binder);

        if (fn.prototype) {
            var Empty = function Empty() {};
            Empty.prototype = fn.prototype;
            boundFn.prototype = new Empty();
            Empty.prototype = null
        }

        return boundFn;
    }

    // 测试
    var obj = { age25 };
    function Person(name{
        this.name = name;
        console.log(this); // (*)
    }

    var fn = Person.myBind(obj, 'Bob');

    // 调用后, (*)的打印内容
    // 普通调用
    fn();
    // console: { age: 25, name: "Bob" }

    // 通过new调用
    new fn();
    // console: Person { name: 'Bob' }

    常用的装饰器函数 once/throttle/debounce

    3.once

    Ensure a function is called only once,函数调用一次即失效

    function once (fn{
        var flag = false;
        return function (...args{
            !flag && fn.apply(this, args);
            flag = true;
        }
    }

    // more elegant way to write a once function
    function once (fn{
        // closure here; var fn = fn;
        return function (...args{
            var res = fn && fn.apply(this, args);
            fn = null;
            return res;
        }
    }
    4.throttle

    节流:连续的高频操作,只有最后一次操作后的一段时间后才调用传入的函数(最终的状态最重要),后一次操作会覆盖前一次操作

    应用:实时搜索里的搜索函数、鼠标移动函数中的更新函数、窗口resize里的更新函数

    (原因是这些传入的函数可能都是一些耗时耗性能的操作,无法也没必要在每次微小动作上执行)

    function throttle (fn, delay{
        var timer = null;
        return function (...args{
            // 如果上一次操作的定时器尚未执行,那么取消它并将其替换为一个新的定时器
            clearTimeout(timer);
            timer = setTimeout(() => fn.apply(this, args), delay);
        }
    }
    5.debounce

    防抖:第一次生效,之后一定时间内无论怎么触发都无效

    function debounce (fn, wait{
        var flag = true, timer = null;
        return function (...args{
            if (flag) {
                flag = false;

                timer = setTimeout(function () {
                    flag = true;
                    clearTimeout(timer);
                }, wait);

                return fn.apply(this, args);
            }
        }
    }

    // 另一种写法
    function debounce (fn, wait{
        var timer = null;
        return function (...args{
            if (!timer) {
                timer = setTimeout(() => { 
                    timer = null
                }, wait);

                return fn.apply(this, args);
            }
        }
    }
    6.throttle_optimize

    升级版的 throttle_optimize 结合了 throttle 与 debounce 的特点

    与 throttle 的区别:throttle_optimize 对于用户的第一次操作总是调用传入的函数

    与 debounce 的区别:如果被忽略的调用是冷却期间的最后一次,那么它会在延迟结束时执行传入的函数

    function throttle_optimize (fn, delay{
        var flag = true, timer = null;
        return function (...args{
            if (flag) {
                flag = false;
                fn.apply(this, args);
                args = null;
            }

            clearTimeout(timer);
            timer = setTimeout(() => {
                flag = true;
                if (args) {
                    fn.apply(this, args);
                }
            }, delay);
        }
    }
    7.throttle_optimize

    用于动画渲染的 throttle_optimize

    对于第一次操作,总是 update;之后的 delay 时间内的最后一次操作总是在 delay 时刻渲染一次;

    也就是说装饰器函数 throttle_optimize 会经常调用,但是传入的更新函数,最多每 delay 时间调用一次(也可能不调用,没有任何操作就不会调用)

    比如,使用鼠标移动和屏幕坐标原点绘制矩形,鼠标持续移动过程中,每隔delay时间渲染一次矩形,而不是每个移动都绘制,或者说只有最后一次移动结束的一段时间后才绘制(throttle的原理)

    function throttle_optimize(func, ms{

        let isThrottled = false,
            savedArgs,
            savedThis;

        function wrapper() {
            if (isThrottled) {
                savedArgs = arguments;
                savedThis = this;
                return;
            }

            func.apply(thisarguments);
            isThrottled = true;

            setTimeout(function () {
                isThrottled = false;
                if (savedArgs) {
                    wrapper.apply(savedThis, savedArgs);
                    savedArgs = savedThis = null;
                }
            }, ms);
        }

        return wrapper;
    }

    8.new

    function myNew (Fn, ...args{
        // 1.生成一个空对象作为this
        // 2.将对象链接到原型上
        let obj = Object.create(Fn.prototype)
        // 3.执行构造函数, 并将obj作为context传入(为实例对象添加属性)
        let res = Fn.apply(obj, args) // 构造函数内的 this 动态改变为 obj
        // 4.return this或者构造函数的执行结果(引用类型)
        return typeof res === 'object' ? res : obj
        // return result instanceof Object ? res : obj
    }

    // test
    function Person (name, age{
        this.name = name;
        this.age = age;
    }

    var a = myNew(Person, 'rencoo'25)
    console.log(a) // Person {name: "rencoo", age: 25}
    var b = new Person('ge can'26)
    console.log(b) // Person {name: "gecan", age: 26}

    Person.prototype.sayHi = function () {
        console.log(this.name)
    }

    a.sayHi() // rencoo
    b.sayHi() // gecan

    9.promise

    class Promise {
        constructor (executor) {
            // 设置属性 status value resolveCbs rejectCbs
        }
        then (onResolved, onRejected) {

        }
        catch (cb) {
            return this.then(null, cb)
        }
    }

    promise链式,实现必须上一个异步完成后再去跑下一个任务

    // 1.
    const template = Promise.resolve();
    promises.forEach((p) => {
        template = template.then(p)
    })
    // 2. 使用 await 

    10.deep copy(deepclone)

    11.extends

    12.singleton

    13.pub-sub

    14.打印出html里所有标签

    15.lazyman

    16.快排

    17.数组乱序

    18.LRU

    19.两数之和

    20.找出一个集合的所有子集

    21.Object.defineProperty

    • 实现的关键在那个闭包

    22.requestAnimation(京东讲座,react间隙渲染)

    /**
     * Provides requestAnimationFrame in a cross browser way.
     * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
     */


    if ( !window.requestAnimationFrame ) {

        window.requestAnimationFrame = ( function() {

            return window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function( 
            /* function FrameRequestCallback */ callback, 
            /* DOMElement Element */ element 
    {

                window.setTimeout( callback, 1000 / 60 );

            };

        } )();

    }

    23.手写 Proxy(使用Object.defineProperty将一个对象的属性代理到另一个对象上)

    24.webpack

    • loader plugin的区别
    • tree-shaking 的工作原理(一个模块导出十个方法,但只用到了一个,那么只打包那一个方法)
    • code splitting用的是什么插件
    • 如何提高 webpack 的构建速度
    • 利用 DIIPlugin 预编译资源模块
    • 利用 Happypack 加速代码构建

    25.jsonp

    //通过JQuery Ajax 发起jsonp请求
    (注:不是必须通过jq发起请求 , 
         例如:Vue Resource中提供 $.http.jsonp(url, [options]))
    $.ajax({
        // 请求方式
        type: "get"
        // 请求地址
        url: "http://169.254.200.238:8080/jsonp.do"
        // 标志跨域请求
        dataType: "jsonp",                
        // 跨域函数名的键值,即服务端提取函数名的钥匙(默认为callback)
        jsonp: "callbackparam",   
        // 客户端与服务端约定的函数名称
        jsonpCallback: "jsonpCallback",
        // 请求成功的回调函数,json既为我们想要获得的数据
        success: function(json{
            console.log(json);
        },
        // 请求失败的回调函数
        error: function(e{
        alert("error");
        }
    });


    @RequestMapping({"/jsonp.do"})
    public String jsonp(@RequestParam("callbackparam"String callback){
        // callback === "jsonpCallback"
        // return callback + "({"result":"success"})";
        return  (request.from === jsonp) ? callback(data) : data ;
    }

    26.实现一个Queue类,要求包含两个函数

    task函数:新增一个任务。包含两个参数,等待时间和回调函数

    start函数:执行任务队列。将所有任务按队列顺序执行, 执行完一个任务才能执行下一个任务

    new Queue()
        .task(1000, () => {
            console.log(1)
        })
        .task(2000, () => {
            console.log(2)
        })
        .task(1000, () => {
            console.log(3)
        })
        .start()
    // 链接:https://www.zhihu.com/question/358075914/answer/915814865
    const queue = {
      a() {
        setTimeout(() => {
          console.log("a is done");
          this.next();
        }, 1000);
      },
      b() {
        setTimeout(() => {
          console.log("b is done");
          this.next();
        }, 1000);
      },
      c() {
        setTimeout(() => {
          console.log("c is done");
        }, 1000);
      }
    };

    Object.defineProperty(queue, Symbol.iterator, {
      enumerablefalse,
      writablefalse,
      configurabletrue,
      value() {
        const o = this;
        const ks = Object.keys(o);
        let idx = 0;
        return {
          next() {
            const key = ks[idx++];
            const func = o[key];
            if (typeof func === "function") {
              func.call(this);
            }
            return {
              value: key,
              done: idx > ks.length
            };
          }
        };
      }
    });

    const qt = queue[Symbol.iterator]();
    qt.next();

    // a is done
    // b is done
    // c is done
    let queue = new Queue();

    queue.push((next) => {
        // do something async
        next();
    })

    queue.push((next) => {
        // do something async
        Ajax.post('xxx').then(() => next());
    });

    queue.run()

    27.实现一个wait

    function wait (time{
        return new Promise((resolve, rejec) => {
            setTimeout(resolve, time);
        });
    }

    wait(2000).then(() => {
        console.log('hello1');
        return wait(2000);
    }).then(() => {
       console.log('hello2'); 
    });

    // 使用 await
    (async function () {
        var res = await wait(2000);
        // console.log(res); // undefined
        console.log('hello1');
        var res1 = await wait(2000);
        // console.log(res1); // undefined
        console.log('hello2');
    })();

    28.实现一个jquery事件代理

    function on(parent, currentClassName, eventName, cb{

      parent.addEventListener(eventName, function (evt{
        var target = evt.target;
        var currentTarget;
        while (target) {
          if (target.tagName === 'BODY'break// 找到body还没找到(说明点击的位置已经是目标元素的父级了; 不可能找到的)
          if (target.getAttribute('class') === currentClassName) {
            currentTarget = target;
            break;
          } else {
            target = target.parentNode; // 往上找; 从目标元素的子元素触发事件
          }
        }

        if (currentTarget) {
          // 获取 currentTarget 上的数据; 并执行事件回调
          cb.apply(currentTarget);
        }
      });

    }
  • 相关阅读:
    ASP.NET在MVC控制器中获取Form表单值的方法
    MVC中几种常用ActionResult
    EF 配置MySQL
    HTTP 错误 403.6
    26个Jquery使用小技巧(转)
    Win2008R2配置WebDeploy(转)
    IIS快捷方式
    发布你的程序包到Nuget
    PostgreSQL recovery.conf恢复配置
    PostgreSQL 9.5 高可用、负载均衡和复制
  • 原文地址:https://www.cnblogs.com/rencoo/p/10901333.html
Copyright © 2011-2022 走看看