记录一些 易忘,不常用的一些知识
Array.isArray() 用来判断一个变量是否是数组
1 | var arr = [] |
除此之外还有五种方式判断一个变量是否是数组
1 | // 1.基于instanceof |
一般主流框架都是基于最后一种方式去判断
既然用这种方式能够判断是不是数组,那是不是能够判断字符串,数组,对象….?马上实验一波
1 | Object.prototype.toString.call('') // "[object String]" |
typeof
typeof 一般只返回 number
boolean
string
function(函数)
object(object,数组,对象)
undefined
Object.getOwnPropertyNames() 遍历方法
Object.getOwnPropertyNames(window)
结果可以看出,window下的方法有822个1
2
3
4
5
6
7
8
9
10
11[0 … 99]
[100 … 199]
[200 … 299]
[300 … 399]
[400 … 499]
[500 … 599]
[600 … 699]
[700 … 799]
[800 … 821]
length:822
__proto__:Array(0)
pop
shift
push
unshift
reverse
sort
splice
copyWithin
fill
这些都是会改变自身的值的数组方法
pop 删除数组的最后一个元素
有意思的是,pop的设计是根据长度进行删除的,也就是说如果在对象中声明了length的长度,也可以删除?
1 | var obj = {1:'javascrip',2:'css',3:'vue',length:4} |
shift 删除数组的第一个元素
同样和pop类似,根据长度删除
1 | var obj = {1:'javascrip',2:'css',3:'vue',length:4} |
push 在数组的末尾添加一个或多个元素 并且返回新的数组长度
同样是根据长度添加元素,那么可以在指定位置上添加元素?
1 | var obj = {1:'javascrip',2:'css',3:'vue',length:4} |
unshift 在数组开头添加一个或多个元素,并且返回新的数组长度
同样可以指定length位置上添加元素,如果使用Array.prorotype.unshift.call(),那么会替换掉第一个原色
但是如果length为0,那么就会被解释器认为数组长度为0,会从对象的下标0开始插入,相应的位置属性会被替换掉
1 | var obj = {1:'javascrip',2:'css',3:'vue',length:4} |
reverse 颠倒数组元素的位置 返回使用该函数的引用
同样可以Array.prorotype.unshift.call(),颠倒的顺序只在length的范围内
1 | var obj = {1:'javascrip',2:'css',3:'vue',length:2} |
sort 对数组的元素排序 并返回这个数组
arr.sort([comparefn])
sort可以指定一个带返回的comparefn函数,如果省略掉,数组元素就会按照各自转换为Unicode(万国码)位点顺序排序,比如javascript比css靠前,数字排序比较的时候,也会先转换成字符串的Unicode进行排序,比如:’25’比’8’靠前
1 | var obj = ['javascript','css','vue'] |
如果指定了带返回的comparefn函数,数组将按照该函数的返回值来排序,若a和b是两个将要比较的元素
1 | comparefn(a,b) > 0 // 如果b比a大,那么排在前面 |
如果数组元素为非ASCII字符的字符串(如包含类似 e、é、è、a、ä 或中文等非英文字符的字符串),则需要使用String.localCompare
1 | var arr = ['互','联','网','改','变','世','界']; |
sort 也同样适用Array.prorotype.unshift.call()
1 | var obj = {0:'互',1:'联',2:'网',3:'改',4:'变',5:'世',6:'界',length:4}; |
Chrome的不同
ECMAscript规范中并未规定具体的sort算法,这就势必导致各个浏览器不尽相同的sort算法,下面是Chrome下使用sort的表现:
1 | arr = [{s:'1',a:'a'},{s:'1',a:'b'},{s:'1',a:"c"},{s:'1',a:'d'},{s:'1',a:'e'},{s:'1',a:'f'},{s:'1',a:'g'},{s:'1',a:'h'},{s:'q',a:'i'},{s:'1',a:'j'},{s:'1',a:'k'}] |
由于s值相等,arr数组排序前后应该不变,然而Chrome输出的却不同,而其他浏览器(如IE 或 Firefox) 表现正常。
当排序的数组长度超过10条时,会调用另一种排序方法(快速排序);而10条及以下采用的是插入排序,所以在使用sort排序时改变排序的返回值即可,应该这么写才能将结果准确输出:
1 | arr = [{s:'1',a:'a'},{s:'1',a:'b'},{s:'1',a:"c"},{s:'1',a:'d'},{s:'1',a:'e'},{s:'1',a:'f'},{s:'1',a:'g'},{s:'1',a:'h'},{s:'q',a:'i'},{s:'1',a:'j'},{s:'1',a:'k'}] |
而且使用数组的sort的排序方法需要注意的是,各浏览器的针对sort方法的内部算法实现不同,排序函数尽量值返回-1,0,1三种不同的值,不要尝试返回true和false等其它数值,因为可能导致不可靠的排序结果
睡过 | 哈哈 | 请问 |
---|---|---|
123 | 123 | 123 |
不同的浏览器及脚本引擎
Browser Name | ECMAScript Engine |
---|---|
Internet Explorer 6 - 8 | JScript |
Internet Explorer 9 - 10 | Chakra |
Firefox | SpiderMonkey, IonMonkey, TraceMonkey |
Chrome | V8 |
Safair | JavaScriptCore(SquirrelFish Extreme) |
Opera | Carakan |
分析以下代码,预期将数组元素进行升序排序:
1 | var array = [7, 6, 5, 4, 3, 2, 1, 0, 8, 9]; |
代码中,comparefn 函数返回值为 bool 类型,并非为规范规定的 -1、0、1 值。那么执行此代码,各 JS 脚本引擎实现情况如何?
输出结果 | 是否符合预期 | |
---|---|---|
JScript | [2, 3, 5, 1, 4, 6, 7, 0, 8, 9] | 否 |
Carakan | [0, 1, 3, 8, 2, 4, 9, 5, 6, 7] | 否 |
Chakra & JavaScriptCore | [7, 6, 5, 4, 3, 2, 1, 0, 8, 9] | 否 |
SpiderMonkey | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | 是 |
V8 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | 是 |
根据表中数据可见,当数组内元素个数小于等于 10 时,现象如下:
- JScript & Carakan 排序结果有误
- Chakra & JavaScriptCore 看起来没有进行排序
- SpiderMonkey 返回了预期的正确结果
- V8 暂时看起来排序正确
将数组元素扩大至 11 位,现象如下:1
2
3
4
5var array = [7, 6, 5, 4, 3, 2, 1, 0, 10, 9, 8];
var comparefn = function (x, y) {
return x > y;
};
array.sort(comparefn);
JavaScript引擎 | 输出结果 | 是否符合预期 |
---|---|---|
JScript | [2, 3, 5, 1, 4, 6, 7, 0, 8, 9, 10] | 否 |
Carakan | [0, 1, 3, 8, 2, 4, 9, 5, 10, 6, 7] | 否 |
Chakra & JavaScriptCore | [7, 6, 5, 4, 3, 2, 1, 0, 10, 8, 9] | 否 |
IonMonkey | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | 是 |
V8 | [5, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10] | 否 |
根据表中数据可见,当数组内元素个数大于 10 时:
- JScript & Carakan 排序结果有误
- Chakra & JavaScriptCore 看起来没有进行排序
- SpiderMonkey 返回了预期的正确结果
- V8 排序结果由正确转为不正确
(感觉sort稍微复杂了点,主要还是ECMAscript规范中并未规定sort算法,所以导致每个浏览器的有不同的算法,不同的结果)
splice 新元素替换旧元素的方式修改数组,特别适用于需要维持原数组引用时,就地删除或者新增元素,splice最适合
arr.splice(start,deleteCount[, item1[, item2[, …]]])
start
指定从哪一位开始修改内容,如果超过了数组的长度,则从数组的末尾开始修改内容;
如果是负数,则其指定的索引位置等同于length+start(length为数组的长度),表示从数组的末尾开始的第-start位开始;
deleteCount
指定要删除的元素个数,若等于0,则不删除。这种情况下,至少应该添加一位新元素,若大于start之后的元素和,则start及之后的元素都被删除。
itemN
指定替换的删除后的元素或新增元素,如果忽略掉,则该方法只会删除元素
返回值
由原数组中被删除元素组成的数组,如果没有删除,则返回一个数组,比如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24var array = ["apple","boy"];
var splices = array.splice(1,1);
console.log(array); // ["apple"]
console.log(splices); // ["boy"] ,可见是从数组下标为1的元素开始删除,并且删除一个元素,由于itemN缺省,故此时该方法只删除元素
array = ["apple","boy"];
splices = array.splice(2,1,"cat");
console.log(array); // ["apple", "boy", "cat"]
console.log(splices); // [], 可见由于start超过数组长度,此时从数组末尾开始添加元素,并且原数组不会发生删除行为
array = ["apple","boy"];
splices = array.splice(-2,1,"cat");
console.log(array); // ["cat", "boy"]
console.log(splices); // ["apple"], 可见当start为负值时,是从数组末尾开始的第-start位开始删除,删除一个元素,并且从此处插入了一个元素
array = ["apple","boy"];
splices = array.splice(-3,1,"cat");
console.log(array); // ["cat", "boy"]
console.log(splices); // ["apple"], 可见即使-start超出数组长度,数组默认从首位开始删除
array = ["apple","boy"];
splices = array.splice(0,3,"cat");
console.log(array); // ["cat"]
console.log(splices); // ["apple", "boy"], 可见当deleteCount大于数组start之后的元素总和时,start及之后的元素都将被删除
同样,splice可以用在Array.prototype.splice.call()上
1 | arr = {1:'javascript',2:'css',3:"jq",length:'2'} |
如果需要删除数组中一个已存在的元素,可参考如下:
1 | var array = ['a','b','c']; |
copyWihtnin 基于ECMAScript 2015 (es6)规范,用于数组内元素之间的替换,即替换的元素和被替换的元素均是数组内的元素。(更简单的来说就是将一个数组内的元素替换到 别的位置及属性)
语法:arr.copyWithin(target, start[, end = this.length])
1 | arr.copyWithin(target) |
target
0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。
如果 target 大于等于 arr.length,将会不发生拷贝。如果 target 在 start 之后,复制的序列将 被修改以符合 arr.length。
start
0 为基底的索引,开始复制元素的起始位置。如果是负数,start 则其指定的索引位置等同于length+start,将从末尾开始计算。
如果 start 被忽略,copyWithin 将会从0开始复制。
end
0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。
如果 end 被忽略,copyWithin 将会复制到 arr.length。(但是我觉得因为换成:如果 end 被忽略,那么end默认为数组的索引长度,)
copyWithin
方法不要求其this值必须是一个数组对象;除此之外,copyWithin是一个可变方法,它可以改变this对象本身,并且返回它,而不仅仅是它的拷贝。
MDN的例子很好的说明了:
1 | [1, 2, 3, 4, 5].copyWithin(-2); |
不支持copyWithin,可以使用polyfill1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63if (!Array.prototype.copyWithin) {
Array.prototype.copyWithin = function(target, start/*, end*/) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-8.
var relativeTarget = target >> 0;
var to = relativeTarget < 0 ?
Math.max(len + relativeTarget, 0) :
Math.min(relativeTarget, len);
// Steps 9-11.
var relativeStart = start >> 0;
var from = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 12-14.
var end = arguments[2];
var relativeEnd = end === undefined ? len : end >> 0;
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 15.
var count = Math.min(final - from, len - to);
// Steps 16-17.
var direction = 1;
if (from < to && to < (from + count)) {
direction = -1;
from += count - 1;
to += count - 1;
}
// Step 18.
while (count > 0) {
if (from in O) {
O[to] = O[from];
} else {
delete O[to];
}
from += direction;
to += direction;
count--;
}
// Step 19.
return O;
};
}
copyWithin也同样可以用Array.prototype.copyWithin.call()
1 | var o = {0:1, 1:2, 2:3, 3:4, 4:5,length:5} |
fill 用一个固定值替换数组内从起始索引到终止索引直接的全部元素
arr.fill(value, start, end)
value替换数组元素的值
start起始索引,默认为0,如果是个负数,则开始索引为length+start
end 终止索引,默认为数组索引的长度,如果是个负数,则终止索引为length+end
1 | [1, 2, 3].fill(4) // [4, 4, 4] |
不支持fill可以使用polyfill1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44if (!Array.prototype.fill) {
Object.defineProperty(Array.prototype, 'fill', {
value: function(value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;
// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
}
});
}
在MDN上有一句:fill 方法故意被设计成通用方法, 它需要this值是个对象,类数组对象调用会报错
但是发现 fill 也同样适用于Array.prototype.fill.call()
1 | var o = {0:1, 1:2, 2:3, 3:4, 4:5,length:5} |
与copyWithin不同的是,copyWithin是只能数组之间替换元素
而fill是指定一个固定值然后替换掉数组相应的数组索引
参考资料1:http://louiszhai.github.io/2017/04/28/array
参考资料2:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array