libin1991 / libin_Blog

爬虫-博客大全
https://libin.netlify.com/
124 stars 17 forks source link

【5星级】JS 面试原生常用api 方法大全 #476

Open libin1991 opened 6 years ago

libin1991 commented 6 years ago

1  1525765911860 1525768647835 1525679332154

1525664552986 1525664584249

1525664411514

image 1524546360877

image

1522401583519

default image image image image image image

 <!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">
            var arr = [
                [1, 2, 2],
                [3, 4, 5, 5],
                [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10
            ];
            var newArr = [];

            function changeArr(arr) {
                for(var i = 0; i < arr.length; i++) {
                    //遍历arr数组得每一个元素,这里也可以用forEach
                    if(arr[i] instanceof Array) {
                        //判断元素是否为数组
                        changeArr(arr[i])
                        //元素为数组则继续调用changeArr方法遍历
                    } else {
                        newArr.push(arr[i])
                        //元素不是数组则可以直接push进新数组
                    }
                }
                //此上的判断也可以直接用三元表达式
            }
            changeArr(arr);
            console.log(newArr);    //[1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]

        </script>
    </head>

    <body>
    </body>

</html>

方案一(仅去重):

var rusultArr=newArr.reduce(function(rusultArr,a){
         if(rusultArr.indexOf(a)==-1){
                rusultArr.push(a)
                }
                return rusultArr
        },[])

方案二(去重加排序):

rusultArr=Array.from(new Set(newArr)).sort(function(a,b){return a-b})

   ##   [参考https://juejin.im/post/5abde97df265da23884d2b91](https://juejin.im/post/5abde97df265da23884d2b91)
camelize方法,转换为驼峰命名风格

function camelize (target) {
        if (target.indexOf('_') < 0 && target.indexOf('_') < 0) {
            return target; //提前判断,提高响应效率        }
        return target.replace(/[-_][^-_]/g , function(match){
            return match.charCodeAt(1).toUpperCase();
        })
    }

underscored方法。转换为下划线风格

function underscored(target){
        return target.replace(/([a-z\d])([A-Z])/g , '$1_$2').replace(/\-/g , '_').toLowerCase();
    }

truncate方法:用于对字符串进行截断处理。当超过限定长度,默认添加3个点号。

function truncate(target, length, truncation) {
    length = length || 30;
    truncation = truncation === void(0) ? '...' : truncation;
    return target.length > length ?
            target.slice(0, length - truncation.length) + truncation : String(target);
}

camelize方法:转换为驼峰风格。

function camelize(target) {
    if (target.indexOf('-') < 0 && target.indexOf('') < 0) {
        return target;//提前判断,提高getStyle等的效率
    }
    return target.replace(/[-][^-]/g, function(match) {
        return match.charAt(1).toUpperCase();
    });
}

underscored方法:转换为下划线风格。

function underscored(target) {
    return target.replace(/([a-z\d])([A-Z])/g, '$1$2').
            replace(/-/g, '').toLowerCase();
}

dasherize方法:转换为连字符风格,即CSS变量的风格。

function dasherize(target) {
    return underscored(target).replace(//g, '-');
}

capitalize方法:首字母大写。

function capitalize(target) {
    return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();
}

POST和GET的区别,HTTP状态码

POST和GET的区别
GET在浏览器回退时是无害的,而POST会再次提交请求
GET产生的URL地址可以被收藏,而POST不可以
GET请求会被浏览器主动缓存,而POST不会,除非手动设置
GET请求只能进行URL编码,而POST支持多种编码方式
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
GET请求在URL中传送的参数是有长度限制的,而POST没有长度限制
对参数的数据类型,GET只能请求ASCII字符,而POST没有限制
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传敏感信息
GET参数通过URL传递,POST放在Request body中

HTTP状态码
1XX:指示信息-表示请求已接受,继续处理
2XX:成功-表示请求已被成功接收200 OK :客户端请求成功
206 Partial Content:客户发送一个带有Range头的GET请求,服务器完成了它 播放视频和音频
3XX:重定向-要完成请求必须进行更进一步的操作301 Move Permanently:所请求的页面已经转移至新的URL
302 Found:所请求的页面已经临时转移到新的URL
304 Not Modified:客户端有缓冲的文档并发出一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
4XX:客户端错误-请求有语法错误或请求无法实现400 Bad Request:客户端请求有语法错误,不能被服务器所理解
401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden:对被请求页面的访问被禁止
404 Not Found:请求资源不存在
5XX:服务错误-服务器未能实现合法的请求500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
503 Server Unavailable:请求未完成,服务器临时过载或当机,一段事件后恢复正常

301与302:二者都是进行重定向,前者为永久重定向,后者为临时重定向。301,302对用户来说没有区别,他们看到效果只是一个跳转,浏览器中旧的URL变成了新的URL。实际工作中,当我们的前一个域名被永久性停止使用,并且不希望用户还能访问以前的域名时,我们会用到301。

www.cnblogs.com/milo-wjh/p/…

有时可能需要在pagehide事件触发时根据persisted的值采取不同的操作。
对于pageshow事件,如果页面是从bfcache中加载的,那么persisted的值就是true;
对于pagehide事件,如果页面在加载之后会保存在bfcache中,那么persisted的值也会被设置为ture。
因此,当第一次触发pageshow时,persisted的值一定是false,而在第一次触发pagehide时,persisted就会变成true(除非页面不会保存在bfcache中)。指定了onunload事件处理程序的页面会被自动排除在bfcache之外,即使事件处理程序是空的。
具体原因:
onunload最常用于撤销在onload中所执行的操作,而跳过onload后再次显示页面很可能会导致页面不正常。 

微信后退不刷新【JS不执行】:
+    var isPageHide = false;
+    window.addEventListener('pageshow', function () {
+        if (isPageHide) {
+            window.location.reload();
+        }
+    });
+    window.addEventListener('pagehide', function () {
+        isPageHide = true;
+    }); 

www.cnblogs.com/blogcxz/p/7…

var a = 1;

functionfoo() {
  if (!a) {
    var a = 2;
  }
  alert(a);
};

foo();   //2

CommonJS,AMD,CMD,ES6 

commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。 

CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。此规范其实是在sea.js推广过程中产生的。 

/ AMD写法 / define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {       // 等于在最前面声明并初始化了要用到的所有模块     a.doSomething();     if (false) {         // 即便没用到某个模块 b,但 b 还是提前执行了         b.doSomething()     }  });

/ CMD写法 / define(function(require, exports, module) {     var a = require('./a'); //在需要时申明     a.doSomething();     if (false) {         var b = require('./b');         b.doSomething();     } }); 

ES6 模块是动态引用:ES6的模块不是对象,import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。 

ES6 模块与 CommonJS 模块的差异

1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用 CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。 ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。 2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。 编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

default

大数相加 http://www.cnblogs.com/kindofblue/p/4672129.html

1522401583519

/*let a;
async functioncorrect() {
    try {
        await Promise.reject('error1')
    } catch (error) {
        console.log(error);
    }
    a = await 1;
    return a;
}

correct().then(v => console.log(a)); // 1
*/

let a;
async functionf() {
    await Promise.reject('error');
    a = await 1; // 这段 await 并没有执行
}
f().then(v => console.log(a));

@font-face {
  font-family: 'source';
  src: url('../fonts/source.eot');
  src:
    url('../fonts/source.eot?#font-spider') format('embedded-opentype'),
    url('../fonts/source.woff2') format('woff2'),
    url('../fonts/source.woff') format('woff'),
    url('../fonts/source.ttf') format('truetype'),
    url('../fonts/source.svg') format('svg');
  font-weight: normal;
  font-style: normal;
}

.home h1, .demo > .test {
    font-family: 'source';
}

var EventUtil = {   //事件处理程序
    addHandler:function(element,type,handler){
        if(element.addEventListener){//检测是否存在DOM2
            element.addEventListener(type,handler,false)
        }elseif(element.attachEvent){//存在ie
            element.attachEvent('on'+type,handler)
        }else{//DOM0
            element['on'+type]=handelr;
        }
    },
    removeHandler:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);
        }elseif(element.detachEvent){
            element.detachEvent('on'+type,handler);
        }else{
            element['on'+type]=null;
        }
    }
}

构造函数
自己的想法
普通的函数就像是按步骤执行的动作,而构造函数更像是可更改零件的木偶,普通函数可以直接调用,但是构造函数需要new
因为构造函数也是函数,所以可以直接被调用,但是它的返回值为undefine,此时构造函数里面的this对象等于全局this对象
扩展实例和对象的区别,从定义上来讲:1、实例是类的具象化产品,2、而对象是一个具有多种属性的内容结构。funciton Foo(name,age){
  this.name = name;
  this.age = age;
  this.class = 'class-1';
  // return this //默认有这一行
}
var f = new Foo('zhangsan',20); //实例化对象
// var f1 = new Foo('lisi',22) //创建多个对象
构造函数-扩展
var a = {} 其实是 var a = new Object()的语法糖
var a = [] 其实是 var a = new Array()的语法糖
functionFoo(){...}其实是 var Foo = new Function(...)
使用 instanceof 判断一个函数是否是一个变量的构造函数如果想判断一个变量是否为“数组”:变量 instanceof Array 

instanceof
用于判断引用类型属于哪个构造函数的方法
f instanceof Foo 的判断逻辑是:
f的__proto__一层一层往上走,是否能对应到Foo.prototype
再试着判断f instanceof Object

magin重叠:边界叠加的大多数问题可以通过添加透明边框或1px的补白来修复。

补充解决方案:
1.外层padding
2.透明边框border:1pxsolidtransparent;
3.绝对定位postion:absolute:
4.外层DIVoverflow:hidden;
5.内层DIV 加float:left;display:inline;
6.外层DIV有时会用到zoom:1;

// Adding extend function to Object.prototype
Object.prototype.extend = function(obj) {
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            this[i] = obj[i];
        }
    }
};

var objA = {"name": "colin", "car": "suzuki"};
var objB = {"name": "james", "age": 17};

objA.extend(objB);
objA; // {"name": "james", "age": 17, "car": "suzuki"};

// Lodash
_.assign(objA, objB);

//assign 实现浅复制
var _extends = Object.assign || function(target) {
                for(var i = 1; i < arguments.length; i++) {
                    var source = arguments[i];
                    for(var key insource) {
                        if(Object.prototype.hasOwnProperty.call(source, key)) {
                            target[key] = source[key];
                        }
                    }
                }
                return target;
            };

functionsum() {
                var cur = Array.prototype.slice.call(arguments).reduce(function(a,b){
                    return a+b; 
                },0);
                functionadd() {
                    return arguments.length==0 ? cur : (cur+=Array.prototype.slice.call(arguments).reduce(function(a,b){return a+b},0), add);
                };
                return arguments.length==0 ? 0 : add;
            }
            console.log(sum());  //0
            console.log(sum(2)(3)());   //5
            console.log(sum(2,3)());    //5
            console.log(sum(2,3,5)());   //10
            console.log(sum(2,3,5)(5)());   //15
            console.log(sum(2,3,5)(5)(5,5)());   //25

functionadd() {
    var a = arguments[0];
    if(arguments.length == 2) {
        return a + arguments[1];
    } else {
            returnfunction(b) {
            return a + b;
            }
    }
}

console.log(add(2, 3));   //5
console.log(add(2)(3));   //5 

add(1)(2)(3)调用方式的方法:
var add = function(a){
    returnfunction(b){
        returnfunction(c){
            return a+b+c;
        };
    };
};

add(1)(2)(3); //6

首先要一个数记住每次的计算值,所以使用了闭包,在tmp中记住了x的值,第一次调用add(),初始化了tmp,并将x保存在tmp的作用链中,然后返回tmp保证了第二次调用的是tmp函数,后面的计算都是在调用tmp, 因为tmp也是返回的自己,保证了第二次之后的调用也是调用tmp,而在tmp中将传入的参数与保存在作用链中x相加并付给sum,这样就保证了计算;
但是在计算完成后还是返回了tmp这个函数,这样就获取不到计算的结果了,我们需要的结果是一个计算的数字那么怎么办呢,首先要知道JavaScript中,打印和相加计算,会分别调用toString或valueOf函数,所以我们重写tmp的toString和valueOf方法,返回sum的值;
function add(x) {
    var sum = x;
    var tmp = function (y) {
        sum = sum + y;
        return tmp;
    };
    tmp.toString = function () {
        return sum;
    };
    return tmp;
}
console.log(add(1)(2)(3));  //6
console.log(add(1)(2)(3)(4));   //10

1522401583519

1. for of和迭代器
ES5中,forEach可以用来遍历数组元素,但它的缺陷是不能使用break语句中断循环,也不能使用return语句返回到外层函数。
强大的for-of循环
for (let value of [1, 2, 3]) {
  console.log(value); //输出 1 2 3
}
最简洁、最直接的遍历数组元素的语法
----------------------------------------------------------------------------------------------
这个方法避开了for-in循环的所有缺陷
与forEach()不同的是,它可以正确响应break、continue和return语句其它集合也支持for-of循环
for-of循环不仅支持数组,还支持大多数类数组对象,例如DOM的NodeList对象。
----------------------------------------------------------------------------------------------
它也支持字符串:
for (let chr of "abc12") {
    console.log(chr); // 输出 "a""b""c""1""2"
}
另外还支持Map和Set对象的遍历。
深入理解
正如其它语言中的for/foreach语句一样,for-of循环语句通过方法调用来遍历各种集合。数组、Map、Set以及我们讨论的其它对象有一个共同点,它们都有一个迭代器方法。
任何对象都可以有/添加迭代器方法。
就像为对象添加myObject.toString()方法,JS知道怎么把这个对象转化为字符串;你为对象添加迭代器方法myObject[Symbol.iterator](),JS也就知道了如何遍历这个对象。
[Symbol.iterator]语法看起来很怪。Symbol是ES6引入的新类型,标准定义了全新的symbol(如Symbol.iterator),来保证不与任何已有代码产生冲突。
任何有迭代器方法[Symbol.iterator]()的对象都是可迭代的。 

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

1522401583519

前端如何设置缓存
通过meta标签的http-equiv属性进行设置
         <meta http-equiv="pragram" content="no-cache">
         <meta http-equiv="cache-control" content="no-cache, must-revalidate">
在ajax请求中通过setRequestHeaders这个方法设置如果还有其他的设置方法,欢迎留言指教。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

var target = { a: 1 };

var source1 = { b: 2 };
var source2 = { c: 3 };

Object.assign(target, source1, source2);    //改变了target ,注意和{...a,...b}的区别
target // {a:1, b:2, c:3}   

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

var a={
  "a":{
    "b":{
       "c":100,
       "d":200
     }
  }
};
var b={
  "a":{
    "b":{
       "e":100
     }
  }
}

console.log({...{},...a,...b})

console.log(a)

//ES5
"use strict";

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key insource) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var a = {
  "a": {
    "b": {
      "c": 100,
      "d": 200
    }
  }
};
var b = {
  "a": {
    "b": {
      "e": 100
    }
  }
};

console.log(_extends({}, a, b));

console.log(a);

构造函数 new
JS中的函数即可以是构造函数又可以当作普通函数来调用,当使用new来创建对象时,
对应的函数就是构造函数,通过对象来调用时就是普通函数。
普通函数的创建有:显式声明、匿名定义、new Function() 等三种方式。
当通过new来创建一个新对象时,JS底层将新对象的原型链指向了构造函数的原型对象,
于是就在新对象和函数对象之间建立了一条原型链,通过新对象可以访问到函数对象原型prototype中的方法和属性。

