The thing about converting a pseudo-array to a real array

I just encountered a problem. The actual parameter accepted by arguments is a list, and what I get is an array-like thing, so I want to achieve infinite parameter summation. When traversing the array summation, I used forEach traversal, but reported an error. , which is why, the code is as follows:

sum(1,2,3,4,5);
function sum () {
    
    
var total = 0;
arguments.forEach(v =>{
    
    
total += v;
})
return total()
}

After executing the code, the error is reported as follows:
insert image description here
Since the arguments get something similar to an array, why does the forEach method report an error? Finally, I searched on Baidu for a long time,In fact, it is not a real array, but an array-like or pseudo-array, which is something similar to an array.

Let's take a look at what is a pseudo-array?
1. Let's take a look at the method arguments used to accept actual parameters. The execution code is as follows:

function fu() {
    
    
console.log(arguments);
}
fu(1,2,3,4,5,)

The console prints out as follows:
insert image description here
Here you can see that the Arguments display also has square brackets [1,2,3,4,5...], but there are some other methods behind;There is also a length attribute, but there are no array push, pop and other methods, like an array is not an array.

2. You can also print to see other things such as getElementsByClassName:

<template>
  <div></div>
  <div></div>
  <div></div>
</template>
<script>
   var oDivs = document.getElementsByTagName('div')
   console.log(oDivs)
</script>

The console prints out as follows:
insert image description here
Here you can see that HTMLCollection also displays square brackets [1,2,3,4,5…], but there are some other methods behind;There is also a length attribute, but there are also no array push, pop and other methods, like an array is not an array.

So in a brief summary, pseudo-arrays have the following characteristics:
1. With the length attribute, you can get the length;
2. With the subscript index, you can traverse through the length attribute to get all the values.
3. However, the built-in methods of arrays cannot be used.

Then we have to study why pseudo-arrays cannot use the built-in methods and properties of arrays?

First of all, from the [[prototype]] marked in the above figure, you can see that the prototype of the arguments pseudo-array points to the Object object and then print the real array. You can see that the
insert image description here
proto of the real array points to the Array array.
It can also be seen from the above figure [[prototype]] You can see that the prototype of the pseudo-array obtained by obtaining the page element through the tag name points to the HTMLCollection object

So far, we can also see that the prototype of the pseudo-array is different, in fact, it is a different object, and the elements of the array are Array, which is different from the essence of the real array, which is why the pseudo-array cannot use the properties and methods of the array. The reason, that is to say, if you don't want to use array methods such as forEach, pop, etc., you must convert them into real arrays to use the various methods of arrays.

The next step is to study how to convert the pseudo-array into a real array:

The following is the code performance stage:
Method 1: Traversal: Create an empty array, loop through the pseudo-array, and put the traversed data in the empty array one by one

伪数组:
var ali = document.getElementsByTagName('li');
console.log(ali);       // [li, li, li, li]
// ali.push("hello");      // TypeError: ali.push is not a function
...........................................................................................分割线
转为真数组:
var arr = [];           // 先创建空数组
for(var i=0;i<ali.length;i++){
    
      // 循环遍历伪数组
    arr[i] = ali[i];    // 取出伪数组的数据,逐个放在真数组中
}

arr.push("hello");
console.log(arr);       // [li, li, li, li, "hello"]

Method 2: By calling the slice interception method of Array. []. slice. call()

	const divs = document.querySelectorAll('div');
	console.log(divs instanceof Array);
	const arr = [].slice.call(divs);
	console.log(arr instanceof Array);
	// false
	// true

If you want to know how to achieve conversion through the slice method, you need to know the internal implementation principle of the slice method of the array. Let me simply simulate the slice method.

	function slice(start,end){
    
    
  		const startIndex = start || 0;
  		const endIndex = end || this.length;
  		let tempArr = [];
  		for(let i = startIndex;i < endIndex;i++){
    
    
          tempArr.push(this[i]);
  		}
  		return tempArr;
	}

The this inside is the key to convert the pseudo-array into a real array. Bind the this inside to the pseudo-array object through the call() method, and then push each item of the pseudo-array into a real array through a loop and return this true array.

