JavaScript易錯知識小點

1、String, Number, Boolean等包裝類型是對象,JavaScript沒有比較對象的方法,即便經過寬鬆比較相等==

var a = new String('abc');
var b = new String('abc');
console.log(a == b) // false
console.log(a === b) // false

2、對包裝類型轉換爲原始值只能正確提取出字符串和數字,而不布爾值不能

Boolean(new Boolean(false)) // true
Number(new Number(123)) // 123
String(new String('abc')) // abc

new Boolean(new Boolean(false)) // true對象

3、String.prototype.split(separator?, limit?)

做用: 經過指定邊界(separator)將字符串分割成子字符串,返回字符串組成的數組。javascript

  • separator: 可選,能夠爲字符串或正則表達式。不傳的話,將原字符串封裝在數組中返回html

  • limit: 可選, 若傳入,最多返回數組中limit個子字符串java

注:若separator爲包含子表達式分組的正則表達式,子表達式匹配的字符串也會做爲數組中的元素返回。正則表達式

'a, b   ,   '.split(/(,)/)
# ['a', ',', ' b   ', ',', '   ']

4、String.prototype.match(regexp)

做用: 捕獲分組或返回全部匹配的子字符串算法

注: 若regexp未設置全局標誌/g, 則返回一個對象(數組: 存在index和input屬性),存放第一次匹配相關的信息;若設置了/g標誌,則將全部匹配的子字符串(不包含分組元素)放在一個數組中返回; 若是未匹配到任何子字符串,返回null跨域

var a = '-abb--aaab'.match(/(a+)b/) // ["ab", "a"]
a.index === 1 // true
a.input === '-abb--aaab' // true
var b = '-abb--aaab'.match(/(a+)b/g) // ["ab", "aaab"]
b.index === undefined // true
b.input === undefined // true
var c = '-abb--aaab'.match(/(a+)bc/g) // null
c === null // true

5、String.prototype.replace(search, replacement)

存在兩個參數: search, replacement數組

  • search: 字符串或者正則表達式。瀏覽器

    1) 字符串: 在字符串中找到匹配的字面量,只能替換一次出現的匹配項,暫不存在
        (replaceAll函數),想要替換屢次出現的匹配項,必須使用正則表達式/g標識。
    2) 正則表達式: 對輸入的字符串進行匹配。若是想替換屢次,必須使用/g標識
  • replacement: 字符串或者函數。安全

    1) 字符串: 描述如何替換找到的匹配項), 使用該字符串替換匹配項,替換字符串中的$
    符號容許使用徹底匹配或者匹配分組進行替換
    2) 函數: 執行替換並提供參數匹配信息.
'iixxxixx'.replace('i', 'o') // oixxxixx
'iixxxixx'.replace(/i/, 'o') // oixxxixx
'iixxxixx'.replace(/i/g, 'o') // ooxxxoxx

// 使用$符號
'iixxxixx'.replace(/i+/g, '($&)') // (ii)xxx(i)xx
'iixxxixx'.replace(/(i+)/g, '($1)') // (ii)xxx(i)xx

// 使用函數替換
function repl(search, group, offset, input) {
    return '{ search: ' + search.toUpperCase() + ', group: ' + group + ', offset: ' + offset + ' }';
}
'iixxxixx'.replace(/(i+)/g, repl)
// { search: II, group: ii, offset: 0 }xxx{ search: I, group: i, offset: 5 }xx

1. replacement爲字符串

內容用來逐字替換匹配項,惟一例外是特殊字符美圓符號($),它會啓動替換指令app

  • 分組:$n在匹配中插入分組n。n必須大於等於1($0沒有任何特殊含義)

  • 匹配的子字符串:

    1) $\` (反引號)插入匹配項以前的文本
    2) $& 插入完整的匹配項
    3) $' (單引號)插入匹配項以後的文本
  • $$ 插入單個$字符

'axb cxd'.replace(/x/g, "($`,$&,$')"); // a(a,x,b cxd)b c(axb c,x,d)d
'"foo" and "bar"'.replace(/"(.*?)"/g, '#$1#'); // #foo# and #bar#

2. replacement爲函數

若是replacement爲函數,它須要對替換匹配項的字符串進行處理。簽名以下:

function replacement (search, group_1, ..., group_n, offset, input){}

search與前面介紹的$&(完整的匹配項)相同, offset表示找到匹配項的位置,input是正在被匹配的字符串

  • search: 正則表達式匹配到的字符串

  • group: 若是存在分組,該值爲匹配到的分組值, 可變參數, 匹配到分組有多少個,這個參數就有多少個, 若是不存在,表示匹配到子字符所在原字符串的位置

  • offset(倒數第二個參數): 匹配到子字符所在原字符串的位置

  • input(最後一個參數): 原字符串

function replaceFunc (match) { return 2 * match; }
'3 peoples and 5 oranges'.replace(/\d+/g, replaceFunc); // 6 peoples and 10 oranges

6、檢查value是否爲對象

function isObject (value) {
    return value === Object(value);
}

7、Array.prototype.forEach()的thisValue

forEach函數存在兩個參數,第一個是回調函數,第二個參數爲第一個參數(回調函數)提供this

var obj = {
    name: 'Jane',
    friends: ['Tarzan', 'Cheeta'],
    loop: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            console.log(this.name + ' knows ' + friend);
        }, this)
    }
}

8、使用給定的prototype建立新對象

使用方法: Object.create(proto, properties?)

參數:

  • proto: 新建立對象的原型

  • properties: 可選,經過描述符給新建立對象添加屬性

var PersonProto = {
    describe: function () {
        return 'Person named ' + this.name;
    }
}

var jane = Object.create(PersonProto, {
    name: { value: 'Jane', writable: true, configurable: true, enumerable: true }
});

jane.describe() // 'Person named Jane'

9、獲取對象的原型

調用方法: Object.getPrototypeOf(obj)

Object.getPrototypeOf(jane) === PersonProto // true

10、檢查一個對象是不是另一個對象的原型

語法: Object.prototype.isPrototypeOf(obj)

