首页 > 代码库 > 《javascript高级程序设计》笔记(二十二)

《javascript高级程序设计》笔记(二十二)

高级技巧

 

(一)高级函数

 

1.安全的类型检测

javascript内置的类型检测机制并非完全可靠,如typeof操作符。instanceof操作符在存在多个全局作用域(一个页面包含多个iframe)时会有问题。

 

//value要是一个数组,且与Array构造函数在同一个全局作用域//如果value是另个iframe的数组,则返回falsevar isArray = value isinstanceof Array;

 

 

原生数组的构造函数名与全局作用域无关,使用toString()能保持返回一致的值。

//检测某个值是不是数组function isArray(value){  return Object.Prototype.toString.call(value) == "object Array";}//检测某个值是不是函数function isFunction(value){  return Object.Prototype.toString.call(value) == "object Function";}//检测某个值是不是正则表达式function RegExp(value){  return Object.Prototype.toString.call(value) == "object RegExp";}//检测某个值是不是原生JSONvar isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) == "object JSON";

 

Object的toSting()方法不能检测非原生构造函数的构造函数名,返回[object Object]。

 

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

当使用new调用时,构造函数内用的this对象会指向新创建的对象实例。

        var person1 = new Person("Nicholas", 29, "Software Engineer");        alert(person1.name);     //"Nicholas"        var person2 = Person("Nicholas", 29, "Software Engineer");        alert(person2);         //undefined        alert(window.name);     //"Nicholas"

 

当没有使用new操作符来调用该构造函数的情况时,由于该this对象是在运行时绑定的,所以直接调用Person (),this会映射到全局对象window。

原本对Person实例的属性被加到window对象,因为构造函数作为普通函数调用,忽略了new操作符。

 

作用域安全的构造函数,在进行任何修改前,先确定this对象是正确类型的实例,不是就创建新的实例并返回。

        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);            }        }

 

如果使用构造函数窃取模式的继承而不使用原型链,这个继承会被破坏。

        function Rectangle(width, height){            Polygon.call(this, 2);            this.width = width;            this.height = height;            this.getArea = function(){                return this.width * this.height;            };        }

因为Polygon的构造函数是作用域安全的,this对象并非Polygon的实例,所以会创建并返回一个新的对象。Rectangle实例没有sides属性。

 

构造函数窃取结合使用原型链或寄生组合可以解决。

 

3.惰性载入函数

 惰性载入表示函数执行的分支仅会执行一次

方法一:在函数被调用时再处理函数,在第一次调用的过程中,该函数会被覆盖为另一个按合适方式执行的函数,任何对原函数的调用都不再经过执行的分支了。

方法二:在声明函数时就指定适当的函数。

优点:只在执行分支代码时牺牲一点系能。

 

4.函数绑定

 在特定的this环境指定函数调用另一个函数。这技巧常常和回调函数与事件处理程序一起用,以便在将函数作为变量传递的同时保留代码执行环境。

bind()函数接收一个函数和一个环境,并返回一个在给定环境中调用给定函数的寒素,将所有参数原封传递过去。

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

支持原生bind()方法  IE9+  Firefox 4+  Chrome

主要用于事件处理程序和setTimeout()和setInterval()。

 

5.函数柯里化

 用于创建已经设置好了一个或多个参数的函数。使用一个闭包返回一个函数,当函数被调用时,返回的函数还要设置一些传入的参数。

创建:调用另一个函数并未它传入要柯里化的函数和必要参数。

        function curry(fn){            var args = Array.prototype.slice.call(arguments, 1);            return function(){                var innerArgs = Array.prototype.slice.call(arguments),                    finalArgs = args.concat(innerArgs);                return fn.apply(null, finalArgs);            };        }

 

 

(二)防篡改对象

一旦把对象定义为防篡改,就无法取消。

1.不可扩展对象

Object.preventExtensions()方法,不能再给对象添加属性和方法,仍然可以修改和删除已有成员。

Object.inExtensible():确定对象是否可以扩展。

 

2.密封的对象

