兼容浏览器原生DOM的各种特性总结

868 阅读10分钟
原文链接: segmentfault.com

原生DOM兼容特性

浏览器主要也就是IE有点独特,所以把IE重点区分开

名称 主流 IE
内文本 innerText textContent
请求对象 XMLHttpRequest ActiveXObject
["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"]
监听事件添加 addEventListener(DOM2),
['on'+eventName](DOM0)
attachEvent
监听事件移除 removeEventListener(DOM2),
['on'+eventName]=null(DOM0)
detachEvent
事件对象 function(e) e window.event(IE7之前)
阻止默认事件 preventDefault e.returnValue=false
阻止冒泡 stopPropagation e.cancelBubble=true
键盘事件键盘编码 e.charCode e.keyCode
获取剪贴板的文本 e.clipboardData window.clipboardData
设置剪贴板文本 e.clipboardData.setData("text/plain",value); window.clipboardData.setData("text",value);
触发事件的元素 e.target e.srcElement
获取样式 getComputedStyle(obj,false)[attr];(Firefox浏览器)
obj.style.attr(只对filter,opacity等有效)
obj.style[attr]
obj.currentStyle[attr];
窗口左边的位置 window.screenLeft window.screenX
页面视口大小 window.innerHeight if(document.compatMode=="CSS1Compat")window.documentElement.clientHeight;
if(document.compatMode=="BackCompat")window.body.clientHeight
获取元素 document.getElementById(id); document.all[id];(IE5)
返回指定的属性 ele.getAttribute(attr) ele.attribute[attr]
ele是否存在指定属性 attr ele.hasAttribute(attr) ele.attributes[attr].specified;
鼠标滚动,正数表示向上滚动 function getWheelDelta(e){
if(e.wheelData){
return (client.engine.opera&&client.engine.opera<9.5)?
-e.wheelData:e.wheelData;
}else {
return -e.detail*40;//firefox
}
}
提取选中的文本 textbox.value.subString(textbox.selectionStart,textbox.selectionEnd); document.selection.createRange().text;
(IE8之前没有selectionStart,selectionEnd属性
设置文本选中 textbox.setSelectionRange(startIndex,stopIndex); var range=textbox.createTextRange();
range.collapse(true);
range.moveStart("character",0);
range.moveEnd("character",stopIndex-startIndex);
range.select()

下面是积累下来的一些兼容函数,可以当做模板用

添加多个onload事件

function addLoadEvent(func){
   var oldonload=window.onload;
   if(typeof window.onload!= 'function'){
       window.onload=func;
   }
   else{
       window.onload=function(){
           oldonload();
           func();
       }
   }

}

处理ActiveXObject/XMLHttpRequest问题

//第一种写法,《js高级程序设计》的写法  惰性载入技巧
function createXHR(){
   if(typeof XMLHttpRequest!="undefined"){//XMLHttpRequest
       createXHR=function(){
           return new XMLHttpRequest();
       };
   }else if(typeof ActiveXObject!="undefined"){//IE ActiveXObject
       createXHR=function(){
           if(typeof arguments.callee.activeXString!="string"){
               var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],//IE
                   i,len;
                for(i=0,len=versions.length;i<len;i++){
                   try{
                       new ActiveXObject(versions[i]);
                       arguments.callee.activeXString=version[i];
                       break;
                   }catch(ex){}
                }
               
           }
           return new ActiveXObject(arguments.callee.activeXString);
       };
   }else{
       createXHR=function(){
           throw new Error("fail");
       }
   }
   return createXHR();
}

//第二种写法
function createXHR(){
   if(typeof XMLHttpRequest=="undefined"){
       XMLHttpRequest= function () {
           try{
               return new ActiveXObject("Msxml2.XMLHTTP.6.0");
           }
           catch (e){}

           try{
               return new ActiveXObject("Msxml2.XMLHTTP.3.0");
           }
           catch (e){}

           try{
               return new ActiveXObject("Msxml2.XMLHTTP");
           }
           catch (e){}

           return false;
       }
   }
   return new XMLHttpRequest();
}

请求对象的属性和方法设置