檢查接受者是不是obj的原型(直接或者間接)

var A = {};
var B = Object.create(A);
var C = Object.create(B);
A.isPrototypeOf(C); // true

PersonProto.isPrototypeOf(jane); // true

11、找到定義屬性的對象

遍歷對象obj的原型鏈, 返回鍵爲propKey的自定義屬性的第一個對象

function getDefiningObject(obj, propKey) {
    while (obj && !Object.prototype.hasOwnProperty.call(obj, propKey)) {
        obj = Object.getPrototypeOf(obj);
    }

    return obj;
}

12、列出自有的屬性鍵

能夠列吃全部自有屬性,也能夠列出可枚舉屬性

1) Object.getOwnPropertyNames(obj) 返回obj的全部自有屬性鍵
2) Object.key(obj) 返回obj全部可枚舉的自有屬性鍵

十3、經過屬性描述符獲取和設置屬性的特性

Object.defineProperty(obj, propKey, propDesc)
建立或改變obj對象的propKey鍵的屬性,並經過propDesc制定這個屬性的特性,返回修改後的對象

Object.getOwnPropertyDescriptor(obj, PropKey)
返回obj對象的propKey鍵自有(非繼承)屬性描述符,若是沒有該屬性,則返回undefined.

十4、複數對象

需求:

1) 就有相同的原型
2) 具備相同的屬性及屬性特性
function copyObject (orig) {
    // 1. copy has same prototype as orig
    var copy = Object.create(Object.getPrototypeOf(orig));

    // 2. copy ass orig's own properties
    function copyOwnPropertiesFromObjct (target, source) {
        var ownProperties = Object.getOwnPropertyNames(source);
        for (var property of ownProperties) {
            var sourcePropertyDescriptor = Object.getOwnPropertyDescriptor(source, property);
            Object.defineProperty(target, property, sourcePropertyDescriptor);
        }
    }

    copyOwnPropertiesFromObjct(copy, orig);
    return copy;
}

十5、繼承的只讀屬性不能被賦值

若是一個對象obj從原型繼承了不可寫的屬性foo, 在嚴格模式下,那麼不能給obj.foo賦值。

var proto = Object.create({}, { foo : { value: 'a', writable: false } });
// var proto = Object.defineProperty({}, 'foo', { value: 'a', writable: false });
var obj = Object.create(proto);

obj.foo = 'b'; // b
obj.foo // a

// IIFE
(function () { 'use strict'; obj.foo = 'b' }());
// Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'

這符合賦值改變但不破壞繼承屬性的理念。若是一個繼承屬性是隻讀的,應該禁止全部修改操做

十6、枚舉性影響下列操做

1) for-in // 循環全部可枚舉屬性(自有+繼承)
2) Object.keys() // 列出全部自有可枚舉屬性
2) JSON.stringify() // 全部自有可枚舉屬性

十7、保護對象之防止擴展(Object.preventExtensions())

不能添加屬性,能夠刪除屬性, 能夠修改已有自有屬性

var obj = { foo: 'a' };
Object.preventExtensions(obj);

obj.bar = 'b'; // 寬鬆模式下,添加失敗
obj.bar // undefined

(function () { 'use strict'; obj.bar = 'b'  }()) // 嚴格模式下,拋出異常
// Uncaught TypeError: Can't add property bar, object is not extensible

// 刪除屬性
delete obj.foo; // a
obj.foo // undefined

判斷某個對象是否能夠擴展: Object.isExtensible(obj)

Object.isExtensible(obj); // false
Object.isExtensible({}); // true

注: 防止擴展時,返回false

十8、保護對象之封閉(Object.seal())

不能添加屬性,且不能刪除屬性, 只能修改已有自有屬性。
(封閉 === 防止擴展 + 不能刪除屬性)

var obj = { foo: 'a' }

Object.getOwnPropertyDescriptor(obj, 'foo'); // before sealing
// {value: "a", writable: true, enumerable: true, configurable: true}

Object.seal(obj);
Object.getOwnPropertyDescriptor(obj, 'foo'); // after sealing
// {value: "a", writable: true, enumerable: true, configurable: false}

// 不能改變其屬性特性
Object.defineProperty(obj, 'foo', { enumerable: false });
// Uncaught TypeError: Cannot redefine property: foo

判斷一個對象是否封閉: Object.isSealed(obj)

Object.isSealed(obj); // true
Object.isSealed({}); // false

注: 對象封閉時,返回true

十9、保護對象之凍結(Object.freeze())

不能添加屬性,不能刪除屬性, 同時也修改已有自有屬性。
(凍結 === 封閉 + 不能修改屬性)

var point = { x: 17, y: -5 };
Object.freeze(point);

// 賦值操做,寬鬆模式下,執行失敗;嚴格模式下,拋出異常。
point.x = 6
point.x // 17

(function () { 'use strict'; point.x = 2; }());
// Uncaught TypeError: Cannot assign to read only property 'x' of object '#<Object>'

(function () { 'use strict'; point.z = 62; }());
// Uncaught TypeError: Can't add property z, object is not extensible

(function () { 'use strict'; delete point.x; }());
// Uncaught TypeError: Cannot delete property 'x' of #<Object>

判斷一個對象是否被凍結: Object.isFrozen(obj)

Object.isFrozen(obj); // true
Object.isFrozen({}); // false

注: 對象被凍結時,返回true

二10、保護是淺層的

全部保護對象的方法,只能保護基本類型的自有屬性,對於對象不起做用(能夠改變屬性值爲對象中對象的屬性)

var obj = {
    foo: 1,
    bar: [1, 2, 3],
    cer: { a: '123' }
};
Object.freeze(obj);

obj.foo = 2; // no effect
obj.foo // 1

obj.bar.push('bar'); // 4: changes obj.bar
obj.bar // [1, 2, 3, "bar"]

obj.cer.b = 'cer'; //cer: changes obj.cer
obj.cer // {a: "123", b: "cer"}

二11、對象不是Object的實例

幾乎全部的對象都是Object的實例,由於Object.prototype在這些對象的原型鏈上,但也有對象不屬於這種狀況。

