Array

基础

new Array()

  • 数组构造函数不能出现空值
    // new Array(,1,,3,5,)
    // new Array(1,,3,5,)
    // new Array(,1,,3,5)
    // new Array(1,,3,5)
    // 以上报错,数组构造函数不能出现空值
    // 本质上是因为构造函数中需要传入参数,而参数是不能出现空值的
    new Array(1,3,5)
    
  • 无参数 空数组
  • 传入一个数字设置数组长度 => [undefined*5]稀疏数组;
    new Array(2)//设置数组的长度
    
  • 创建数组
    new Array('a')//['a']
    
  • ES6中新增方法Array.of(), 将所有传入参数作为新建数组的元素,即使传入单个数值元素,Array.of(5) => [5];

Array()

var arr3 = Array();//Array是一个安全构造函数

Array的表现形式与new Array是一样的,他们没有区别

规范说明

When Array is called as a function rather than as a constructor, it creates and initialises a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.

当数组作为函数调用而不是构造函数调用时,它会创建并初始化一个新的数组对象。因此当Array(…)和new Array(…)接收同样的参数时,它们是相同的。

Array.of() -ES6

Array.of()方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

  • Array.of() 和 Array 构造函数之间的区别在于处理整数参数
    • Array.of(7) 创建一个具有单个元素 7 的数组,
    • 而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

字面量

var arr1 = [];//数组字面量 推荐
var arr2 = new Array();//通过系统内置的Array构造函数
var arr3 = Array();//Array是一个安全构造函数
// 所以数组都继承于Array.prototype

数组就是对象

var arr = [1,2,3]
var obj ={
    0:1,
    1:2,
    2:3
}
console.log(arr[2],obj[2])
// 从访问机制可以看出数组就是对象

尾空值截取

var arr = [, , ,]
console.log(arr.length)//3
// 如果数组最后一位逗号后面是空值,这个空值默认会被截取掉

稀疏数组

arr = [,,1,3,5,,7] //这种叫做稀疏数组

数组中不会有索引超出边界的问题

var arr = [0,1,2]
console.log(arr[3]) //undefined
// 为什么是undefined
var obj ={
    0:0,
    1:1,
    2:2
}
console.log(obj[3]) //undefined
// js 数组中不会有索引超出边界的问题

Array.from(arrayLike[, mapFn[, thisArg]])-ES6

  • Array.from() 方法从一个类数组或可迭代对象创建一个新的,浅拷贝的数组实例。

    • arrayLike
      想要转换成数组的伪数组对象或可迭代对象。
    • mapFn 可选
      如果指定了该参数,新数组中的每个元素会执行该回调函数。
    • thisArg 可选
      可选参数,执行回调函数 mapFn 时 this 对象。
  • 它是静态方法,返回一个新的,浅拷贝的数组实例。

//字符串它是可迭代对象,它是由string构造函数构造的
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]

// Symbol类型不做处理返回一个空数组
const sm = Symbol('123');
const newArr = Array.from(sm)
console.log(newArr)//[]

//Number 是不可迭代的
const num = 123;
const newArr = Array.from(num)
console.log(newArr)//[]

// 布尔、正则、普通对象也是不可迭代的
// null undefined 会直接报错
// 不写参数相当于undefined、也会报错
  • MDN示例
  1. 从 String 生成数组
//字符串它是可迭代对象,它是由string构造函数构造的
Array.from('foo');
// [ "f", "o", "o" ]
  1. 从 Set 生成数组
const set = new Set(['foo', 'bar', 'baz', 'foo']);
Array.from(set);
// [ "foo", "bar", "baz" ]
  1. 从 Map 生成数组
const map = new Map([[1, 2], [2, 4], [4, 8]]);
// Map(3) {1 => 2, 2 => 4, 4 => 8}
Array.from(map);
// [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([['1', 'a'], ['2', 'b']]);
// Map(2) {'1' => 'a', '2' => 'b'}
Array.from(mapper.values());
// ['a', 'b'];
Array.from(mapper.keys());
// ['1', '2'];
  1. 从类数组对象(arguments)生成数组
// 类数组可以正常使用,有length,并且key从0开始的顺序数字的
// length决定了数组的长度,key决定了填充该数组的位置
function f() {
  return Array.from(arguments);
}

f(1, 2, 3);

// [ 1, 2, 3 ]
  1. 在 Array.from 中使用箭头函数(必须要有返回值)
// Using an arrow function as the map function to
// manipulate the elements
// mapFn(item.index),这个回调函数没有第三个参数arr
Array.from([1, 2, 3], x => x + x);
// [2, 4, 6]
// Generate a sequence of numbers
// Since the array is initialized with `undefined` on each position,
// the value of `v` below will be `undefined`
Array.from({length: 5}, (v, i) => i);
// [0, 1, 2, 3, 4]

// from第二个参数的执行原理 Array.from+map
Array.from({length: 5}).map((v, i) => i);
  1. 序列生成器(指定范围)
// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
// Generate numbers range 0..4
range(0, 4, 1);
// [0, 1, 2, 3, 4]

// Generate numbers range 1..10 with step of 2
range(1, 10, 2);
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
  1. 数组去重合并,set去重,contact合并
function combine(){
    let arr = [].concat.apply([], arguments);  //没有去重复的新数组
    return Array.from(new Set(arr));
}

var m = [1, 2, 2], n = [2,3,3];
console.log(combine(m,n));                     // [1, 2, 3]
  • 实现(很重要-没看懂-todo)

堆栈方法

push unshift 类数组

  • push是在数组末尾后添加元素
  • unshift在数组首位前添加元素
  • 它们的返回值是方法执行后数组的长度
Array.prototype.myPush = function(){
    for(var i =0;i<arguments.length;i++){
        // arr[arr.length]=arguments[i]
        this[this.length] = arguments[i]
    }
    return this.length;
}

var arr = [1,2]
arr.myPush(4,5)

  • push原理
Array.prototype.push = function(elem){
    this[this.length] = elem;
    this.length++
}
  • 类数组
function test(){
    // arguments.push(7) 没有继承push方法
    console.log(arguments)
}
test(1,2,3)

var divs = document.getElementsByTagName('div')
console.log(divs)
// arguments、divs都是类数组

类数组的原型是Object
数组的原型是Array

  • 类数组与push