var xhr=createXHR();
xhr.onreadystatechange=function(){//firfox引入onlaod,readyState==4时触发,代替onreadystatechange
    if(xhr.readyState==4){
        try{
            if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
                alert(xhr.responseText);
            }else {
                alert("unsuccessful");
            }
        }catch(ex){}
    }
}
xhr.onload=function(){
    if((xhr.status>=200&&xhr.status<300)||xhr.status==304){
                alert(xhr.responseText);
            }else {
                alert("unsuccessful");
            }
}
xhr.onprogress=function(event){
    if(event.lengthComputable){//进度信息是否可用
        console.log("Received"+event.position+"of"+event.totalSize);
    }
}
xhr.onerror=function(){
    alert("error");
}
xhr.timeout=1000;
xhr.ontimeout=function(){
    alert("timeout");
}
xhr.open("get","example.php",true);
xhr.overrideMimeType("text/xml");
xhr.send(null);//GET

发送表单数据

var form=document.getElementById("info");
xhr.send(serialize(form));//第一种写法
xhr.send(new FormData(form));//第二种写法

跨浏览器的CORS

function createCORSRequest(method,url){
    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 request=createCORSRequest("get","http://www.somewhere");
if(request){
    request.onload=function(){};
    request.send();
}

跨浏览器事件处理程序

var eventUtil={
   // 页面加载完成后
   readyEvent : function(fn) {
       if (fn==null) {
           fn=document;
       }
       var oldonload = window.onload;
       if (typeof window.onload != 'function') {
           window.onload = fn;
       } else {
           window.onload = function() {
               oldonload();
               fn();
           };
       }
   },

   addEventHandler: function (obj, eventName, handler) {
       if (document.attachEvent) {//IE
           obj.attachEvent("on" + eventName, handler);
       } else if (document.addEventListener) {//DOM2级
           obj.addEventListener(eventName, handler, false);//false- 默认。事件句柄在冒泡阶段执行
       }
       else{//DOM0级
           obj['on'+eventName]=handler;
       }
   },

   removeEventHandler:function(obj, eventName, handler){
       if (document.attachEvent) {//IE
           obj.detachEvent("on" + eventName, handler);
       } else if (document.addEventListener) {//DOM2级
           obj.removeEventListener(eventName, handler, false);
       }
       else{//DOM0级
           obj['on'+eventName]=null;
       }
   },
  //获取event对象的引用,取到事件的所有信息,确保随时能使用event;
   getEvent: function (e) {
       var ev = e || window.event;
       if (!ev) {
           var c = this.getEvent.caller;
           while (c) {
               ev = c.arguments[0];
               if (ev && Event == ev.constructor) {
                   break;
               }
               c = c.caller;
           }
       }
       return ev;
   },
   //事件类型
   getType: function (e) {
       return e.type;

   },
   //调用事件的元素
   getElement: function (e) {
       return e.target|| e.srcElement;
   },
   //阻止默认事件
   preventDefault: function (e) {
       e= this.getEvent(e);
       if(e.preventDefault){
           e.preventDefault();
       }
       else {
           return e.returnValue=false;//IE
       }
   },
   //阻止冒泡
   stopPropagation:function(e) {
     if(e.stopPropagation){
         e.stopPropagation();
     }
       else {
         e.cancelBubble=true;//IE
     }

   },
   //键盘事件键盘的编号
   getCharCode:function (e){
       if(typeof e.charCode=="number")return e.charCode;
       else return e.keyCode;
   },
   //获取剪贴板的文本
   getClipbordText:function(e){
       var clipboardData=(e.clipboardData||window.clipboardData);
       return clipboardData.getData("text");
   },
   //设置剪贴板文本
   setClipboardText:function(e,value){
       if(e.clipboardData){
           return e.clipboardData.setData("text/plain",value);
       }else if(window.clipboardData){
           return window.clipboardData.setData("text",value);
       }
   },

}

处理target/srcelemnt问题,代替this

function getActivatedObject(e) {
   var obj;
   if (!e) {
       // early version of IE
       obj = window.event.srcElement;
   } else if (e.srcElement) {
       // IE 7 or later
       obj = e.srcElement;
   } else {
       // DOM Level 2 browser
       obj = e.target;
   }
   return obj;
}

实现insertAfter

/**
* 把newElement插在targetElement后面 ,js的API只有insertBefore,没有insertAfter
*/
function insertAfter(newElement,targetElement) {
   var parent = targetElement.parentNode;
   if (parent.lastChild == targetElement) {
       parent.appendChild(newElement);
   } else {
       parent.insertBefore(newElement,targetElement.nextSibling);
   }
}

给element加上类名

function addClass(element,value) {
   if (!element.className) {
       element.className = value;
   } else {
       newClassName = element.className;
       newClassName+= " ";
       newClassName+= value;
       element.className = newClassName;
   }
}

判断是不是数组

function isArray(arg) {
   //return Object.prototype.toString.call(arr)=='[Object Array]';这种方法也可以
   if (typeof arg == 'object') {
       //所有数组都有一个包含单词'arry'的constructor,最后的i表示不区分大小写
       var criteria = arg.constructor.toString().match(/array/i);
       return (criteria != null);
   }
   return false;
}

IE10之前不支持docunment.getElementByClassName

function getByClass(clsName,parent){
   if(docunment.getElementByClassName) return docunment.getElementByClassName(clsName);
   //IE10之前
   var oParent=parent?document.getElementById(parent):document,
       eles=[],
       elements=oParent.getElementsByTagName('*');

   for(var i=0,l=elements.length;i<l;i++){
       if(elements[i].className==clsName){
           eles.push(elements[i]);
       }
   }
   return eles;
}

获取css样式

function getStyle(obj,attr){
   if(obj.currentStyle) {//IE 浏览器
       return obj.currentStyle[attr];
   }else{//Firefox浏览器
       return getComputedStyle(obj,false)[attr];
   }
}

手写动画

//动画 startMove(oLi,{width:400,height:200,opacity:100})
function startMove(obj,json,fn){
   clearInterval(obj.timer);
   obj.timer=setInterval(function () {
       for(var attr in json){
           var cur=0;
           if(attr=='opacity'){
               cur=Math.round(parseFloat(getStyle(obj,attr))*100);
           }
           else {
               cur=parseInt(getStyle(obj,attr));
           }
           var speed=(json[attr]-cur)/8;
           speed=speed>0?Math.ceil(speed):Math.floor(speed);
           var flag=true;
           if(cur!=json[attr]){//使得所有属性做完运动才结束
               flag=false;
           }
           if(attr=='opacity'){
               obj.style.filter='alpha(opacity:'+(cur+speec)+')';
               obj.style.opacity=(cur+speed)/100;
           }
           else{
               obj.style[attr]=(cur+speed)+'px';
           }
       }
       if(flag){
           clearInterval(obj.timer);
           if(fn){
               fn();
           }
       }
   })
}

取得窗口左边和上边的位置

var leftPos=(typeof window.screenLeft =="number")?window.screenLeft:window.screenX;

取得页面视口大小

var pageWidth=window.innerWidth,
   pageHeight=window.innerHeight;
   if(typeof pageHeight!="number"){
       if(document.compatMode=="CSS1Compat"){//标准模式
           pageHeight=window.documentElement.clientHeight;
           pageWidth=window.documentElement.clientWidth;
       }
       else {//BackCompat
           pageHeight=window.body.clientHeight;
           pageWidth=window.body.clientWidth;
       }
   }

检测插件方法

/**
*
* 检测插件方法一,IE下无效
*/
function hasPlugin(name){
   name=name.toLowerCase();
   for(var i=0;i<navigator.plugins.length;i++){
       if(navigator.plugins[i].toLowerCase().indexOf(name)!=-1)
       return true;
   }
   return false;
}

/**
* 检测IE插件方法二 ,name是COM对象唯一标识符
*/
function hasIEPlugin(name){
   try{
       new ActiveXObject(name);
       return true;
   }catch(ex){
       return false;
   }
}

获取元素

function getElement(id){
   if(document.getElementById){
       return document.getElementById(id);
   }else if(document.all){//IE5前
       return document.all[id];
   }else {
       throw new Error("no way to retrieve element!");
   }
}

检查对象的某个特性是否存在

function isHostMethod(object,property){
   var t =typeof object[property];
   return t=="function"||
          (!!(t=="object")&&object[property])||
          t=="unknown";//不懂
}

对象转换成数组

function convertToArray(nodes){
   var array=null;
   try{
       array=Array.propotype.slice.call(nodes,0);//IE8前无效
   }catch(ex){
       for(var i=0,len=nodes.length;i<len;i++){
           array.push(nodes[i]);
       }
   }
}

返回指定的属性

/**
* 
* 返回指定的属性 IE ele.attribute[]
* Element.getAttribute()
*/
function outputAttribute(ele){
   var pairs=new Array(),
       attrname,attrvalue,u,len;
   for(i=0,len=ele.attribute.length;i<len;i++){
       attrname=ele.attributes[i].nodeName;
       attrvalue=ele.attributes[i].nodeValue;
       if(ele.attributes[i].specified){//IE
           pairs.push(attrname+'=\"'+attrvalue+'\"');
       }
   }
}

ele是否存在指定属性

/**
* 
* ele是否存在指定属性 attr
*/
function hasattribute(ele,attr){
   if(ele.hasAttribute){
       return ele.hasAttribute(attr);
   }else {//IE
       return ele.attributes[attr].specified;
   }
}

ele是否符合选择器selector

/**
* ele是否符合选择器selector
*/
function matchesSelector(ele,selector){
   if(ele.matchesSelector){
       return ele.matchesSelector(selector);
   }else if(ele.msmatchesSelector){
       return ele.msmatchesSelector(selector);
   }else if(ele.mozmatchesSelector){
       return ele.mozmatchesSelector(selector);
   }else if(ele.webkitmatchesSelector){
       return ele.webkitmatchesSelector(selector);
   }else{
       throw new Error("not support");
   }
}

获取内文本

//innerText/textContent
function getInnerText(ele){
   return (typeof ele.innerText=="string")?
   ele.innerText:ele.textContent;
}

获取鼠标事件的父元素

   function getRelatedTarget(e){
       if(e.relatedTarget) return e.relatedTarget;
       else if(e.fromElement) return e.fromElement;//mouseover
       else if(e.toElement) return e.toElement;//mouseout
       else return null;
   }

探测按的是鼠标的哪个键

function getButton(e){
       if(document.implementation.hasFeature("MouseEvents","2.0")){
           return e.button;
       }else {
           switch(e.button){
               case 0 :
               case 1:
               case 3:
               case 5:
               case 7: return 0;
               case 2:
               case 6:return 2;
               case 4:return 1;
           }
       }
   }

鼠标滚动事件

//鼠标滚动,正数表示向上滚动
function getWheelDelta(e){
       if(e.wheelData){
           return (client.engine.opera&&client.engine.opera<9.5)?
       -e.wheelData:e.wheelData;
       }else {
           return -e.detail*40;//firefox
       }
       
}

提取选中的文本

function getSelectedText(textbox){
   if(typeof selectionStart=="number"){
       return textbox.value.subString(textbox.selectionStart,textbox.selectionEnd);
   }else if(document.selection){//IE8之前没有selectionStart,selectionEnd属性
       return document.selection.createRange().text;
   }
}

设置文本选中

function selectText(textbox,startIndex,stopIndex){
    if(textbox.setSelectionRange){
        textbox.setSelectionRange(startIndex,stopIndex);
    }else if(textbox.createTextRange){//IE
        var range=textbox.createTextRange();
        range.collapse(true);
        range.moveStart("character",0);
        range.moveEnd("character",stopIndex-startIndex);
        range.select();
    }
}

bind方法对老版本的浏览器不起作用


Function.prototype.bind = Function.prototype.bind || function(context){
     var self = this;

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

包装cookie



//cookie
var cookieUtil={
   // 创建cookie
   setcookie:function (name, value, expires, path, domain, secure) {
   var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value);
   if (expires instanceof Date) {
       cookieText += '; expires=' + expires.toGMTString();
   }
   if (path) {
       cookieText += '; path=' + path;
   }
   if (domain) {
       cookieText += '; domain=' + domain;
   }
   if (secure) {
       cookieText += '; secure';
   }
   document.cookie = cookieText;
},
   // 获取cookie
   getcookie:function (name) {
   var cookieName = encodeURIComponent(name) + '=';
   var cookieStart = document.cookie.indexOf(cookieName);
   var cookieValue = null;
   if (cookieStart > -1) {
       var cookieEnd = document.cookie.indexOf(';', cookieStart);
       if (cookieEnd == -1) {
           cookieEnd = document.cookie.length;
       }
       cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
   }
   return cookieValue;
},
  // 删除cookie
   unsetcookie:function (name,path,domain,secure) {
   this.setcookie(name,"",new Date(0),path,domain,secure);
}
}

包装子cookie

//子cookie
var subcookieUtil={
   get:function(name,subname){
       var subcookie=getAll(name);
       if(subcookie){
           return subcookie[subname];
       }else {
           return null;
       }
   },
   getAll:function(name){
       var cookieName = encodeURIComponent(name) + '=';
       var cookieStart = document.cookie.indexOf(cookieName);
       var cookieValue = null;
       var subcookie,result={};
       var len,i,parts;
       if (cookieStart > -1) {
           var cookieEnd = document.cookie.indexOf(';', cookieStart);
           if (cookieEnd == -1) {
           cookieEnd = document.cookie.length;
          }
       cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
       if(cookieValue.length>0){
           subcookie=cookieValue.split('&');
           len=subcookie.length;
           for(i=0;i<llen;i++){
               parts=subcookie[i].split('=');
               result[parts[0]]=parts[1];
           }
           return result;
       }
      }
   return null;
   },
   
   set:function (name, subname,value, expires, path, domain, secure) {
       var subcookies=this.getAll(name)||{};
       subcookies[subname]=value;
       this.setAll(name,subcookies,expires,path,domain,secure);
   },
   
   setAll:function(name,subcookies,expires,path,domain,secure){
       var cookieText = encodeURIComponent(name) + '=';
       var subcookiesParts=new Array();
       var subname;
       for(subname in subcookies){
           if(subname.length>0&&subcookies.hasOwnProperty(subname)){
               subcookiesParts.push(encodeURIComponent(subname)+'='+encodeURIComponent(subcookies[subname]));
           }
       }
       if(subcookiesParts.length>0){
           cookieText+=subcookiesParts.join('&');
           if (expires instanceof Date) {
               cookieText += '; expires=' + expires.toGMTString();
           }
           if (path) {
               cookieText += '; expires=' + expires;
            }
           if (domain) {
               cookieText += '; domain=' + domain;
           }
           if (secure) {
               cookieText += '; secure';
           }
       }
   
       document.cookie = cookieText;
   },
   unset:function(name,subname,path,domain,secure){
         var subcookies=this.getAll(name);
       if(subcookies){
             delete subcookies[subname];
             this.setAll(name,subcookies,null,path,domain,secure);
         }
       },
   unsetAll:function(name,path,domain,secure){
         this.setAll(name,null,new Date(0),path,domain,secure);
   }
}

indexedDB


var indexedDB=window.indexedDB||window.mozIndexedDB||window.msIndexedDB||window.webkitIndexedDB;
var idbRequest=indexedDB.open('vvv');
idbRequest.onsuccess=function(event){
   database=event.target.result;
}
idbRequest.onerror=function(event){
   alert(event.target.errorCode);
}

手写typeof

function type(obj) {
   var toString = Object.prototype.toString;
   var map = {
       '[object Boolean]'  : 'boolean', 
       '[object Number]'   : 'number', 
       '[object String]'   : 'string', 
       '[object Function]' : 'function', 
       '[object Array]'    : 'array', 
       '[object Date]'     : 'date', 
       '[object RegExp]'   : 'regExp', 
       '[object Undefined]': 'undefined',
       '[object Null]'     : 'null', 
       '[object Object]'   : 'object'
   };
   if(obj instanceof Element) {//因为对不同标签,toString会返回对应不同标签的构造函数
       return 'element';
   }
   return map[toString.call(obj)];
}

深度克隆

/**
* 
* 深度克隆方法一,用的是instanceof
*/
function clone(Obj) {
   var buf;   
   if (Obj instanceof Array) {
       buf = [];  // 创建一个空的数组
       var i = Obj.length;
       while (i--) {
           buf[i] = clone(Obj[i]);
       }
       return buf;
   } else if (Obj instanceof Object){
       buf = {};  // 创建一个空对象
       for (var k in Obj) {  // 为这个对象添加新的属性
           buf[k] = clone(Obj[k]);
       }
       return buf;
   }else{
       return Obj;
   }
}

/**
* 
* 深度拷贝方法二,用的是 toString
*/
function deepClone(data) {
   var t = type(data), o, i, ni;
   
   if(t === 'array') {
       o = [];
   }else if( t === 'object') {
       o = {};
   }else {
       return data;
   }
   
   if(t === 'array') {
       for (i = 0, ni = data.length; i < ni; i++) {
           o.push(deepClone(data[i]));
       }
       return o;
   }else if( t === 'object') {
       for( i in data) {
           o[i] = deepClone(data[i]);
       }
       return o;
   }
}

//通过JSON.stringify一下,然后再JSON.parse一下,就能实现深拷贝。但是数据类型只支持基本数值类型。
var obj = {
   a: 'a',    
   b: function(){console.log('b')}
}
JSON.stringify(obj);// "{"a":"a"}"

组合使用构造函数模式和原型模式创建对象

//组合使用构造函数模式和原型模式创建对象
function Person(name,age){
   this.name=name;
   this.age=age;
   this.friends=["may","john"];
}
Person.prototype={
   constructor:Person,//字面量形式的原型默认构造函数是object,所以在这里要指定constructor
   sayName=function(){
       alert(this.name);
   }
}

组合继承

//组合继承
funcion super(name){
   this.name=name;
   this.color=["red","blue"];
   
}
Super.prototype.sayname=function(){
   alert(this.name);
}
function Sub(age){
   Super.call(this);
   this.age=age;
}
Sub.prototype=new Super();//
Sub.prototype.constructor=Sub;//这个很重要!!
Sub.prototype.sayage=function(){
   alert(this.age);
}

观察者模式

//观察者模式
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 i=0, len=handlers.length; i < len; i++){
                handlers[i](event); 
           }
        }
    },
    removeHandler: function(type, handler){ 
       if (this.handlers[type] instanceof Array){ 
           var handlers = this.handlers[type]; 
           for (var i=0, len=handlers.length; i < len; i++){ 
               if (handlers[i] === handler){ 
                   break;
                }
            }
            handlers.splice(i, 1); 
         }
     }
};