Object.create(null) instanceof Object // false
Object.prototype instanceof Object // false

Object.getPrototypeOf(Object.create(null)) // null
Object.getPrototypeOf(Object.prototype) // null

這類對象位於原型鏈的末端,但typeof能夠正確的把這些對象歸類爲對象:

typeof Object.create(null) // object
typeof Object.prototype // object

二12、跨域對象檢測

在Web瀏覽器中,每個幀和窗口都擁有本身的域,具備獨立的全局變量。這使得instanceof不可用於那些跨域的對象。

若是myvar是來自另外一個域的數組,那麼它的原型是那個域的Array.prototype。在當前域中,使用instanceof檢測的是否爲myvar的原型鏈上是否存在當前域的Array.prototype, 會返回false。

可使用ECMAScript 5的函數Array.isArray()來判斷是否爲數組

<!DOCTYPE html>
<html>
<head>
  <title>Test</title>
</head>
  <script type="text/javascript">
    function test (myvar) {
      var iframe = window.frames[0];

      console.log(myvar instanceof Array); // false
      console.log(myvar instanceof iframe.Array); // true
      console.log(Array.isArray(myvar)); // true
    }
  </script>
<body>
  <iframe srcdoc="<script type='text/javascript'>window.parent.test([])</script>"></iframe>
</body>
</html>

二十3、備忘錄: 對象的使用

1. 對象字面量

var jane = {
    name: 'Jane',
    'not an indentifier': 123,
    describe: function () {
        return 'Person named ' + this.name;
    }
}

jane.describe() // Person named Jane

2. 點操做符(.):經過固定鍵值訪問屬性

obj.propKey // 訪問
obj.propKey = value // 賦值
delete obj.propKey // 刪除

3. 中括號操做符([]): 經過計算出的鍵值訪問屬性

obj['propKey'] // 訪問
obj['propKey'] = value // 賦值
delete obj['propKey'] // 刪除

4.獲取和設置原型

Object.create(proto, propDescObj?) // 建立指定原型的對象
Object.getPrototypeOf(obj) // 獲取原型

5.屬性的遍歷和檢測

Object.keys(obj)

Object.getOwnPropertyNames(obj)

for (var propKey in obj)
Object.prototype.hasOwnProperty.call(obj, propkey)

6. 經過描述符獲取和定義屬性特性

// 定義
Object.defineProperty(obj, propKey, propDesc)
Object.defineProperties(obj, propDescObj)
Object.create(obj, propDescObj)

// 獲取
Object.getOwnPropertyDescriptor(obj, propKey)

7. 保護對象: 防止擴展、封閉、凍結

// 防止擴展: 不能添加屬性,能刪除屬性或修改屬性值
Object.preventExtensions(obj)
Object.isExtensible(obj)

// 封裝: 不能添加或者刪除屬性,能修改屬性值
Object.seal(obj)
Object.isSealled(obj)

// 凍結: 不能添加或者刪除屬性,也不能修改屬性值
Object.freeze(obj)
Object.isFrozen(obj)

8. 全部對象的方法

// 將對象轉換爲基本類型的值
Object.prototype.toString()
Object.prototype.valueOf()

// 返回本地語言環境的表明對象的字符串
Object.prototype.toLocalString()

// 原型式繼承和屬性
Object.prototype.isPrototypeOf(subObj)
Object.prototype.hasOwnProperty(propKey)
Object.prototype.propertyIsEnumerable(propKey)

二十4、經過字面量建立數組時,最後的逗號會被忽略

['a', 'b'].length // 2

['a', 'b',].length // 2

['a', 'b', ,].length // 3

二十5、空缺數組

數組是由索引到值的映射,這意味這數組能夠有空缺,索引的個數小於數組的長度說明數組爲空缺數組。

var a = new Array(5);
a[1] = 1;
a[4] = 4;

// a[0], a[2], a[3]都未賦值,a爲空缺數組

二十6、數組中某些方法會忽略空缺

1. 數組遍歷方法

1) forEach()遍歷時會跳過空缺

['a', , 'b'].forEach(function (x, i) { console.log(i + ': ' + x); });

// 0: a
// 2: b

2) every()也會跳過空缺(相似的還有some()方法)

['a', , 'b'].every(function (x) { return typeof x === 'string' });

// true

3) map()雖然會跳過空缺,但會保留空缺

['a', , 'b'].map(function (x, i) { return i + '.' + x; });
// ['0.a', , '2.b']

4) filter()會去除空缺

['a', , 'b'].filter(function (x) { return true; });
// ['a', 'b']

2. 其餘方法

1) join()會把空缺、undefined、null轉換成空的字符串

['a', , 'b'].join('-');
// a--b

['a', , 'b', undefined, 'c', null,'d'].join('-');
//a--b--c--d

2) sort()排序時保留空缺

['a', , 'b'].sort();
// ['a', 'b', ,]

3. for-in循環

for-in循環能夠正確列出屬性建, 跳過空缺

for (var key in ['a', , 'b']) { console.log(key) }
// 0
// 2

for (var key in ['a', undefined, 'b']) { console.log(key) }
// 0
// 1
// 2

4. Function.prototype.apply()

apply()把每一個空缺轉化爲undefined參數。

function f () { return Array.prototype.slice.call(arguments); }
f.apply(null, [,,,]);
// [undefined, undefined, undefined]

Array.apply(null, [,,,]);
// [undefined, undefined, undefined]

二十7、數組構造函數

Array.isArray(obj) 判斷obj是否爲一個數組,可以正確處理跨域對象。

二十8、數組的原型方法

1. 添加和刪除元素(破壞性的)

1) Array.prototype.shift()

移除索引0處的元素並返回該元素。隨後元素的索引依次減1:

var arr = ['a', 'b'];
var first = arr.shift(); // 'a'
console.log(arr); // ['b']

2) Array.prototype.unshift(elem1?, elem2?, ...)

在數組前面插入指定的元素,返回新的數組長度

var arr = ['a', 'b'];
arr.unshift('c', 'd'); // 4
console.log(arr); // ['c', 'd', 'a', 'b']