var obj ={
    0:1,
    1:2,
    2:3,
    length:3,
    push:Array.prototype.push,
    'splice':Array.prototype.splice,//对象的key有没有引号都可以,但json不行   
}
// 也可以直接定义在Object原型上
Object.prototype.push = Array.prototype.push;
obj.push(7)
console.log(obj)
  • 类数组push的面试题
var obj = {
    '2':3,
    '3':4,
    'length':2,
    'push':Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)
// {2: 1, 3: 2, length: 4, push: ƒ}
  • unshift自定义方法1
var arr = ['d','e','f'];
Array.prototype.myUnshift = function(){
    this.splice(0,0,...arguments)
    return this.length;
}
arr.myUnshift('a','b','c')
  • unshift自定义方法2
var arr = ['d','e','f'];

Array.prototype.myUnshift = function(){
    // 把类数组转换为数组的方法
    var argArr = Array.prototype.slice.call(arguments)
    console.log(this)
    var newArr = argArr.concat(this);
    // console.log(newArr)
    // this = argArr.concat(this);
    // 不能直接给this赋值
    // this = 1
    // VM768:1 Uncaught SyntaxError: Invalid left-hand side in assignment
    return newArr.length;
}

arr.myUnshift('a','b','c')

pop shift

  • pop 弹出数组最后一位元素并返回这个元素
  • shift 弹出数组第一位元素并返回这个元素
  • 以上两个方法没有参数

删除修改

splice

  • array.splice(start[, deleteCount[, item1[, item2[, …]]]])
    • start​

      • 指定修改的开始位置(从0计数)。
      • 如果超出了数组的长度,则从数组末尾开始添加内容;
      • 如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);
      • 如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
    • deleteCount 可选

      • 整数,表示要移除的数组元素的个数。
      • 如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
      • 如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
      • 如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
    • item1, item2, … 可选

      • 要添加进数组的元素,从start 位置开始。
      • 如果不指定,则 splice() 将只删除数组元素。
    • 返回值
      由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

var arr = ['a','b','c','d']
// 从索引2后面添加元素,而不删除
console.log(arr.splice(2,0, 1,2)) //[]
console.log(arr) //['a', 'b', 1, 2, 'c', 'd']

// 数组末尾添加元素 相当于push
var arr2 = ['a','b','c','d']
arr2.splice(arr2.length,0,'e')
console.log(arr2) //['a', 'b', 'c', 'd', 'e']

slice([begin[, end]])

  • slice() 方法返回一个新的数组对象,
  • 这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)
  • 原始数组不会被改变。
  1. begin 可选
    • 提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。
    • 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
    • 如果省略 begin,则 slice 从索引 0 开始。
    • 如果 begin 超出原数组的索引范围,则会返回空数组。
  2. end 可选
    • 提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。
    • slice 会提取原数组中索引从 begin 到 end 的所有元素(包含 begin,但不包含 end)。
    • slice(1,4) 会提取原数组中从第二个元素开始一直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。
    • 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
    • 如果 end 被省略,则 slice 会一直提取到原数组末尾。
    • 如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。。
  • slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。
    你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子。
function list() {
  return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]

除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程。

var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function list() {
  return slice(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

copyWithin(target[, start[, end]])

赋值->取符合区间的元素->粘贴到target(数组长度不会发生变化)
浅复制数组的一部分到
同一数组中的另一个位置
并返回它,
不会改变原数组的长度。

  1. [start,end) 之间的集合替换target,区间前闭后开
  2. target:复制的[start,end)区间从target开始替换
  3. end>len-1:取到 末尾
  4. target>len-1: 不发生任何替换
  5. target>start: 正常替换
  6. start或end是负数:start+len、end+len(可以理解为从数组末尾-1开始计算)
  7. 如果没有start:区间取整个数组区间
  8. 如果没有end:区间取到数组末尾
  9. 返回的是原数组的引用
//es2015
const array1 = ['a', 'b', 'c', 'd', 'e'];
// copy to index 0 the element at index 3
console.log(array1.copyWithin(0, 3, 4));
// expected output: Array ["d", "b", "c", "d", "e"]
  1. this不一定非要指向一个数组也可以指向一个对象
var obj = {
    0:0,
    1:1,
    2:2,
    3:3,
    4:4,
    length:5
}
// const newobj = [].copyWithin.call(obj,0,2,3)
const newobj = Array.prototype.copyWithin.call(obj,0,2,3)
console.log(newobj);//newobj===obj
// {0: 2
// 1: 1
// 2: 2
// 3: 3
// 4: 4
// length: 5}

方法重写-todo(补充位移和位运算 )

Array.prototype.myCopyWithin = function(target){
    if(this==null){
        throw new TypeError("this is null or not defined")
    }
    var obj = Object(this),
        // len = obj.length || 0 
        len =obj.length>>>0 //表明它是一个正整数
        // target = arguments[0]
        start = arguments[1],
        end = arguments[2];
        // null >> 0
        // 0
        // undefined >> 0
        // 0
        // 'abc'>>0
        // 0
        // 'abc'>>>0
        // 0
        // 'abc'>>0
        // 0
        target = target >> 0
        target = target <0
} 

排序

sort([compareFunction])

  1. sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码
  2. 返回的是对原数组的引用
  • 为什么要转换为字符串然后排序呢
    如果仅限用一种类型的话,sort功能性就太弱
    任何对象都有tostring方法,这样可排序的范围就大了
// 它会安装首字母顺序排序
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]

// 它不会按照数字大小进行排序
// 它会调用toString把数字转换为字符串然后排序
// 默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]
  • compareFunction
  1. 如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:
  2. 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  3. 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。
  4. 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
  5. compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。
    var arr = [2,1,9,3].sort(function(){
      //Math.random()方法可返回介于 0(包含) ~ 1(不包含) 之间的一个随机数
        if(Math.random()>0.5){
            return 1
        }
        if(Math.random()<0.5){
            return -1
        }
        return 0 
    })
    console.log(arr)
    // 每次排序结果都不一样
    
    所以,比较函数格式如下:
    function compare(a, b) {
    if (a < b ) {           // 按某种排序标准进行比较, a 小于 b
        return -1;
    }
    if (a > b ) {
        return 1;
    }
    // a must be equal to b
    return 0;
    }
    
  • 要比较数字而非字符串,比较函数可以简单的以 a 减 b,如下的函数将会将数组升序排列
function compareNumbers(a, b) {
  return a - b;
}
  • sort 方法可以使用 函数表达式 方便地书写:
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
  return a - b;
});
console.log(numbers);
// 也可以写成:
var numbers = [4, 2, 5, 1, 3];
numbers.sort((a, b) => a - b);
console.log(numbers);
// [1, 2, 3, 4, 5]
  • 对非 ASCII 字符排序
    当排序非 ASCII 字符的字符串。一些非英语语言的字符串需要使用 String.localeCompare。这个函数可以将函数排序到正确的顺序。