分享一个题目


//[附加题] 请实现下面的自定义事件 Event 对象的接口,功能见注释(测试1)
//该 Event 对象的接口需要能被其他对象拓展复用(测试2)
//测试1
Event.on('test', function (result) {
   console.log(result);
});
Event.on('test', function () {
   console.log('test');
});
Event.emit('test', 'hello world'); // 输出 'hello world' 和 'test'
// 测试2
var person1 = {};
var person2 = {};
Object.assign(person1, Event);
Object.assign(person2, Event);
person1.on('call1', function () {
   console.log('person1');
});
person2.on('call2', function () {
   console.log('person2');
});
person1.emit('call1'); //输出 'person1'
person1.emit('call2'); // 没有输出
person2.emit('call1'); // 没有输出
person2.emit('call2'); // 输出 'person2'
var Event = {
   // 通过on接口监听事件eventName
   // 如果事件eventName被触发,则执行callback回调函数
   on: function (eventName, callback) {
       //你的代码
       if(!this.handles){
           //Object.assign(target, source);
//这个是ES6的新对象方法,用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
           Object.defineProperty(this, "handles", {
               value: {},
               enumerable: false,//关键!
               configurable: true,
               writable: true
           })
       }
      
      if(!this.handles[eventName]){
           this.handles[eventName]=[];
      }
      this.handles[eventName].push(callback);
   },
   // 触发事件 eventName
   emit: function (eventName) {
       //你的代码
      if(this.handles[arguments[0]]){
          for(var i=0;i<this.handles[arguments[0]].length;i++){
              this.handles[arguments[0]][i](arguments[1]);
          }
      }
   }
};
//输出所有页面宽度和高度大于50像素的节点。
function traverse(oNode) {
   var aResult = [];
   oNode = oNode || document.body;
   if (oNode.style) {
       var nWidth = window.parseInt(oNode.style.width, 10) || 0;
       var nHeight = window.parseInt(oNode.style.height, 10) || 0;
       if (nWidth > 50 && nHeight > 50) {
           aResult.push(oNode);
       }
   }
   var aChildNodes = oNode.childNodes;
   if (aChildNodes.length > 0) {
       for (var i = 0, l = aChildNodes.length; i < l; i++) {
           var oTmp = aChildNodes[i];
           aResult = aResult.concat(traverse(oTmp));
       }
   }
   return aResult;
}