3) Array.prototype.pop()

移除數組最後的元素並返回該元素

var arr = ['a', 'b'];
var last = arr.pop(); // 'b'
console.log(arr); // ['a']

4) Array.prototype.push(elem1?, elem2?, ...)

在數組的最後追加指定的元素,返回新的數組長度

var arr = ['a', 'b'];
arr.push('c', 'd'); // 4
console.log(arr); // ['a', 'b', 'c', 'd']

小技巧: 可使用apply函數將一個數組追加到另外一個數組的後面

var arr1 = ['a', 'b'];
var arr2 = ['c', 'd'];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1); // ["a", "b", "c", "d"]

5) Array.prototype.splice(start, deleteCount?, elem1?, elem2?, ...)

從索引start位置開始,刪除deleteCount個元素,並在該位置插入給定元素, 返回刪除的元素組成的數組

var arr = ['a', 'b', 'c', 'd'];
arr.splice(1, 2, 'X'); // ['b', 'c']
console.log(arr); // ['a', 'X', 'd']

注: start能夠爲負值,表示從倒數幾位開始。deleteCount是可選的,若是省略,移除從start位置開始的全部元素

var arr = ['a', 'b', 'c', 'd'];
arr.splice(-2); // ['c', 'd']
console.log(arr); // ['a', 'd']

2. 排序和顛倒元素順序(破壞性的)

1) Array.prototype.reverse()

顛倒數組中的元素,並返回指向原數組的引用:

var arr = ['a', 'b', 'c'];
arr.reverse(); // ['c', 'b', 'a']
console.log(arr); // ['c', 'b', 'a']

2) Array.prototype.sort(compareFunction?)

數組排序, 並返回排序後的數組

var arr = ['banana', 'apple', 'pear', 'orange'];
arr.sort() // ['apple', 'banana', 'orange', 'pear'];
console.log(arr); // ['apple', 'banana', 'orange', 'pear'];

注: 排序是經過將元素轉換爲字符串再進行比較,這意味着數字不是按照數值進行排序

[-1, -20, 7, 50].sort(); // [-1, -20, 50, 7]

能夠經過compareFunction來解決該問題,該函數的簽名爲:

function compareFunction (a, b)

比較參數a和b的值,返回的規則以下:

  • 小於0的整數, a < b, 則a排在b的前面

  • 等於0, a === b

  • 大於0的整數, a > b, 則a排在b的後面

注: 對於數字,通常的就簡單的返回a-b, 可能致使數值溢出,爲了防止這種狀況,可能須要更復雜的邏輯

[-1, -20, 7, 50].sort(function (a, b) {
  return a < b ? -1 : (a > b ? 1 : 0);
}); // [-20, -1, 7, 50]

3. 合併、切分和鏈接(非破壞性)

1) Array.prototype.concat(arr1?, arr2?, ...)

建立一個新數組,其中包含接受者的全部元素,其次是arr1中的全部元素,依次類推,若是其中一個參數不是數組, 當中元素添加到數組中

var arr = ['a', 'b'];
var newArray = arr.concat('c', ['d', 'e']); // ["a", "b", "c", "d", "e"]
console.log(newArray); // ["a", "b", "c", "d", "e"]
console.log(arr); // ['a', 'b']

2) Array.prototype.slice(start?, end?)

把數組中從start開始到end(不包含end)的元素複製到新數組中

['a', 'b', 'c', 'd'].slice(1, 3) // ['b', 'c']

['a', 'b', 'c', 'd'].slice(1) // ['b', 'c', 'd']
['a', 'b', 'c', 'd'].slice() // ['a', 'b', 'c', 'd']

注: 不包含end時,拷貝從start開始到最後的全部元素, 若是不傳start和end參數,拷貝數組, start和end均可以是負值,表示從倒數開始

3) Array.prototype.join(separator?)

對全部數組元素應用toString()建立字符串, 並使用separator鏈接字符,若是缺乏separator,默認使用','

[3, 4, 5].join('-') // 3-4-5
[3, 4, 5].join() // 3,4,5
[3, 4, 5].join('') // 345

注:若是數組中某個元素爲undefined, null或者空缺, 調用join方法是,會將該元素轉換成空字符串

[undefined, null, 'a'].join('#') // ##a
[undefined, , 'a'].join('#') // ##a

4. 值的查找(非破壞性)

1) Array.prototype.indexOf(searchValue, startIndex?)

從數組的startIndex位置開始查找searchValue,返回第一次出現searchValue的索引,沒有找到,就返回-1。缺乏startIndex, 查找整個數組

[3, 1, 17, 1, 4].indexOf(1); // 1
[3, 1, 17, 1, 4].indexOf(1, 2); // 3

注:查找時使用的是嚴格相等, 不能查找NaN

[NaN].indexOf(NaN); // -1

2) Array.prototype.lastIndexOf(searchValue, startIndex?)

從數組的startIndex位置開始反向查找searchValue,返回第一次出現searchValue的索引,沒有找到,就返回-1。缺乏startIndex, 查找整個數組。查找時使用的是嚴格相等

[3, 1, 17, 1, 4].indexOf(1); // 3
[3, 1, 17, 1, 4].indexOf(1, -3); // 1
[3, 1, 17, 1, 4].indexOf(1, 2); // 1

5. 迭代(非破壞性)

能夠劃分爲三種迭代方法:

  • 檢查方法: 主要觀察數組的內容(forEach, every, some)

  • 轉化方法: 從已有的數組中得到新數組(map, filter)

  • 歸約方法: 基於接受者的元素計算出結果(reduce, reduceRight)

1) Array.prototype.forEach(callback, thisValue?)

遍歷數組中的元素

var arr = ['apple', 'banana', 'pear'];
arr.forEach(function (elem) {
    console.log(elem);
});
// apple
// banana
// pear

注: 存在一個缺陷,不支持break或者相似於提早終止循環的處理,除非throw Error。

2) Array.prototype.every(callback, thisValue?)

若是對每一個元素。回調函數都返回true,則every方法返回true, 若是某個元素回調函數返回false, 則中止迭代。相似全稱量詞

