前言

前两天和朋友探讨一个react-render 通过map循环一个列表性能问题,主要讨论了一个争议点: map循环state中的一个数组,为了提高性能加key的重要性,由于常见的应用场景如下来分页加载,根据测试在[].length比较庞大,其中key的设置:

  1. 为map的index属性
  2. 为数组中的对象的唯一恒定不变的属性值

以上两种设置在实验证明有a比b的循环输出要慢100ms, 其中由于我猜想:在数组中不会存在元素的顺序的变化,key设置为index是一个唯一的索引,应该不会执行深度diff, 会有性能的提高,但是同等情况下,设置成数组中的唯一不变属性值确实会快一些。 我看了官方的解释给出的清晰解释:
image.png

image.png

https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

其实并没有解决我的问题: 因为我的数组是没有顺序变化的, 但是性能还是有一点小的偏差,此时我猜想是不是map的index有什么特殊的设置???

1. for循环(ES5)

最基本的循环,应该不需要解释了

2. for…in(ES5) obj遍历,并且只能访问允许枚举的属性

以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。
强调一下: 一个obj通过 Object.defineProperty 创建的属性中必须将enumerable设置为true才能被循环
其中也包括从父元素继承来的元素
另外[for...in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in)迭代中不能枚举符号。另外,Object.getOwnPropertyNames()不会返回符号对象属性

2.1语法

  1. variable

在每次迭代时,将不同的属性名分配给变量

  1. object

被迭代枚举其属性的对象。
无返回值,不支持链式操作

  1. for (variable in object) {...}

2.2 特点

  1. 最好是不要用于数组的循环,可能导致索引无序,
  2. 可以遍历自己的允许枚举及继承的父对象上允许枚举的属性,如果需要只是自己对象上的则可以通过hasOwnProperty()getOwnPropertyNames()

3.3 举例

  1. var obj = {a:1, b:2, c:3};
  2. for (var prop in obj) {
  3. console.log("obj." + prop + " = " + obj[prop]);
  4. }
  5. 下面的函数说明了hasOwnProperty()的用法:继承的属性不显示。
  6. var triangle = {a: 1, b: 2, c: 3};
  7. function ColoredTriangle() {
  8. this.color = 'red';
  9. }
  10. ColoredTriangle.prototype = triangle;
  11. var obj = new ColoredTriangle();
  12. for (var prop in obj) {
  13. if (obj.hasOwnProperty(prop)) {
  14. console.log(`obj.${prop} = ${obj[prop]}`);
  15. }
  16. }
  17. // Output:
  18. // "obj.color = red"


3. forEach( ES5 ) []

方法对数组的每个元素执行一次提供的函数。

3.1 语法

  1. callback:传入三个参数
  2. 1.数组当前项的值
  3. 2. 数组当前项的索引
  4. 3. 数组对象本身
  5. thisArg: 指定this 如果使用箭头函数表达式来传入函数参数,thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this 值。
  6. arr.forEach(callback[, thisArg]);