Method 3: Use the slice method: use the slice method of the Array prototype object, cooperate with apply, and point this in the slice to the pseudo-array. Array.prototype.slice.call()
The third method is also the interception method by calling the slice of Array, and the difference between the second and third methods is analyzed in the summary below.

伪数组:
var ali = document.getElementsByTagName('li');
console.log(ali);       // [li, li, li, li]
// ali.push("hello");      // TypeError: ali.push is not a function
..........................................................................................分割线
转为真数组:
var arr = Array.prototype.slice.apply(ali);

arr.push("hello");
console.log(arr);       // [li, li, li, li, "hello"]

Method 4: Use the from method of Array provided by ES6 /There are compatibility issues with this method。ES6 Array.form()

伪数组:
var ali = document.getElementsByTagName('li');
console.log(ali);       // [li, li, li, li]
// ali.push("hello");      // TypeError: ali.push is not a function
........................................................................................分割线
转换为真数组:
var arr = Array.from(ali);

arr.push("hello");
console.log(arr);       // [li, li, li, li, "hello"]

Method 5: Use the expansion operator provided by ES6 (…)/There are compatibility issues with this method

伪数组:
var ali = document.getElementsByTagName('li');
console.log(ali);       // [li, li, li, li]
// ali.push("hello");      // TypeError: ali.push is not a function
...........................................................................................分割线
转换为真数组:
var arr = [...ali];
arr.push("hello");
console.log(arr);       // [li, li, li, li, "hello"]

Method 6: Copying the prototype: copy the proto of the pseudo-array as the prototype of the Array.But this approach has limitations

  • Manually create objects with indices and lengths, as pseudo-arrays
var obj = {
    
    
    0:"a",
    1:"b",
    2:"c",
    length:3
}
console.log(obj);       // {0: "a", 1: "b", 2: "c", length: 3}
// obj.push();          // TypeError: obj.push is not a function

obj.__proto__ = Array.prototype;

console.log(obj);       // ["a", "b", "c"]
obj.push("hello");
console.log(obj);       // ["a", "b", "c", "hello"]
  • arguments also apply
function fn(){
    
    
    var arg = arguments;
    console.log(arg);       // ["a", "b", ...]
    // arg.push("hello");   // TypeError: arg.push is not a function

    arg.__proto__ = Array.prototype;

    arg.push("hello");
    console.log(arg);       // ["a", "b", "hello", ...]
}
fn("a","b");
  • The element set returned by the selector is not applicable, because even if the prototype of the element set is changed to an array prototype (as shown in Figure 5), the element set itself is read-only and cannot be modified
var ali = document.getElementsByTagName('li');
console.log(ali);            // [li, li, li, li]
// ali.push("hello");        // TypeError: ali.push is not a function

ali.__proto__ = Array.prototype;

// ali.push("hello");        // Index property setter is not supported

Figure 5
But it doesn't mean that it can be used without modifying the method of the original array (note the above picture, there is no length attribute)

ali.forEach(val => {
    
    
    console.log(val);
});

// 会发现浏览器没执行,原因是此时ali缺少length或length为0
console.log(ali.length);    // 0

The length of ali needs to be obtained in advance, and then manually set after modifying the prototype

var ali = document.getElementsByTagName('li');
console.log(ali);            // [li, li, li, li]
// ali.push("hello");        // TypeError: ali.push is not a function

var len = ali.length;        // 获取初始length
ali.__proto__ = Array.prototype;
ali.length = len;            // 设置给修改原型之后的数组对象

// ali.push("hello");        // Index property setter is not supported
ali.forEach(val => {
    
    
    console.log(val);        // 遍历打印数组中的值
});
console.log(ali.length);     // 4

Tip: The element array returned by the selector uses the copy prototype method, and the length property needs to be manually set, and the method that will change the original array cannot be used.

Summarize:

  • The difference between [].slice.call() and Array.prototype.slice.call() is that it is more efficient to search through Array.prototype.
  • To call the slice method through [], you need to find the slice method through the prototype chain.
  • When destructuring and assigning manually created pseudo-array objects, the iterator interface needs to be added.

The above is the editor’s understanding and solutions for converting pseudo-arrays to real arrays. Some methods are inquired. If there are mistakes in the text, please leave a message to correct me...

Guess you like

Origin blog.csdn.net/wen15191038073/article/details/126470189