var items = ['我', '爱', '你'];
items.sort(function (a, b) {
  return a.localeCompare(b);
});
// (3) ['爱', '你', '我']
  • 使用映射改善排序
var arr = ['E','e','B','a','C']
arr.sort(function(a,b){
    var _a = a.toLocaleLowerCase(),
        _b = b.toLocaleLowerCase();
    // 对每个入参都要进行处理,负载太大
    if(_a<_b){
        return -1
    }
    if(_a>_b){
        return 1
    }
    return 0
})

console.log(arr)// ['a', 'B', 'C', 'E', 'e']

compareFunction 可能需要对元素做多次映射以实现排序,尤其当 compareFunction 较为复杂,且元素较多的时候,某些 compareFunction 可能会导致很高的负载。
使用 map 辅助排序将会是一个好主意。基本思想是首先将数组中的每个元素比较的实际值取出来,排序后再将数组恢复。

// 需要被排序的数组
var list = ['Delta', 'alpha', 'CHARLIE', 'bravo'];
// 对需要排序的数字和位置的临时存储
var newList = list.map(function(el, i) {
  return { index: i, value: el.toLowerCase() };
})

// 按照多个值排序数组
newList.sort(function(a, b) {
  return +(a.value > b.value) || +(a.value === b.value) - 1;
});

// 根据索引得到排序的结果
var result = newList.map(function(el){
  return list[el.index];
});
  • 排序稳定性
    自 ES10(EcmaScript 2019)起,规范 要求 Array.prototype.sort 为稳定排序。
    假设有一个包含学生名字和年级的列表,已经将它按学生名字字母顺序进行预排序:
const students = [
  { name: "Alex",   grade: 15 },
  { name: "Devlin", grade: 15 },
  { name: "Eagle",  grade: 13 },
  { name: "Sam",    grade: 14 },
];

对这个数组执行 grade 升序排序后:

students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);

students 变量如下:

[
  { name: "Eagle",  grade: 13 },
  { name: "Sam",    grade: 14 },
  { name: "Alex",   grade: 15 }, // grade 相同时维持原先的顺序 (稳定排序)
  { name: "Devlin", grade: 15 }, // grade 相同时维持原先的顺序 (稳定排序)
];

注意,那些年级相同的学生(如 Alex 和 Devlin)会维持调用排序之前的顺序,这是稳定排序所确保的。

ES10(EcmaScript 2019)以前没有要求稳定性,意味着你可能会得到以下结果:

[
  { name: "Eagle",  grade: 13 },
  { name: "Sam",    grade: 14 },
  { name: "Devlin", grade: 15 }, // 没有维持原先的顺序
  { name: "Alex",   grade: 15 }, // 没有维持原先的顺序
];

::: warning
虽然sort中可以传入排序规则、但因为每个浏览器对sort排序实现所使用的算法不同,所以会出现以上情况。
ES10规定了无论浏览器使用什么排序算法都应该实现sort的稳定性排序
:::

  • 数组按照元素的字节数排序
// Unicode 0-255 1个字节  256- 2个字节
function getBytes(str){
    var bytes = str.length;
    for(var i =0;i<str.length;i++){
        if(str.charCodeAt(i)>255){
            bytes++
        }
    }
    return bytes;
}
console.log(getBytes('我爱你')) //6

// 数组按照元素的字节数排序
var arr = ['我爱你','Ok','Hello','你说what','可以']
arr.sort((a,b)=>getBytes(a)-getBytes(b))
// ['Ok', '可以', 'Hello', '我爱你', '你说what']

reverse

reverse() 方法将数组中元素的位置颠倒,并返回该数组。
数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。
该方法会改变原数组。

var arr =[1,2,3]
arr.reverse();
console.log(arr);//[3, 2, 1]

以上都是对原数组的修改

拼接

concat

var new_array = old_array.concat(value1[, value2[, …[, valueN]]])

  • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

  • 返回的数组是对原有数组的一个浅拷贝

  • 连接两个数组

var alpha = ['a', 'b', 'c'];
var numeric = [1, 2, 3];
alpha.concat(numeric);
// result in ['a', 'b', 'c', 1, 2, 3]
  • 将值连接到数组
// 以下代码将三个值连接到数组:
var alpha = ['a', 'b', 'c'];
var alphaNumeric = alpha.concat(1, [2, 3]);
console.log(alphaNumeric);
// results in ['a', 'b', 'c', 1, 2, 3]
  • 合并嵌套数组
    以下代码合并数组并保留引用:
var num1 = [[1]];
var num2 = [2, [3]];
var num3=[5,[6]];

var nums = num1.concat(num2);
console.log(nums);
// results is [[1], 2, [3]]

var nums2=num1.concat(4,num3);
console.log(nums2)
// results is [[1], 4, 5,[6]]

// modify the first element of num1
num1[0].push(4);
console.log(nums);
// results is [[1, 4], 2, [3]]

join([separator])

  • separator 可选
    指定一个字符串来分隔数组的每个元素。如果需要,将分隔符转换为字符串。如果缺省该值,数组元素用逗号(,)分隔。如果separator是空字符串(“”),则所有元素之间都没有任何字符。
  • 如果 arr.length 为0,则返回空字符串。
  • 如果一个元素为 undefined 或 null,它会被转换为空字符串。

String.prototype.split()

  • String.prototype.split()方法使用指定的分隔符字符串将一个String对象分割成子字符串数组,以一个指定的分割字串来决定每个拆分的位置。

索引

indexof

lastIndexOf