1.创建一个新的空对象;
2.将this绑定到该对象;
3.添加一个名为__proto__的新属性,并且指向构造函数的原型(prototype);
4.返回该this对象。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

setTimeout(fn, 0) 的作用

setTimeout(0)单线程和异步队列

setTimeout和setInterval是JS内置的两个定时器,使用很简单,但这两个方法背后的原理却不简单。
我们知道,JS是单线程语言,在浏览器中,当JS代码被加载时,浏览器会为其分配一个主线程来执行任务(函数),
主线程会形成一个全局执行环境,执行环境采用栈的方式将待执行任务按顺序依次来执行。
但在浏览器中有一些任务是非常耗时的,比如http请求、定时器、事件回调等,为了保证其他任务的执行效率不被影响,
JS在执行环境中维护了一个异步队列(也叫工作线程),并将这些任务放入队列中进行等待,这些任务的执行时机并不确定,
只有当主线程的任务执行完成以后,才会去检查异步队列中的任务是否需要开始执行。这就是为什么setTimeout(fn,0)
 始终要等到最后执行的原因。关于单线程和异步队列问题请参考:setTimeout(0)

Array的reduce方法   //educe需要两个参数,一个是回调函数,
一个是初始值,没有初始值,会默认把数组第一个当初始值,并从第二个开始
arr.reduce(callback,[initialValue])

callback
    执行数组中每个值的函数,包含四个参数
    accumulator上一次调用回调返回的值,或者是提供的初始值(initialValue)
    currentValue数组中正在处理的元素
    currentIndex数据中正在处理的元素索引,如果提供了 
    initialValue ,从0开始;否则从1开始array调用 reduce 的数组
initialValue可选项,其值用于第一次调用 callback 的第一个参数。

var sum = [0, 1, 2, 3].reduce(function(a, b) {
    return a + b;
}, 0);
// sum is 6

var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function(allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});   //{}就是上面的allNames,这里当做默认值
// countedNames is { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

Array.from()   //扩展运算符(…)也可以将某些数据结构转为数组
将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)
//类似数组的对象
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
  console.log(p);
});
// arguments对象
functionfoo() {
  var args = Array.from(arguments);
  // ...
}

Array.of()
用于将一组值,转换为数组
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

深拷贝浅拷贝

1、浅拷贝  //Object.assign() //浅拷贝,类似{...obj1,...obj2} 都是浅拷贝
拷贝就是把父对象的属性,全部拷贝给子对象。
接下来,我们看一个拷贝的例子:
function extendCopy(b) {
  var a = {};
  for (var i in b) {
    a[i] = b[i];
  }
  return a;
}
调用的时候,这样写:
// 调用
var copyA = {
  titleA: '标题A'
};
var copyB = extendCopy(copyA);
console.log(copyB.titleA); // 标题A
但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
接下来,我们看一个篡改的示例:
function extendCopy(b) {
  var a = {};
  for (var i in b) {
    a[i] = b[i];
  }
  return a;
}
// 调用
var copyA = {
  arrayA: [1, 2, 3, 4]
};
var copyB = extendCopy(copyA);
copyB.arrayA.push(5);
console.log(copyA.arrayA); // [1, 2, 3, 4, 5]
结果是增加了一个5。
所以,extendCopy() 只是拷贝了基本类型的数据,我们把这种拷贝叫做“浅拷贝”。

//数组深拷贝   [].concat(arr1),   var =[...arr1],   arr1.slice(0)

2、深拷贝
function deepCopy(p, c) {
  var c = c || {};
  for (var i in p) {
    if (typeof p[i] === 'object') {
      c[i] = (p[i].constructor === Array) ? [] : {};
      deepCopy(p[i], c[i]);
    } else {
      c[i] = p[i];
    }
  }
  return c;
}
// 调用
var copyA = {
    arrayA: [1, 2, 3, 4]
};
var copyB = deepCopy(copyA);
copyB.arrayA.push(5);
console.log(copyA.arrayA); // [1, 2, 3, 4]

Element.matches 精确匹配

document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性处理
  var event = e || window.event;
  var target = event.target || event.srcElement;
  if (target.matches('li.class-1')) {
    console.log('the content is: ', target.innerHTML);
  }
});

移动端touch事件(区分webkit 和 winphone)
当用户手指放在移动设备在屏幕上滑动会触发的touch事件
以下支持webkit
touchstart——当手指触碰屏幕时候发生。不管当前有多少只手指
touchmove——当手指在屏幕上滑动时连续触发。通常我们再滑屏页面,会调用event的preventDefault()可以阻止默认情况的发生:阻止页面滚动
touchend——当手指离开屏幕时触发
touchcancel——系统停止跟踪触摸时候会触发。例如在触摸过程中突然页面alert()一个提示框,此时会触发该事件,这个事件比较少用TouchEvent
touches:屏幕上所有手指的信息
targetTouches:手指在目标区域的手指信息
changedTouches:最近一次触发该事件的手指信息
touchend时,touches与targetTouches信息会被删除,changedTouches保存的最后一次的信息,最好用于计算手指信息参数信息(changedTouches[0])
clientX、clientY在显示区的坐标
target:当前元素

全局错误监控
监听window上的error事件,过滤事件代理的error。

手动触发一个dom事件,需要3步,如果你对document.createEvent,�不是很熟悉,可以点击查看。
创建一个事件对象 document.createEvent(event)
初始化事件对象 event.initEvent(type, bubbles, true)
分发事件 dom.dispatchEvent(event)

Object.preventExtensions(obj)  让一个对象变的不可扩展,也就是永远不能再添加新的属性。
Object.isExtensible(obj) 判断一个对象是否是可扩展的
Object.seal(obj)让一个对象密封(只能读写 不能新增)
Object.isSealed(obj)判断一个对象是否密封
Object.isFrozen(arr)  让一个对象被冻结(只能读)
Object.isFrozen(obj):判断一个对象是否被冻结
Object.keys(obj) 返回一个由给定对象的所有可枚举自身属性的属性名组成的数组
Object.getOwnPropertyNames(obj):返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组
Object.is(value1, value2):判断两个值是否是同一个值,Object.is它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的。
Object.assign 把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。【浅复制】
//var copy = Object.assign({}, obj);
Object.defineProperty() 定义单个对象属性或方法(可以设置读写可枚举)
Object.defineProperties() 定义多个对象属性或方法(可以设置读写可枚举)

Object.assign() //浅拷贝,类似{...obj1,...obj2} 都是浅拷贝
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)

var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

//如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

var obj1 = {a: {b: 1}};
var obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2

对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
var target = { a: { b: 'c', d: 'e' } }
var source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

停止jq中的ajax请求用abort()函数   

<html>  
<head>  
<mce:script type="text/javascript"><!--  
var currentAjax = null;  
functionstartAjax(){  
    //方法就是将XHR对象指向currentAjax,再调用currentAjax的.abort()来中止请求  
    currentAjax = $.ajax({  
           type:'POST',  
           beforeSend:function(){},  
           url:'test.php',  
           data:'username=xxx',  
           dataType:'JSON',  
           error:function(){alert('error')},  
           success:function(data){alert(data)}  
    });  
}  
functionstopAjax(){  
    //如若上一次AJAX请求未完成,则中止请求  
    if(currentAjax) {currentAjax.abort();}  
}  
// --></mce:script>  
</head>  
<body>  
<input type="button" value="触发请求" onclick="startAjax()" />  
<input type="button" value="停止请求" onclick="stopAjax()" />  
</body>  
</html>  

JSONP 超时:

  设置超时标志位flag=false,当超时后将flag=true;
  异步加载JS文件并执行回调函数:

  function loadJS(src, callback){
    var script = document.createElement('script');
    var head = document.getElementsByTagName('head')[0];
    var loaded;
    script.src = src;
    if(typeof callback === 'function'){
        script.onload = script.onreadystatechange = function(){
            if(!loaded && (!script.readyState || /loaded|complete/.test(script.readyState))){
                script.onload = script.onreadystatechange = null;
                loaded = true;
                callback();
            }
        }
    }
    head.appendChild(script);
}

加setTimeout 超时后,当超时后将标志位设为true,在onload 里面判断flag为true的时候直接return 掉,
后面的回调函数就不会运行了,JOSNP也就失去了作用!

//获取元素的绝对位置
                function getPosition(node) {
                    var width = node.offsetWidth; //元素宽度
                    var height = node.offsetHeight; //元素高度
                    var left = node.offsetLeft; //获取元素相对于其根元素的left值var left
                    var top = node.offsetTop; //获取元素相对于其根元素的top值var top
                    current = node.offsetParent; // 取得元素的offsetParent

                    // 一直循环直到根元素  
                    while(current != null) {  
                        left += current.offsetLeft;  
                        top += current.offsetTop;  
                        current = current.offsetParent;  
                    }
                    return {
                        "width": width,
                        "height": height,
                        "left": left,
                        "top": top
                    };
                }

// 动画结束时事件
o.addEventListener("webkitAnimationEnd", function() {
    console.log("动画结束");
})

-webkit-animation动画有三个事件:
开始事件: webkitAnimationStart
结束事件:  webkitAnimationEnd
重复运动事件: webkitAnimationIteration// 动画开始时事件

o.addEventListener("webkitAnimationStart", function() {
    console.log("动画开始");
})
// 动画重复运动时事件
o.addEventListener("webkitAnimationIteration", function() {
    console.log("动画重复运动");
})
// 动画结束时事件
o.addEventListener("webkitAnimationEnd", function() {
    console.log("动画结束");
})

首先我们需要在高度过渡动画完成后执行数字跳动动画,这里我们需要监听'transitionend'事件,对于这个事件需要特别注意的点:
每个过渡属性完成后多会触发一次transitionend;
transitionend事件支持冒泡,如果子元素也有过渡效果的话,一定要阻止冒泡。    // watch : 
    active (newValue) {
        if (newValue) {
            this.$refs.zfbitem.addEventListener('transitionend', this.transitionAction, false);
        }
    }

    // methods:
    transitionAction (e) {
        //不再需要监听时,一定要移除监听
        this.$refs.zfbitem.removeEventListener('transitionend', this.transitionAction, false);
        this.numberBounce();
    }

一、节点
1.1 节点属性
Node.nodeName   //返回节点名称,只读
Node.nodeType   //返回节点类型的常数值,只读
Node.nodeValue  //返回Text或Comment节点的文本值,只读
Node.textContent  //返回当前节点和它的所有后代节点的文本内容,可读写
Node.baseURI    //返回当前网页的绝对路径

Node.ownerDocument  //返回当前节点所在的顶层文档对象,即document
Node.nextSibling  //返回紧跟在当前节点后面的第一个兄弟节点
Node.previousSibling  //返回当前节点前面的、距离最近的一个兄弟节点
Node.parentNode   //返回当前节点的父节点
Node.parentElement  //返回当前节点的父Element节点
Node.childNodes   //返回当前节点的所有子节点
Node.firstChild  //返回当前节点的第一个子节点
Node.lastChild   //返回当前节点的最后一个子节点

//parentNode接口
Node.children  //返回指定节点的所有Element子节点
Node.firstElementChild  //返回当前节点的第一个Element子节点
Node.lastElementChild   //返回当前节点的最后一个Element子节点
Node.childElementCount  //返回当前节点所有Element子节点的数目。
1.2 操作
Node.appendChild(node)   //向节点添加最后一个子节点
Node.hasChildNodes()   //返回布尔值,表示当前节点是否有子节点
Node.cloneNode(true);  // 默认为false(克隆节点), true(克隆节点及其属性,以及后代)
Node.insertBefore(newNode,oldNode)  // 在指定子节点之前插入新的子节点
Node.removeChild(node)   //删除节点,在要删除节点的父节点上操作
Node.replaceChild(newChild,oldChild)  //替换节点
Node.contains(node)  //返回一个布尔值,表示参数节点是否为当前节点的后代节点。
Node.compareDocumentPosition(node)   //返回一个7个比特位的二进制值,表示参数节点和当前节点的关系
Node.isEqualNode(noe)  //返回布尔值,用于检查两个节点是否相等。所谓相等的节点,指的是两个节点的类型相同、属性相同、子节点相同。
Node.normalize()   //用于清理当前节点内部的所有Text节点。它会去除空的文本节点,并且将毗邻的文本节点合并成一个。

//ChildNode接口
Node.remove()  //用于删除当前节点
Node.before()  //
Node.after()
Node.replaceWith()
1.3 Document节点
1.3.1 Document节点的属性
document.doctype   //
document.documentElement  //返回当前文档的根节点
document.defaultView   //返回document对象所在的window对象
document.body   //返回当前文档的<body>节点
document.head   //返回当前文档的<head>节点
document.activeElement  //返回当前文档中获得焦点的那个元素。

//节点集合属性
document.links  //返回当前文档的所有a元素
document.forms  //返回页面中所有表单元素
document.images  //返回页面中所有图片元素
document.embeds  //返回网页中所有嵌入对象
document.scripts  //返回当前文档的所有脚本
document.styleSheets  //返回当前网页的所有样式表