// 檢查數組中的元素是否所有爲偶數
function isEven(x) { return x % 2 === 0 }
[2, 4, 6].every(isEven); // true
[2, 4, 5].every(isEven); // false

注: 若是爲空數組,返回true,不會調用callback方法

[].every(function () { throw new Error('Empty array') }); // true

3) Array.prototype.some(callback, thisValue?)

若是對其中某個元素。回調函數返回true,則some方法返回true, 若是某個元素回調函數返回true, 則中止迭代。相似存在量詞

// 檢查數組中的元素是否存在偶數
function isEven(x) { return x % 2 === 0 }
[1, 3, 5].some(isEven); // false
[2, 2, 3].some(isEven); // true

注: 若是爲空數組,返回false,不會調用callback方法

[].some(function () { throw new Error('Empty array') }); // false

4) Array.prototype.map(callback, thisValue?)

轉化數組的每一個元素都是原數組中元素調用回調函數callback後的結果

[1, 2, 3],map(function (elem) { return 2 * elem; }); // [2, 4, 6]

5) Array.prototype.filter(callback, thisValue?)

轉化數組的元素都是原數組中元素調用回調函數callback返回true的元素

[1, 0, 3, 0].filter(function (x) { return !!x; }); // [1, 3]

6) Array.prototype.reduce(callback, initialValue?)

從左至右進行迭代,其中callback參數的結構爲:

function callback(previousValue, currentValue, currentIndex, array);

除初次調用外,每次調用callback時,參數previousValue是上一次回調函數的返回值。初次調用回調函數有兩種狀況

  • 提供顯式initialValue參數,previousValue就是initialValue,而currentValue爲數組中的第一個元素

  • 未提供顯式initialValue參數,previousValue就是第一個元素,而currentValue爲數組中的第二個元素

reduce方法返回最後一次調用回調函數時的結果

function add (pre, cur) {
    return pre + cur;
}

[1, 10, -3].reduce(add); // 8

注: 只有一個元素的數組調用reduce,則返回該元素

function add (pre, cur) {
    return 2 * pre + 3 * cur;
}

function add2 (pre, cur) {
    return 1;
}

[7].reduce(add); // 7
[7].reduce(add2); // 7

注: 若是對空數組調用reduce, 必須指定initialValue, 不然會拋出異常

[].reduce(add);
// Uncaught TypeError: Reduce of empty array with no initial value

7) Array.prototype.reduceRight(callback, initialValue?)

工做原來與Array.prototype.reduce類似,可是是從右至左歸約

二十9、類數組對象

javascript中存在一類對象看起來像數組,實際上不是,能夠經過索引訪問元素具備length屬性,但沒有數組的其餘實例屬性。

類數組對象主要有:特殊對象arguments、DOM節點列表和字符串。

咱們可使用泛型方法來使類數組對象防訪問數組原型上的方法。

三10、正則表達式原子(分組)

分組的語法:

  • (<<pattern>>) 捕獲分組。任何匹配pattern的內容均可以經過反向引用訪問或做爲匹配結果。

  • (?:<<pattern>>) 非捕獲分組。仍然根據pattern匹配,但不保存捕獲的內容。分組沒有數字能夠引用。

12...依次類推稱爲方向引用;他們指向以前匹配的分組。第一個數字必須不是0

/^(a+)-\1$/.test('a-a'); // true
/^(a+)-\1$/.test('aaa-aaa'); // true
/^(a+)-\1$/.test('aaa-a'); // false

注:方向引用保證破折號先後的字符數一致

使用方向引用來匹配HTML標籤

var tagName = /<([^>]+)>[^<]*<\/\1>/;
tagName.exec('<b>bold</b>')[1] // b

tagName.exec('<strong>text</strong>')[1] // strong
tagName.exec('<strong>text</stron>')[1] // TypeError: Cannot read property '1' of null

三11、正則表達式量詞

  • ? 表示從未匹配或只匹配一次

    • 表示從匹配零次或屢次

    • 表示匹配一次或屢次

  • {n} 表示徹底匹配n次

  • {n, } 表示匹配至少n次

  • {n, m} 表示匹配至少n次,最多m次

默認狀況下,量詞是貪婪匹配的,他們會盡量多的匹配,若是想使用勉強匹配(儘量少),能夠經過量詞後面加問號(?)

// 貪婪匹配
'<a> <strong></strong>'.match(/^<(.*)>/)[1] // a> <strong></strong
// 勉強匹配
'<a> <strong></strong>'.match(/^<(.*?)>/)[1] // a

注:*?是十分有用的模式,它能夠匹配一切,直到後面的原子出現。
上述匹配HTML標籤的正則能夠改成

var tagName = /<(.+?)>.*?<\/\1>/;
tagName.exec('<b>bold</b>')[1] // b

tagName.exec('<strong>text</strong>')[1] // strong
tagName.exec('<strong>text</stron>')[1] // TypeError: Cannot read property '1' of null

三12、正則表達式斷言

  • ^ 只匹配輸入的開始位置

  • $ 只匹配輸入結束位置

  • b 只匹配單詞的邊界。不要與[b]混淆,它匹配一個退格。

  • B 只匹配非單詞邊界

  • (?=<<pattern>>) 正向確定斷言:只匹配pattern所匹配的接下來的內容。pattern只是用來向前查找,但會忽略匹配的pattern部分

  • (?!<<pattern>>) 正向確定斷言:只匹配pattern不匹配的接下來的內容。pattern只是用來向前查找,但會忽略匹配的pattern部分

匹配單詞邊界

/\bell\b/.test('hello'); // false
/\bell\b/.test('ello'); // false
/\bell\b/.test('a ell v'); // true

經過B匹配單詞的內部元素

/\Bell\B/.test('ell'); // false
/\Bell\B/.test('ello'); // false
/\Bell\B/.test('hello'); // true

三十3、正則表達式標識

標識是正則表達式字面量的後綴和正則表達式構造函數的參數;它修改正則表達是的匹配行爲。