find(callback[, thisArg])-ES6

  1. find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
  2. callback函数带有3个参数:当前元素的值、当前元素的索引,以及数组本身。callback返回值为布尔,如果返回true,结束遍历返回当前元素的值
  3. 如果是对象数组,返回的元素为对应数组中对象的引用
  4. 第二个参数thisArg执行回调时用作this的对象,如果没有第二个参数,在严格模式下this=undefined,非严格模式下this=window
  5. find会遍历稀疏数组的空隙empty,空隙处遍历的值由undefined占位,其他数组遍历方法不会遍历稀疏数组中的空隙,只有寻找find才有必要
  6. find在callback中不能更改当前元素的值,但可以改变数组本身,比如通过push添加数组元素
  7. find在第一次调用callback函数时会确定元素的索引范围,因此在 find方法开始执行之后添加到数组的新元素将不会被 callback函数访问到
  8. 如果数组中一个尚未被callback函数访问到的元素的值被callback函数所改变,那么当callback函数访问到它时,它的值是将是根据它在数组中的索引所访问到的当前值。被删除的元素仍旧会被访问到,但是其值已经是undefined了。
const array1 = [5, 12, 8, 130, 44];
const found = array1.find(element => element > 10);
console.log(found);
// expected output: 12

// 更改test
const array1 = [5, 12, 8, 130, 44];
const found = array1.find((element,index,arr) => {
    console.log(element,index,arr);
    if(index==0){
        arr[1]=999;
        arr[2] = null;
    }
    return false;
});

// 删除test、
const array1 = [0, 1, 2, 3, 4,5,6,7,8,9];
const found = array1.find((element,index,arr) => {
    console.log(element,index,arr);
    if(index==0){
        delete arr[1] //删除该项并填入undefined
        arr.splice(3,3);//删除该项,位置不保留,最后补上undefined
        arr.pop();//删除该项,位置不保留,最后补上undefined
    }
    // [0, empty, 2, 6, 7, 8]
    return false;
});
  • 稀疏数组
const arr =Array(5)
arr[0] = 1;
arr[2] = 3;
arr[4] = 5;
console.log(arr) // [1, empty, 3, empty, 5] 稀疏数组
  • mdn find 的实现
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    value: function(predicate) {
     // 1. Let O be ? ToObject(this value).
      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If IsCallable(predicate) is false, throw a TypeError exception.
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }

      // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
      var thisArg = arguments[1];

      // 5. Let k be 0.
      var k = 0;

      // 6. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kValue be ? Get(O, Pk).
        // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
        // d. If testResult is true, return kValue.
        var kValue = o[k];
        if (predicate.call(thisArg, kValue, k, o)) {
          return kValue;
        }
        // e. Increase k by 1.
        k++;
      }

      // 7. Return undefined.
      return undefined;
    }
  });
}

findIndex(callback[, thisArg])-ES6

  1. 基本特性同find,只不过findIndex返回的是索引
  2. 如果没找到符合条件的数组,或 调用数组为空数组,返回-1
  • mdn的实现Polyfill
// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
if (!Array.prototype.findIndex) {
  Object.defineProperty(Array.prototype, 'findIndex', {
    value: function(predicate) {
     // 1. Let O be ? ToObject(this value).
      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If IsCallable(predicate) is false, throw a TypeError exception.
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }

      // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
      var thisArg = arguments[1];

      // 5. Let k be 0.
      var k = 0;

      // 6. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kValue be ? Get(O, Pk).
        // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
        // d. If testResult is true, return k.
        var kValue = o[k];
        if (predicate.call(thisArg, kValue, k, o)) {
          return k;
        }
        // e. Increase k by 1.
        k++;
      }

      // 7. Return -1.
      return -1;
    }
  });
}

includes(valueToFind[, fromIndex])-ES7

  • fromIndex 可选
    1. 从fromIndex 索引处开始查找 valueToFind。
    2. 如果为负值,则按升序从 array.length + fromIndex 的索引开始搜。如果array.length + fromIndex <0 则从0开始搜素
    3. 默认为 0。
    4. 如果 fromIndex 大于等于数组的长度,则将直接返回 false,且不搜索该数组。
[1, 2, 3].includes(2);     // true
[1, 2, 3].includes(4);     // false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true
[1, 2, 3].includes(3, -9); // true
[1, 2, 3].includes(1, -9); // true
[1, 2, NaN].includes(NaN); // true

技术上来讲:是全等+零值相等+同值相等

  • 作为通用方法的 includes()
    includes() 方法有意设计为通用方法。
    它不要求this值是数组对象,所以它可以被用于其他类型的对象 (比如类数组对象)。
    下面的例子展示了 在函数的 arguments 对象上调用的 includes() 方法。
(function() {
  console.log([].includes.call(arguments, 'a')); // true
  console.log([].includes.call(arguments, 'd')); // false
})('a','b','c');
  • 实现
Array.prototype.myIncludes = function(){
    if(this==null){
        throw new TypeError("this is null")
    }
    var value = arguments[0],
        fromIndex = arguments[0],
        obj = Object(this),
        len = obj.length >>> 0;
    
    if(len===0){
        return false
    }

    fromIndex = fromIndex | 0;
    fromIndex = Math.max(fromIndex>=0?fromIndex:len+fromIndex);
    while(fromIndex<len){
        if(obj[fromIndex] === value){
            return true;
        }
        fromIndex++
    }
    return false;
}
  • MDN Polyfill
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(valueToFind, fromIndex) {

      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If len is 0, return false.
      if (len === 0) {
        return false;
      }

      // 4. Let n be ? ToInteger(fromIndex).
      //    (If fromIndex is undefined, this step produces the value 0.)
      var n = fromIndex | 0;

      // 5. If n ≥ 0, then
      //  a. Let k be n.
      // 6. Else n < 0,
      //  a. Let k be len + n.
      //  b. If k < 0, let k be 0.
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

      function sameValueZero(x, y) {
        return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
      }

      // 7. Repeat, while k < len
      while (k < len) {
        // a. Let elementK be the result of ? Get(O, ! ToString(k)).
        // b. If SameValueZero(valueToFind, elementK) is true, return true.
        if (sameValueZero(o[k], valueToFind)) {
          return true;
        }
        // c. Increase k by 1.
        k++;
      }

      // 8. Return false
      return false;
    }
  });
}

迭代