//文档信息属性
document.documentURI  //表示当前文档的网址
document.URL  //返回当前文档的网址
document.domain  //返回当前文档的域名
document.lastModified  //返回当前文档最后修改的时间戳
document.location  //返回location对象,提供当前文档的URL信息
document.referrer  //返回当前文档的访问来源
document.title    //返回当前文档的标题
document.characterSet属性返回渲染当前文档的字符集,比如UTF-8、ISO-8859-1。
document.readyState  //返回当前文档的状态
document.designMode  //控制当前文档是否可编辑,可读写
document.compatMode  //返回浏览器处理文档的模式
document.cookie   //用来操作Cookie
1.3.2 Document节点的方法
(1)读写方法
document.open()   //用于新建并打开一个文档
document.close()   //不安比open方法所新建的文档
document.write()   //用于向当前文档写入内容
document.writeIn()  //用于向当前文档写入内容,尾部添加换行符。
(2)查找节点
document.querySelector(selectors)   //接受一个CSS选择器作为参数,返回第一个匹配该选择器的元素节点。
document.querySelectorAll(selectors)  //接受一个CSS选择器作为参数,返回所有匹配该选择器的元素节点。
document.getElementsByTagName(tagName)  //返回所有指定HTML标签的元素
document.getElementsByClassName(className)   //返回包括了所有class名字符合指定条件的元素
document.getElementsByName(name)   //用于选择拥有name属性的HTML元素(比如<form>、<radio>、<img>、<frame>、<embed>和<object>等)
document.getElementById(id)   //返回匹配指定id属性的元素节点。
document.elementFromPoint(x,y)  //返回位于页面指定位置最上层的Element子节点。
(3)生成节点
document.createElement(tagName)   //用来生成HTML元素节点。
document.createTextNode(text)   //用来生成文本节点
document.createAttribute(name)  //生成一个新的属性对象节点,并返回它。
document.createDocumentFragment()  //生成一个DocumentFragment对象
(4)事件方法
document.createEvent(type)   //生成一个事件对象,该对象能被element.dispatchEvent()方法使用
document.addEventListener(type,listener,capture)  //注册事件
document.removeEventListener(type,listener,capture)  //注销事件
document.dispatchEvent(event)  //触发事件
(5)其他
document.hasFocus()   //返回一个布尔值,表示当前文档之中是否有元素被激活或获得焦点。
document.adoptNode(externalNode)  //将某个节点,从其原来所在的文档移除,插入当前文档,并返回插入后的新节点。
document.importNode(externalNode, deep)   //从外部文档拷贝指定节点,插入当前文档。
1.4 Element节点
1.4.1 Element节点的属性
(1)特性属性
Element.attributes  //返回当前元素节点的所有属性节点
Element.id  //返回指定元素的id属性,可读写
Element.tagName  //返回指定元素的大写标签名
Element.innerHTML   //返回该元素包含的HTML代码,可读写
Element.outerHTML  //返回指定元素节点的所有HTML代码,包括它自身和包含的的所有子元素,可读写
Element.className  //返回当前元素的class属性,可读写
Element.classList  //返回当前元素节点的所有class集合
Element.dataset   //返回元素节点中所有的data-*属性。
(2)尺寸属性
Element.clientHeight   //返回元素节点可见部分的高度
Element.clientWidth   //返回元素节点可见部分的宽度
Element.clientLeft   //返回元素节点左边框的宽度
Element.clientTop   //返回元素节点顶部边框的宽度
Element.scrollHeight  //返回元素节点的总高度
Element.scrollWidth  //返回元素节点的总宽度
Element.scrollLeft   //返回元素节点的水平滚动条向右滚动的像素数值,通过设置这个属性可以改变元素的滚动位置
Element.scrollTop   //返回元素节点的垂直滚动向下滚动的像素数值
Element.offsetHeight   //返回元素的垂直高度(包含border,padding)
Element.offsetWidth    //返回元素的水平宽度(包含border,padding)
Element.offsetLeft    //返回当前元素左上角相对于Element.offsetParent节点的垂直偏移
Element.offsetTop   //返回水平位移
Element.style  //返回元素节点的行内样式
(3)节点相关属性
Element.children   //包括当前元素节点的所有子元素
Element.childElementCount   //返回当前元素节点包含的子HTML元素节点的个数
Element.firstElementChild  //返回当前节点的第一个Element子节点  
Element.lastElementChild   //返回当前节点的最后一个Element子节点  
Element.nextElementSibling  //返回当前元素节点的下一个兄弟HTML元素节点
Element.previousElementSibling  //返回当前元素节点的前一个兄弟HTML节点
Element.offsetParent   //返回当前元素节点的最靠近的、并且CSS的position属性不等于static的父元素。
1.4.2 Element节点的方法
(1)位置方法
getBoundingClientRect()  
// getBoundingClientRect返回一个对象,包含top,left,right,bottom,width,height // width、height 元素自身宽高
// top 元素上外边界距窗口最上面的距离
// right 元素右外边界距窗口最上面的距离
// bottom 元素下外边界距窗口最上面的距离
// left 元素左外边界距窗口最上面的距离
// width 元素自身宽(包含border,padding) 
// height 元素自身高(包含border,padding) 

getClientRects()   //返回当前元素在页面上形参的所有矩形。

// 元素在页面上的偏移量  
var rect = el.getBoundingClientRect()  
return {   
  top: rect.top + document.body.scrollTop,   
  left: rect.left + document.body.scrollLeft  
}
(2)属性方法
Element.getAttribute():读取指定属性  
Element.setAttribute():设置指定属性  
Element.hasAttribute():返回一个布尔值,表示当前元素节点是否有指定的属性  
Element.removeAttribute():移除指定属性
(3)查找方法
Element.querySelector()  
Element.querySelectorAll()  
Element.getElementsByTagName()  
Element.getElementsByClassName()
(4)事件方法
Element.addEventListener():添加事件的回调函数  
Element.removeEventListener():移除事件监听函数  
Element.dispatchEvent():触发事件

//ie8
Element.attachEvent(oneventName,listener)
Element.detachEvent(oneventName,listener)

// event对象  
var event = window.event||event;    

// 事件的目标节点  
var target = event.target || event.srcElement;

// 事件代理  
ul.addEventListener('click', function(event) {   
  if (event.target.tagName.toLowerCase() === 'li') {   
    console.log(event.target.innerHTML)   
  }  
});
(5)其他
Element.scrollIntoView()   //滚动当前元素,进入浏览器的可见区域

//解析HTML字符串,然后将生成的节点插入DOM树的指定位置。
Element.insertAdjacentHTML(where, htmlString); 
Element.insertAdjacentHTML('beforeBegin', htmlString); // 在该元素前插入  
Element.insertAdjacentHTML('afterBegin', htmlString); // 在该元素第一个子元素前插入 
Element.insertAdjacentHTML('beforeEnd', htmlString); // 在该元素最后一个子元素后面插入 
Element.insertAdjacentHTML('afterEnd', htmlString); // 在该元素后插入

Element.remove()  //用于将当前元素节点从DOM中移除
Element.focus()   //用于将当前页面的焦点,转移到指定元素上
二、CSS操作
(1)类名操作
//ie8以下
Element.className  //获取元素节点的类名
Element.className += ' ' + newClassName  //新增一个类名

//判断是否有某个类名
function hasClass(element,className){
  return new RegExp(className,'gi').test(element.className);
}

//移除class
function removeClass(element,className){
  element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'),'');
}

//ie10 
element.classList.add(className)  //新增
element.classList.remove(className)  //删除
element.classList.contains(className)  //是否包含
element.classList.toggle(className)  //toggle class
(2)style操作
element.setAttribute('style','')

element.style.backgroundColor = 'red'

element.style.cssText //用来读写或删除整个style属性

element.style.setProperty(propertyName,value)  //设置css属性
element.style.getPropertyValue(property)  //获取css属性
element.style.removeProperty(property)  //删除css属性
操作非内联样式
//ie8
element.currentStyle[attrName]
//ie9+
window.getComputedStyle(el,null)[attrName] 
window.getComputedStyle(el,null).getPropertyValue(attrName)
//伪类
window.getComputedStyle(el,':after')[attrName]
三、对象
3.1 Object对象
(1)生成实例对象
var o = new Object()
(2)属性
Object.prototype   //返回原型对象
(3)方法
Object.keys(o)   //遍历对象的可枚举属性
Object.getOwnPropertyName(o)   //遍历对象不可枚举的属性
对象实例的方法
valueOf():返回当前对象对应的值。  
toString():返回当前对象对应的字符串形式。  
toLocaleString():返回当前对象对应的本地字符串形式。  
hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。 
isPrototypeOf():判断当前对象是否为另一个对象的原型。
propertyIsEnumerable():判断某个属性是否可枚举。
3.2 Array对象
(1)生成实例对象
var a = new Array()
(2)属性
a.length  //长度
(3)Array.isArray()
Array.isArray(a)   //用来判断一个值是否为数组
(4)Array实例的方法

[1, [2, [3, 4]]].toString() // "1,2,3,4"

a.valueof()   //返回数组本身
a.toString()  //返回数组的字符串形式
a.push(value,vlaue....)   //用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。
pop()   //用于删除数组的最后一个元素,并返回该元素
join()  //以参数作为分隔符,将所有数组成员组成一个字符串返回。如果不提供参数,默认用逗号分隔。
concat()  //用于多个数组的合并。它将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不变。
shift()  //用于删除数组的第一个元素,并返回该元素。
unshift(value)  //用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。
reverse()   //用于颠倒数组中元素的顺序,返回改变后的数组
slice(start_index, upto_index);   //用于提取原数组的一部分,返回一个新数组,原数组不变。第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员。负数表示倒数第几个。
splice(index, count_to_remove, addElement1, addElement2, ...);   //用于删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员,返回值是被删除的元素。第一个参数是删除的起始位置,第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
sort()   //对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数,表示按照自定义方法进行排序。该函数本身又接受两个参数,表示进行比较的两个元素。如果返回值大于0,表示第一个元素排在第二个元素后面;其他情况下,都是第一个元素排在第二个元素前面。
map()   //对数组的所有成员依次调用一个函数,根据函数结果返回一个新数组。
map(elem,index,arr)   //map方法接受一个函数作为参数。该函数调用时,map方法会将其传入三个参数,分别是当前成员、当前位置和数组本身。
forEach()   //遍历数组的所有成员,执行某种操作,参数是一个函数。它接受三个参数,分别是当前位置的值、当前位置的编号和整个数组。
filter()   //参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。
some()    //用来判断数组成员是否符合某种条件。接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。只要有一个数组成员的返回值是true,则整个some方法的返回值就是true,否则false。
every()   //用来判断数组成员是否符合某种条件。接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。所有数组成员的返回值都是true,才返回true,否则false。
reduce()   //依次处理数组的每个成员,最终累计为一个值。从左到右处理(从第一个成员到最后一个成员)
reduceRight()  //依次处理数组的每个成员,最终累计为一个值。从右到左(从最后一个成员到第一个成员)
indexOf(s)   //返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1。可以接受第二个参数,表示搜索的开始位置
lastIndexOf()  //返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1。
3.3 Number对象
(1)生成对象
var n = new Number()
(2)Number对象的属性
Number.POSITIVE_INFINITY:正的无限,指向Infinity。  
Number.NEGATIVE_INFINITY:负的无限,指向-Infinity。  
Number.NaN:表示非数值,指向NaN。  
Number.MAX_VALUE:表示最大的正数,相应的,最小的负数为-Number.MAX_VALUE。  
Number.MIN_VALUE:表示最小的正数(即最接近0的正数,在64位浮点数体系中为5e-324),相应的,最接近0的负数为-Number.MIN_VALUE。  
Number.MAX_SAFE_INTEGER:表示能够精确表示的最大整数,即9007199254740991。  
Number.MIN_SAFE_INTEGER:表示能够精确表示的最小整数,即-9007199254740991。
(4)Number对象实例的方法
toString()   //用来将一个数值转为字符串形式.可以接受一个参数,表示输出的进制。如果省略这个参数,默认将数值先转为十进制,再输出字符串;否则,就根据参数指定的进制,将一个数字转化成某个进制的字符串。
toFixed()   //用于将一个数转为指定位数的小数,返回这个小数对应的字符串。
toExponential()  //用于将一个数转为科学计数法形式。可传入一个参数,参数表示小数点后有效数字的位数,范围为0到20,超出这个范围,会抛出一个RangeError。
toPrecision()  //用于将一个数转为指定位数的有效数字。
3.4 String 对象
(1)生成实例对象
var s = new String()
(2)String对象的属性
s.length   //返回字符串的长度
(3)方法
s.chatAt(index)   //返回指定位置的字符    //"123456"[0] == "1"
s.fromCharCode()    //该方法的参数是一系列Unicode码点,返回对应的字符串。
s.charCodeAt(index)    //返回给定位置字符的Unicode码点(十进制表示)
s.concat(s2)  //用于连接两个字符串
s.slice(start,end)   //用于从原字符串取出子字符串并返回,不改变原字符串。第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置)。如果参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度。
s.substring(start,end)  //用于从原字符串取出子字符串并返回,不改变原字符串.第一个参数表示子字符串的开始位置,第二个位置表示结束位置。
s.substr(start,length)   //用于从原字符串取出子字符串并返回,不改变原字符串。第一个参数是子字符串的开始位置,第二个参数是子字符串的长度。如果第一个参数是负数,表示倒数计算的字符位置。如果第二个参数是负数,将被自动转为0,因此会返回空字符串。
s.indexOf(s)   //返回给定元素在字符串中第一次出现的位置,如果没有出现则返回-1。可以接受第二个参数,表示搜索的开始位置 
s.lastIndexOf()  //返回给定元素在字符串中最后一次出现的位置,如果没有出现则返回-1。
s.trim()  //用于去除字符串两端的空格,返回一个新字符串
s.toLowerCase()  //用于将一个字符串全部转为小写,返回一个新字符串,不改变原字符串。
s.toUpperCase()  //全部转为大写
s.localeCompare(s2)  //用于比较两个字符串。它返回一个整数,如果小于0,表示第一个字符串小于第二个字符串;如果等于0,表示两者相等;如果大于0,表示第一个字符串大于第二个字符串。
s.match(regexp)   //用于确定原字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串。如果没有找到匹配,则返回null。
s.search()  //返回值为匹配的第一个位置。如果没有找到匹配,则返回-1。
s.replace(oldValue,newValue)  //用于替换匹配的子字符串,一般情况下只替换第一个匹配(除非使用带有g修饰符的正则表达式)。
s.split()  //按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组。还可传入第二个参数,决定了返回数组的成员数。
3.5 Math对象
(1)属性
Math.E:常数e。  
Math.LN2:2的自然对数。  
Math.LN10:10的自然对数。  
Math.LOG2E:以2为底的e的对数。  
Math.LOG10E:以10为底的e的对数。  
Math.PI:常数Pi。  
Math.SQRT1_2:0.5的平方根。  
Math.SQRT2:2的平方根。
(2)数学方法
Math.abs():返回参数的绝对值  
Math.ceil():向上取整,接受一个参数,返回大于该参数的最小整数。 
Math.floor():向下取整  
Math.max(n,n1,...):可接受多个参数,返回最大值  
Math.min(n,n1,..):可接受多个参数,返回最小值  
Math.pow(n,e):指数运算, 返回以第一个参数为底数、第二个参数为幂的指数值。 
Math.sqrt():返回参数值的平方根。如果参数是一个负值,则返回NaN。  
Math.log():返回以e为底的自然对数值。
Math.exp():返回e的指数,也就是常数e的参数次方。
Math.round():四舍五入  
Math.random():返回0到1之间的一个伪随机数,可能等于0,但是一定小于1。
(3)三角函数方法
Math.sin():返回参数的正弦  
Math.cos():返回参数的余弦  
Math.tan():返回参数的正切  
Math.asin():返回参数的反正弦(弧度值)  
Math.acos():返回参数的反余弦(弧度值)  
Math.atan():返回参数的反正切(弧度值)
3.6 JSON对象
(1)方法
JSON.stringify()   
//用于将一个值转为字符串。该字符串应该符合JSON格式,并且可以被JSON.parse方法还原。
//(JSON.stringify(obj, selectedProperties))还可以接受一个数组,作为第二个参数,指定需要转成字符串的属性。
//还可以接受第三个参数,用于增加返回的JSON字符串的可读性。如果是数字,表示每个属性前面添加的空格(最多不超过10个);如果是字符串(不超过10个字符),则该字符串会添加在每行前面。

JSON.parse()   //用于将JSON字符串转化成对象。
3.7 console对象
(1)方法
console.log(text,text2,...)   //用于在console窗口输出信息。它可以接受多个参数,将它们的结果连接起来输出。如果第一个参数是格式字符串(使用了格式占位符),console.log方法将依次用后面的参数替换占位符,然后再进行输出。
console.info()   //在console窗口输出信息,同时,会在输出信息的前面,加上一个蓝色图标。
console.debug()  //在console窗口输出信息,同时,会在输出信息的前面,加上一个蓝色图标。
console.warn()  //输出信息时,在最前面加一个黄色三角,表示警告;
console.error()  //输出信息时,在最前面加一个红色的叉,表示出错,同时会显示错误发生的堆栈
console.table()  //可以将复合类型的数据转为表格显示。
console.count()  //用于计数,输出它被调用了多少次。
console.dir()    //用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示。
console.dirxml()  //用于以目录树的形式,显示DOM节点。
console.assert()  //接受两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果。

//这两个方法用于计时,可以算出一个操作所花费的准确时间。
console.time()
console.timeEnd()
//time方法表示计时开始,timeEnd方法表示计时结束。它们的参数是计时器的名称。调用timeEnd方法之后,console窗口会显示“计时器名称: 所耗费的时间”。

console.profile()  //用来新建一个性能测试器(profile),它的参数是性能测试器的名字。
console.profileEnd()  //用来结束正在运行的性能测试器。