簡稱 全稱 描述
g global 給定的正則表達式能夠匹配屢次,它會影響幾種方法,尤爲是String.prototype.replace()
i ignoreCase 試圖匹配給定的正則表達式時忽略大小寫
m multiline 在多行模式時,開始操做符^和結束操做符$匹配每一行,而不是輸入的整個字符串

簡稱用於正則字面量後綴和構造函數參數,全稱用於正則表達式對象的屬性

三十4、正則表達式的實例屬性

  • 標識: 布爾值表示設置什麼標誌。

    global(全局): 是否設置/g標識
    ignoreCase(忽略大小寫): 是否設置了/i標識
    multiline(多行): 是否設置/m標識
  • 屢次匹配數據(設置了/g標識)

    lastIndex: 下次繼續查找的起始索引
var regex = /abc/i;
regex.ignoreCase; // true
regex.multiline; // false

三十5、RegExp.prototype.test: 是否存在匹配

test()方法用來檢查正則表達式regex是否匹配字符串str:

regex.test(str)

若是沒有設置/g標識,該方法只檢查是否在str某處存在匹配;若是設置了/g標識,則該方法會屢次匹配regex並返回true,屬性lastIndex包含最後一次匹配以後的索引

// 未設置/g
var str = '_x_x';
/x/.test(str); // true
/a/.test(str); // false

// 設置/g
var regex = /x/g;
regex.lastIndex; // 0

regex.test(str); // true
regex.lastIndex; // 2

regex.test(str); // true
regex.lastIndex; // 4

regex.test(str); // false

三十6、String.prototype.search: 匹配位置的索引

search()方法查找str匹配regex的位置

str.search(regex)

若是存在匹配返回匹配位置的索引,不然,返回值爲-1, 進行查找時,regex的global和lastIndex屬性被忽略(其中lastIndex沒有改變)

'abba'.search(/b/); // true
'abba'.search(/B/i); // true
'abba'.search(/x/i); // true

注:若是search()的參數不是正則表達式,它被轉換爲正則:

'aaab'.search('^a+b+$'); // 0

三十7、RegExp.prototype.exec: 捕獲分組

在str匹配regex的同時捕獲分組

var matchData = regex.exec(str);

若是沒有匹配,matchData爲null。不然,matchData爲匹配結果,是帶有兩個附屬屬性的數組
(1) 數組元素

  • 元素0是完整正則表達式的匹配結果。

  • 元素n>1是捕獲的分組N。

(2)屬性

  • input 是輸入的完整字符串

  • index 是查找到匹配處的索引

若是未設置/g標識,只返回第一次匹配的結果:

var regex = /a(b+)/;
regex.exec('_abbb_ab_');
// ['abbb', 'bbb', index: 1, input: '_abbb_ab_']
regex.lastIndex; // 0

若是設置了/g標識,能夠反覆調用exec()方法返回全部的匹配項,返回值null表示沒有任何匹配。屬性lastIndex表示下次匹配從哪裏開始

var regex = /a(b+)/g;
var str = '_abbb_ab_';
console.log(regex.exec(str));
// ['abbb', 'bbb', index: 1, input: '_abbb_ab_']
console.log(regex.lastIndex); // 5

console.log(regex.exec(str));
// ['ab', 'b', index: 6, input: '_abbb_ab_']
console.log(regex.lastIndex); // 8

regex.exec(str); // null

使用循環遍歷全部的匹配項

var regex = /a(b+)/g;
var str = '_abbb_ab_';
var match;
while(match = regex.exec(str)) {
    console.log(match[1]);
}

// bbb
// b

三十8、String.prototype.match: 捕獲分組已經返回全部匹配的子字符串

var matchData = str.match(regex);

若是未設置/g標識,該方法相似與RegExp.prototype.exec()

'abba'.match(/a(b+)/);
// ["abb", "bb", index: 0, input: "abba"]

若是設置了/g標識,則返回str中含有全部匹配的子字符串的數組;若是沒有任何匹配,則返回null

'_abbb_ab_'.match(/a(b+)/g);
// ["abbb", "ab"]

三十9、String.prototype.replace: 查找和替換

存在兩個參數: search, replacement

  • search: 字符串或者正則表達式。

    1) 字符串: 在字符串中找到匹配的字面量,只能替換一次出現的匹配項,暫不存在
        (replaceAll函數),想要替換屢次出現的匹配項,必須使用正則表達式/g標識。
    2) 正則表達式: 對輸入的字符串進行匹配。若是想替換屢次,必須使用/g標識
  • replacement: 字符串或者函數。

    1) 字符串: 描述如何替換找到的匹配項), 使用該字符串替換匹配項,替換字符串中的$
    符號容許使用徹底匹配或者匹配分組進行替換
    2) 函數: 執行替換並提供參數匹配信息.
'iixxxixx'.replace('i', 'o') // oixxxixx
'iixxxixx'.replace(/i/, 'o') // oixxxixx
'iixxxixx'.replace(/i/g, 'o') // ooxxxoxx

// 使用$符號
'iixxxixx'.replace(/i+/g, '($&)') // (ii)xxx(i)xx
'iixxxixx'.replace(/(i+)/g, '($1)') // (ii)xxx(i)xx

// 使用函數替換
function repl(search, group, offset, input) {
    return '{ search: ' + search.toUpperCase() + ', group: ' + group + ', offset: ' + offset + ' }';
}
'iixxxixx'.replace(/(i+)/g, repl)
// { search: II, group: ii, offset: 0 }xxx{ search: I, group: i, offset: 5 }xx

1. replacement爲字符串

內容用來逐字替換匹配項,惟一例外是特殊字符美圓符號($),它會啓動替換指令

  • 分組:$n在匹配中插入分組n。n必須大於等於1($0沒有任何特殊含義)

  • 匹配的子字符串:

    1) $\` (反引號)插入匹配項以前的文本
    2) $& 插入完整的匹配項
    3) $' (單引號)插入匹配項以後的文本
  • $$ 插入單個$字符

'axb cxd'.replace(/x/g, "($`,$&,$')"); // a(a,x,b cxd)b c(axb c,x,d)d
'"foo" and "bar"'.replace(/"(.*?)"/g, '#$1#'); // #foo# and #bar#