数组和字符串的去重

  1. 对象中的key都是唯一的,利用这个特性来进行判断重复元素

    var arr= [0,0,1,1,2,1,2,2,3,1,2,'a',3,'a'];
    
    Array.prototype.unique = function(){
        var temp = {};
        // 对象中的key都是唯一的,利用这个特性来进行判断重复元素
        var newArr = [];
        for(var i =0;i<this.length;i++){
            if(!temp[this[i]]){
                // temp[this[i]] = this[i];
                temp[this[i]] = 'value';//1. 解决temp对象属性中的value为0的问题
                newArr.push(this[i]);
            }
        }
        return newArr
    }
    
  2. 也可以hasOwnProperty解决对象值为0的问题

    // 2.也可以hasOwnProperty解决对象值为0的问题
    Array.prototype.unique = function(){
        var temp = {};
        // 对象中的key都是唯一的,利用这个特性来进行判断重复元素
        var newArr = [];
        for(var i =0;i<this.length;i++){
            if(!temp.hasOwnProperty(this[i])){
                temp[this[i]] = this[i];
                newArr.push(this[i]);
            }
        }
        return newArr
    }
    
    console.log(arr.unique()) 
    // [0, 1, 2, 3, 'a']
    
    var str = '11000112112211aa';
    String.prototype.unique = function(){
        var temp = {};
        var newStr = '';
        for(var i =0;i<this.length;i++){
            if(!temp.hasOwnProperty(this[i])){
                temp[this[i]] = this[i];
                newStr+=this[i];
            }
        }
        return newStr
    }
    console.log(str.unique()) //102a
    
  3. Set也可以解决

    • Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

    • Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

    • 数组去重

    // Use to remove duplicate elements from the array
    const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
    console.log([...new Set(numbers)])
    console.log(Array.from(new Set(numbers)))
    // [2, 3, 4, 5, 6, 7, 32]
    
    • 对于 Set s, +0 (+0 严格相等于-0)和-0是不同的值。然而,在 ECMAScript 2015规范中这点已被更改。有关详细信息,请参阅浏览器兼容性 表中的“Key equality for -0 and 0”。
    • 另外,NaN和undefined都可以被存储在Set 中, NaN之间被视为相同的值(NaN被认为是相同的,尽管 NaN !== NaN)。
  4. 双重for循环

    var arr= [0,0,1,1,2,1,2,2,3,1,2,'a',3,'a'];
    
    function uniqueArr(array){
        var _arr = [];
        var isRepeat = true;
        for (var i = 0;  i < array.length; i++) {
            isRepeat = false;
            for (var j = 0;  j < _arr.length; j++) {
                if(_arr[j] == array[i]){
                    isRepeat = true;
                    break
                }
            }
            if(!isRepeat){
                _arr.push(array[i])
            }
        }
        return _arr;
    }
    console.log(uniqueArr(arr))
    
  5. filter+indexOf

    var arr= [0,0,1,1,2,1,2,2,3,1,2,'a',3,'a'];
    
    function uniqueArr(array){
        return array.filter(function(item,index){
            // array.indexOf(item) 只返回第一次item出现的位置
            return array.indexOf(item) === index
        });
    }
    console.log(uniqueArr(arr))
    
  6. forEach + indexOf/includes

    • indexOf与includes的比较

      • indexOf和includes都代表检测数组或字符串中是否包含某一个元素
      • 其中indexOf返回的是索引数值类型,而includes返回的是布尔类型
      • 数组中的indexOf不能判断数组中是否有NaN,而includes可以做到
      • 如果想查找某个元素在数组中的索引位置,就用indexOf
      • 如果想查找某个元素在数组中是否存在,就用includes
      • 两者都是字符串和数组共同的方法
    • 字符串的indexOf和数组中的indexOf的比较

      1. 这两个方法都可以接收两个参数
      2. 这两个方法在没有查找的指定的字符都返回-1
      3. 字符串中的indexOf中的第二个参数不支持负数而数组的indexOf支持
      4. 字符串的indexOf在传入参数不是字符串的情况下默认会转换为字符串而数组的indexOf不会进行数据类的转换
    • 字符串的includes和数组中的includes的比较

      1. 这两个方法都可以接收两个参数
      2. 这两个方法在没有查找的指定的字符都返回false
      3. 字符串中的includes中的第二个参数不支持负数而数组的includes支持
      4. 字符串的includes在传入参数不是字符串的情况下默认会转换为字符串而数组的includes不会进行数据类的转换
  7. Map

扁平化

flat([depth])-ES2019

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

depth 可选

  1. 指定要提取嵌套数组的结构深度,默认值为 1。
  2. 不可以是负数和0,必须从1开始,可以为数字字符串和true,它会隐式转换为数字
  3. 使用 Infinity,可展开任意深度的嵌套数组
  4. flat() 方法会移除数组中的空项
  • 替代方案
  1. 使用 reduce与concat展开一层数组

    var arr = [1, 2, [3, 4]];
    // 展开一层数组
    arr.flat();
    // 等效于
    arr.reduce((acc, val) => acc.concat(val), []);
    // [1, 2, 3, 4]
    // 使用扩展运算符 ...
    const flattened = arr => [].concat(...arr);
    
  2. 使用 reduce、concat 和递归展开无限多层嵌套的数组

    var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
    function flatDeep(arr, d = 1) {
      return d > 0
            ? arr.reduce((acc, val) => acc.concat(
                Array.isArray(val)
                      ? flatDeep(val, d - 1)
                      : val), []
            )
            : arr.slice();
    }
    flatDeep(arr1, Infinity);
    // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
    
  3. forEach+isArray+push+recursivity

    // forEach 遍历数组会自动跳过空元素
    const eachFlat = (arr = [], depth = 1) => {
      const result = []; // 缓存递归结果
      // 开始递归
      (function flat(arr, depth) {
        // forEach 会自动去除数组空位
        arr.forEach((item) => {
          // 控制递归深度
          if (Array.isArray(item) && depth > 0) {
            // 递归数组
            flat(item, depth - 1)
          } else {
            // 缓存元素
            result.push(item)
          }
        })
      })(arr, depth)
      // 返回递归结果
      return result;
    }
    
    // for of 循环不能去除数组空位,需要手动去除
    const forFlat = (arr = [], depth = 1) => {
      const result = [];
    // 函数内部使用立即执行函数进行递归
      (function flat(arr, depth) {
        for (let item of arr) {
          if (Array.isArray(item) && depth > 0) {
            flat(item, depth - 1)
          } else {
            // 去除空元素,添加非undefined元素
            item !== void 0 && result.push(item);
          }
        }
      })(arr, depth)
      return result;
    }
    
  4. 使用堆栈

    // 无递归数组扁平化,使用堆栈
    // 注意:深度的控制比较低效,因为需要检查每一个值的深度
    // 也可能在 shift / unshift 上进行 w/o 反转,但是末端的数组 OPs 更快
    var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
    function flatten(input) {
      const stack = [...input];
      const res = [];
      while (stack.length) {
        // 使用 pop 从 stack 中取出并移除值
        const next = stack.pop();
        if (Array.isArray(next)) {
          // 使用 push 送回内层数组中的元素,不会改动原始输入
          stack.push(...next);
        } else {
          res.push(next);
        }
      }
      // 反转恢复原数组的顺序
      return res.reverse();
    }
    flatten(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
    
    // 递归版本的反嵌套
    function flatten(array) {
      var flattend = [];
      (function flat(array) {
        array.forEach(function(el) {
          if (Array.isArray(el)) flat(el);
          else flattend.push(el);
        });
      })(array);
      return flattend;
    }
    
  5. Use Generator function 利用生成器函数

    • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/yield*
    function* flatten(array) {
        for (const item of array) {
            if (Array.isArray(item)) {
                yield* flatten(item);
                //yield* 表达式迭代操作数,并产生它返回的每个值。
            } else {
                yield item;
            }
        }
    }
    
    var arr = [1, 2, [3, 4, [5, 6]]];
    const flattened = [...flatten(arr)];
    // [1, 2, 3, 4, 5, 6]
    

flatMap-ES2020

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。

语法

var new_array = arr.flatMap(function callback(currentValue[, index[, array]]) {
    // return element for new_array
}[, thisArg])
  • map() 与 flatMap(), flatMap = Map+flat

遍历+扁平化

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// only one level is flattened
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]