console.group()
console.groupend()
//上面这两个方法用于将显示的信息分组。它只在输出大量信息时有用,分在一组的信息,可以用鼠标折叠/展开。
console.groupCollapsed()  //用于将显示的信息分组,该组的内容,在第一次显示时是收起的(collapsed),而不是展开的。

console.trace()  //显示当前执行的代码在堆栈中的调用路径。
console.clear()  //用于清除当前控制台的所有输出,将光标回置到第一行。

正则表达式:
只允许输入汉字:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" 
只允许输入数字:onkeyup="this.value=this.value.replace(/\D/g,'')"
[1, [2, [3, 4]]].toString() // "1,2,3,4"

0、常用选择器
$('#div1')   //id为div1的节点,如<div id='div1'></div> 

$('span')   //所有的span结点,一个包装集
$('p span')   //p标签下的所有span节点,后代节点
$('p>span')   //p标签下的所有span子节点,子代节点

$('.red')  //使用样式red的节点,如<span class="red"></span>

$('*')  //所有节点

$("div,span,p.cls")  //选取所有<div>,<span>和拥有class为cls的<p>标签的一组元素

1、基本筛选器
$('span:first')    //第一个节点
$('span:last')     //最后一个节点

$("td:even")     //索引为偶数的节点,从 0 开始
$("td:odd")      //索引为奇数的节点,从 0 开始

$("td:eq(1)")    //给定索引值的节点
$("td:gt(0)")    //大于给定索引值的节点
$("td:lt(2)")    //小于给定索引值的节点

$(":focus")      //当前获取焦点的节点
$(":animated")   //正在执行动画效果的节点

2、内容选择器
$("div:contains('hello')")    //包含hello文本的节点
$("td:empty")    //不包含子节点或者文本的空节点
$("div:has(p)")  //含有选择器所匹配的节点
$("td:parent")   //含有子节点或者文本的节点

3、表单选择器
$("input:checked")    //所有选中的节点
$("select option:selected")    //select中所有选中的option节点

$(":input")      //匹配所有 input, textarea, select 和 button 节点
$(":text")       //所有的单行文本框
$(":password")   //所有密码框
$(":radio")      //所有单选按钮
$(":checkbox")   //所有复选框
$(":submit")     //所有提交按钮
$(":reset")      //所有重置按钮
$(":button")     //所有button按钮
$(":file")       //所有文件域

4、筛选与查找
$("p").eq(0)       //当前操作中第N个jQuery对象,类似索引
$('li').first()    //第一个节点
$('li').last()     //最后一个节点
$(this).hasClass("node")    //节点是否含有某个特定的类,返回布尔值
$('li').has('ul')  //包含特定后代的节点

$("div").children()      //div中的每个子节点,第一层
$("div").find("span")    //查找div下的所有span节点

$("p").next()          //紧邻p节点后的一个同辈节点
$("p").nextAll()         //p节点之后所有的同辈节点
$("#node").nextUntil("#node2")    //id为"#node"节点之后到id为'#node2'之间所有的同辈节点,掐头去尾

$("p").prev()            //紧邻p节点前的一个同辈节点
$("p").prevAll()         //p节点之前所有的同辈节点
$("#node").prevUntil("#node2")    //id为"#node"节点之前到id为'#node2'之间所有的同辈节点,掐头去尾

$("p").parent()          //每个p节点的父节点
$("p").parents()         //每个p节点的所有祖先节点,body,html
$("#node").parentsUntil("#node2")    //id为"#node"节点到id为'#node2'之间所有的父级节点,掐头去尾

$("div").siblings()      //所有的同辈节点,不包括自己

5、属性操作
$("img").attr("src");           //返回文档中所有图像的src属性值
$("img").attr("src","node.jpg");    //设置所有图像的src属性
$("img").removeAttr("src");       //将文档中图像的src属性删除

$("input[type='checkbox']").prop("checked", true);    //选中复选框
$("input[type='checkbox']").prop("checked", false);   //不选中复选框
$("img").removeProp("src");       //删除img的src属性

6、样式操作
$("p").addClass("selected");      //为p节点加上 'selected' 类
$("p").removeClass("selected");    //从p节点中删除 'selected' 类
$("p").toggleClass("selected");    //如果存在就删除,否则就添加HTML代码/文本/值

7、内容操作
$('p').html();               //返回p节点的html内容
$("p").html("Hello <b>hello</b>!");  //设置p节点的html内容
$('p').text();               //返回p节点的文本内容
$("p").text("hello");           //设置p节点的文本内容
$("input").val();             //获取文本框中的值
$("input").val("hello");          //设置文本框中的内容

8、CSS操作
$("p").css("color");          //访问查看p节点的color属性
$("p").css("color","red");    //设置p节点的color属性为red
$("p").css({ "color": "red", "background": "yellow" });    //设置p节点的color为red,background属性为yellow(设置多个属性要用{}字典形式)

9、定位与偏移
$('p').offset()     //节点在当前视口的相对偏移,对象 {top: 5, left: 9}
$('p').offset().top
$('p').offset().left
$("p").position()   //节点相对父节点的偏移,对可见节点有效,Object {top: 5, left: 8}

$(window).scrollTop()    //获取滚轮滑的高度
$(window).scrollLeft()   //获取滚轮滑的宽度
$(window).scrollTop('25')    //设置滚轮滑的高度为25

10、尺寸
$("p").height();    //获取p节点的高度
$("p").width();     //获取p节点的宽度

$("p:first").innerHeight()    //获取第一个匹配节点内部区域高度(包括补白、不包括边框)
$("p:first").innerWidth()     //获取第一个匹配节点内部区域宽度(包括补白、不包括边框)

$("p:first").outerHeight()    //匹配节点外部高度(默认包括补白和边框)
$("p:first").outerWidth()     //匹配节点外部宽度(默认包括补白和边框)
$("p:first").outerHeight(true)    //为true时包括边距

11、DOM内部插入
$("p").append("<b>hello</b>");    //每个p节点内后面追加内容
$("p").appendTo("div");        //p节点追加到div内后
$("p").prepend("<b>Hello</b>");  //每个p节点内前面追加内容
$("p").prependTo("div");        //p节点追加到div内前

12、DOM外部插入
$("p").after("<b>hello</b>");     //每个p节点同级之后插入内容
$("p").before("<b>hello</b>");    //在每个p节点同级之前插入内容
$("p").insertAfter("#node");     //所有p节点插入到id为node节点的后面
$("p").insertBefore("#node");    //所有p节点插入到id为node节点的前面

13、DOM替换
$("p").replaceWith("<b>Paragraph. </b>");    //将所有匹配的节点替换成指定的HTML或DOM节点
$("<b>Paragraph. </b>").replaceAll("p");     //用匹配的节点替换掉所有 selector匹配到的节点

14、DOM删除
$("p").empty();     //删除匹配的节点集合中所有的子节点,不包括本身
$("p").remove();    //删除所有匹配的节点,包括本身
$("p").detach();    //删除所有匹配的节点(和remove()不同的是:所有绑定的事件、附加的数据会保留下来)

15、DOM复制
$("p").clone()      //克隆节点并选中克隆的副本
$("p").clone(true)   //布尔值指事件处理函数是否会被复制

16、DOM加载完成事件
$(document).ready(function(){
  您的代码...
});

//缩写
$(function($) {
  您的代码...
});

17、绑定事件
//bind 为每个匹配节点绑定事件处理函数,绑定多个用{}。
$("p").bind("click", function(){
  alert( $(this).text() );
});
$('#div1').bind({
    "mouseover":function () {
     $('#div1').parent().removeClass("hide");
     },"mouseout":function () {
     $('#div1').parent().addClass("hide");
}
});         

$("p").one( "click", function(){})    //事件绑定后只会执行一次
$("p").unbind( "click" )        //反绑一个事件

// 与bind 不同的是当时间发生时才去临时绑定。
$("p").delegate("click",function(){
  您的代码
});

$("p").undelegate();       //p节点删除由 delegate() 方法添加的所有事件
$("p").undelegate("click")   //从p节点删除由 delegate() 方法添加的所有click事件

$("p").click();      //单击事件
$("p").dblclick();    //双击事件
$("input[type=text]").focus()  //节点获得焦点时,触发 focus 事件
$("input[type=text]").blur()   //节点失去焦点时,触发 blur事件
$("button").mousedown()//当按下鼠标时触发事件
$("button").mouseup()  //节点上放松鼠标按钮时触发事件
$("p").mousemove()     //当鼠标指针在指定的节点中移动时触发事件
$("p").mouseover()     //当鼠标指针位于节点上方时触发事件
$("p").mouseout()     //当鼠标指针从节点上移开时触发事件
$(window).keydown()    //当键盘或按钮被按下时触发事件
$(window).keypress()   //当键盘或按钮被按下时触发事件,每输入一个字符都触发一次
$("input").keyup()     //当按钮被松开时触发事件
$(window).scroll()     //当用户滚动时触发事件
$(window).resize()     //当调整浏览器窗口的大小时触发事件
$("input[type='text']").change()    //当节点的值发生改变时触发事件
$("input").select()    //当input 节点中的文本被选择时触发事件
$("form").submit()     //当提交表单时触发事件
$(window).unload()     //用户离开页面时

18、事件对象
$("p").click(function(event){  
 alert(event.type); //"click"  
}); 

(evnet object)属性方法:
event.pageX   //事件发生时,鼠标距离网页左上角的水平距离
event.pageY   //事件发生时,鼠标距离网页左上角的垂直距离
event.type   //事件的类型
event.which   //按下了哪一个键
event.data   //在事件对象上绑定数据,然后传入事件处理函数
event.target  //事件针对的网页节点
event.preventDefault()  //阻止事件的默认行为(比如点击链接,会自动打开新页面)
event.stopPropagation()  //停止事件向上层节点冒泡

19、动态事件绑定
 $("p").on("click",'span',function(){
alert( $(this).text() );
});
//当p中增加span时仍然有效

20、动画效果
$("p").show()        //显示隐藏的匹配节点
$("p").show("slow");    //参数表示速度,("slow","normal","fast"),也可为600毫秒
$("p").hide()        //隐藏显示的节点
$("p").toggle();      //切换 显示/隐藏

$("p").slideDown("600");    //用600毫秒时间将段落滑下
$("p").slideUp("600");     //用600毫秒时间将段落滑上
$("p").slideToggle("600");  //用600毫秒时间将段落滑上,滑下淡入淡出

$("p").fadeIn("600");        //用600毫秒时间将段落淡入
$("p").fadeOut("600");       //用600毫秒时间将段落淡出
$("p").fadeToggle("600");     //用600毫秒时间将段落淡入,淡出
$("p").fadeTo("slow", 0.6);    //用600毫秒时间将段落的透明度调整到0.6

21、工具方法
$("#form1").serialize()    //序列表表格内容为字符串。
$("select, :radio").serializeArray();  //序列化表单元素为数组返回 JSON 数据结构数据
$.trim()   //去除字符串两端的空格
$.each()   //遍历一个数组或对象,for循环
$.inArray() //返回一个值在数组中的索引位置,不存在返回-1  
$.grep()   //返回数组中符合某种标准的节点
$.extend({a:1,b:2},{b:3,c:4},{c:5:d:6})  //将多个对象,合并到第一个对象{a:1,b:3,c:5,d:6}
$.makeArray() //将对象转化为数组
$.type()    //判断对象的类别(函数对象、日期对象、数组对象、正则对象等等
$.isArray() //判断某个参数是否为数组
$.isEmptyObject() //判断某个对象是否为空(不含有任何属性)
$.isFunction()    //判断某个参数是否为函数
$.isPlainObject() //判断某个参数是否为用"{}"或"new Object"建立的对象
$.support()       //判断浏览器是否支持某个特性

22、AJAX
//保存数据到服务器,成功时显示信息
$.ajax({
   type: "POST",
   url: "some.php",
   data: "name=John&location=Boston",
   success: function(msg){
     alert( "Data Saved: " + msg );
   }
});

//加载 feeds.html 文件内容。
$("#feeds").load("feeds.html");

//请求 test.php 网页,传送2个参数,忽略返回值。
$.get("test.php", { name: "John", time: "2pm" } );

//从 Flickr JSONP API 载入 4 张最新的关于猫的图片。
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format
=json&jsoncallback=?", function(data){
  $.each(data.items, function(i,item){
    $("<img/>").attr("src", item.media.m).appendTo("#images");
    if ( i == 3 ) returnfalse;
  });
});

//加载并执行 test.js ,成功后显示信息
$.getScript("test.js", function(){
  alert("Script loaded and executed.");
});

//向页面 test.php 发送数据,并输出结果(HTML 或 XML,取决于所返回的内容):
$.post("test.php", { name: "John", time: "2pm" },
   function(data){
     alert("Data Loaded: " + data);
   });

//AJAX 请求完成时执行函数。
 $("#msg").ajaxComplete(function(event,request, settings){
   $(this).append("<li>请求完成.</li>");
 });

//AJAX 请求失败时显示信息。
$("#msg").ajaxError(function(event,request, settings){
     $(this).append("<li>出错页面:" + settings.url + "</li>");
});

//AJAX 请求发送前显示信息。
 $("#msg").ajaxSend(function(evt, request, settings){
   $(this).append("<li>开始请求: " + settings.url + "</li>");
 });

 //AJAX 请求开始时显示信息。
 $("#loading").ajaxStart(function(){
   $(this).show();
 });

//AJAX 请求结束后隐藏信息。
 $("#loading").ajaxStop(function(){
   $(this).hide();
 });

//当 AJAX 请求成功后显示消息。
 $("#msg").ajaxSuccess(function(evt, request, settings){
   $(this).append("<li>请求成功!</li>");
 });

//请求前过滤
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) { 
   // Modify options, control originalOptions, store jqXHR, etc 
 });

 //设置全局 AJAX 默认选项,设置 AJAX 请求默认地址为 "/xmlhttp/",禁止触发全局 AJAX 事件,用 POST 代替默认 GET 方法。其后的 AJAX 请求不再设置任何选项参数。
$.ajaxSetup({
  url: "/xmlhttp/",
  global: false,
  type: "POST"
});
$.ajax({ data: myData });

JavaScript中巧用位运算
日常前端开发中我们很少用到位运算,容易让人遗忘,让我们一起回顾下一下js中的位运算。
位运算详细说明查看JavaScript|MDN
下面主要回顾一下一些常用的位运算的巧用。
将十进制转化为二进制
var number = 3;
var result = number.toString(2);

var result2 = 14..toString(2); // "1110"

我们使用位运算来代替Math.floor()来向下取整
var data = 2.2352524535;
var result = data | 0; // 2

var re2 = ~~data; // 2

将颜色从RGA转换为Hex格式

var color = {r: 186, g: 218, b: 85};

// RGB to HEX
var rgb2hex = function(r, g, b) {
    return'#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).substr(1);
}
rgb2hex(color.r, color.g, color.b);//"#bada55"

区分两个数的大小
// variables
var a = 9285;
var b = 3569;

// 取大
var max = a ^ ((a ^ b) & -(a < b));//9285;

