前言
什么决定了数组中的length
首先我们要探究到底数组的length的值是怎么确定的?
不手动修改数组中的length,访问length
其实length的值是与数组中的","数量及最后一个"," 后面到底你有没有填值进去相关 如果最后一个","后面你没有填值进去,length的值就是数组中","的数量 如果最后一个","后面你有填值进去(包括你填了null或者undefined),length的值就是数组中","的数量加一
console.log([1,2,3,4,,].length); //结果为5, 因为“,”数量为5,最后一个“,”后面没有填值,length的值为“,”的数量
console.log([1, 2, 3, 4,,null].length);//结果为6, 因为“,”数量为5,最后一个“,”后面填了null,length的值为“,”的数量加一
console.log([1, 2, 3, 4,,undefined].length);//结果为6, 因为“,”数量为5,最后一个“,”后面填了undefined,length的值为“,”的数量加一
console.log([1, 2, 3,,,].length)//结果为5, 因为“,”数量为5,最后一个“,”后面没有填值,length的值为“,”的数量
console.log([1, 2, 3,,,undefined].length)//结果为6, 因为“,”数量为5,最后一个“,”后面填了undefined,length的值为“,”的数量加一
关于empty
我们可以认为undefined和not defined其实不是一个东西,not defined可以认为是一个不合法的值,会被编译解析时更改为undefined或者不更改直接被编译解析时剔除出数组,而undefined或者null都是合法的值,而且empty是数组中特殊的undefined,我们自己赋值的undefined和编译器为我们赋值的undefined在数组中有不同的显示结果
当我们将数组定义为[1, 2, 3, ,,]时,可以认为我们定义后这数组中有三个not defined,但是编译时引擎会对这种情况进行判断处理:
1.判断除最后一个","之外所有的","的前后是不是有not defined,有的话将对应位置赋值为undefined并用empty进行标记显示,但如果单独访问对应的值会显示为undefined
2.对于最后一个","如果前面有not defined,将对应位置赋值为undefined并用empty进行标记显示,如果后面有not defined,则将该位置舍弃
console.log([1, 2, 3,,,]) //[1, 2, 3, empty × 2]
console.log([1, 2, 3,,,][3] === undefined); //true
那么如果手动增加undefined或者null会怎样显示?
console.log([1, 2, 3,undefined,undefined]) //[1, 2, 3, undefined, undefined]
console.log([1, 2, 3, null, null]) //[1, 2, 3, null, null]
换句话说自己加undefined和引擎给你加undefined是不一样的
手动改变length
手动改变length可以延长或者缩短数组的长度,增加长度让length值赋值为一个更大的值,可以认为是在编译解析好的原数组里的最后位置再增加若干个 "," 并将 "," 后面的值初始化为undefined
let a = [1, 2, 3, 4];
console.log(a); //[1, 2, 3, 4]
a.length = 5;
console.log(a); //[1, 2, 3, 4, empty]
缩短数组长度可以让length值赋值为一个比原来更小的值,可以认为是将编译解析好的原数组的第x个","开始所有的","(包括了这个x位置) 的后面所有的值都变为not defined作为标记,然后从数组中剔除,这个x的值就由这个length的值来确定,比如原来length为4,将length改为3,则是将第3个","开始所有的","(包括了这个3的位置)后面所有的值都变为not defined作为标记,然后从数组中剔除
let a = [1, 2, 3, 4];
console.log(a); //[1, 2, 3, 4]
a.length = 3;
console.log(a); //[1, 2, 3]
数组原型API
toString
Array.prototype.toString方法可以将数组中的各个属性值按照顺序并以","进行连接,通过字符串的形式进行展示,该方法也会对数组进行扁平化处理
console.log(Array.prototype.toString.call([1, 2, 3, 4, 5])); //"1, 2, 3, 4, 5"
console.log([1,[2,[[3,[4]],5]],null, [6,[7],8],,].toString()); //"1,2,3,4,5,,6,7,8,"
toString的实现与length的值相关,我们可以自己手写一个array_toString方法并将其绑定在Array.prototype上,该方法用数组调用的执行效果和数组原型上的toString一致:
function array_toString() {
let str = '';
if(this.length === 0){
return '';
}
for(let i = 0; i < this.length; i++) {
if(i !== this.length - 1){
if(Object.prototype.toString.call(this[i]) === "[object Array]"){
str += this[i].array_toString() + ',';
}else{
if(this[i] != undefined){
str += this[i] + ',';
}else{
str += ','
}
}
}else{
if(Object.prototype.toString.call(this[i]) === "[object Array]"){
str += this[i].array_toString();
}else{
if(this[i] != undefined){
str += this[i];
}
}
}
}
return str;
}
Array.prototype.array_toString = array_toString;
console.log([1,[2,[[3,[4]],5]],null, [6,[7],8],,].array_toString()); //"1,2,3,4,5,,6,7,8,"
forEach方法
手写一个forEach方法
function my_forEach(fn){
let _this = [];
for(let i = 0; i < this.length; i++){
_this[i] = fn(this[i], i);
}
return _this;
}
Array.prototype.my_forEach = my_forEach;
let a = [1, 2, 3];
let b = a.my_forEach((item, idx) => {
console.log(item, idx);
return item + 1;
});
console.log(b);
push方法
push的返回值是修改后的原数组的长度 手写一个自己的push方法
function my_push() {
let this_length = this.length;
for (let i = 0; i < arguments.length; i++) {
this[this_length + i] = arguments[i];
}
return this;
}
Array.prototype.my_push = my_push;
let a = [1, 2];
let b = a.my_push(3, 4, 5);
console.log(b);
console.log(a === b);
pop方法
pop方法返回值是删除的值 手写pop方法
function my_pop(){
if(this.length > 0){
this.length -= 1;
}
return this;
}
Array.prototype.my_pop = my_pop;
reverse方法
reverse方法返回的是原数组,也就是说操作的是原数组 手写reverse方法
function my_reverse(){
let reverse_arr = [];
if(!this.length){
return reverse_arr;
}
let last_idx = this.length - 1;
for(let i = this.length - 1; i >= 0; i--){
reverse_arr[last_idx - i] = this[i];
}
for(let i = 0; i < this.length; i++){
this[i] = reverse_arr[i];
}
return this;
}
Array.prototype.my_reverse = my_reverse;
unshift方法
unshift和push一样返回值是修改后的数组的长度 手写unshift方法
function my_unshift(){
let new_arr = [];
new_arr.length = this.length + arguments.length;
let new_arr_len = new_arr.length,
arg_len = arguments.length;
for(let i = 0; i < new_arr_len; i++){
if(i < arg_len){
new_arr[i] = arguments[i];
}else{
new_arr[i] = this[i - arg_len];
}
}
for(let i = 0; i < new_arr_len; i++){
this[i] = new_arr[i];
}
return this;
}
Array.prototype.my_unshift = my_unshift;
shift方法
shift方法和pop一样,返回值是删除的值 手写shift方法
function my_shift(){
if(!this.length){
return this;
}
let rev_arr = this.my_reverse(),
re_rev_arr = rev_arr.my_pop().my_reverse(),
re_rev_arr_length = re_rev_arr.length;
for(let i = 0; i < re_rev_arr_length; i++){
this[i] = re_rev_arr[i];
}
this.length -= 1;
return this;
}
Array.prototype.my_shift = my_shift;
sort方法
function my_sort (fn) {
for(let i = 0; i < this.length; i++){
for(let j = i;j < this.length; j++){
if(fn(this[i], this[j]) >= 0){
temp = this[i];
this[i] = this[j];
this[j] = temp;
// console.log(this[i]);
}
}
}
return this;
}
Array.prototype.my_sort = my_sort;
乱序方法
function compate(){
return Math.random() - 0.5;
}
console.log(a.my_sort(compate));
concat方法
concat方法中如果传入一维数组,可以认为是将数组的中的元素作为若干个参数进行传递,多维数组的话,只会去掉外面那一层,而且concat返回值是一个新的数组
let a = [1, 2, 3];
let b = a.concat(6,[4,5]);
console.log(a, b); //[1, 2, 3] [1, 2, 3, 6, 4, 5]
let c = [1, 2, 3];
let d = c.concat(6, [[4], 5]);
console.log(c, d); // [1, 2, 3] [1, 2, 3, 6, [4], 5]
slice方法
slice方法返回值是一个新数组,第一个参数是截取的开始位置,第二个参数是截取的结束位置(左闭右开区间)
let a = [1, 2, 3, 4, 5];
console.log(a.slice(2)); //[3, 4, 5]
console.log(a.slice(2, 4)); //[3, 4]
console.log(a.slice(4, 2)); //[]
console.log(a.slice(-2)); //[4, 5]
console.log(a.slice(-2, -1)); //[4]
let str = '123456';
console.log([].slice.call(str)); //["1", "2", "3", "4", "5", "6"]
function test() {
console.log([].slice.call(arguments)); //[1, 2, 3, 4, 5]
}
test(1, 2, 3, 4, 5);
splice方法
splice方法返回的是更改原数组从而生成的新数组,也就是说这个方法会更改原数组,所谓的更改就不只是删除,也包括增加 splice方法如果只有第一个参数,那截取的长度由第一个参数指定的位置决定,指定位置之后的参数全部从原数组截取掉然后进入新数组 splice方法如果没有传参或者第二个参数非法,都会返回空数组 如果传入的第一个参数是非法的,则会返回一个与原数组内容一样的新数组,同时原数组变为空数组
indexOf/lastIndexOf
Array.of 填充单个的值 Array.from 来源的是类数组 将类数组转换为真正的数组 Array.keys Array.values Array.entries Array.includes Array.copyWithin arr.fill(es6) 数组遍历的方法:forEach/filter/some/every/map/reduce/reduceRight find:参数为函数,返回值为第一个符合条件的元素 findIndex:参数为函数,返回值为第一个符合条件的元素的索引 forEach对于空数组,传入的函数并不会执行,稀疏数组中的empty项也不会导致函数的执行 这和for循环不一样 map映射关系,有返回值
let a = ['a', 'b', 'c'];
let b = [0, 1].map(function (item, idx, arr) {
return this[item];
}, a);
console.log(b);
some/every some/every返回值为布尔值