// flatMap = Map+flat
const arr = ['123','456','789']
// 把它展开为单个数字的数组
// const newArr = arr.map((item)=>{
//     return item.split('')
// })
// 返回一个二维数组
// 0: (3) ['1', '2', '3']
// 1: (3) ['4', '5', '6']
// 2: (3) ['7', '8', '9']
// length: 3
const newArr = arr.map((item)=>{
    return item.split('')
}).flat()
console.log(newArr) //['1', '2', '3', '4', '5', '6', '7', '8', '9']

  • 单词拆分常见
// 为了更好的展示 flatMap 的作用,下面我们将包含几句话的数组拆分成单个词组成的新数组。
let arr1 = ["it's Sunny in", "", "California"];
arr1.map(x => x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]
arr1.flatMap(x => x.split(" "));
// ["it's","Sunny","in", "", "California"]
  • flatMap 能用于在map期间增删项目(也就是修改items的数量)
// Let's say we want to remove all the negative numbers and split the odd numbers into an even number and a 1
// 移除负数,并将偶数拆成1和另外一个元素
let a = [5, 4, -3, 20, 17, -33, -4, 18]
//       |\  \  x   |  | \   x   x   |
//      [4,1, 4,   20, 16, 1,       18]

a.flatMap( (n) =>
  (n < 0) ?      [] :
  (n % 2 == 0) ? [n] :
                 [n-1, 1]
)

// expected output: [4, 1, 4, 20, 16, 1, 18]
  • 实现-reduce() 与 concat()
var arr = [1, 2, 3, 4];
arr.flatMap(x => [x, x * 2]);
// is equivalent to
arr.reduce((acc, x) => acc.concat([x, x * 2]), []);
// [1, 2, 2, 4, 3, 6, 4, 8]

请注意,这是低效的,并且应该避免大型阵列:在每次迭代中,它创建一个必须被垃圾收集的新临时数组,并且它将元素从当前的累加器数组复制到一个新的数组中,而不是将新的元素添加到现有的数组中。

generator和iterator(生成器和迭代器)

var arr = [1,2,3]

7种-数组遍历的方法

  1. foreach-普通的数组遍历方法、for循环的优化
  2. map-映射->每一次遍历返回一个数组元素->返回一个新的数组
  3. filter-过滤->每一次遍历,返回bool,来决定当前元素是否纳入新的数组中
  4. reduce-归纳->每一次遍历将元素收入到容器中
  5. reduceRight->reduce的反向操作
  6. every-判定所有元素是否都符合一个条件
  7. some- 判定是否有一个或多个元素符合一个条件

以上底层都是for循环遍历:一次性对数组中的每一个元素进行操作、
我们希望遍历的过程是可控的(遍历的过程可停止,可继续)->迭代

  • 生成器和迭代器
    生成器:函数
    迭代器:生成器函数执行后返回的一个带有next方法的对象
    生成器对迭代器的控制是通过yield关键字来执行的
function * generator(){
    yield '姓名:小鱼';
    yield '年龄:34';
    yield '爱好:我爱js'
}
const itreator = generator();
console.log(itreator.next())
// {value: '姓名:小鱼', done: false}
  • 升级
const arr = ['姓名:小鱼','年龄:26','爱好:我爱js']
function * gen(arr){
    for(let i =0;i<arr.length;i++){
        yield arr[i]
    }
}
const itreator = gen(arr);
console.log(itreator.next())
  • 生成器的实现
    假如有n个元素,可以迭代n+1次,第n+1次的next返回{value:undefine,done:false}
const arr = ['姓名:小鱼','年龄:26','爱好:我爱js']
function gen(arr){
    var nextIndex = 0;
    return {
        next:function(){
            return nextIndex < length ? 
                    {value:arr[nextIndex++],done:false}:
                    {value:arr[nextIndex++],done:true}
        }
    }
}
const itreator = gen(arr);
console.log(itreator)
console.log(itreator.next())

foreach

for…of、可迭代对象、接口Symbol.iterator

for…of语句在可迭代对象上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

  • 可迭代对象包含
    1. Array->for (let value of iterable) / for (const value of iterable)
    2. String
    3. Map->for (let entry of iterable) / for (let [key, value] of iterable)
    4. Set
    5. arguments
    6. DOM 集合
    7. 实现Symbol.iterator接口的类数组
    8. 可以迭代生成器
  • 关闭迭代器
    对于for…of的循环,可以由break, throw continue 或return终止。在这些情况下,迭代器关闭。