// 取小
var min =  b ^ ((a ^ b) & -(a < b);//3569

交换变量
var a = 10;
var b = 99;

a = (b^=a^=b)^a;

console.log(a) // 99
console.log(b) // 10

判断正负
function isPos(n) {
  return (n === (n >>> 0)) ? true : false;  
}
isPos(-1); // false
isPos(1); // true

Document

每个在浏览器中加载的页面都有一个document对象。通过document对象可以访问页面的内容(比如dom树,和内部的元素)

document对象可以由以下几种方法获取

1. 通常情况下直接使用`document`或`window.document`取得`document`对象
2. 通过iframe的`contentDocument`属性获取
3. The responseXML of an XMLHttpRequest object
4. 通过`node`或`element`对象的`ownerDocument`属性获取

所有的document对象都是通过Document接口实现的

PropertiesDocument.body

返回当前文档的<body><frameset>元素,如果元素不存在则返回null

<body id="oldBodyElement"></body>

例(在页面加载完成后执行):

alert(document.body.id);    //'oldBodyElement'

// 创建一个新的body元素
var aNewBodyElement = document.createElement("body");
aNewBodyElement.id = "newBodyElement";

// 把创建好的body元素赋给document.body
document.body = aNewBodyElement;
alert(document.body.id);    //"newBodyElement"

给文档设置一个新的body需要先删除已经存在的<body>元素

Document.documentElement

它是一个只读属性,返回文档的根节点元素(例如对于html文档来说,返回的是<html>元素)

例:

var rootElement = document.documentElement;
var firstTier = rootElement.childNodes;

// 遍历根节点的直接子元素
for (var i = 0; i < firstTier.length; i++) {
   // 对每一个直接子元素做些事情
   // as firstTier[i]
}

Notes:

* 这个属性可以很方便的获取文档的直接子元素
* html文档只包含一个子节点`<html>`,xml文档通常会包含多个子节点。
* 通常要使用`document.documentElement`而不是`document.firstChild`去获取根节点元素

Document.forms

返回当前文档中form元素的集合(一个HTMLCollection对象)

例:

<body>
<form id="robby">
    <input type="button" onclick="alert(document.forms[0].id);" value="robby's form" />
</form>

<form id="dave">
    <input type="button" onclick="alert(document.forms[1].id);" value="dave's form" />
</form>

<form id="paul">
    <input type="button" onclick="alert(document.forms[2].id);" value="paul's form" />
</form>
</body>

var forms = document.forms;
alert(forms.length);    //3
var form1 = forms[0];   //取得第一个form元素

Document.head

返回当前文档的<head>元素,如果文档中大于一个<head>元素,返回第一个元素。

Notes:

这个属性是只读的,试图给这个属性赋值都会出错。

Document.images

返回当前文档image元素的集合

例子:

var ilist = document.images;

for(var i = 0; i < ilist.length; i++) {
    if(ilist[i].src == "banner.gif") {
        // found the banner
    }
}

Notes:

* document.images.length返回当前页面的图片数
* document.images它是html dom的一部分,只工作在html中

Document.scripts

返回文档的script元素列表(是一个HTMLCollection对象)

例子:

var scripts = document.scripts;

if (scripts.length) {
    alert("This page has scripts!");
}

Document.title

获取或设置文档的标题

例子:

<!DOCTYPE html>
<html>
<head>
<title>Hello World!</title> 
</head>
<body>

<script>
alert(document.title); // displays "Hello World!"
document.title = "Goodbye World!";
alert(document.title); // displays "Goodbye World!"
</script>

</body>
</html>

Document.anchors

返回文档中的锚点列表

例子:

<h1>Title</h1>
<h2><a name="contents">Contents</a></h2>
<h2><a name="plants">Plants</a></h2>
<h2><a name="veggies">Veggies</a></h2>

// document.anchors返回一个数组包含3个值,[<a name="contents">Contents</a>, <a name="plants">Plants</a>, <a name="veggies">Veggies</a>]
alert(document.anchors.length); //3

Notes:

返回的锚点集合中只包含带有name属性的<a>,并不包含使用id属性创建的锚点(<a>标签的name属性在html5已经不支持,html5推荐使用id属性来设置锚点)

Document.links

links属性返回文档中带href属性的<area>元素和<a>元素的集合

<a href="http://baidu.com">baidu</a>
<a href="http://taobao.com">taobao</a>
<area shape="" coords="" href="" alt=""/>

alert(document.links.length);     //3

Document.location

Document.location是一个只读属性,返回一个Location对象,它包含了当前文档URL的信息以及提供了一些方法来改变URL地址或者是加载另一个URL

Document.URL

返回当前文档的url,但不能设置url

document.location = 'http://www.mozilla.org';   //Equivalent to
document.location.href = 'http://www.mozilla.org';

Document.referrer

返回链接到当前文档的url(之前link过来的路径)

Document.domain

获取或设置文档的域名

Document.activeElement

返回当前文档焦点所在的元素

Example

<input type="text">

var input = document.querySelector('input');
input.focus();
console.log(document.activeElement === input);  //true

注:当文档加载完成后,document.activeElement 返回document.body,在文档加载期间document.activeElement是null.

Document.readyState

当文档正在加载时返回"loading",解析完成但仍然加载资源返回"interactive",所有的资源全部加载完成返回"complete"。

document.onreadystatechange = function () {
    if (document.readyState === 'complete') {
        console.log('complete');
    }

    if (document.readyState === 'interactive') {
        console.log('interactive');
    }
};

// output
// interactive
// complete

readystatechange事件当readyState属性改变时触发。

Document.cookie

设置或读取当前文档的cookie

Document.defaultView

返回文档对应的window对象

MethodsDocument.getElementById()

通过元素id返回元素的引用,如果在文档中找不到id,返回null(id是大小写敏感的)

Document.getElementsByTagName()

根据标签名返回元素的集合(一个HTMLCollection对象)

Document.getElementsByName()

根据节点的属性name值返回元素的集合(一个NodeList对象)

Example

<div id="div1" name="test"></div>
<div id="div2" name="test"></div>
<div id="div3" name="test"></div>

// 返回一个HTMLCollection对象
var divs = document.getElementsByTagName('div');
console.log(divs);

// 返回一个NodeList对象
var names = document.getElementsByName('test');
console.log(names);

// divs[0]和names[0]都返回了第一个div元素的引用
console.log(divs[0] === names[0]);

Document.getElementsByClassName()

根据类名返回元素的集合(一个类数组的对象HTMLCollection),当作用在document上,将搜索整个文档的。也可以通过element.getElementsByClassName()作用在指定的节点元素上查找。

var elements = document.getElementsByClassName(names); // or:
var elements = rootElement.getElementsByClassName(names);

Example1

// Get all elements that have a class of 'test'
document.getElementsByClassName('test');

// Get all elements that have both the 'red' and 'test' classes
document.getElementsByClassName('red test');

// Get all elements that have a class of 'test', inside of an element that has the ID of 'main'
document.getElementById('main').getElementsByClassName('test');

Example2

<ul>
    <li class="test"></li>
    <li class="test"></li>
    <li class="test"></li>
</ul>

var lis = document.getElementsByClassName('test');

console.log(lis.length);   //3
console.log(lis instanceof HTMLCollection);  //true

Document.querySelector()

通过匹配css选择器,返回第一个匹配的元素。

var element = document.querySelector(selectors);

Example

<ul>
    <li class="test" id="l1"></li>
    <li class="test" id="l2"></li>
    <li class="test" id="l3"></li>
    <li class="test" id="l4"></li>
    <li class="test" id="l5"></li>
</ul>

var ul = document.querySelector('.test');   // equal to "document.querySelector('#l1')"
console.log(ul);    //<li class="test" id="l1"></li>

Document.querySelectorAll()

通过匹配css选择器返回NodeList对象

Example1

<ul>
    <li class="test"></li>
    <li class="test"></li>
    <li class="test"></li>
    <li class="test"></li>
    <li class="test"></li>
</ul>

var uls = document.querySelectorAll('.test');
console.log(uls.length);    //5

Example2 返回div元素带有calss为'note'或'alert'元素的集合

var matches = document.querySelectorAll("div.note, div.alert");

Document.createElement()

创建一个html元素

var element = document.createElement(tagName);

Document.createTextNode()

创建一个文本节点

var text = document.createTextNode(data);

Document.createAttribute()

创建一个属性节点

attribute = document.createAttribute(name)

Example

初始html为:

<div>
    <span id="childSpan">foo bar</span>
</div>

var sp1 = document.createElement('span');
sp1.setAttribute('id', 'newSpan');

// 创建属性节点a,属性名为myattr
var a = document.createAttribute('myattr');
// 设置属性值为my_value
a.value = 'my_value';
// 通过setAttributeNode设置节点属性
sp1.setAttributeNode(a);

var sp1_content = document.createTextNode('new replacement span element.');
sp1.appendChild(sp1_content);

var sp2 = document.getElementById('childSpan');
var parentDiv = sp2.parentNode;

parentDiv.appendChild(sp1);

现在html为:

<div>
    <span id="childSpan">foo bar</span>
    <span id="newSpan" myattr="my_value">new replacement span element.</span>
</div>

Document.hasFocus()

指示文档或文档中的任何元素是否处于焦点状态。这个方法用来检查在文档中是否有焦点元素

Example

<p>Click anywhere in the document (the right frame) to get focus. If you click outside the document, it will lose focus.</p>

<p id="demo"></p>

setInterval(myFunction, 1);

functionmyFunction() {
    var x = document.getElementById("demo");
    if (document.hasFocus()) {
        x.innerHTML = "The document has focus.";
    } else {
        x.innerHTML = "The document DOES NOT have focus.";
    }
}

Document.createDocumentFragment()

创建一个空的文档片段

多次使用节点方法(如:appendChild)绘制页面,每次都要刷新页面一次。效率也就大打折扣了,而使用document.createDocumentFragment()创建一个文档碎片,把所有的新结点附加在其上,然后把文档碎片的内容一次性添加到document中,这也就只需要一次页面刷新就可

var ul = document.getElementsByTagName('ul')[0];
var docfrag = document.createDocumentFragment();
var browserList = ["Internet Explorer", "Mozilla Firefox", "Safari", "Chrome", "Opera"];
browserList.forEach(function (item) {
  var li = document.createElement('li');
  li.textContent = item;
  docfrag.appendChild(li);
});
ul.appendChild(docfrag);

Js 数组——filter()、map()、some()、every()、forEach()、lastIndexOf()、indexOf()

filter():

语法:

var filteredArray = array.filter(callback[, thisObject]);

参数说明:

callback: 要对每个数组元素执行的回调函数。 thisObject : 在执行回调函数时定义的this对象。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//过滤掉小于 10 的数组元素:

//代码:
function isBigEnough(element, index, array) {
    return (element >= 10);
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// 12, 130, 44
//结果:[12, 5, 8, 130, 44].filter(isBigEnough) : 12, 130, 44 

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

功能说明:

对数组中的每个元素都执行一次指定的函数(callback),并且创建一个新的数组,该数组元素是所有回调函数执行时返回值为 true 的原数组元素。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略,同时,新创建的数组也不会包含这些元素。

回调函数可以有三个参数:当前元素,当前元素的索引和当前的数组对象。

如参数 thisObject 被传递进来,它将被当做回调函数(callback)内部的 this 对象,如果没有传递或者为null,那么将会使用全局对象。

filter 不会改变原有数组,记住:只有在回调函数执行前传入的数组元素才有效,在回调函数开始执行后才添加的元素将被忽略,而在回调函数开始执行到最后一个元素这一期间,数组元素被删除或者被更改的,将以回调函数访问到该元素的时间为准,被删除的元素将被忽略。

map():

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//将所有的数组元素转换为大写:

var strings = ["hello", "Array", "WORLD"];
function makeUpperCase(v)
{
    return v.toUpperCase();
}
var uppers = strings.map(makeUpperCase);
// uppers is now ["HELLO", "ARRAY", "WORLD"]
// strings is unchanged
//结果:["hello", "Array", "WORLD"].map(makeUpperCase) : HELLO, ARRAY, WORLD 

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

some():

对数组中的每个元素都执行一次指定的函数(callback),直到此函数返回 true,如果发现这个元素,some 将返回 true,如果回调函数对每个元素执行后都返回 false ,some 将返回 false。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//检查是否有数组元素大于等于10:

function isBigEnough(element, index, array) {
    return (element >= 10);
}
var passed = [2, 5, 8, 1, 4].some(isBigEnough);
// passed is false
passed = [12, 5, 8, 1, 4].some(isBigEnough);
// passed is true
//结果:
//[2, 5, 8, 1, 4].some(isBigEnough) : false 
//[12, 5, 8, 1, 4].some(isBigEnough) : true

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

every():

对数组中的每个元素都执行一次指定的函数(callback),直到此函数返回 false,如果发现这个元素,every 将返回 false,如果回调函数对每个元素执行后都返回 true ,every 将返回 true。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//测试是否所有数组元素都大于等于10:

function isBigEnough(element, index, array) {
    return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);
// passed is true
//结果:
//[12, 5, 8, 130, 44].every(isBigEnough) 返回 : false 
//[12, 54, 18, 130, 44].every(isBigEnough) 返回 : true

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

forEach():

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//打印数组内容:

functionprintElt(element, index, array) {
    document.writeln("[" + index + "] is " + element + "<br />");
}
[2, 5, 9].forEach(printElt);
// Prints:
// [0] is 2
// [1] is 5
// [2] is 9
//结果:
//[0] is 2
//[1] is 5
//[2] is 9

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

lastIndexOf():

语法

var index = array.lastIndexOf(searchElement[, fromIndex]);

参数说明

searchElement: 要搜索的元素

fromIndex : 开始搜索的位置,默认为数组的长度(length),在这样的情况下,将搜索所有的数组元素。搜索是反方向进行的。

功能说明

比较 searchElement 和数组的每个元素是否绝对一致(===),当有元素符合条件时,返回当前元素的索引。如果没有发现,就直接返回 -1 。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//查找符合条件的元素:

var array = [2, 5, 9, 2];
var index = array.lastIndexOf(2);
// index is 3
index = array.lastIndexOf(7);
// index is -1
index = array.lastIndexOf(2, 3);
// index is 3
index = array.lastIndexOf(2, 2);
// index is 0
index = array.lastIndexOf(2, -2);
// index is 0
index = array.lastIndexOf(2, -1);
// index is 3
//结果:
//[2, 5, 9, 2].lastIndexOf(2) : 3 
//[2, 5, 9, 2].lastIndexOf(7) : -1 
//[2, 5, 9, 2].lastIndexOf(2, 3) : 3 
//[2, 5, 9, 2].lastIndexOf(2, 2) : 0 
//[2, 5, 9, 2].lastIndexOf(2, -2) : 0 
//[2, 5, 9, 2].lastIndexOf(2, -1) : 3 

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

indexOf():

功能与lastIndexOf()一样,搜索是正向进行的

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//查找符合条件的元素:

var array = [2, 5, 9];
var index = array.indexOf(2);
// index is 0
index = array.indexOf(7);
// index is -1
//结果:
//[2, 5, 9].indexOf(2) : 0 
//[2, 5, 9].indexOf(7) : -1 

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

JavaScript中提供了多种数组方法,如下:

  1. 转换方法—toLocaleString()方法、toString()方法、valueOf()方法
  2. 栈方法——push()方法、pop()方法
  3. 队列方法——shift()方法、unshift()方法
  4. 重排序方法——reverse()方法、sort()方法
  5. 操作方法——concat()方法、slice()方法、splice()方法
  6. 位置方法——indexOf()方法、lastIndexOf()方法
  7. 迭代方法——every()方法、filter()方法、forEach()方法、map()方法、some()方法
  8. 归并方法——reduce()方法、reduceRight()方法

转换方法:

①:toString()方法返回由数组中每个值的字符串形式拼接并且以逗号相隔的字符串 ②:valueOf()方法返回的还是数组 ③:toLocaleString()方法也会返回一个数组值以逗号相隔的字符串,但与toString()方法不同的是在返回日期对象时格式不同。

具体看一下例子:

var colors=["red","blue","green"];
console.log(colors.toString());    //"red,blue,green"
console.log(colors.valueOf());    //red,blue,green
console.log(colors.toLocaleString());    //"red,blue,green"

//toLocaleString()方法与toString()方法在返回日期对象时格式不同
var today=new Date();
console.log(today.toString());    //    Sun Mar 05 2017 12:57:11 GMT+0800 (中国标准时间)
console.log(today.toLocaleString());    //    2017/3/5 下午12:57:11

栈方法:

①:push()方法可以接受任意数量的参数,逐个添加到数组末尾,返回修改后数组的长度 ②:pop()方法从数组末尾移除最后一项,返回被移除的项 具体看下面例子:

var arr=new Array();    //使用构造函数创建数组
var count=arr.push("red","blue");    //push()返回数组长度

console.log("count="+count);    //count=2
console.log(arr);    //red,blue

count=arr.push("black");    //count=3

var item=arr.pop();
console.log("item="+item);//pop返回被移除的项--item=black

队列方法:

①:shift()方法移除数组的第一次项并返回该项 ②:unshift()方法在数组前端添加任意项,并返回新数组的长度

具体看一下例子:

var colors=new Array();    //创建数组
var count=colors.unshift("red","green");    //在数组前端添加两项
console.log(count);    //2

count=colors.unshift("black");     //此时数组各项顺序为"black","red","green"
console.log(count)    //3

item=colors.shift();
console.log(item);    //black

作者: 关于郑州想的都是你

链接:www.imooc.com/article/167…

来源:慕课网

一、题目

 用JS代码求出页面上一个元素的最终的background-color,不考虑IE浏览器,不考虑元素float情况。

二、题目解析

 1.考察底层JavaScript基础  前端开发,日常最常接触的就是页面样式的编写。而摆脱jQuery等工具库,用原生js获取样式,是每个前端程序猿进阶阶段必须掌握的技能。

 2.考察面试者的思维缜密程度和开发经验  如果认为单单求元素计算后的样式,就有点too young了。页面的样式的复杂,永远是最虐心的。就算前端有多牛逼,一听到兼容IE6,论谁都会心塞😓。所以还要考虑特殊的情况:display,opacity,visibility的取值。

三、理论基础

 1. 内联样式  内联样式可以通过元素的style属性获取,如果style属性有background-color值,则可以直接获取出来 (暂不考虑!important) 。

 2. 外联的层叠样式  DOM2样式规范在document.defaultView中包含了一个getComputedStyle()方法。该方法返回一个只读的CSSStyleDeclaration对象,其中包含特定元素的所有计算样式。

四、解题

4.1 将所有工具方法封装在WDS(wall dom script)命名空间中

(function(WDS, undefined){
  // 封装代码...
})(window.WDS || (window.WDS = {}));

 代码封装在命名空间里,不会造成无意间的代码污染。

4.2 工具方法camelize

// 字符串转换为驼峰写法
function camelize(str) {
    return str.replace(/-(\w)/g, function (strMatch, p1){
        return p1.toUpperCase();
    });
}

 该方法是为了方便后续getStyle()方法的编写,而独立出来的。  作用是将连字符类的css属性值,转换成驼峰写法。  例如:将background-color转换为backgroundColor

4.3 获取特定元素的计算样式

// 获取元素计算后的样式
function getStyle(elem, property){
    if(!elem || !property){
        returnfalse;
    }

    var value = elem.style[camelize(property)], // 先获取是否有内联样式
        css; // 获取的所有计算样式

    // 无内联样式,则获取层叠样式表计算后的样式
    if(!value){
        if(document.defaultView && document.defaultView.getComputedStyle){
            css = document.defaultView.getComputedStyle(elem, null);
            value = css ? css.getPropertyValue(property) : null;
        }
    }

    return value;
}

 做到这一步,第一个考察点基本就满足了。也能获知面试者是否具备足够扎实的js基础。  另外,像安全保护性的判断if(!elem || !property)和功能嗅探if(document.defaultView && document.defaultView.getComputedStyle),都能很好地体现开发者的代码逻辑和开发经验。

4.4 排除特殊情况

// 检测获取的背景色是否有效
function checkBgValue(elem){
    var value = getStyle(elem, 'background-color'),
        hasColor = value ? true : false; // 是否有颜色

    // 排除特殊情况
    if(value == "transparent" || value == "rgba(0, 0, 0, 0)"){
        // 未设置background-color,或者设置为跟随父节点
        hasColor = false;
    }elseif(getStyle(elem, 'opacity') == "0"){
        // dom节点透明度为全透明
        hasColor = false;
    }elseif(getStyle(elem, 'visibility') == "hidden"){
        // dom节点不可见
        hasColor = false;
    }elseif(getStyle(elem, 'display') == "none"){
        // dom节点不可见
        hasColor = false;
    }

    return hasColor;
}

4.5 获取div在页面最终显示的颜色

// 获取div最终显示的颜色
function getRealBg(elem){
    if(checkBgValue(elem)){
        return getStyle(elem, 'background-color');
    }elseif(elem != document.documentElement){
        return getRealBg(elem.parentNode);
    }

    return'';
}

 获取样式值采用递归方式处理。  如果能顺利获取到元素样式,且不触发4.4 排除特殊情况中的一种,则直接返回结果。  触发了特殊情况,则需要查找父节点以及更上层的节点的样式,来获取肉眼能看到,显示在页面上的background-color值。  在向上回溯的过程中,如果已经回溯到html根节点,则可以停止回溯。所以加了判断else if(elem != document.documentElement)

五、遗漏的大boss

5.1 大boss !important  如果乱用 !important,对大型项目的维护和开发,绝对是一场噩梦。因为优先级规则的计算,!important永远处在食物链的最顶层。  当前题目不考虑这种情况,也是我的偷懒😆。确实很棘手,就不写这个逻辑分支的代码了。这里提醒一下~

5.2 大boss 父节点及根节点设置了不可见css属性  只要设置该css语句:html {display:none;},页面所有元素立刻消失不见。而任意特定元素的上级节点,只要设置了 opacity,display,visibility,判断逻辑瞬间变得复杂起来。所以,这个浑水我也不趟 O(∩_∩)O哈哈~

六、改进的点

 其实特殊情况排除的判断,我偷懒没做到最好——rgb颜色值和特定颜色值(比如red)没有进行统一的转换,只是加了生硬的判断if(value == "transparent" || value == "rgba(0, 0, 0, 0)")。  有兴趣的可以搜索下颜色值转换的js方法,这里我就不写了。

七、源码和demo

源码地址[github.com/wall-wxk/bl…](https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fwall-wxk%2FblogDemo%2Fblob%2Fmaster%2F2017%2F02%2F05%2FgetStyle.html) demo[wall-wxk.github.io/blogDemo/20…](https://link.juejin.im?target=https%3A%2F%2Fwall-wxk.github.io%2FblogDemo%2F2017%2F02%2F05%2FgetStyle.html)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

补充

 谢谢@烟雨雾岚 的提醒的新思路:canvas截图+Js获取图片某点颜色,这样可以完美解决所有的问题。

一、值

1)数字

JavaScript只有一种数值类型:number(数字),包括“整数”和带小数的十进制数。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//数字的语法
var a = 5E10; // 50000000000
a.toExponential();  // "5e+10"
var b = a * a;  // 2.5e+21 
var c = 1 / a; // 2e-11
var d = 0.42;
var e = .42; //数字前面的0可以省略
var f = 42.; //小数点后小数部分最后面的0也可以省略

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

由于数字值可以使用Number对象进行封装,因此数字值可以调用Number.prototype中的方法。例如,tofixed(..)方法可指定小数部分的显示位数:

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

// 无效语法:
42.toFixed( 3 );    // SyntaxError
// 下面的语法都有效:
a = (42).toFixed(3); // "42.000" 
b = 0.42.toFixed(3); // "0.420" 
c = 42..toFixed(3); // "42.000"
d = 42 .toFixed(3); // "42.000"

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

2)整数检测

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//整数检测
if (!Number.isInteger) {
  Number.isInteger = function(num) {
    return typeof num == "number" && num % 1 == 0;
  };
}
//安全整数检测
if (!Number.isSafeInteger) {
  Number.isSafeInteger = function(num) {
    return Number.isInteger(num) &&
      Math.abs(num) <= Number.MAX_SAFE_INTEGER;
  };
}

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

3)null与undefined