不可扩展,而且已有成员的[[Configurable]]特性被修改为false,不能删除属性和方法,但可以修改。

使用Object.seal()方法。

Object.isSealed() 检测是否被密封。

 

3.冻结的对象

不可扩展、密封而且对象数据的[[Writable]]特性被设置为false。

Object.freeze()

Object.isFrozen()方法  检测

 

(三)高级定时器

页面载入时,先值任何包含在<script>的代码,通常是页面生命周期后面要用的简单的函数和变量的声明。

1.重复的定时器

链式setTimeout()

setTimeout ( function)(){                 //处理中          setTimeout(arguement.callee,interval);}, interval);

在前一个定时器执行完之前,不会向队列插入新的定时器代码,确保不会有缺失的间隔,避免了连续的运行。主要用于重复计时器。

 

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

 

2.Yielding Progresses

脚本长时间运行通常由两个原因造成:过长的、过深嵌套的函数调用或者进行大量处理的循环。后者是比较容易解决的问题。

数组分块:为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目进行处理,接着再设置另一个定时器。

setTimeout(function (){    //取出下一个条目并处理    var item = array.shift();    process(item);        //若还有条目,再设置另一个定时器    if(array.length>0){           setTimeout (arguments.callee, 100);     }},  100)
        function chunk(array, process, context){            setTimeout(function(){                var item = array.shift();                process.call(context, item);                                if (array.length > 0){                    setTimeout(arguments.callee, 100);                         }            }, 100);                }

 

 

3.函数节流

代码不可以在没有间断的情况下连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。第二次调用时它会清楚前一次的定时器并设置另一个。如果前一个定时器已经执行,则这个操作没有意义。如果未执行,将其代替为新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。

var processor = {      timeoutId: null,            //实际进行处理的方法      performProcessing: function(){            //实际执行的代码      },      //初始化进行调用的方法      process: function(){               clearTimeout(this.timeoutId);               var that = this;               this.timeoutId = setTimeout(function)(){                    that.performProcessing();               }, 100);      }};

 

简化,自动进行定时器的设置和清除

        //参数:要执行的函数  在哪个作用域执行
function throttle(method, scope) { clearTimeout(method.tId); method.tId= setTimeout(function(){ method.call(scope); }, 100); }

 

节流在resize时间最常用。

 

(四)自定义事件

主体负责发布时间,观察者通过订阅时间来观察主体。主体不知道观察者的任何事,它可以独自存在并正常运作即使观察者不存在。

观察者知道主体并能注册事件的回调函数。

自定义事件:创建一个管理事件的对象,让其他对象监听那些事件。

代码中存在多个部分在特定时刻相互交互的情况下,自定义事件非常有必要、

 

(五)拖放

基本概念:创建一个绝对定位的元素,使其可以用鼠标移动。

1.修缮拖动功能

计算元素左上角和指针之间的差,这个值在mousedown事件发生时就确定,并一直保持知道mouseup发生。

 

        var DragDrop = function(){                    var dragging = null,                diffX = 0,                diffY = 0;                        function handleEvent(event){                            //获取事件和目标
event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); //确定事件类型 switch(event.type){ case "mousedown": if (target.className.indexOf("draggable") > -1){ dragging = target; diffX = event.clientX - target.offsetLeft; diffY = event.clientY - target.offsetTop; } 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(){ EventUtil.addHandler(document, "mousedown", handleEvent); EventUtil.addHandler(document, "mousemove", handleEvent); EventUtil.addHandler(document, "mouseup", handleEvent); }, disable: function(){ EventUtil.removeHandler(document, "mousedown", handleEvent); EventUtil.removeHandler(document, "mousemove", handleEvent); EventUtil.removeHandler(document, "mouseup", handleEvent); } } }(); DragDrop.enable();

公共方法enable()和disable()只是相应添加和删除所有的事件处理程序,这两个函数提供了额外的对拖放功能的控制手段。

 

2.添加自定义事件

 先创建一个新的EventTarget对象,然后添加enable()和disable()方法,最后返回这个对象。

 

《javascript高级程序设计》笔记(二十二)