for…of与for…in的区别

  • for…in 语句以任意顺序迭代对象的可枚举属性。
    for (let i in iterable) {
    if (iterable.hasOwnProperty(i)) {
        console.log(i); // logs 0, 1, 2, "foo"
      }
    }
    
    let iterable = [3, 5, 7];
    iterable.foo = 'hello';
    for (let i in iterable) {
      console.log(i); //  0, 1, 2, "foo"
    }
    //此循环仅以原始插入顺序记录iterable 对象的可枚举属性。它不记录数组元素3, 5, 7 或hello,因为这些不是枚举属性
    
  • for…of 语句遍历可迭代对象定义要迭代的数据。

  • of只能迭代数组
  • in可以迭代对象和数组
// 打印出对象的键值对
var o = {
    a:1,
    b:2,
    c:3
}

for(let k in o){
    console.log(k,o[k])
}
// 无法用of迭代对象 Uncaught TypeError: o is not iterable
// for(let k of o){
//     console.log(k)
// }

// 打印出数组的键值对1
const arr = [1,2,3]
for(let k in arr){
    console.log(k,arr[k])
}
// 打印出数组的键值对2
for(let k of arr){
    console.log(k)//let of 只能打印出值
}
// 打印出数组的键值对2 升级
const it = arr.entries();
for(let c of it){
    const [i,v] = c;
    console.log(c[0],c[1])
}

entries()-ES6

entries() 方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。

const arr = [1,2,3]
const it = arr.entries();
console.log(it) //数组的迭代器对象  next()=>{value:[indext,item],done:?}
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())//{value: undefined, done: true}

类数组、Symbol.iterator可迭代对象接口

类数组
数组实际上就是一个特殊的对象,key健名是一个从0开始递增的数字,所以我们可以用一个对象模拟数组形成类数组
解决类数组对象无法使用 of 进行迭代的问题

var o = {
    0:1,
    1:2,
    2:3,
    length:3
}

for(let v of o){
    console.log(v)
}
// err o is not iterable

// 解决1
var o = {
    0:1,
    1:2,
    2:3,
    length:3,
    [Symbol.iterator]:Array.prototype[Symbol.iterator]
}

for(let v of o){
    console.log(v)
}
// 是否是可迭代对象判断是否有继承于[Symbol.iterator]:Array.prototype[Symbol.iterator]的属性

// 解决2
Object.prototype[Symbol.iterator]=Array.prototype[Symbol.iterator]
var o = {
    0:1,
    1:2,
    2:3,
    length:3
}

for(let v of o){
    console.log(v)
}

// 解决3

var o = {
    0:1,
    1:2,
    2:3,
    length:3
}

for(let v of Array.from(o)){
    console.log(v)
}
  • mdn上的示例

. . . . 1、Array Iterator

var arr = ["a", "b", "c"];
var iterator = arr.entries();
console.log(iterator);

/*Array Iterator {}
         __proto__:Array Iterator
         next:ƒ next()
         Symbol(Symbol.toStringTag):"Array Iterator"
         __proto__:Object
*/

. . . . 2、iterator.next()

var arr = ["a", "b", "c"];
var iterator = arr.entries();
console.log(iterator.next());

/*{value: Array(2), done: false}
          done:false
          value:(2) [0, "a"]
           __proto__: Object
*/
// iterator.next()返回一个对象,对于有元素的数组,
// 是next{ value: Array(2), done: false };
// next.done 用于指示迭代器是否完成:在每次迭代时进行更新而且都是false,
// 直到迭代器结束done才是true。
// next.value是一个["key","value"]的数组,是返回的迭代器中的元素值。

. . . . 3、iterator.next方法运行

var arr = ["a", "b", "c"];
var iter = arr.entries();
var a = [];

// for(var i=0; i< arr.length; i++){   // 实际使用的是这个
for(var i=0; i< arr.length+1; i++){    // 注意,是length+1,比数组的长度大
    var tem = iter.next();             // 每次迭代时更新next
    console.log(tem.done);             // 这里可以看到更新后的done都是false
    if(tem.done !== true){             // 遍历迭代器结束done才是true
        console.log(tem.value);
        a[i]=tem.value;
    }
}

console.log(a);                         // 遍历完毕,输出next.value的数组

. . . . 4、二维数组按行排序

function sortArr(arr) {
    var goNext = true;
    var entries = arr.entries();
    while (goNext) {
        var result = entries.next();
        if (result.done !== true) {
            result.value[1].sort((a, b) => a - b);
            goNext = true;
        } else {
            goNext = false;
        }
    }
    return arr;
}

var arr = [[1,34],[456,2,3,44,234],[4567,1,4,5,6],[34,78,23,1]];
sortArr(arr);

/*(4) [Array(2), Array(5), Array(5), Array(4)]
    0:(2) [1, 34]
    1:(5) [2, 3, 44, 234, 456]
    2:(5) [1, 4, 5, 6, 4567]
    3:(4) [1, 23, 34, 78]
    length:4
    __proto__:Array(0)
*/

5、使用for…of 循环

var arr = ["a", "b", "c"];
var iterator = arr.entries();
// undefined

for (let e of iterator) {
    console.log(e);
}

// [0, "a"]
// [1, "b"]
// [2, "c"]

map

filter

reduce

some

enties

keys

values

toString

Array对象覆盖了Object的 toString 方法。
对于数组对象,toString 方法连接数组并返回一个字符串,其中包含用逗号分隔的每个数组元素。

const array1 = [1, 2, 'a', '1a'];
console.log(array1.toString());
// expected output: "1,2,a,1a"