特殊数值undefined与null。它们的名称既是类型也是值。

null指空值(empty value),曾赋过值,但是目前没有值。null是一个特殊关键字,不是标识符,我们不能将其当作变量来使用和赋值。

undefined指没有值(missing value),从未赋值。undefined是一个标识符,可以被当作变量来使用和赋值。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//将undefined当作变量来使用和赋值
functionfoo() {
  "use strict";
  var undefined = 2;
  console.log(undefined); // 2
}
foo();

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

4)不是数字的数字

NaN意指“不是一个数字”(not a number),将它理解为“无效数值”、“失败数值”或者“坏数值”可能更准确些。

NaN是一个特殊值,它和自身不相等,是唯一一个非自反(自反,即x === x不成立)的值。而NaN != NaN为true。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

var a = 2 / "foo"; // NaN
typeof a === "number"; // trueif (!Number.isNaN) {
  Number.isNaN = function(n) { //非数字类型的值在isNaN中也会返回truereturn (
      typeof n === "number" &&
      window.isNaN(n)
    );
  };
}
//利用NaN不等于自身这个特点
if (!Number.isNaN) {
  Number.isNaN = function(n) {
    return n !== n;
  };
}

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

5)零值

加法和减法运算不会得到负零(negative zero)。负零在开发调试控制台中通常显示为“-0”,但在一些老版本的浏览器中仍然会显示为“0”。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//零值
a = 0 / -3; // -0
b = 0 * -3; // -0
c = +"-0"; // -0
d = Number("-0"); // -0
e = JSON.parse("-0"); // -0
//值比较
-0 == 0; // true
-0 === 0; // true
0 > -0; // false
//-0的判断方式
function isNegZero(n) {
  n = Number(n);
  return (n === 0) && (1 / n === -Infinity);
}
isNegZero(-0); // true
isNegZero(0 / -3); // true
isNegZero(0); // false

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

6)特殊等式

NaN和-0在相等比较时的表现有些特别。

由于NaN和自身不相等,所以必须使用ES6中的Number.isNaN(..)。 而-0等于0(对于===也是如此),因此我们必须使用isNegZero(..)这样的工具函数。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//特殊等式
if (!Object.is) {
  Object.is = function(v1, v2) {
    // 判断是否是-0
    if (v1 === 0 && v2 === 0) {
      return 1 / v1 === 1 / v2;
    }
    // 判断是否是NaN
    if (v1 !== v1) {
      return v2 !== v2;
    }
    // 其他情况
    return v1 === v2;
  };
}

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

7)值和引用

JavaScript引用指向的是值。如果一个值有10个引用,这些引用指向的都是同一个值,它们相互之间没有引用/指向关系。

向函数传递值的时候,实际是将引用值的一个复本传递进去,不管是基本类型还是对象。

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//基本类型
var a = 2;
var b = a; // b是a的值的一个副本
b++;
a; // 2
b; // 3

//变量引用同一个值
var c = [1, 2, 3];
var d = c; // d是[1,2,3]的一个引用
d.push(4);
c; // [1,2,3,4]
d; // [1,2,3,4]

//变量引用不同的值
var a = [1, 2, 3];
var b = a;
b = [4, 5, 6]; //给b重新赋值,引用新的值,不影响a的引用
a; // [1,2,3]
b; // [4,5,6]

//函数内让参数重新引用值
function foo2(x) {
  x.push(4);
  x; // [1,2,3,4]

  x = [4, 5, 6];// 然后重新引用新的值
  x.push(7);
  x; // [4,5,6,7]
}
var a = [1, 2, 3];
//向函数传递a的时候,实际是将引用a的一个复本赋值给x,而a仍然指向[1,2,3]
foo2(a);
a; // 是[1,2,3,4],不是[4,5,6,7]

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

上面的源码可以在此次浏览

二、原生函数

常用的原生函数有:String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol()。

1)内部属性[[Class]]

这个属性无法直接访问,一般通过Object.prototype.toString(..)来查看。

//内部属性[[Class]]
Object.prototype.toString.call([1, 2, 3]); // "[object Array]" 
Object.prototype.toString.call(/regex-literal/i); // "[object RegExp]"
//基本类型
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"

虽然Null()和Undefined()这样的原生构造函数并不存在,但是内部[[Class]]属性值仍然是"Null"和"Undefined"。

基本类型值被各自的封装对象自动包装,所以它们的内部[[Class]]属性值分别为"String"、"Number"和"Boolean"。

Object.prototype.toString.call("abc"); // "[object String]"
Object.prototype.toString.call(42); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"

2)封装对象包装

由于基本类型值没有.length和.toString()这样的属性和方法,需要通过封装对象才能访问,此时JavaScript会自动为基本类型值包装(box或者wrap)一个封装对象:

//封装对象
var a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"

如果想要自行封装基本类型值,可以使用 Object(..) 函数(不带 new 关键字)

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//自行封装基本类型
var a = "abc";
var b = new String(a);
var c = Object(a);

typeof a; // "string" 
typeof b; // "object" 
typeof c; // "object"
b instanceof String; // true 
c instanceof String; // true
Object.prototype.toString.call(b); // "[object String]" 
Object.prototype.toString.call( c ); // "[object String]"

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

3)拆封

如果想要得到封装对象中的基本类型值,可以使用valueOf()函数:

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

//拆封
var a = new String("abc");
var b = new Number(42);
var c = new Boolean(true);

console.log(a.valueOf()); // "abc"
console.log(b.valueOf()); // 42
console.log(c.valueOf()); // true

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg&quot; version="1.1" width="800" height="600"></svg>)

在需要用到封装对象中的基本类型值的地方会发生隐式拆封。

/*========================常用函数========================*/

/*时间格式化*/
Date.prototype.Format = function (fmt) {
    var o = {
        "M+": this.getMonth() + 1, /*月份*/
        "d+": this.getDate(), /*日*/
        "h+": this.getHours(), /*小时*/
        "m+": this.getMinutes(), /*分*/
        "s+": this.getSeconds(), /*秒*/
        "q+": Math.floor((this.getMonth() + 3) / 3), /*季度*/
        "S": this.getMilliseconds() /*毫秒*/
    };
    if (/(y+)/.test(fmt))
        fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt))
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
};

/*IE浏览器不支持date(time),所以用此方法转换*/
function NewDate(fmt) {
    /*首先将日期分隔 ,获取到日期部分 和 时间部分*/
    var day = fmt.split(' ');
    /*获取日期部分的年月日*/
    var days = day[0].split('-');
    /*获取时间部分的 时分秒*/
    var mi = day[day.length - 1].split(':');
    /*获取当前date类型日期*/
    var date = new Date();
    /*给date赋值  年月日*/
    date.setUTCFullYear(days[0], days[1] - 1, days[2]);
    /*给date赋值 时分秒  首先转换utc时区 :+8*/
    date.setUTCHours(mi[0] - 8, mi[1], mi[2]);
    return date;
}

/*为空判断*/
function isEmpty(s) {
    switch (typeof(s)) {
        case'string':
            return !s.length;
            break;
        case'array':
        case'object':
            for (var i in s) returnfalse;
            returntrue;
            break;
        case'undefined':
            returntrue;
            break;
        default:
            return !s;
            break;
    }
}

/*数字判断*/
function isNumber(s) {
    return typeof(s) == 'number' ? true : false;
}