2. replacement爲函數

若是replacement爲函數,它須要對替換匹配項的字符串進行處理。簽名以下:

function replacement (search, group_1, ..., group_n, offset, input){}

search與前面介紹的$&(完整的匹配項)相同, offset表示找到匹配項的位置,input是正在被匹配的字符串

  • search: 正則表達式匹配到的字符串

  • group: 若是存在分組,該值爲匹配到的分組值, 可變參數, 匹配到分組有多少個,這個參數就有多少個, 若是不存在,表示匹配到子字符所在原字符串的位置

  • offset(倒數第二個參數): 匹配到子字符所在原字符串的位置

  • input(最後一個參數): 原字符串

function replaceFunc (match) { return 2 * match; }
'3 peoples and 5 oranges'.replace(/\d+/g, replaceFunc); // 6 peoples and 10 oranges

四10、標識符/g的一些問題

正則表達式設置了/g標識,有些方法必須屢次(循環)調用才能放回全部結果。這些方法以下所示

  • RegExp.prototype.test()

  • RegExp.prototype.exec()

正則表達式做爲迭代器成爲結果序列的指針時,會被不正確地使用。這致使的問題以下:

1. 帶有/g的正則表達式不能內聯

// 反模式
var count = 0;
while (/a/g.test('babaa')) count++;

// 正確用法
var count = 0;
var regex = /a/g;
while (regex.test('babaa')) count++;

上述第一種狀況中會建立無限循環,每次循環迭代都會建立一個新的正則表達式,致使從新開始迭代。

// 反模式
function extractQuoted(str) {
    var match;
    var result = [];
    while ((match = /"(.*?)"/g.exec(str)) !== null) {
        result.push(match[1]);
    }
    return result;
}
// 會致使無限循環

// 正確的方法
function extractQuoted(str) {
    var QUOTE_REGEX = /"(.*?)"/g;
    var match;
    var result = [];
    while ((match = QUOTE_REGEX.exec(str)) !== null) {
        result.push(match[1]);
    }
    return result;
}

extractQuoted('"hello", "world"'); // ["hello", "world"]

注:爲了不上述這種無限循環的狀況,最好的解決辦法就是在任何狀況下,都不使用內聯正則表達式,這要求咱們造成一個良好的編碼習慣。

2. 帶/g的正則表達式做爲參數

須要屢次調用test()和exec()方法時,把正則做爲參數傳遞給方法時必需要當心。必須設置/g標識,爲了安全起見,應該設置lastIndex爲0

3. 共享帶有/g的正則表達式

當你引用不是新建立的正則表達式時,在把它做爲迭代器前,須要手動把lastIndex設置爲0。因爲迭代依賴lastIndex,這種正則表達式不能同時用於多個迭代。

function countOccurrences(regex, str) {
    var count = 0;
    while (regex.test(str)) count++;
    return count;
}

countOccurrences(/x/g, '_x_x'); // 2

// 問題一: 若是不加/g標識,會進入無限循環
countOccurrences(/x/, '_x_x'); // 無限循環

// 問題二: lastIndex未設置爲0
var regex = /x/g;
regex.lastIndex = 2;
countOccurrences(regex, '_x_x'); // 1

// 修復上述兩個問題
function countOccurrences(regex, str) {
    if (!regex.global) {
        throw new Error('Please set flag /g of regex');
    }

    var origLastIndex = regex.lastIndex; // store
    regex.lastIndex = 0;

    var count = 0;
    while (regex.test(str)) count++;

    regex.lastIndex = origLastIndex;
    return count;
}

// 更簡單的方式,使用String.prototype.match
function countOccurrences(regex, str) {
    if (!regex.global) {
        throw new Error('Please set flag /g of regex');
    }

    return (str.match(regex) || []).length;
}

四11、引用文本

給制定的字符串拼裝成正則表達式,全部特殊是字符都須要進行轉義。

function quoteText(text) {
    return text.replace(/[\\^$.*+?()[\]{}|=!<>:-]/g, '\\$&');
}

console.log(quoteText('*All* (most?) aspects.')); // \*All\* \(most\?\) aspects\.

四12、缺乏斷言(^、$)的正則表達式能夠在任意位置匹配

/aa/.test('xaay'); // true
/^aa$/.test('xaay'); // false

四十3、匹配一切或者什麼都不匹配

一種特殊狀況:函數能夠把正則表達式當作參數,用於過濾,若是缺乏這個參數,須要提供一個能夠匹配一切的默認值

1. 匹配一切

空的正則表達式能夠匹配一切

new RegExp('').test('dfadsfdas'); // true
new RegExp('').test(''); // true

空的正則表達式應該是\/\/, 可是它被解釋爲JavaScript的註釋。全部咱們可使用最接近的字面量代替\/(?:)\/(空的非捕獲分組)。這個分組能夠匹配一切並且不捕獲任何字符串。

new RegExp(''); // /(?:)/

2. 不匹配任何字符

空的正則表達式相反的正則表達式

var never = /.^/;
never.test('asdsad'); // false
never.test(''); // false

四十4、正則表達式備忘錄

1. 原子

  • .(點) 匹配除了行結束符的一切字符。使用[\s\S]能夠正則匹配一切

  • 轉義字符

    \d 匹配數字([0-9]); \D 匹配非數字([^0-9])。
    \w 匹配拉丁字母數字的字符以及下劃線([a-zA-Z0-9_]); \W 匹配其餘字符。
    \s 匹配全部空白字符(空格、製表、換行符等); \S 匹配全部的非空白字符。
    須要轉移的字符: * . ? + $ ^ [ ] ( ) { } | \ /
  • 字符類(字符集合): [...]和[^...]

    源字符: [abc](除了\]-的全部匹配其自己的字符)
    轉義字符: [\d\w]
    範圍: [A-Za-z0-9]
  • 分組

    捕獲分組: (...); 方向引用\1
    非捕獲分組: (?:...)