fill(value[, start[, end])-ES6

用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引

  1. 区间前闭后开
  2. 返回的是对原数组的引用
  3. start=end=undefined:全部覆盖、start = undefined 相当于start=0、end = undefined相当于没有填写
  4. 非数字、NaN、null 结果都是一样的:如果start=end则不变,否则同上
const array1 = [1, 2, 3, 4];

// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// expected output: [1, 2, 0, 0]

// fill with 5 from position 1
console.log(array1.fill(5, 1));
// expected output: [1, 5, 5, 5]

console.log(array1.fill(6));
// expected output: [6, 6, 6, 6]

const newArr = array1.fill(6);
console.log(newArr===array1)//true 返回的数组是对原数组的引用

console.log(array1.fill())//用undefined填充
  • 数组转类数组
function makeArrayLike(arr){
    var arrLike = {
        length:arr.length,
        push:[].push
    }
    arr.forEach((item,index) => {
        [].fill.call(arrLike,item,index,index+1)
    });
    return arrLike;
}
const newobj = makeArrayLike(["a","b","c"]);
console.log(newobj)
  • 关于拷贝:Array.from是浅拷贝
function deepCopy(o){
    let arr = [];
    arr.push(o);
    return Array.from(arr)[0]
}
let p1 = {a:1};
let p2 = deepCopy(p1)
console.log(p1===p2)
  • 实现:
Array.prototype.myFill = function(){
    var value = arguments[0] || undefined,
        start = arguments[1] >> 0,
        end = arguments[2];
    
    if(this==null){
        throw new TypeError('this is null or undefined')
    }

    var obj = Object(this),
        len = obj.length >>> 0;
    
    start = start < 0 ? Math.max(len+start,0):Math.min(start,len);
    end = end === undefined ? len:end>>0;
    end = end < 0 ? Math.max(len+end,0):Math.min(end,len);

    while(start < end){
        obj[start] = value;
        start++;
    }
    return obj
}

[1,2,3].myFill('a',0,1) //(3) ['a', 2, 3]

相等性判断与Object.is方法-ES6

ES2015中有四种相等算法:

  1. 抽象(非严格)相等比较 (==)
  2. 严格相等比较 (=): 用于 Array.prototype.indexOf, Array.prototype.lastIndexOf, 和 case-matching。+0=-0
    js var arr = [+0,-0,0] console.log(arr.indexOf(0))//0
  3. 同值零相等: 用于 %TypedArray% 和 ArrayBuffer 构造函数、以及Map和Set操作, 并将用于 ES2016/ES7 中的String.prototype.includes
    js var s = new Set(); s.add(+0) s.add(-0) s.add(0) console.log(s) //Set(1) {0} //+0===-0
  4. 同值: 用于所有其他地方(object.is)
    可以理解为严格相等比较和相等比较都包含了同值零规则
    JavaScript提供三种不同的值比较操作:
  5. 严格相等比较 (也被称作"strict equality", “identity”, “triple equals”),使用 === ,
  6. 抽象相等比较 (“loose equality”,“double equals”) ,使用 ==
  7. 以及 Object.is (ECMAScript 2015/ ES6 新特性)

选择使用哪个操作取决于你需要什么样的比较。

简而言之,在比较两件事情时,

  1. 双等号将执行类型转换;

  2. 三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false );

  3. 而Object.is的行为方式与三等号相同,但是对于NaN和-0和+0进行特殊处理,所以最后两个不相同,而Object.is(NaN,NaN)将为 true。(通常使用双等号或三等号将NaN与NaN进行比较,结果为false,因为IEEE 754如是说.)

  4. 严格相等 ===
    全等操作符比较两个值是否相等,两个被比较的值在比较前都不进行隐式转换。
    如果两个被比较的值具有不同的类型,这两个值是不全等的。
    否则,如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值全等。
    最后,如果两个值都是 number 类型,当两个都不是 NaN,并且数值相同,或是两个值分别为 +0 和 -0 时,两个值被认为是全等的。

    对于数值,全等操作符使用略加修改的语义来处理两个特殊情况:
    第一个情况是,浮点数 0 是不分正负的。区分 +0 和 -0 在解决一些特定的数学问题时是必要的,但是大部分情况下我们并不用关心。全等操作符认为这两个值是全等的。
    第二个情况是,浮点数包含了 NaN 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。全等操作符认为 NaN 与其他任何值都不全等,包括它自己。(等式 (x !== x) 成立的唯一情况是 x 的值为 NaN)

  5. 非严格相等 ==
    相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。
    在转换后(等式的一边或两边都可能被转换),使用全等操作符 === 的比较方式
    参考MDN隐式类型转换

    一般而言,根据 ECMAScript 规范,所有的对象都与 undefined 和 null 不相等。
    但是大部分浏览器允许非常窄的一类对象(即,所有页面中的 document.all 对象),在某些情况下,充当效仿 undefined 的角色。
    当且仅当 A 效仿 undefined,A==undefined,在其他所有情况下,一个对象都不会等于 undefined 或 null。

    null == undefined//true
    0==+0==-0=="0"“false”
    [1,2]
    “1,2”
    new String(“foo”)==“foo”
    null == undefined
    除了零值外,以上在其他相等判断中都不相等

    有些开发者认为,最好永远都不要使用相等操作符。全等操作符的结果更容易预测,并且因为没有隐式转换,全等比较的操作会更快。(MDN)

    组件开发中最好使用非严格相等,比如 myWidth=1,myWidth=“1” 都应该是正确的

  6. 同值相等(同值相等由 Object.is-ES6 方法提供)
    同值相等解决了最后一个用例:确定两个值是否在任何情况下功能上是相同的。(这个用例演示了里氏替换原则的实例。)
    当试图对不可变(immutable)属性修改时发生出现的情况:

    // 向 Nmuber 构造函数添加一个不可变的属性 NEGATIVE_ZERO
    Object.defineProperty(Number, "NEGATIVE_ZERO",
                        { value: -0, writable: false, configurable: false, enumerable: false });
    
    function attemptMutation(v)
    {
    Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
    }
    

    Object.defineProperty 在试图修改不可变属性时,如果这个属性确实被修改了则会抛出异常,反之什么都不会发生。
    例如
    如果 v 是 -0 ,那么没有发生任何变化,所以也不会抛出任何异常。
    但如果 v 是 +0 ,则会抛出异常。不可变属性和新设定的值使用 same-value 相等比较。
    同值相等由 Object.is 方法提供。

    零值相等:与同值相等类似,不过会认为 +0 与 -0 相等,并且也认为NaN等于NaN.

  • Object.is的实现
Object.prototype.myIs = function(a,b){
    if(a===b){
        // 零值相等原则
        return a!==0 || 1/a===1/b;
        //注意 1/0=Infinity,1/-0=Infinity=>Infinity==Infinity=>Infinity===Infinity
    }
    // 同值相等(NaN等于NaN) 、而NaN!==NaN 
    return a!==a && b!==b;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值