3.2 特点
1.没有返回值,不支持链式操作
2. 没有办法中止或者跳出 forEach() 循环,除了抛出一个异常。如果你需要这样,使用 forEach() 方法是错误的。
若你需要提前终止循环,你可以使用:

  1. - 简单循环
  2. - [for...of](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of) 循环
  3. - [`Array.prototype.every()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
  4. - [`Array.prototype.some()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
  5. - [`Array.prototype.find()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
  6. - [`Array.prototype.findIndex()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)

3.如果数组中存在某一个索引下的值不存在直接就跳过
3.3 示例

  1. const items = ['item1', 'item2',, 'item3'];
  2. const copy = [];
  3. // after
  4. items.forEach(function(item,index){
  5. copy.push(item);
  6. console.log(`${index}`, item);
  7. });
  8. //0 item1
  9. //1 item2
  10. //3 item3
  11. 在数组循环中修改了数组的值结构, 可能造成的后果
  12. var words = ['one', 'two', 'three', 'four'];
  13. words.forEach(function(word) {
  14. console.log(word);
  15. if (word === 'two') {
  16. words.shift(); //移除数组中的第一项,导致数组前移,'three'丢失
  17. }
  18. });
  19. // one
  20. // two
  21. // four

3.4 兼容写法

Polyfill

  1. // Production steps of ECMA-262, Edition 5, 15.4.4.18
  2. // Reference: http://es5.github.io/#x15.4.4.18
  3. if (!Array.prototype.forEach) {
  4. Array.prototype.forEach = function(callback, thisArg) {
  5. var T, k;
  6. if (this == null) {
  7. throw new TypeError(' this is null or not defined');
  8. }
  9. // 1. Let O be the result of calling toObject() passing the
  10. // |this| value as the argument.
  11. var O = Object(this); //创建一个对象
  12. // 2. Let lenValue be the result of calling the Get() internal
  13. // method of O with the argument "length".
  14. // 3. Let len be toUint32(lenValue).
  15. var len = O.length >>> 0; //验证数组长度大于0
  16. // 4. If isCallable(callback) is false, throw a TypeError exception.
  17. // See: http://es5.github.com/#x9.11
  18. //callback 必须是数组
  19. if (typeof callback !== "function") {
  20. throw new TypeError(callback + ' is not a function');
  21. }
  22. // 5. If thisArg was supplied, let T be thisArg; else let
  23. // T be undefined.
  24. if (arguments.length > 1) {
  25. T = thisArg;
  26. }
  27. // 6. Let k be 0
  28. k = 0;
  29. // 7. Repeat, while k < len
  30. while (k < len) {
  31. var kValue;
  32. // a. Let Pk be ToString(k).
  33. // This is implicit for LHS operands of the in operator
  34. // b. Let kPresent be the result of calling the HasProperty
  35. // internal method of O with argument Pk.
  36. // This step can be combined with c
  37. // c. If kPresent is true, then
  38. if (k in O) {
  39. // i. Let kValue be the result of calling the Get internal
  40. // method of O with argument Pk.
  41. kValue = O[k];
  42. // ii. Call the Call internal method of callback with T as
  43. // the this value and argument list containing kValue, k, and O.
  44. //Function.prototype.call(); 改变this,传入值
  45. //kValue:当前值
  46. //k 索引
  47. //o 对象
  48. callback.call(T, kValue, k, O);
  49. }
  50. // d. Increase k by 1.
  51. k++;
  52. }
  53. // 8. return undefined
  54. };
  55. }

3. for…of [] | Symbol

可迭代对象(包括 ArrayMapSetStringTypedArrayarguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句

3.1 语法

variable
在每次迭代中,将不同属性的值分配给变量。
iterable
被迭代枚举其属性的对象。

  1. for (variable of iterable) {
  2. //statements
  3. }

3.1 特点

  1. 可以终止break, throw continuereturn终止

3.2示例
可以被迭代的数据类型
1.Array
2.string
3.TypesArray
4.Map对象
5.Set对象
6.arguments
7.DOM集合
8.Symbol

  1. 1.
  2. let iterable = [10, 20, 30];
  3. for (let value of iterable) {
  4. value += 1;
  5. console.log(value);
  6. }
  7. // 11
  8. // 21
  9. // 31
  10. 2.
  11. let iterable = "boo";
  12. for (let value of iterable) {
  13. console.log(value);
  14. }
  15. // "b"
  16. // "o"
  17. // "o"
  18. 3.
  19. let iterable = new Uint8Array([0x00, 0xff]);
  20. for (let value of iterable) {
  21. console.log(value);
  22. }
  23. // 0
  24. // 255
  25. 4.
  26. let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);
  27. for (let entry of iterable) {
  28. console.log(entry);
  29. }
  30. // ["a", 1]
  31. // ["b", 2]
  32. // ["c", 3]
  33. for (let [key, value] of iterable) {
  34. console.log(value);
  35. }
  36. // 1
  37. // 2
  38. // 3
  39. 5.
  40. let iterable = new Set([1, 1, 2, 2, 3, 3]);
  41. for (let value of iterable) {
  42. console.log(value);
  43. }
  44. // 1
  45. // 2
  46. // 3
  47. 6.
  48. (function() {
  49. for (let argument of arguments) {
  50. console.log(argument);
  51. }
  52. })(1, 2, 3);
  53. // 1
  54. // 2
  55. // 3
  56. 7.
  57. //注意:这只能在实现了NodeList.prototype[Symbol.iterator]的平台上运行
  58. let articleParagraphs = document.querySelectorAll("article > p");
  59. for (let paragraph of articleParagraphs) {
  60. paragraph.classList.add("read");
  61. }
  62. 8.
  63. var iterable = {
  64. [Symbol.iterator]() {
  65. return {
  66. i: 0,
  67. next() {
  68. if (this.i < 3) {
  69. return { value: this.i++, done: false };
  70. }
  71. return { value: undefined, done: true };
  72. }
  73. };
  74. }
  75. };
  76. for (var value of iterable) {
  77. console.log(value);
  78. }
  79. // 0
  80. // 1
  81. // 2

3.4 for..of 与for…in的区别

  1. for...in 语句以原始插入顺序迭代对象的可枚举属性(key)
  2. for...of 语句遍历可迭代对象定义要迭代的数据。(value)

以下示例显示了与Array一起使用时,for...of循环和for...in循环之间的区别。

  1. Object.prototype.objCustom = function() {};
  2. Array.prototype.arrCustom = function() {};
  3. let iterable = [3, 5, 7];
  4. iterable.foo = 'hello';
  5. //原型链上可以被枚举的所有属性key
  6. for (let i in iterable) {
  7. console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
  8. }
  9. for (let i in iterable) {
  10. if (iterable.hasOwnProperty(i)) {
  11. console.log(i); // 0, 1, 2, "foo"
  12. }
  13. }
  14. //迭代的属性值
  15. for (let i of iterable) {
  16. console.log(i); // 3, 5, 7
  17. }

4. Array.map []

map()方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

4.1 特点:

  1. 会返回一个新数组
    2. 数组对象遍历
    3. 默认不会修改原来数组的值
    4. 数组元素的范围在callback被第一个调用时就已经确定
    5. 在map循环中,修改原数组的值,原数组中新增加的元素将不会被 callback 访问到
    6. 若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 map 方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到。

4.2用法

  1. /*
  2. * curentObj: 正在处理的当前元素。
  3. index:数组中正在处理的当前元素的索引。
  4. currentOrigin: 方法被调用的数组,也就是a
  5. thisArg :每次 callback 函数被调用的时候,this 都会指向 thisArg 参数上的这个对象,
  6. 如果省略了 thisArg 参数,或者赋值为 null 或 undefined,则 this 指向全局对象 。
  7. */
  8. let a = [1,2,3];
  9. let thisArg = this;
  10. let _a = a.map((curentObj, index, currentOrigin)=>{}, thisArg ) //会返回一个新值

4.3 ES5兼容写法(查看MDN)

  1. // 实现 ECMA-262, Edition 5, 15.4.4.19(如果不支持map的浏览器)
  2. // 参考: http://es5.github.com/#x15.4.4.19
  3. if (!Array.prototype.map) {
  4. Array.prototype.map = function(callback, thisArg) {
  5. var T, A, k;
  6. if (this == null) {
  7. throw new TypeError(" this is null or not defined");
  8. }
  9. // 1. 将O赋值为调用map方法的数组.
  10. var O = Object(this);
  11. // 2.将len赋值为数组O的长度.
  12. var len = O.length >>> 0;
  13. // 3.如果callback不是函数,则抛出TypeError异常.
  14. if (Object.prototype.toString.call(callback) != "[object Function]") {
  15. throw new TypeError(callback + " is not a function");
  16. }
  17. // 4. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined.
  18. if (thisArg) {
  19. T = thisArg;
  20. }
  21. // 5. 创建新数组A,长度为原数组O长度len
  22. A = new Array(len);
  23. // 6. 将k赋值为0
  24. k = 0;
  25. // 7. 当 k < len 时,执行循环.
  26. while(k < len) {
  27. var kValue, mappedValue;
  28. //遍历O,k为原数组索引
  29. if (k in O) {
  30. //kValue为索引k对应的值.
  31. kValue = O[ k ];
  32. // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
  33. mappedValue = callback.call(T, kValue, k, O);
  34. // 返回值添加到新数组A中.
  35. A[ k ] = mappedValue;
  36. }
  37. // k自增1
  38. k++;
  39. }
  40. // 8. 返回新数组A
  41. return A;
  42. };
  43. }

5.array.prototype.filter(callback(element, index, array), thisArg)

方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。 filter 不会改变原数组,它返回过滤后的新数组。

5.1 语法

element:当前元素
index:索引
array:当前被轮训的对象
thisArg: 执行 callback 时,用于 this 的值。

  1. var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

5.2 返回值

一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

5.3 应用示例

  1. 1. 筛选排除所有满足条件的值
  2. function isBigEnough(element) {
  3. return element >= 10; //过滤出大于10的数,为 true 则返回
  4. }
  5. var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
  6. // filtered is [12, 130, 44]
  7. 2. 过滤 JSON 中的无效条目
  8. 以下示例使用 filter() 创建具有非零(NaN, undefined, 0,不存在) id 的元素的 json
  9. var arr = [
  10. { id: 15 },
  11. { id: -1 },
  12. { id: 0 },
  13. { id: 3 },
  14. { id: 12.2 },
  15. { },
  16. { id: null },
  17. { id: NaN },
  18. { id: 'undefined' }
  19. ];
  20. var invalidEntries = 0;
  21. function isNumber(obj) {
  22. return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
  23. }
  24. function filterByID(item) {
  25. if (isNumber(item.id) && item.id !== 0) {
  26. return true;
  27. }
  28. invalidEntries++;
  29. return false;
  30. }
  31. var arrByID = arr.filter(filterByID);
  32. console.log('Filtered Array\n', arrByID);
  33. // Filtered Array
  34. // [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]
  35. 3. 在数组中搜索
  36. 下例使用 filter() 根据搜索条件来过滤数组内容。
  37. var fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
  38. function filterItems(query) {
  39. return fruits.filter(function(el) {
  40. return el.toLowerCase().indexOf(query.toLowerCase()) > -1;
  41. })
  42. }
  43. console.log(filterItems('ap')); // ['apple', 'grapes']
  44. console.log(filterItems('an')); // ['banana', 'mango', 'orange']

5.4 兼容写法

  1. if (!Array.prototype.filter){
  2. Array.prototype.filter = function(func, thisArg) {
  3. 'use strict';
  4. if ( ! ((typeof func === 'Function' || typeof func === 'function') && this) )
  5. throw new TypeError();
  6. var len = this.length >>> 0,
  7. res = new Array(len), // preallocate array
  8. t = this, c = 0, i = -1;
  9. //没有传入thisArg
  10. if (thisArg === undefined){
  11. while (++i !== len){
  12. // checks to see if the key was set
  13. if (i in this){
  14. //t[i]: 当前值
  15. //i:索引
  16. //t:当前对象
  17. //如果满足条件则放入新建res的数组中
  18. if (func(t[i], i, t)){
  19. res[c++] = t[i];
  20. }
  21. }
  22. }
  23. }
  24. else{
  25. while (++i !== len){
  26. // checks to see if the key was set
  27. if (i in this){
  28. //指定了this对象
  29. if (func.call(thisArg, t[i], i, t)){
  30. res[c++] = t[i];
  31. }
  32. }
  33. }
  34. }
  35. //最后返回一个数组重新处理res的长度
  36. res.length = c; // shrink down array to proper size
  37. //返回res
  38. return res;
  39. };
  40. }

6. find(满足条件的第一值即返回) []

6.1 概念

方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
其中与[findIndex()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) 返回满足条件的第一个值的索引
如果支持判断一个值是否存在在一个数组中可以用Array.prototype.indexOf()Array.prototype.includes()
find方法不会改变数组。

6.2 返回值

数组中第一个满足所提供测试函数的元素的值,否则返回 undefined

6.3 示例

  1. 1.用对象的属性查找数组里的对象
  2. var inventory = [
  3. {name: 'apples', quantity: 2},
  4. {name: 'bananas', quantity: 0},
  5. {name: 'cherries', quantity: 5}
  6. ];
  7. console.log(inventory.find((fruit)=>{
  8. return fruit.name === 'cherries';
  9. })); // { name: 'cherries', quantity: 5 }
  10. 2. 寻找数组中的质数
  11. 下面的例子展示了如何从一个数组中寻找质数(如果找不到质数则返回undefined
  12. function isPrime(element, index, array) {
  13. var start = 2;
  14. while (start <= Math.sqrt(element)) {
  15. if (element % start++ < 1) {
  16. return false;
  17. }
  18. }
  19. return element > 1;
  20. }
  21. console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
  22. console.log([4, 5, 8, 12].find(isPrime)); // 5

6.4 兼容写法

  1. // https://tc39.github.io/ecma262/#sec-array.prototype.find
  2. if (!Array.prototype.find) {
  3. Object.defineProperty(Array.prototype, 'find', {
  4. value: function(predicate) {
  5. // 1. Let O be ? ToObject(this value).
  6. if (this == null) {
  7. throw new TypeError('"this" is null or not defined');
  8. }
  9. var o = Object(this);
  10. // 2. Let len be ? ToLength(? Get(O, "length")).
  11. var len = o.length >>> 0;
  12. // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  13. if (typeof predicate !== 'function') {
  14. throw new TypeError('predicate must be a function');
  15. }
  16. // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  17. var thisArg = arguments[1];
  18. // 5. Let k be 0.
  19. var k = 0;
  20. // 6. Repeat, while k < len
  21. while (k < len) {
  22. // a. Let Pk be ! ToString(k).
  23. // b. Let kValue be ? Get(O, Pk).
  24. // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
  25. // d. If testResult is true, return kValue.
  26. var kValue = o[k];
  27. if (predicate.call(thisArg, kValue, k, o)) {
  28. return kValue;
  29. }
  30. // e. Increase k by 1.
  31. k++;
  32. }
  33. // 7. Return undefined.
  34. return undefined;
  35. }
  36. });
  37. }

7. Array.prototype.every []

**every()** 方法测试数组的所有元素是否都通过了指定函数的测试。(只有所有的满足条件才会返回true),只要有一个不满足则返回false

7.1 语法

element:当前元素
index:索引
array:当前被轮训的对象
thisArg: 执行 callback 时,用于 this 的值

  1. arr.every(callback[curentValue,[index,[arr,] ,thisArg])

7.2 特点

  1. 不会改变原来的数组
  2. 不能用于链式操作,因为返回的是一个true/false

7.3 示例

  1. 下例检测数组中的所有元素是否都大于 10
  2. function isBigEnough(element, index, array) {
  3. return (element >= 10);
  4. }
  5. var passed = [12, 5, 8, 130, 44].every(isBigEnough);
  6. // passed is false
  7. passed = [12, 54, 18, 130, 44].every(isBigEnough);
  8. // passed is true

7.4 兼容写法

  1. if (!Array.prototype.every)
  2. {
  3. Array.prototype.every = function(fun /*, thisArg */)
  4. {
  5. 'use strict';
  6. if (this === void 0 || this === null)
  7. throw new TypeError();
  8. var t = Object(this);
  9. var len = t.length >>> 0;
  10. if (typeof fun !== 'function')
  11. throw new TypeError();
  12. var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
  13. for (var i = 0; i < len; i++)
  14. {
  15. //只要有一个不满足则返回false
  16. if (i in t && !fun.call(thisArg, t[i], i, t))
  17. return false;
  18. }
  19. return true;
  20. };
  21. }

8. some []

**some()** 方法测试是否至少有一个元素通过由提供的函数实现的测试。
只要有一个元素满足条件则通过 注意:对于放在空数组上的任何条件,此方法返回false

8.1 返回值

返回值为true/false; 不能用于链式操作
用法同every; some是整体,every是局部

8.2 兼容写法

  1. // Production steps of ECMA-262, Edition 5, 15.4.4.17
  2. // Reference: http://es5.github.io/#x15.4.4.17
  3. if (!Array.prototype.some) {
  4. Array.prototype.some = function(fun/*, thisArg*/) {
  5. 'use strict';
  6. if (this == null) {
  7. throw new TypeError('Array.prototype.some called on null or undefined');
  8. }
  9. if (typeof fun !== 'function') {
  10. throw new TypeError();
  11. }
  12. var t = Object(this);
  13. var len = t.length >>> 0;
  14. var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
  15. for (var i = 0; i < len; i++) {
  16. //只要有一个满足条件则返回true
  17. if (i in t && fun.call(thisArg, t[i], i, t)) {
  18. return true;
  19. }
  20. }
  21. return false;
  22. };
  23. }

9. reduce []

方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

9.1 语法

reducer两个参数

  1. callback 函数接收4个参数:
    1. Accumulator (acc) (累计器) :每一次累计后的结果
    2. Current Value (cur) (当前值): 正在处理的元素
    3. Current Index (idx) (当前索引)
    4. Source Array (src) (源数组)

2. initialValue
作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
您的 reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

注意:
如果数组为空且没有提供initialValue,会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。
用一个示例体现展示一下返回的结过

  1. var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
  2. var maxCallback2 = ( max, cur ) => Math.max( max, cur );
  3. // reduce() 没有初始值
  4. [ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
  5. [ { x: 22 } ].reduce( maxCallback ); // { x: 22 }
  6. [ ].reduce( maxCallback ); // TypeError
  7. // map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行
  8. [ { x: 22 }, { x: 42 } ].map( el => el.x )
  9. .reduce( maxCallback2, -Infinity ); //传入了一个初始值

9.2 返回值

函数累计处理的结果;如果返回的是一个数组则支持链式操作

9.3 示例

  1. let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
  2. let result = arr.sort().reduce((init, current)=>{
  3. if(init.length===0 || init[init.length-1]!==current){
  4. init.push(current);
  5. }
  6. return init;
  7. }, []);
  8. console.log(result); //[1,2,3,4,5]

9.4 兼容性写法

  1. // Production steps of ECMA-262, Edition 5, 15.4.4.21
  2. // Reference: http://es5.github.io/#x15.4.4.21
  3. // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
  4. if (!Array.prototype.reduce) {
  5. Object.defineProperty(Array.prototype, 'reduce', {
  6. value: function(callback /*, initialValue*/) {
  7. if (this === null) {
  8. throw new TypeError( 'Array.prototype.reduce ' +
  9. 'called on null or undefined' );
  10. }
  11. if (typeof callback !== 'function') {
  12. throw new TypeError( callback +
  13. ' is not a function');
  14. }
  15. // 1. Let O be ? ToObject(this value).
  16. var o = Object(this);
  17. // 2. Let len be ? ToLength(? Get(O, "length")).
  18. var len = o.length >>> 0;
  19. // Steps 3, 4, 5, 6, 7
  20. var k = 0;
  21. var value;
  22. if (arguments.length >= 2) {
  23. value = arguments[1];
  24. } else {
  25. //不存在继续
  26. while (k < len && !(k in o)) {
  27. k++;
  28. }
  29. // 3. If len is 0 and initialValue is not present,
  30. // throw a TypeError exception.
  31. if (k >= len) {
  32. throw new TypeError( 'Reduce of empty array ' +
  33. 'with no initial value' );
  34. }
  35. value = o[k++];
  36. }
  37. // 8. Repeat, while k < len
  38. while (k < len) {
  39. // a. Let Pk be ! ToString(k).
  40. // b. Let kPresent be ? HasProperty(O, Pk).
  41. // c. If kPresent is true, then
  42. // i. Let kValue be ? Get(O, Pk).
  43. // ii. Let accumulator be ? Call(
  44. // callbackfn, undefined,
  45. // « accumulator, kValue, k, O »).
  46. if (k in o) {
  47. //value:计算的第一次结果
  48. //o[k]:当前需要操作的元素
  49. value = callback(value, o[k], k, o);
  50. }
  51. // d. Increase k by 1.
  52. k++;
  53. }
  54. // 9. Return accumulator.
  55. return value;
  56. }
  57. });
  58. }

10.include [] / 类数组arguments对象

**includes()** 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

10.1 语法

valueToFind
需要查找的元素值。
fromIndex 可选 如果为负数,则是从后面的第一个为开始搜索
fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。

  1. arr.includes(valueToFind[, fromIndex])

10.2 返回值

返回一个布尔值 Boolean ,如果在数组中找到了(如果传入了 fromIndex ,表示在 fromIndex 指定的索引范围中找到了)则返回 true

10.3 示例

  1. [1, 2, 3].includes(2); // true
  2. [1, 2, 3].includes(4); // false
  3. [1, 2, 3].includes(3, 3); // false
  4. [1, 2, 3].includes(3, -1); // true
  5. [1, 2, NaN].includes(NaN); // true
  6. 如果 fromIndex 大于等于数组的长度,则会返回 false,且该数组不会被搜索。
  7. var arr = ['a', 'b', 'c'];
  8. arr.includes('c', 3); // false
  9. arr.includes('c', 100); // false
  10. var arr = ['a', 'b', 'c'];
  11. 如果 fromIndex 为负值,计算出的索引将作为开始搜索searchElement的位置。
  12. 如果计算出的索引小于 0,则整个数组都会被搜索。
  13. var arr = ['a', 'b', 'c'];
  14. arr.includes('a', -100); // true
  15. arr.includes('b', -100); // true
  16. arr.includes('c', -100); // true
  17. arr.includes('a', -2); // false
  18. includes() 方法有意设计为通用方法。它不要求this值是数组对象,
  19. 所以它可以被用于其他类型的对象 (比如类数组对象)。
  20. 下面的例子展示了 在函数的 arguments 对象上调用的 includes() 方法
  21. (function() {
  22. console.log([].includes.call(arguments, 'a')); // true
  23. console.log([].includes.call(arguments, 'd')); // false
  24. })('a','b','c');

10.4 兼容写法

  1. // https://tc39.github.io/ecma262/#sec-array.prototype.includes
  2. if (!Array.prototype.includes) {
  3. Object.defineProperty(Array.prototype, 'includes', {
  4. value: function(valueToFind, fromIndex) {
  5. if (this == null) {
  6. throw new TypeError('"this" is null or not defined');
  7. }
  8. // 1. Let O be ? ToObject(this value).
  9. var o = Object(this);
  10. // 2. Let len be ? ToLength(? Get(O, "length")).
  11. var len = o.length >>> 0;
  12. // 3. If len is 0, return false.
  13. if (len === 0) {
  14. return false;
  15. }
  16. // 4. Let n be ? ToInteger(fromIndex).
  17. // (If fromIndex is undefined, this step produces the value 0.)
  18. var n = fromIndex | 0;
  19. // 5. If n ≥ 0, then
  20. // a. Let k be n.
  21. // 6. Else n < 0,
  22. // a. Let k be len + n.
  23. // b. If k < 0, let k be 0.
  24. var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
  25. function sameValueZero(x, y) {
  26. //值的相等如果为NAN的验证
  27. return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
  28. }
  29. // 7. Repeat, while k < len
  30. while (k < len) {
  31. // a. Let elementK be the result of ? Get(O, ! ToString(k)).
  32. // b. If SameValueZero(valueToFind, elementK) is true, return true.
  33. if (sameValueZero(o[k], valueToFind)) {
  34. return true;
  35. }
  36. // c. Increase k by 1.
  37. k++;
  38. }
  39. // 8. Return false
  40. return false;
  41. }
  42. });
  43. }

11. 总结

名称 语法 返回值 是否支持链式 针对类型 是够可以终止 是够会修改原数组
for for(let i=0; i<= len;i++) no 数组,对象(string),类数组 可以; break; continue;return no
for…in for(o in obj)
o:代表key
属性循环
no 对象中可以枚举的属性(包括原型链)
数组最好不要用,索引可能不是整数
no
for…of for(v of obj)
v:代表值
值循环
no ArrayMapSetStringTypedArrayarguments, symbal 可以; break; continue;return no
forEach [].forEach(function(item,index){ })
item:当前值
index:当前值对应的索引
no 数组 不能中断,如果需要中断则只能抛异常 no
map [].map(function(curentObj, index,currentOrigin){}, this)
curentObj: 正在处理的当前元素。
index:数组中正在处理的当前元素的索引。
currentOrigin: 方法被调用的数组,也就是a
thisArg :每次 callback 函数被调用的时候,this 都会指向 thisArg 参数上的这个对象
返回一个新数组; yes 数组, arguments no 不能中断 no
filter var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
element:当前元素
index:索引
array:当前被轮训的对象
thisArg: 执 callback 时,用于 this 的值。
1.返回新数组
2.如果没有任何元素则返回空数组
yes 数组,数组对象 no
find var newArray = arr.find(callback(element[, index[, array]])[, thisArg]) 1.返回第一个满足条件的值
2. 否则返回undefined
no 数组 yes
return
no
every arr.every(callback[curentValue,[index,[arr,] ,thisArg]) true/false
全部满足条件则返回true
no 数组 yes
return
no
some arr.some(callback[curentValue,[index,[arr,] ,thisArg]) true/false
只要有一个不满足则返回false
no 数组 yes

return
no
reduce [],reduce(callback(Accumulator CurrentValue,index, Array ),initialValue) 返回一个累计处理的结果,可以是值类型,所以是引用类型 yes 数组 yes
return
no
includes arr.includes(valueToFind[, fromIndex])
valueToFind:包含的值fromIndex: 索引
true/false
找到就为true
no 数组 yes
return
no