JavaScript易错学问小点

一、String, Number, Boolean等包装范例是对象,JavaScript没有比较对象的要领,纵然经由过程宽松比较相称==

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

二、对包装范例转换为原始值只能准确提取出字符串和数字,而不布尔值不能

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

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

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

作用: 经由过程指定边境(separator)将字符串分割成子字符串,返回字符串构成的数组。

  • separator: 可选,可认为字符串或正则表达式。不传的话,将原字符串封装在数组中返回

  • limit: 可选, 若传入,最多返回数组中limit个子字符串

注:若separator为包括子表达式分组的正则表达式,子表达式婚配的字符串也会作为数组中的元素返回。

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

四、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

五、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为字符串

内容用来逐字替代婚配项,唯一破例是特别字符美圆标记($),它会启动替代指令

  • 分组:$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

六、搜检value是不是为对象

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

七、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)
    }
}

八、运用给定的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'

九、猎取对象的原型

挪用要领: Object.getPrototypeOf(obj)

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

十、搜检一个对象是不是是别的一个对象的原型

语法: 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

十一、找到定义属性的对象

遍历对象obj的原型链, 返回键为propKey的自定义属性的第一个对象

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

    return obj;
}

十二、列出自有的属性键

可以列吃统统自有属性,也可以列出可罗列属性

1) Object.getOwnPropertyNames(obj) 返回obj的统统自有属性键
2) Object.key(obj) 返回obj统统可罗列的自有属性键

十三、经由过程属性形貌符猎取和设置属性的特征

Object.defineProperty(obj, propKey, propDesc)
建立或转变obj对象的propKey键的属性,并经由过程propDesc制订这个属性的特征,返回修正后的对象

Object.getOwnPropertyDescriptor(obj, PropKey)
返回obj对象的propKey键自有(非继承)属性形貌符,假如没有该属性,则返回undefined.

十四、复数对象

需求:

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

十五、继承的只读属性不能被赋值

假如一个对象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>'

这相符赋值转变但不损坏继承属性的理念。假如一个继承属性是只读的,应当制止统统修正操纵

十六、罗列性影响以下操纵

1) for-in // 轮回统统可罗列属性(自有+继承)
2) Object.keys() // 列出统统自有可罗列属性
2) JSON.stringify() // 统统自有可罗列属性

十七、庇护对象之防备扩大(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

十八、庇护对象之关闭(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

十九、庇护对象之凝结(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

二十、庇护是浅层的

统统庇护对象的要领,只能庇护基础范例的自有属性,关于对象不起作用(可以转变属性值为对象中对象的属性)

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"}

二十一、对象不是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

二十二、跨域对象检测

在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>

二十三、备忘录: 对象的运用

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)

二十四、经由过程字面量建立数组时,末了的逗号会被疏忽

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

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

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

二十五、空缺数组

数组是由索引到值的映照,这意味这数组可以有空缺,索引的个数小于数组的长度申明数组为空缺数组。

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

// a[0], a[2], a[3]都未赋值,a为空缺数组

二十六、数组中某些要领会疏忽空缺

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]

二十七、数组组织函数

Array.isArray(obj) 推断obj是不是为一个数组,可以准确处置惩罚跨域对象。

二十八、数组的原型要领

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类似,然则是从右至左归约

二十九、类数组对象

javascript中存在一类对象看起来像数组,实际上不是,可以经由过程索引接见元素具有length属性,但没有数组的其他实例属性。

类数组对象重要有:特别对象arguments、DOM节点列表和字符串。

我们可以运用泛型要领来使类数组对象防接见数组原型上的要领。

三十、正则表达式原子(分组)

分组的语法:

  • (<<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

三十一、正则表达式量词

  • ? 示意从未婚配或只婚配一次

    • 示意从婚配零次或屡次

    • 示意婚配一次或屡次

  • {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

三十二、正则表达式断言

  • ^ 只婚配输入的最先位置

  • $ 只婚配输入完毕位置

  • 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

三十三、正则表达式标识

标识是正则表达式字面量的后缀和正则表达式组织函数的参数;它修正正则表达是的婚配行动。

简称全称形貌
gglobal给定的正则表达式可以婚配屡次,它会影响几种要领,尤其是String.prototype.replace()
iignoreCase试图婚配给定的正则表达式时疏忽大小写
mmultiline在多行形式时,最先操纵符^和完毕操纵符$婚配每一行,而不是输入的悉数字符串

简称用于正则字面量后缀和组织函数参数,全称用于正则表达式对象的属性

三十四、正则表达式的实例属性

  • 标识: 布尔值示意设置什么标志。

    global(全局): 是不是设置/g标识
    ignoreCase(疏忽大小写): 是不是设置了/i标识
    multiline(多行): 是不是设置/m标识
  • 屡次婚配数据(设置了/g标识)

    lastIndex: 下次继承查找的肇端索引
    
var regex = /abc/i;
regex.ignoreCase; // true
regex.multiline; // false

三十五、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

三十六、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

三十七、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

三十八、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"]

三十九、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

四十、标识符/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;
}

四十一、援用文本

给制订的字符串拼装成正则表达式,统统特别是字符都需要举行转义。

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

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

四十二、缺乏断言(^、$)的正则表达式可以在恣意位置婚配

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

四十三、婚配统统或许什么都不婚配

一种特别状况:函数可以把正则表达式当作参数,用于过滤,假如缺乏这个参数,需要供应一个可以婚配统统的默许值

1. 婚配统统

空的正则表达式可以婚配统统

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

空的正则表达式应当是\/\/, 然则它被解释为JavaScript的解释。统统我们可以运用最接近的字面量替代\/(?:)\/(空的非捕捉分组)。这个分组可以婚配统统而且不捕捉任何字符串。

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

2. 不婚配任何字符

空的正则表达式相反的正则表达式

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

四十四、正则表达式备忘录

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, 以此类推)返回字符串
    

四十五、笔墨的编码和解码

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/

四十六、Console在IE9中存在兼容性题目

在Internet Explorer9中存在一个bug。在IE9中,console对象只要当开发者工具栏被翻开过最少一次,才会存在。
这意味着假如你的代码援用了console对象,同时又没有预先翻开开发者工具栏,你能够会获得一个援用毛病。
为了确保兼容性,最幸亏运用console对象之前先推断其是不是存在。

四十七、模块体系

1. CommonJS模块(CommonJS Module, CJS)

CJS的重要化身是Node.js模块,其特性:

  • 紧凑的语法

  • 同步加载的设想

  • 重要用于服务端

2. 异步模块定义(Asynchronous Module Definition, AMD)

AMD 最典范的完成就是Requirejs, 其特性:

  • AMD语法轻微庞杂,但不经由过程eval()或许静态编译步骤就可以事情

  • 异步加载的设想

  • 重要用于浏览器

四十八、算法: 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

注: 个人笔记,延续更新

    原文作者:一只渴望飞翔的猪
    原文地址: https://segmentfault.com/a/1190000008433640
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