/*整数判断*/
function isInt(s) {
    var re = /^-?\d*$/;
    return re.test(s);
}

/*正整数判断*/
function isUInt(s) {
    var re = /^\d*$/;
    return re.test(s) && s >= 0;
}

/*小数判断*/
function isDecimal(s, bit) {
    if (!arguments[1]) bit = -1;
    if (bit == -1) {
        var re = /^-?\d*.?\d*$/;
        return re.test(s);
    } else {
        var re = new RegExp('^-?\\d*.?\\d{0,' + bit + '}$');
        return re.test(s);
    }
}

/*正小数判断*/
function isUDecimal(s, bit) {
    if (!arguments[1]) bit = -1;
    if (bit == -1) {
        var re = /^\d*.?\d*$/;
        return re.test(s) && s >= 0;
    } else {
        var re = new RegExp('^\\d*.?\\d{0,' + bit + '}$');
        return re.test(s) && s >= 0;
    }
}

/*字符串判断*/
function isString(s) {
    return typeof(s) == 'string';
}

/*========================/常用函数========================*/

js onkeyup replace 自动替换

检测浮点数 只能是整数或者小数 
多余的就replace 掉 的表单验证

function checkFloatNum(obj)
{
    //先把非数字的都替换掉,除了数字和.
    obj.value = obj.value.replace(/[^\d.]/g,"");
    //必须保证第一个为数字而不是.
    obj.value = obj.value.replace(/^\./g,"");
    //保证只有出现一个.而没有多个.
    obj.value = obj.value.replace(/\.{2,}/g,".");
    //保证.只出现一次,而不能出现两次以上
    obj.value = obj.value.replace(".","$#$").replace(/\./g,"").replace("$#$",".");
}
libin1991 commented 6 years ago

contains方法:判定一个字符串是否包含另一个字符串。常规思维,使用正则,但每次都要用new RegExp来构造,太麻烦,性能太差。转而使用原生字符串方法。如indexOf , lastIdexOf , search

function contains (target, it) {
        return target.lastIndexOf(it) != -1; //indexOf改成search, lastIndexOf也可以
    }

在mootools版中,我们看到它支持更多的参数,目的是判定一个元素的className是否包含某个特定的class。众所周知,元素可以添加多个class,中间以空格隔开,使用mootoos的contains就很方便检测包含关系了。

function contains (target, str, separator) {
        return separator ? ( separator + target + separator).indexOf(separator + str +separator) > -1 : target.indexOf(str) > -1;
    }

repeat方法: 将一个字符串重复n次,如repeat("ruby",2)得到rubyruby

版本1:利用空数组的join方法

function repeat (target, n) {
        return (new Array(n + 1)).join(target);
    }

版本2.....6...

版本7,递归在浏览器下做了优化 ,包括ie6,属于最好的实现方式之一

function repeat (target, n) {
        if (n == 1){
            return target
        }
        var s = repeat(target, Math.floor(n/2));
        s += s;
        if (n % 2) {
            s += target;
        }
        return s;
    }

**byteLen方法:取得一个字符串所有字节的长度。**这是一个后端转过来的方法。在前端,我们要用户填写文本,需要字节有限制。

版本1:假设字符每个字符Unicode编码小于等于255,byteLength为字符串长度。再遍历字符串,遇到unicode编码大于255时,为byteLength加1

function byteLen (target) {
        var byteLength = target.length, 
            i = 0;
        for ( ; i < target.length ; i++) {
            if (target.charCodeAt(i) > 255) {
                byteLength++;
            }
        }
        return byteLength;
    }

truncate方法,用于对字符串进行截断处理,当超过限定长度,默认添加三个点号等

function truncate (target, length , truncation) {
        length = length || 30;
        truncation = truncation === void(0) ? '...' : truncation;
        return target.length > length ? target.slice(0, length - truncation.length) + truncation : String(target);
    }

camelize方法,转换为驼峰命名风格

function camelize (target) {
        if (target.indexOf('_') < 0 && target.indexOf('_') < 0) {
            return target; //提前判断,提高响应效率        }
        return target.replace(/[-_][^-_]/g , function(match){
            return match.charCodeAt(1).toUpperCase();
        })
    }

underscored方法。转换为下划线风格

function underscored(target){
        return target.replace(/([a-z\d])([A-Z])/g , '$1_$2').replace(/\-/g , '_').toLowerCase();
    }

dasherize方法,转换为连字符风格,亦指css变量风格(承上面的方法)

function dasherize(target){
        return underscored(target).replace(/_/g, '-');
    }

capitalize方法,首字母大写

function capitalize(target) {
        return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();
    }

stripTags方法,移除字符中的html标签。但有一个缺陷,如果其中有script标签,会把不该显示的脚本也显示出来。

function stripTags(target){
        return String(target || "") .replace(/<[^>]+>/g, '');
    }

escapeHTML和unescapeHTML略

escapeRegExp方法:将字符串安全格式转换为正则表达式的源码

function escapeRegExp(target){
        return target.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
    }

** pad方法**,与trim方法相反,pad可以为字符串的某一端添加字符串。常见的是在日历的月份前面加补0,因此,被称为fillZero

function pad(target,n){
        var zero = new Array(n).join('0');
        var str = zero + target;
        var resulet = str.substr(-n);
        return resulet;
    }

高级方法,也是mass Framework使用的版本,支持更多的参数。允许从左边或从右填充。以及使用什么内容进行填充。

function pad (target, n, filling, right, radix){
        var num = target.toString(radix || 10);
        filling = filling || "0";
        while (num.length < n){
            if(!right){
                num = filling + num;
            } else {
                num += filling;
            }
            return num;
        }
    }

wbr方法,为目标字符串添加wbr换行。不过需要注意的是,它不是为每个字符串都插入字样,而是相当于在组成文本节点的报语法中的每个字符后插入字样。如果是aabbcc,返回aabbcc,在opear浏览器上,默认的css不会添加上wbr样式,需要在css上添加,wbr:after{content:"\00200B"}解决此问题

function wbr (target){
        return String(target).replace(/(?:<[^>]+>) | (?:&#?[0-9a-z]{2,6};) | (.{1})/gi,'$&<wbr>').replace(/><wbr>/g,'>');
    }

format方法,在c语言中,有一个printf方法,我们可以在后面添加不同的类型的参数嵌入到将要输出的字符串中。这是非常有用的方法,因为在javascript中涉及到大量这样字符串拼接的工作 ,如果涉及逻辑,我们可以用模板,如果轻量点,我们可以用这个方法。

在不同的框架中名字不同,prototype.js 叫interpolate,Base2叫format,mootools叫substitute

function format (str, object){
        var array = Array.prototype.slice.call(arguments, 1);
        return str.replace(/\\?\#{([^{}]+)\}/gm,function(match, name) {
            if(match.charAt(0) == '\\')
                return match.slice(1);
            var index = Number(name)
            if(index >= 0)
                return array[index];
            if (object && object[name] !== void 0)
                return object[name];
            return '';
        });
    }
    var a = format("resulet is #{0}, #{1}",22,33)
    console.log(a) // resulet is 22, 33var b = format ( "#{name} is a #{sex} #{am}" ,{
        name:"wangjie",
        sex:"man",
        am:"111"
    });
    console.log(b) // wangjie is a man 111

它支持两种传参方法,如果字符串的占位符为0,1,2这样的非零整数,要求传入两个或以上的参数,否则就传入一个对象,键名为占位符。

quote方法,在字符串的两端加上双引号。然后内部需要转义的地方都要转义。用于接装JSON的键名或模析系统中

//code.google.com/jQuery-jsonvar escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
                    meta = {
                        '\b':'\\b',
                        '\t':'\\t',
                        '\n':'\\n',
                        '\f':'\\f',
                        '\r':'\\r',
                        '"':'\\"',
                        '\\':'\\\\'
                    };
    function quote(target){
        if (target.match(escapeable)){
            return '"' + target.replace(escapeable,function(a) {
                var c = meta[a];
                if(typeof c === 'string') {
                    return c;
                }
                return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
            }) + '"';
        }
        return '"' + target + '"';
    }               

当然,如果浏览器支持原生的JSON,我们直接用JSON.stringify就行了,另外,FF在JSON发明之前,就支持String.prototype.quote与String.quote方法了,我们使用quote之前判定浏览器是否内置这些方法

字符串好像没有打的浏览器兼容问题,有的话是IE6,IE7不支持用数组中括号取它的每一个字符,需要用charAt来取。IE678不支持垂直分表符,因此有如下hack

var isIe678 = !+"\v1";

修复旧版本IE中的trim函数。这是一个很常用的操作,通常我们需要把表单的两侧空白清除掉

版本1,用了两次正则,实际速度非常惊人,主要得益于浏览器的内部优化。base2使用这种优化,引起了其它浏览器的跟风。于是正则的实现再也比不过字符串方法了。一个著名的例子,字符串拼接。直接相加比Array做成的StringBuffer还快。而StringBuffer技术在早些年备受推崇。

function trim(str){
        return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    }

版本2

利用两个候选操作符链接两个正则,这样做就失去了浏览器优化的机会。由于看起来很优雅,很多类库都使用它。如jQuery,mootools

function trim(str){
        return str.replace(/^\s+|\s+$/g, '');
    }

版本3

更好的改进版

function trim(str){
        var str = str.replace(/^\s\s*/, ""),
                ws = /\s/,
                i = str.length;
    while (ws.test(str.charAt(--i)))
        return str.slice(0, i + 1);
    }

2.数组的扩展与修复

得益于Prototype.js的ruby式数组方法的侵略,让jser()的前端工程师大开眼界,原来对数组的操作是如此的丰富多彩。原来的javascript数组方法就是基于栈与队列的一套方法,像splice()还是很晚的时候加入的。让我们回顾一下用法。

**pop()**方法,出栈操作,删除并返回数组的最后一个元素

**push()**方法,出栈操作,向数组的末尾添加一个或更多元素,并返回新的长度。

**shift()**方法,出队操作,删除并返回数组的第一个元素

**unshift()**方法,入队操作,向数组的开头添加一个或更多的元素,返回新的长度

**slice()**方法,切片操作,从数组中分离出一个子数组,功能类似字符串的substring、slice、substr这三兄弟。此方法也常用于转换类数组对象为真正的数组

**sort()**方法,对数组元素进行排序,有一个可选参数,为比较函数。

**reverse()**方法,颠倒数组中元素的顺序。

**splice()**方法,用于用于同时对原数组进行增删操作,数组的remove方法就是基于它而写的

**concat()**方法,用于把原数组与参数合并成一个新数组,如果参数为数组,那么它会把其第一维的元素放入新的数组中。因此我们可以利用它实现数组的平坦化操作和克隆操作。

**join()**方法,把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。你可以想象成字符串的split的反操作。

在ecma262v5中,它把标准浏览器早已经实现的几个方法进行了入户处理。从此,我们可以安心的使用forEach()方法,不用担心他们被废弃掉了。

**indexOf()**方法,定位操作。同上,不是从后遍历。索引操作可以说是字符串的同名方法的翻版,存在就返回非负操作。不存在就返回-1.

**forEach()**方法。迭代操作。将数组的元素依次传入一个函数中执行,prototype.js对应的名字为 each。

**map()**方法,收集操作。将数组的元素依次传入一个函数中执行,然后把它们的返回值组成一个新数组返回。prototype.js对应的名字为collect.

**fliter()**方法。过滤操作,将数组的元素依次传入一个函数中执行,然后把返回值为true的那个元素放入新的数组中返回。prototype.js中它有三个名字,select、filter、findAll

**some()**方法,只要数组中有一个元素满足条件(放进函数返回true)。那么它就返回true.prototype.js对应的名字为any

**every()**方法,只有数组的元素满足调条件(放进给定函数返回true)它才返回true。prototype.js对应的名字为any

**reduce()**方法,归化操作。将数组中的元素归化为一个简单的数值。prototype.js对应的名字为inject

**reduceRight()**方法:归化操作,将数组中的元素归化为一个简单的数值。同上,不过是从后遍历。

由于许多扩展也基于这些新的标准方法,因此我们先给出IE678兼容方案,全部在数组原型上修复他们。

    Array.prototype.indexOf = function(item, index){
        var n = this.length, i = ~~index;
        if(i<0)
            i += n;
        for(; i<n; i++)
            if(this[i] === item)
                return i;
            return -1;
    }

    Array.prototype.lastIndexOf = function(ietm, index){
    var n = this.length,
        i = index == null ? n - 1 : index;
    if (i < 0)
        i = Math.max(0, n + 1);
    for (; i >= 0; i--)
        if (this[i] === item)
           return i;
    return -1;
    }

像forEach map filter some every这几个方法,在结构上非常相似,我们可以这样生成他们。

function iterator (vars, body, ret){
        var fun = 'for (var ' + vars + 'i = 0,n =  this.length; i < n; i++ ){' +
             body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') 
             + '}' + ret
             return Function ("fn,scope", fun);
    }
    Array.prototype.forEach = iterator('','_','');
    Array.prototype.filter = iterator('r=[],j=0', 'if(_)r[j++]=this[i]', 'return r');
    Array.prototype.map = iterator('r=[],', 'r[i]=_' , 'return r');
    Array.prototype.some = iterator('','if(_)return true', 'return false');
    Array.prototype.every = iterator('','if(!_)return false','return true')

造轮子的同学注意:数组的空元素不会再上述方法中遍历出来。

    [1,2,,4].forEach(function(e){
        console.log(e)
    })

接下来,我们将对数组的扩展进行总结,除去上述的

prototype.js的数组扩展:eachSlice , detect , grep , include , inGruopsOf , invoke, max, min, partition, pluck , reject, sortBy , zip , size, clear, first, last, compact, faltten , without, uniq , intersect, clone, inspect

Right.js 的数组扩展有 include, clean , clone , conpact, empty , first , flatten , includes, last, max , merge , min , random, reject, shuffle , size , sortBy, sum , uniq ,walk, without

mootools的数组扩展,clean, clone , associate , link , contains, append, getLast , getRandom, include, combine, erase, empty, flatten, min, max, mean, sum, erase, insert.

Ext的数组扩展, contains, pluck, clean, qunique, form , remove, include, merge,insersect, difference, flatten, min , max, mean, sum, erase, insert

Underscore.js 的数组扩展, detect, reject, invoke , pluck, sortBy, groupBy, sortedIndex, first, last, compact, flatten,without , union, intersection , difference, quiq, zip

qooxdoo数组扩展, insertAfter, insertAt, insertBefore , max , min , remove , removeAll , removeAt, sum , unique.

百度七巧板扩展, contains ,empty , find , remove , removeAt , unique

我们可以发现,Prototype.js 的一套方法影响深远, 许多库都有它的影子。我们可以根据需要与框架宗旨指定自己的数组扩展, 我们在这些方面考虑如下: 至少包含**:平坦化**,** 去重**, 乱序移除这几个操作,其次是两个集合间的操作,如取并集差集交集。 下面是各种具体实现。

contains方法, 判定数组是否包含指定的目标

function contains(target, item){
        return target.indexOf(item) > -1
    }

removeAt方法,移除数组中指定位置的元素,返回布尔表示成功与否

function removeAt(target, index){
        return !!target.splice(index, 1).length
    }

remove方法,移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否。

function remove(target, ietm){
        var index = target.indexOf(ietm);
        if(~index)
            return removeAt(target,index);
        returnfalse;
    }

**shuffle方法,对数组进行洗牌。**若不影响原数组,可以先拷贝一份出来操作,有关洗牌,可以参考 博文:http://bost.ocks.org/mike/shuffle/

