文章目录
基础
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 对象。
- arrayLike
-
它是静态方法,返回一个新的,浅拷贝的数组实例。
//字符串它是可迭代对象,它是由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示例
- 从 String 生成数组
//字符串它是可迭代对象,它是由string构造函数构造的
Array.from('foo');
// [ "f", "o", "o" ]
- 从 Set 生成数组
const set = new Set(['foo', 'bar', 'baz', 'foo']);
Array.from(set);
// [ "foo", "bar", "baz" ]
- 从 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'];
- 从类数组对象(arguments)生成数组
// 类数组可以正常使用,有length,并且key从0开始的顺序数字的
// length决定了数组的长度,key决定了填充该数组的位置
function f() {
return Array.from(arguments);
}
f(1, 2, 3);
// [ 1, 2, 3 ]
- 在 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);
- 序列生成器(指定范围)
// 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"]
- 数组去重合并,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)
- 原始数组不会被改变。
- begin 可选
- 提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。
- 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
- 如果省略 begin,则 slice 从索引 0 开始。
- 如果 begin 超出原数组的索引范围,则会返回空数组。
- 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(数组长度不会发生变化)
浅复制数组的一部分到
同一数组中的另一个位置
并返回它,
不会改变原数组的长度。
- [start,end) 之间的集合替换target,区间前闭后开
- target:复制的[start,end)区间从target开始替换
- end>len-1:取到 末尾
- target>len-1: 不发生任何替换
- target>start: 正常替换
- start或end是负数:start+len、end+len(可以理解为从数组末尾-1开始计算)
- 如果没有start:区间取整个数组区间
- 如果没有end:区间取到数组末尾
- 返回的是原数组的引用
//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"]
- 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])
- sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码
- 返回的是对原数组的引用
- 为什么要转换为字符串然后排序呢
如果仅限用一种类型的话,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
- 如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:
- 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
- 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。
- 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
- 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
- find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
- callback函数带有3个参数:当前元素的值、当前元素的索引,以及数组本身。callback返回值为布尔,如果返回true,结束遍历返回当前元素的值
- 如果是对象数组,返回的元素为对应数组中对象的引用
- 第二个参数thisArg执行回调时用作this的对象,如果没有第二个参数,在严格模式下this=undefined,非严格模式下this=window
- find会遍历稀疏数组的空隙empty,空隙处遍历的值由undefined占位,其他数组遍历方法不会遍历稀疏数组中的空隙,只有寻找find才有必要
- find在callback中不能更改当前元素的值,但可以改变数组本身,比如通过push添加数组元素
- find在第一次调用callback函数时会确定元素的索引范围,因此在 find方法开始执行之后添加到数组的新元素将不会被 callback函数访问到
- 如果数组中一个尚未被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
- 基本特性同find,只不过findIndex返回的是索引
- 如果没找到符合条件的数组,或 调用数组为空数组,返回-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 可选
- 从fromIndex 索引处开始查找 valueToFind。
- 如果为负值,则按升序从 array.length + fromIndex 的索引开始搜。如果array.length + fromIndex <0 则从0开始搜素
- 默认为 0。
- 如果 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;
}
});
}
迭代
数组和字符串的去重
-
对象中的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 }
-
也可以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
-
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)。
-
-
双重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))
-
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))
-
forEach + indexOf/includes
-
indexOf与includes的比较
- indexOf和includes都代表检测数组或字符串中是否包含某一个元素
- 其中indexOf返回的是索引数值类型,而includes返回的是布尔类型
- 数组中的indexOf不能判断数组中是否有NaN,而includes可以做到
- 如果想查找某个元素在数组中的索引位置,就用indexOf
- 如果想查找某个元素在数组中是否存在,就用includes
- 两者都是字符串和数组共同的方法
-
字符串的indexOf和数组中的indexOf的比较
- 这两个方法都可以接收两个参数
- 这两个方法在没有查找的指定的字符都返回-1
- 字符串中的indexOf中的第二个参数不支持负数而数组的indexOf支持
- 字符串的indexOf在传入参数不是字符串的情况下默认会转换为字符串而数组的indexOf不会进行数据类的转换
-
字符串的includes和数组中的includes的比较
- 这两个方法都可以接收两个参数
- 这两个方法在没有查找的指定的字符都返回false
- 字符串中的includes中的第二个参数不支持负数而数组的includes支持
- 字符串的includes在传入参数不是字符串的情况下默认会转换为字符串而数组的includes不会进行数据类的转换
-
-
Map
扁平化
flat([depth])-ES2019
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
depth 可选
- 指定要提取嵌套数组的结构深度,默认值为 1。
- 不可以是负数和0,必须从1开始,可以为数字字符串和true,它会隐式转换为数字
- 使用 Infinity,可展开任意深度的嵌套数组
- flat() 方法会移除数组中的空项
- 替代方案
-
使用 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);
-
使用 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]
-
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; }
-
使用堆栈
// 无递归数组扁平化,使用堆栈 // 注意:深度的控制比较低效,因为需要检查每一个值的深度 // 也可能在 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; }
-
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种-数组遍历的方法
- foreach-普通的数组遍历方法、for循环的优化
- map-映射->每一次遍历返回一个数组元素->返回一个新的数组
- filter-过滤->每一次遍历,返回bool,来决定当前元素是否纳入新的数组中
- reduce-归纳->每一次遍历将元素收入到容器中
- reduceRight->reduce的反向操作
- every-判定所有元素是否都符合一个条件
- 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语句在可迭代对象上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
- 可迭代对象包含
- Array->for (let value of iterable) / for (const value of iterable)
- String
- Map->for (let entry of iterable) / for (let [key, value] of iterable)
- Set
- arguments
- DOM 集合
- 实现Symbol.iterator接口的类数组
- 可以迭代生成器
- 关闭迭代器
对于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
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引
- 区间前闭后开
- 返回的是对原数组的引用
- start=end=undefined:全部覆盖、start = undefined 相当于start=0、end = undefined相当于没有填写
- 非数字、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中有四种相等算法:
- 抽象(非严格)相等比较 (==)
- 严格相等比较 (=): 用于 Array.prototype.indexOf, Array.prototype.lastIndexOf, 和 case-matching。+0=-0
js var arr = [+0,-0,0] console.log(arr.indexOf(0))//0
- 同值零相等: 用于 %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
- 同值: 用于所有其他地方(object.is)
可以理解为严格相等比较和相等比较都包含了同值零规则
JavaScript提供三种不同的值比较操作:- 严格相等比较 (也被称作"strict equality", “identity”, “triple equals”),使用 === ,
- 抽象相等比较 (“loose equality”,“double equals”) ,使用 ==
- 以及 Object.is (ECMAScript 2015/ ES6 新特性)
选择使用哪个操作取决于你需要什么样的比较。
简而言之,在比较两件事情时,
-
双等号将执行类型转换;
-
三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false );
-
而Object.is的行为方式与三等号相同,但是对于NaN和-0和+0进行特殊处理,所以最后两个不相同,而Object.is(NaN,NaN)将为 true。(通常使用双等号或三等号将NaN与NaN进行比较,结果为false,因为IEEE 754如是说.)
-
严格相等 ===
全等操作符比较两个值是否相等,两个被比较的值在比较前都不进行隐式转换。
如果两个被比较的值具有不同的类型,这两个值是不全等的。
否则,如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值全等。
最后,如果两个值都是 number 类型,当两个都不是 NaN,并且数值相同,或是两个值分别为 +0 和 -0 时,两个值被认为是全等的。对于数值,全等操作符使用略加修改的语义来处理两个特殊情况:
第一个情况是,浮点数 0 是不分正负的。区分 +0 和 -0 在解决一些特定的数学问题时是必要的,但是大部分情况下我们并不用关心。全等操作符认为这两个值是全等的。
第二个情况是,浮点数包含了 NaN 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。全等操作符认为 NaN 与其他任何值都不全等,包括它自己。(等式 (x !== x) 成立的唯一情况是 x 的值为 NaN) -
非严格相等 ==
相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。
在转换后(等式的一边或两边都可能被转换),使用全等操作符 === 的比较方式
参考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” 都应该是正确的
-
同值相等(同值相等由 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;
}