2. 量詞

  • 貪婪匹配

    ? * +
    {n}, {n, }, {n, m}
  • 非貪婪: 把?放在任何貪婪量詞後面

3. 斷言

  • 輸入的開始和結束: ^ $。

  • 單詞的邊界,非單詞邊界: b B。

  • 正向確定查找: (?=...) 匹配緊隨其後的模式,但其會被忽略

  • 正向否認查找: (?!...) 不匹配緊隨其後的模式,但其會被忽略

4. 析取(或): |

5. 建立正則表達式

  • 字面量: /abc/i (加載時編譯)

  • 構造函數: new RegExp('zxc', 'igm'); (運行時編譯)

6. 標識

  • global(全局): /g(影響正則表達式的一些方法)。

  • ignoreCase(忽略大小寫): /i 。

  • multiline(多行): /m (^和$匹配每一行,而不是完整的輸入)。

7. 方法

  • regexp.test(str): 是否存在匹配

    不設置`/g`: 是否在某處存在匹配
    設置`/g`: 存在多少次匹配就返回多少次true
  • str.search(regex): 在哪一個全部存在匹配

  • regexp.exec(str): 捕獲分組

    不設置`/g`: 只捕獲第一次匹配的分組
    設置`/g`: 捕獲全部匹配的分組
  • str.match(regexp): 捕獲分組或返回全部匹配的字符串

    不設置`/g`: 捕獲分組
    設置`/g`: 返回全部匹配的子字符串數組
  • str.replace(search, replacement): 查找和替換

    search: 字符串和正則表達式(使用後者,設置/g)
    replacement: 字符串(使用$1, 以此類推)或函數(arguments[1]是分組1, 以此類推)返回字符串

四十5、文字的編碼和解碼

1. encodeURI(uri)

在uri中咱們用百分號來編碼特殊字符,除了下列字符,其他的特殊字符都會被編碼。

  • URI字符: ; , / ? : @ & = + $ #

  • 下面字符也不會被編碼: a-z A-Z 0-9 - _ . ! ~ * ' ( )

encodeURI('http://example.com/Fur Elise/'); // http://example.com/Fur%20Elise/

2. encodeURIComponent(uri)

除了下列字符,全部字符都會被百分號編碼(URI字符也會被編碼):

  • 下面字符也不會被編碼: a-z A-Z 0-9 - _ . ! ~ * ' ( )

encodeURIComponent('http://example.com/Fur Elise/'); // http%3A%2F%2Fexample.com%2FFur%20Elise%2F

3. decodeURI(encodeURI)

對已經進行encodeURI編碼的uri進行解碼操做

decodeURI('http://example.com/Fur%20Elise/'); // http://example.com/Fur Elise/

4. decodeURIComponent(encodeURIComponent)

對已經進行encodeURIComponent編碼的uri進行解碼操做, 全部的百分號編碼都會被解碼

decodeURIComponent('http%3A%2F%2Fexample.com%2FFur%20Elise%2F'); // http://example.com/Fur Elise/

四十6、Console在IE9中存在兼容性問題

在Internet Explorer9中存在一個bug。在IE9中,console對象只有當開發者工具欄被打開過至少一次,纔會存在。
這意味着若是你的代碼引用了console對象,同時又沒有預先打開開發者工具欄,你可能會獲得一個引用錯誤。
爲了確保兼容性,最好在使用console對象以前先判斷其是否存在。

四十7、模塊系統

1. CommonJS模塊(CommonJS Module, CJS)

CJS的主要化身是Node.js模塊,其特色:

  • 緊湊的語法

  • 同步加載的設計

  • 主要用於服務端

2. 異步模塊定義(Asynchronous Module Definition, AMD)

AMD 最典型的實現就是Requirejs, 其特色:

  • AMD語法稍微複雜,但不經過eval()或者靜態編譯步驟就能夠工做

  • 異步加載的設計

  • 主要用於瀏覽器

四十8、算法: ToPrimitive() —— 將值轉換爲原始值

要將任意值轉換成數字或者字符串,首先會被轉換成任意的原始值,而後在轉換成最終的結果。

ECMAScript規範中有一個內部函數, ToPrimitive()(不能訪問),可以實現這個功能。簽名以下:

ToPrimitive(input, PreferredType?)

可選參數PreferredType代表轉換後餓類型:它能夠是NumberString,具體取決於ToPrimitive的結果是但願轉換成數字仍是字符串

若是PreferredTypeNumber,會執行如下步驟。

  • (1) 若是input是原始值,返回這個值。

  • (2) 不然,若是input是對象,調用input.valueOf()。若是結果是原始值,返回結果。

  • (3) 不然,調用input.toString()。若是結果是原始值,返回結果。

  • (4) 不然,拋出一個TypeError(說明輸入轉換原始值出錯了)

若是PreferredTypeString,會執行如下步驟。

  • (1) 若是input是原始值,返回這個值。

  • (2) 不然,若是input是對象,調用input.toString()。若是結果是原始值,返回結果。

  • (3) 不然,調用input.valueOf()。若是結果是原始值,返回結果。

  • (4) 不然,拋出一個TypeError(說明輸入轉換原始值出錯了)

PreferredType也能夠省略,這種狀況下,日期會被認爲是String而其餘值會被認爲是Number。所以+操做符和===運算符能夠操做ToPrimitive()。

ToPrimitive()實戰
valueOf的默認實現會返回this,而toString()的默認實現會返回類型信息

var empty = {};
empty.valueOf() === empty; // true
empty.toString(); // '[object Object]'

Number() 跳過了valueOf()而且將toString()執行結果轉換爲數字,因此,它將'[object Object]'轉換成了NaN

Number({}); // NaN

下面對象重寫了valueOf(), 這會影響Number(), 可是不會對String()形成任何改變

var n = { valueOf: function () { return 123; } };
Number(n); // 123
String(n); // '[object Object]'

下面對象重寫了toString()方法,由於結果會轉換成數字,Number()返回了一個數字

var s = { toString: function () { return '7'; } };
String(s); // '7'
Number(s); // 7

注: 我的筆記,持續更新

相關文章
相關標籤/搜索