zoukankan      html  css  js  c++  java
  • JS高级程序设计之高级技巧

    1. 安全的类型检测

    // 安全类型检测
    /* 检测是不是原生的函数 */
    function isFunction(value) {
        return Object.prototype.toString.call(value) == "[object Function]";
    }

    2.作用域安全的构造函数

    // 作用域安全的构造函数
    function Person(name, age, job) {
        if (this instanceof Person) {
            this.name = name;
            this.age = age;
            this.job = job;
        } else {
            return new Person(name, age, job);
        }
    }
    var person1 = Person("sundily", 22, 'Software Engineer');

    3.惰性载入函数:表示函数执行的分支仅会发生一次 (主要用在浏览器的兼容上 做一次判断)两种实现方法:
      ● 函数被调用时再处理函数。在第一次调用的过程中,该函数就会被重新覆盖为另一个按合适方法执行的函数
      ● 在声明函数时就指定适当的函数。这样第一次调用函数就不会损失性能了,而在代码首次加载时会损失一点性能

    /* 惰性载入函数  表示函数执行的分支仅会发生一次 */
    // 原例
    function createXHR() {
        if (typeof XMLHttpRequest != "undefined") {
            return new XMLHttpRequest();
        } else if (typeof ActiveXObject != "undefined") {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp"];
                for (var index = 0; index < versions.length; index++) {
                    try {
                        new ActiveXObject(versions[index]);
                        arguments.callee.activeXString = versions[index];
                        break;
                    } catch (error) {
    
                    }
    
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        } else {
            throw new Error("NO XHR object available");
        }
    }
    
    // 使用惰性函数之后
    function createXHR() {
        if (typeof XMLHttpRequest != "undefined") {
            createXHR = function () {
                return new XMLHttpRequest();
            }
        } else if (typeof ActiveXObject != "undefined") {
            createXHR = function () {
                if (typeof arguments.callee.activeXString != "string") {
                    var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp"];
                    for (var index = 0; index < versions.length; index++) {
                        try {
                            new ActiveXObject(versions[index]);
                            arguments.callee.activeXString = versions[index];
                            break;
                        } catch (error) {
                            // skip
                        }
                    }
                }
                return new ActiveXObject(arguments.callee.activeXString);
            };
        } else {
            createXHR = function () {
                throw new Error("no xhr object available");
            };
        }
        return createXHR();
    }
    
    // 第二种惰性函数    匿名的立即执行函数   返回匿名函数
    var createXHR = (function () {
        if (typeof XMLHttpRequest != 'undefined') {
            return function () {
                return new XMLHttpRequest();
            };
        } else if (typeof ActiveXObject != "undefined") {
            return function () {
                if (typeof arguments.callee.activeXString != "string") {
                    var versions = ["MSXML2.XMLHttp", "MSXML2.XMLHttp.6.0"];
                    for (var index = 0; index < versions.length; index++) {
                        try {
                            new ActiveXObject(versions[index]);
                            arguments.callee.activeXString = versions[index];
                            break;
                        } catch (error) {
                            // skip
                        }
                    }
                }
                return new ActiveXObject(arguments.callee.activeXString);
            };
        } else {
            return function () {
                throw new Error("no xhr object available");
            };
        }
    })();

    4 函数绑定——一个将函数绑定到指定环境的函数 bind()

    /* 函数绑定 */
    var handler = {
    message: "event handler",
    handlerClick: function () {
    alert(this.message);
    }
    };
    var btn = document.getElementById("mybtn");
    btn.addEventListener("click", handler.handlerClick.bind(handler), false);

    只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,就应该用函数绑定
    主要应用与事件处理程序以及setInterval() setTimeout() 比普通函数需要更多的内存,所以最好必要时用

    5 .函数柯里化(function currying)——用于创建已经设置好了一个或者多个参数的问题
    函数柯里化基本方法和函数绑定时一样的,使用一个闭包返回一个函数
    动态创建柯里化:调用另一个函数并为他传入要柯里化的函数和必要的参数

    /* 函数柯里化:调用另一个函数并为他传入要柯里化的函数和必要的参数
    主要工作 就是将返回函数的参数进行排序
    concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
    */
    function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);//arguments中第一个参数时fn 所以要从1开始slice()
    return function () {
    var innerArgs = Array.prototype.slice.call(arguments);
    var finalArgs = args.concat(innerArgs);
    return fn.apply(null, finalArgs);//调用函数fn自己本身,apply的第一个参数就是null,finalArgs就是传入的参数
    }
    }
    
    es5中bind()方法也实现了柯里化
    // bind()的柯里化
    var handler={
    message:"event handler",
    handlerClick:function(name,event){
    alert(this.message+":"+name+":"+event.type);
    };
    }
    var btn=document.getElementById("mybtn");
    btn.addEventListener('click',handler.handlerClick.bind(handler,"mybtn"));


    6. 防篡改对象
      1. 不可扩展的对象

    var person={name:"sundjly"};
    Object.preventExtensions(person);//不能再给person添加属性和方法 但仍然可以修改 删除已有的成员
    // 使用Object.isExtensible()方法确定对象是否可以扩展
    Object.isExtensible(person);//false

        1. 1密封的对象(sealed object):密封对象不可扩展,而且已有属性方法不能删除,

    Object.seal(person);
    Object.isSealed(person);//判断对象是否被密封

        1.2冻结对象 Object.freeze(person);//无法对对象做出操作

    7. 高级定时器:
    js是单线程的,由代码队列先后顺序执行。js中没有任何代码是立即执行的,但一旦进程空闲则尽快执行。
    setInterval:这种重复定时器的规则有两个问题:
        1. 某些间隔会被跳过
        2. 多个定时器的代码执行之间的间隔可能比预期小

    /* 避免setInterval()重复定时器的两个缺点,可以使用链式setTimeout()调用
    这个模式链式调用setTimeout()函数,每次函数执行的时候都会创建一个新的定时器,第二个setTimeout调用arguments.callee
    获取当前函数的引用
    优点:在前一个定时器代码执行之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔,
    而且它可以保证在下次定时器代码执行之前,至少等待间隔时间,避免了连续运行。
    */
    setTimeout(function() {
    // 处理函数
    setTimeout(arguments.callee, interval);
    }, interval);

    具体的例子:

    setTimeout(function () {
        var div = document.querySelector("#div");
        left = parseInt(div.style.left) + 5;
        div.style.left = left + "px";
        if (left < 200) {
            setTimeout(arguments.callee, 50);
        }
    }, 50);

    8.Yielding Processes
      1. 脚本长时间运行的的原因:
        a. 过长的,过深嵌套的函数调用
        b. 进行大量处理的循环
      2. 如果处理不必同步完成,数据不必按顺序完成,那么可以使用定时器分割循环——数组分块(array chunking)
        a. 基本思路:为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目进行处理,接着在设置另一个定时器

    /* 
    数组分块技术:shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
    array变量的本质就是一个“待办事宜”列表,包含要处理的列表
    */
    setTimeout(function () {
        // 取出第一个条目并处理
        var item = array.shift();
        process(item);
        // 若还有条目,再设置定时器
        if (array.length > 0) {
            setTimeout(arguments.callee, 100);
        }
    }, 100);

      3.

    function chunk(array, process, context) {
        setTimeout(function () {
            var item = array.shift();
            process.call(context, item);
            if (array.length > 0) {
                setTimeout(arguments.callee, 100);
            }
        }, 100);
    }

    一旦某个函数需要花费50ms以上的时间完成,那么最好考虑能否将任务分割为一系列可以使用定时器的小任务


    9 函数节流
    DOM操作比起非dom操作交互需要更多的内存和CPU时间,连续尝试过多的DOM相关操作可能会导致浏览器挂起,崩溃。尤其是IE中onresize事件,为了绕开这个问题,可以使用定时器对该函数进行节流。
    基本思路:代码可以在间断的情况下连续重复的执行,目的只有在执行函数的请求停止一段时间之后才执行:

    /* 函数节流:
     */
    var processor = {
        timeoutID: null,
        // 实际进行处理的方法
        performProcessing: function () {
            // 实际执行的代码
        },
        // 初始处理调用的方法
        process: function () {
            clearTimeout(this.timeoutID);
            var that = this;
            this.tomeoutID = setTimeout(function () {
                that.performProcessing();
            }, 100);
        }
    };
    // 尝试开始执行
    processor.process();
    // 简化
    function throttle(method, context) {
        clearTimeout(method.tId);
        method.tId = setTimeout(function () {
            method.call(context);
        }, 100);
    }
    // 列子
    function resizeDiv() {
        var div = document.querySelector("#div");
        div.style.height = div.offsetWidth + "px";
    }
    window.onresize = function () {
        throttle(resizeDiv);
    }

    只要代码是周期性执行的,都要用函数节流,控制处理的频率,确保浏览器不会再短时间内进行过多的计算

    10 自定义事件:
    事件——一种叫观察者的设计模式(创建松散耦合代码的技术)
    观察者模式是由两类对象组成:主体和观察者。主体负责发布事件,观察者通过订阅这些事件来观察主体,在DOM中,DOM元素就是主体,你的事件处理程序就是观察者
    事件是与DOM交互最常见的方式。
    自定义事件的适用场景:当代码中存在多个部分在特定时刻互相交互的情况下

    /* 自定义事件 */
    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 index = 0; index < handlers.length; index++) {
                    handlers[index](event);
                }
            }
        },
        removeHandler: function (type, handler) {
            if (this.handlers[type] instanceof Array) {
                var handlers = this.handlers[type];
                for (var index = 0; index < handlers.length; index++) {
                    if (handlers[index] == handler) {
                        break;
                    }
                }
                handlers.slice(index, 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); //删除事件处理程序

    12 拖放

    /* 拖放
    <div class="draggable" style="position:absolute; background:red;"></div>
    */
    var DragDrop = function () {
    // DragDrop对象封装了拖放的所有基本功能,这是一个单例,并使用了模块模式来隐藏某些实现细节 但是该方法没有提供任何方法表示拖动开始,正在拖动,或者已经结束
    var dragging = null, //存放被拖动的元素
    diffX = 0,
    diffY = 0;
    
    function handleEvent(event) {
    // 获取事件目标
    var target = event.target;
    // 确定事件类型
    switch (event.type) {
    case "mousedown":
    if (target.className.indexof("draggable") > -1) {
    dragging = target;
    diffX = event.clientX - target.offsetLeft;
    diffY = event.clientY - target.offsetWidth;
    }
    break;
    case "mousemove":
    if (dragging != null) {
    // 指定位置
    dragging.style.left = (event.clientX - diffX) + "px";
    dragging.style.top = (event.clientY - diffY) + "px";
    }
    break;
    case "mouseup":
    dragging = null;
    break;
    
    }
    };
    return {
    enable: function () {
    document.addEventListener("mousedown", handleEvent, false);
    document.addEventListener("mousemove", handleEvent, false);
    document.addEventListener("mouseup", handleEvent, false);
    },
    disable: function () {
    document.removeEventListener("mousedown", handleEvent, false);
    document.removeEventListener("mousemove", handleEvent, false);
    document.removeEventListener("mouseup", handleEvent, false);
    }
    }
    }();
  • 相关阅读:
    【LeetCode】Validate Binary Search Tree
    【LeetCode】Search in Rotated Sorted Array II(转)
    【LeetCode】Search in Rotated Sorted Array
    【LeetCode】Set Matrix Zeroes
    【LeetCode】Sqrt(x) (转载)
    【LeetCode】Integer to Roman
    贪心算法
    【LeetCode】Best Time to Buy and Sell Stock III
    【LeetCode】Best Time to Buy and Sell Stock II
    CentOS 6 上安装 pip、setuptools
  • 原文地址:https://www.cnblogs.com/sundjly/p/7898991.html
Copyright © 2011-2022 走看看