function shuffle(target){
        var j, x,  i = target.length;
        for(; i > 0; j = parseInt(Math.random() * i) ,
            x = target[--i],target[i] = target[j] = x) {
        }
        return target
    }

random方法,从数组中随机选出一个元素出来

function random(target){
        return target[Math.floor(Math.random() * target.length)];
    }

flatten方法,对数组进行平坦化处理,返回一个一维的新数组

function flatten(target){
        var resulet = [];
        target.forEach(function(item){
            if (Array.isArray(item)) {
                resulet = resulet.concat(flatten(item));
            } else{
                resulet.push(ietm)
            }
        });
        return resulet;
    }

unique方法,对数组去重,返回一个没有重复元素的新数组

function unique(target){
        var resulet = [];
        loop: for (var i = 0, n = target.length; i < n; i++){
            for (var x = i + 1; x < n; x++){
                if (target[x] === target[i])
                    continue loop;
            }
            resulet.push(target[i]);
        }
        return resulet;
    }

compact方法,过滤数组中的null和undefined,但不影响数组。

function compact (target) {
        return target.filter(function(el){
            return el != null;
        });
    }

pluck方法,获得对象数组中的每个元素的指定属性,组成数组返回

function pluck(target, name){
        var resulet = [], prop;
        target.forEach(function(ietm){
            prop = ietm[name];
            if (prop != null)
                resulet.push(prop);
        });
        return resulet;
    }

groupBy方法:根据指定条件(如回调或对象的某个属性)进行分组,构成对象返回

function groupBy(target, val){
        var resulet = {};
        var iterator = $.isFunction(val) ? val : function (obj){
            return obj[val];
        };
        target.forEach(function(value, index){
            var key = iterator (value, index);
            (resulet[key] || resulet[key] = [])).push(value);
        });
        return resulet
    }

sortBy方法:根据指定条件进行排序,通常用于对象数组

function sortBy(target, fn, scope){
        var array = target.map(function(ietm, index){
            return {
                el: ietm,
                re: fn.call(scope, ietm, index)
            };
        }).sort(function(left, right){
            var a = left.re, b = right.re;
            return a < b ? -1 : a > b ? 1: 0;
        })
        return pluck(array, 'el');
    }

union方法,对两个数组取并集

function union (target, array){
        return unique(target.concat(array));
    }

intersect方法:对两个数组取交集

function intersect(target, array){
        return target.filter(function(n){
            return ~array.indexOf(n);
        });
    }

diff方法,对两个数组取差集

function diff(target, array){
        var resulet = target.slice();
        for (var i = 0, i < resulet.length; j++) {
            for (var j = 0; j < resulet.length; j++) {
                if (resulet[i] === array[j]){
                    resulet.splice(i, 1);
                    i--;
                    break;
                }
            }
        }
        return resulet;
    }

min方法,返回数组的最小值,用于数字数组

function min (target){
        return Math.min.apply(0, target);
    }

max方法,返回数组中的最大值。用于数字数组

function max(target){
        return Math.max.apply(0, target);
    }

** 3.数值的扩展与修复**

数值没有什么好扩展的,而且javascript的数值精度问题一向臭名昭著,修复不是一两行代码的事情。先看扩展。

Prototype.js为它添加了8个原型方法,Succ是加1,times是将回调重复执行制定次数,toPaddingString与上面提到的字符串扩展方法pad作用一样,toColorPart是转十六机制,abs,ceil,floor,是从Math中提取出来的

mootools的情况,limit是从数值限在一个闭开间中,如果大于或小于其边界,则等于其最大值或最小值,times与prototype.js用法相似,round是Math.round的增强版,添加了精度控制,toFloat,toInt是从window中偷学来的。

看看mass Framework对数字的扩展

limit方法,确保数值在[n1,n2]闭区间之内,如果超出界限,则置换为离它最近的最大值或最小值。

function limit(target, n1, n2){
        var a = [n1, n2].sort();
        if (target < a[0])
            target = a[0];
        if(target > a[1])
            target = a[1];
        return target;
    }

nearer方法,求出距离数值最近的那个数。

function nearer(target, n1, n2){
        var diff1 = Math.abs(target - n1),
            diff2 = Math.abs(target - n2);
        return diff1 < diff2 ? n1 : n2
    }

Number下唯一要修复的方法是toFixed,它是用来校正精确度,最后的那个数会四舍五入操作,但在一些浏览器中,并未这么操作。 简单修复可以这样处理

if(0.9.toFixed(0) !== '1') {
        Number.Prototype.toFixed = function(n) {
            var power = Math.pow(10, n);
            var fixed = (Math.round(this*power)/power).toString();
                if(n == 0)
                    return fixed;
                if(fixed.indexOf('.') < 0)
                    fixed += '.';
                var padding = n + 1 (fixed.length - fixed.indexOf('.'));
                for (var i = 0; i < padding; i++)
                    fixed += '0';
                return fixed;
        }
    }

关于javascript误差运算不在这里呈现了,但在工作中,我们尽量避免小数操作与大数操作,或者转交后台处理,实在避免不了,可以引入专业的库来实现。

4.函数的扩展与修复

V5对函数的唯一扩展就是bind函数,众所周知,这来自prototype.js,此外,其它重要的函数都来自prototype.js

prototype.js的函数扩展如下

argumentNames:取得函数的形参,以字符串形式返回,这只要用于其类工厂的方法链设计

bind,不用多描述,劫持作用域,并预先添加更多的参数。

bindAsEventListener 如bind相似,但强制返回函数的第一个参数为事件对象,这是用于修复IE多投事件API与标准API的差异。

curry 函数柯里化,用于一个操作分成多步进行,并可以改变原函数的行为。

wrap:AOP 的实现。

delay:setTimeout的偷懒写法。

defer:强制延迟0.01秒才执行原函数

methodize:将一个函数变成其调用对象的方法,这也是为其类工厂的方法服务。

首先我们看bind方法,它用到了著名的闭包(所谓闭包,就是引用着外部变量的内部函数),比如下面这个函数

[

复制代码
](javascript:void(0);)

var observable = function(val){
        var cur = val; //一个内部变量function field(neo) {
            if (arguments.length){ //setterif (cur !== neo) {
                    cur = neo
                }
            } else { //getterreturn cur;
            }
        }
        field();
        return field;
    }

它里边的field函数将于外部的cur构成一个闭包。prototype.js中的bind方法只要依仗原来=函数与经过切片话的args构成闭包,而这个方法就是名副其实的curry柯里化,用户最初的那个传参,劫持到返回函数修正this的指向。

    Function.Prototype.bind = function(context) {
        if (arguments.length < 2 && context == void 0)
            returnthis;
        var _method = this, args = [].slice.call(arguments, 1);
        returnfunction() {
            return _method.apply(context, args.context.apply(args, arguments));
        }
    }

因为有这个原型扩展,我们才可以修复ie的多投事件API attachEvent回到的this问题,它总是指向window对象,而标准的浏览器的的addEventListener中的this则其调用对象

var addEvent = document.addEventListener ? 
           function (el, type, fn, capture){
               el.addEventListener (type, fn, capture)
           } : 
           function (el, type, fn) {
               el.attachEvent("on" + type, fn.bind(el, event))
           }

ECMA62 V5对其认证以后,唯一的增强是对调用者进行监测,确保它是一个函数。顺便总结一下这三个东西。

call是obj.method(a, b, c)到method(obj,[a, b, c])的变换,它要求第二个参数必须存在,一定是数组或Arguments这样的类数组,NodeList这样具有争议性的东西就不能传入进去! 因此,jQuery对两个数组或类数组的合并就使用jQuery.merge,放弃使用Array.prototype.push.apply。

bind就是apply的变种,保证返回值是一个函数。

这三个方法非常有用,我们设法将其还原出来。

var bind = function(bind) {
        return {
            bind: bind.bind(bind),
            call: bind.bind(bind.call),
            apply: bind.bind(bind.apply)
        }
    } (Function.Prototype.bind)

那么怎么使用它们呢,比如我们想合并两个数组,直接调用concat方法:

var concat = bind.apply([].concat);
    var a = [1, [2,3], 4];
    var b = [1, 3];

使用bind.bind方法可以将它们的结果进一步平坦化

var concat = bind.apply([].concat);
    console.log(concat(b,a)); //=> [1,3,1,2,3,4]

又如切片化操作,它经常用于转换类数组对象为纯数组

var slice = bind([].slice)
    var array = slice({
        0: "aaa",
        1: "bbb",
        2: "ccc",
        3: "ddd"
        length: 4
    });
    console.log(array) //=> ["aaa","bbb","ccc","ddd"]

更常用的操作是转换arguments对象,目的是为了使用数组的一系列方法

function test() {
        var args = slice(arguments)
        console.log(args)//=> [1,2,3,4,5]    }
    test(1, 2, 3, 4, 5)

我们可以将hasOwnProperty提取出来,判定对象是否在本地就拥有某属性

var hasOwn = bind.call(Object.Prototype.hasOwnProperty);
    hasOwn([],"xx") //false//使用bind.bind就需要多执行一次var hasOwn2 = bind.bind(Object.Prototype.hasOwnProperty);
    hasOwn2([],"xx")() //false

上面的bind.bind的行为就是一种curry,它给了你一种传参的机会,这样你就可以在内部判定参数的个数,决定是否继续返回函数还是结果。这在设计计算器的连续运算上非常有用。从此角度,我们可以得到一条信息,bind着重作用域的劫持,curry在于参数的不断补充

我们可以编写一个curry,当所有步骤输入的参数个数等于最初定义时的函数形参个数,就执行它。

function curry(fn) {
        function inner(len, arg) {
            if (len == 0)
                return fn.apply(null, arg);
            returnfunction(x) {
                return inner(len - 1, arg.concat(x));
            };
        }
        return inner(fn.length, []);
    }
    function sum(x, y, z, w){
        return x + y + z + w;
    }

    curry(sum)('a')('b')('c')('d'); //=> 'abcd'

不过这里我们假定了用户每次都只传入一个参数,我们可以改进下

function curry2(fn) {
        function inner (len, arg){
            if (len <= 0)
                return fn.apply(null, arg);
            returnfunction() {
                return inner (len - arguments.length,
                    arg.concat(Array.apply([],arguments)));
            }
        }
        return inner(fn.length, []);
    }

这样,我们就可以在中途传递多个参数,或不传递参数

curry2(sum)('a')('b','c')('d'); //=> 'abcd'
curry2(sum)('a')()('b','c')()('d'); //=> 'abcd'    

不过,上面的函数形式有个更帅气的名称,叫self-curry,或recurry.它强调的是递归自身来补全参数。 与curry相似的partial。curry的不足 参数总是通过push的方式来补全,而partial则是在定义时所有参数都已经有了,但某些位置上的参数值是个占位符,我们在接下来的传参只是替换掉它们。博客上专门有《partial application in javascript》来介绍这个内容。

    Function.Prototype.partial = function(){
        var fn = this, args = Array.prototype.slice.call(arguments);
        returnfunction() {
            var arg = 0;
            for (var i = 0 ;  i < args.length && arg < arguments.length; i++)
                if (args[i] === undefined)
                    args[i] = arguments[args++];
            return fn.apply(this, args);
        }
    }

它是使用undefined作为占位符的。

var delay = setTimeout.partial(undefined, 10);
    //接下来的工作就代替掉第一个参数
    delay(function() {
        console.log("A  call to this function will be temporarily delayed")
    })

curry、partial应用场景在前端世界应用的并不多,前端讲究的是即时显示,许多API都是同步的,后端由于IO操作等耗时够长,像node.js提供了大量的异步函数来提高性能,防止堵塞。但是过多的异步函数必然带来回调嵌套的问题。因此,我们需要curry等函数变换。将嵌套减少到可以接受的程度。在ajax中会讲述他们的使用办法。

**函数的修复,这涉及到方法,apply和call,这两个方法的本质就是生成一个新的函数。**将原函数与用于的传参放到里面执行而已,在javascript中创造一个函数有很多办法,常见的有函数声明和函数表达式,次之是函数构造器,再次之是eval,setTimeout....

5.日期的扩展与修复

Date构造器是javascript中传参最丰富的构造器。大致分为四种

new Date();
    new Date(value); //传入毫秒鼠new Date(dateString)
    new Date(year, month, day/*, hour, minute, second, minllisecond*/)

其中,第三种可以玩N多花样,个人建议只使用"2009/07/12 12:34:56",后面的分秒可以省略。这个所有的浏览器都支持,此构造器的兼容列表可见此文。

http://dygraphs.com/date-formats.html

若要修正它的传参,可能是个庞大的工程。并且影响Object.prototype.toString的类型的判断。因此,不建议修正。es5.js中的源码,可以参考

https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js

javascript的日期是抄自java.util.Date,但是Date这个类的很多方法对时区等支持不够,且不少都过时,java程序员也推荐使用calnedar类代替Date类。javascript可以选择余地比较小。如对属性使用了前后矛盾的偏移量。月份与小时都是基于0.月份中的天数则是基于1,而年则是从1900开始的.

接下来,我们为旧版的浏览器添加几个ecma262标准化日期方法吧

if(!Date.now) {
        Date.now = function(){
            return + new Date;
        }
    }
    if (!Date.prototype.toISOString) {
        voidfunction() {
            function pad (number) {
                var r = String(number);
                if (r.length === 1) {
                    r = '0' + r ;
                }
                return r
            }
        Date.prototype.toJSON = Date.prototype.toISOString = function() {
            returnthis.getUTCFllYear()
            + '_' + pad(this.getUTCMonth() + 1)
            + '_' + pad(this.getUTCDate())
            + 'T' + pad(this.getUTCHours())
            + ':' + pad(this.getUTCMinutes())
            + ':' + pad(this.getUTCSeconds())
            + '.' + String((this.getUTCMilliseconds()/1000).toFixed(3)).clice(2, 5)
            + 'Z';
        }
        }();
    }

IE67 中,getYear, setYear方法存在bug.这个修起来比较简单:

if ((new Date).getYear() > 1900){
        Date.prototype.getYear = function(){
            returnthis.getFullYear() - 1900;
        };
    Date.prototype.setYear = function(year){
        returnthis.setFullYear(year); //+1900    };
    }

至于扩展,由于涉及本地化的原因,外国许多日期库都需要改一改才能用。其中以dataFormat这个很有用的方法为最。先给一些常用的扩展。

传入两个Date类型的日期,求其相隔的天数

var getDatePeriod = function(start, finish){
        return Math.abs(start * 1 - finish * 1) / 60 / 60 / 1000 / 24;
    }

传入一个Date类型的日期,判断它所在月的第一天。

var getFirstDateInMouth = function(date) {
        returnnew Date(Date.getFullYear(), Date.getMonth(), 1);
    }

传入一个Date类型的日期,判断它所在月的最后一天。

var getLastDateInMouth = function(date) {
        returnnew Date(Date.getFullYear(), Date.getMonth() + 1, 0);
    }

判断是否是闰年

    Date.prototype.isLeapYear = function(){
        returnnew Date(this.getFullYear(),2 ,0).getDate() == 29;
    }

获得当前月份的天数

function getDaysInMotn1(date) {
        switch (Date.getMonth()) {
            case 0:
            case 2:
            case 4:
            case 6:
            case 7:
            case 9:
            case 11:
                return 31;
            case 1:
                var y = Date.getFullYear();
                return y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
            default :
                return 30;
        }
    }

    function getDaysInMonth2(date) {
        returnnew Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
    }

dateFormat方法太长,此处略。