JavaScript Advanced Programming (4th edition) Reading Notes

Chapter 6 _ Collection reference types

This paper mainly

The Object and Array types are very important in our development and Js concepts

The Object,

The array,

ECMAScript 6’s new iterators and extension operators are particularly useful for collection reference types. This chapter describes 4 kinds: Set, Map, WeakSet, WeakMap, but I did not understand the book, and then I found a website:

JS Set, Map, WeakSet, WeakMap usage summary and difference

6.1 the Object

So far, most examples of reference values have used the Object type. Object is one of the commonly used types in ECMAScript. Although instances of Object don’t have much functionality, they are great for storing and exchanging data between applications.

There are two ways to explicitly create an instance of Object. The first is to use the new operator and the Object constructor, as follows:

let person = new Object(a); person.name ="Nicholas";
person.age = 29; 
Copy the code

Another approach is to use object literal notation. Object literals are shorthand for object definitions to simplify the creation of objects that contain a large number of attributes.

let person = {   
  name: "Nicholas".age: 29 ,
  5:true
}; 
// Note that numeric attributes are automatically converted to strings.
let person = {}; // An Object with the same default properties and methods as new Object()
Copy the code

Although attributes are generally accessed through point syntax, which is the convention of object-oriented languages, they can also be accessed using brackets. There is no difference between the two methods of accessing attributes. The main advantage of using brackets is that attributes can be accessed through variables. Alternatively, you can use the bracketed syntax if the attribute name contains a character that could cause a syntax error, or if it contains a keyword/reserved word

let propertyName = "name"; 
console.log(person[propertyName]); // "Nicholas" 
person["first name"] = "Nicholas"; 
//"first name" contains a space, so cannot be accessed using dot syntax. However, it is possible to include non-alphanumeric characters in attribute names, so you can access them using brackets syntax.
Copy the code

6.2 Array

6.2.1 Creating an Array

There are several basic ways to create arrays. One is to use the Array constructor. If you know the number of elements in the Array, you can pass a value to the constructor, and the Length property is automatically created and set to that value. You can also pass elements to the Array constructor to save. Such as:

let colors = new Array(a);let colors = new Array(20); 
let colors = new Array("red"."blue"."green");
// When using the Array constructor, you can also omit the new operator and get the same result.
let colors = Array(3);     // Create a wot array with three elements
wotlet names = Array("Greg"); // Create an array containing only one element, the string "Greg"
Copy the code

Another way to create arrays is to use array literal notation. Array literals contain a comma-separated list of elements in brackets:

let colors = ["red"."blue"."green"];  // Create an array of three elements
let names = [];                         // Create an empty array
let values = [1.2,];                    // Create an array of 2 elements
Copy the code

The Array constructor also has two new static methods for creating arrays in ES6: from() and of(). From () is used to convert a class array structure into an array instance, while of() is used to convert a set of parameters into an array instance. The first argument to array.from () is an array-like object, that is, any iterable structure, or one that has a length attribute and indexable elements.

// The string is split into a single character array
console.log(Array.from("Matt")); // ["M", "a", "t", "t"]  
 
// Collections and maps can be converted to a new array using from()
const m = new Map().set(1.2).set(3.4);
const s = new Set().add(1).add(2).add(3).add(4); 
 
console.log(Array.from(m)); // [1, 2], [3, 4]]
console.log(Array.from(s)); // [1, 2, 3, 4] 
 
// array.from () performs a shallow copy of an existing Array
const a1 = [1.2.3.4];
const a2 = Array.from(a1); 
console.log(a1);        // [1, 2, 3, 4
alert(a1 === a2); // false 
 
 
// You can use any iterable
const iter = {  
  *[Symbol.iterator]() {   
      yield 1;    
      yield 2;    
      yield 3;    
      yield 4; }};console.log(Array.from(iter)); // [1, 2, 3, 4] 
// Arguments objects can be easily converted to arrays
function getArgsArray() {  
  return Array.from(arguments); 
} 
console.log(getArgsArray(1.2.3.4)); // [1, 2, 3, 4] 
 
// From () can also convert custom objects with the necessary attributes
const arrayLikeObject = {   0: 1.1: 2.2: 3.3: 4.length: 4 }; 
console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4] 
Copy the code

Array.from() also receives a second optional mapping function argument. This function enhances the value of the new Array directly, rather than creating an intermediate Array like array.from ().map(). You can also accept a third optional argument that specifies the value of this in the mapping function. But the overridden this value does not apply in the arrow function.

const a1 = [1.2.3.4]; 
const a2 = Array.from(a1, x= > x**2); 
const a3 = Array.from(a1, function(x) {return x**this.exponent}, {exponent: 2}); console.log(a2);  // [1, 4, 9, 16] 
console.log(a3);  // [1, 4, 9, 16] 
Copy the code

Array.of() converts a list of arguments to an Array. This method is used to replace the pre-ES6 array.prototype.slice. call(arguments), which is an unusually clumsy way to convert arguments objects to arrays:

console.log(Array.of(1.2.3.4)); // [1, 2, 3, 4]
console.log(Array.of(undefined));  // [undefined] 
Copy the code

6.2.2 Array vacancies

When you initialize an array with an array literal, you can use a string of commas to create a hole. ECMAScript treats the values of the corresponding index positions between commas as empty Spaces, and the ES6 specification redefines how to handle these empty Spaces.

const options = [,,,,,]; // Create an array of 5 elements
console.log(options.length);   / / 5
console.log(options);          / / /,,,,,,,
Copy the code

The new methods and iterators in ES6 differ from the method behavior that existed in earlier versions of ECMAScript. The ES6 new method generally treats these empty Spaces as existing elements with a value of undefined:

const options = [1.5]; 
 
for (const option of options) {  
  console.log(option === undefined);  
} 
// false
// true 
// true
// true 
// false
Copy the code

Methods prior to ES6 ignored this void, but the behavior varies from method to method:

const options = [1.5]; 
 
// Map () skips empty positions
console.log(options.map(() = > 6));  // [6, undefined, undefined, undefined, 6] 
 
// join() treats empty positions as empty strings
console.log(options.join(The '-'));     1-5 "/ /"
Copy the code

6.2.3 Array Indexes

To get or set the value of an array, use brackets and provide a numeric index of the corresponding value

let colors = ["red"."blue"."green"];  // Define an array of strings
alert(colors[0]);                       // Display the first item
colors[2] = "black";                    // Modify the third item
colors[3] = "brown";                    // Add the fourth item
Copy the code

If the index is less than the number of elements contained in the array, the element stored at the corresponding location is returned. You set the value of the array in the same way, by replacing the value at the specified location. If you set a value to an index that exceeds the large index of the array, the array length automatically expands to that index value plus 1 (the index set in the example is 3, so the array length becomes 4). You can remove or add elements from the end of an array by modifying the Length attribute. Using the Length attribute makes it easy to add elements to the end of an array.

let colors = ["red"."blue"."green"]; // Create an array of three strings
colors.length = 2; 
alert(colors[2]);  // undefined 

let colors = ["red"."blue"."green"];  // Create an array of three strings
colors.length = 4; 
alert(colors[3]);  // undefined 

let colors = ["red"."blue"."green"];  // Create an array of three strings
colors[colors.length] = "black";        // Add a color (position 3)
colors[colors.length] = "brown";        // Add another color (position 4)
Copy the code

6.2.4 Checking arrays

The instanceof operator:

if (value instanceof Array) {// operate array}
Copy the code

ECMAScript provides the array.isarray () method

if (Array.isArray(value)){    // operate array}
Copy the code

6.2.5 Iterator method

In ES6, the Array prototype exposed three methods for retrieving the contents of an Array: keys(), values(), and entries(). Keys () returns an iterator for array indexes, values() returns an iterator for array elements, and entries() returns an iterator for index/value pairs:

const a = ["foo"."bar"."baz"."qux"]; 
 
// Because these methods all return iterators, their contents can be changed
// Convert directly from array.from () to an Array instance
const aKeys = Array.from(a.keys());
const aValues = Array.from(a.values());
const aEntries = Array.from(a.entries()); 
 
console.log(aKeys);     // [0, 1, 2, 3]
console.log(aValues);   // ["foo", "bar", "baz", "qux"] 
console.log(aEntries);  // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]] 

// Using ES6 deconstruction it is very easy to split key/value pairs in loops:
const a = ["foo"."bar"."baz"."qux"]; 
 
for (const [idx, element] of a.entries()) { 
  alert(idx);    
  alert(element); 
} 
/ / 0
// foo 
/ / 1
// bar 
/ / 2
// baz
/ / 3
// qux 
Copy the code

6.2.6 Copy and Fill methods

ES6 has two new methods: the bulk copy method copyWithin(), and the fill array method fill(). The function signature of both methods is similar in that you need to specify a range on the existing array instance, with the start index and no end index. Using this method does not change the size of the array.

Use the fill() method to insert all or part of the same values into an existing array. The start index is used to specify where to start the population, which is optional. If no closing index is provided, the population continues to the end of the array. Negative indexes are evaluated from the end of the array. You can also think of a negative index as the array length plus a positive index:

const zeroes = [0.0.0.0.0]; 
 // Fill the array with 5
zeroes.fill(5); 
console.log(zeroes);  // [5, 5, 5, 5, 5]
zeroes.fill(0);       / / reset
 
// Populate elements whose index is greater than or equal to 3 with 6
zeroes.fill(6.3); 
console.log(zeroes);  // [0, 0, 0, 6, 6] 
zeroes.fill(0);       / / reset
 
// Populate elements with an index greater than or equal to 1 and less than 3 with 7
zeroes.fill(7.1.3); 
console.log(zeroes);  // [0, 7, 7, 0, 0];
zeroes.fill(0);       / / reset
 
// Fill in elements with an index greater than or equal to 1 and less than 4 with 8
// (-4 + zeroes.length = 1)
// (-1 + zeroes.length = 4)
zeroes.fill(8, -4, -1); 
console.log(zeroes);  // [0, 8, 8, 8, 0];Fill () silently ignores index ranges beyond array boundaries, zero length, and in opposite directions:const zeroes = [0.0.0.0.0]; 
 
// Index too low, ignore zeroes. Fill (1, -10, -6);
console.log(zeroes);  // [0, 0, 0, 0, 0] 
 
// Index is too high, ignore zeroes. Fill (1, 10, 15);
console.log(zeroes);  // [0, 0, 0, 0, 0] 
 
// Index reverse, ignore zeroes. Fill (2, 4, 2);
console.log(zeroes);  // [0, 0, 0, 0, 0] 
 
Zeroes. Fill (4, 3, 10)
console.log(zeroes);  // [0, 0, 0, 4, 4] 
Copy the code

Unlike fill(), copyWithin() shallowly copies portions of the array in the specified range and inserts them at the beginning of the specified index. Start and end indexes are evaluated in the same way as fill() :

let ints,      
    reset = () = > ints = [0.1.2.3.4.5.6.7.8.9]; 
reset(); 
 
// Copy the contents of index 0 from ints and insert them at the beginning of index 5
// Stops when the source or destination index reaches the array boundary
ints.copyWithin(5); 
console.log(ints);  // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
reset(); 
 
// Copy the contents of index 5 from ints and insert them at the beginning of index 0
ints.copyWithin(0.5); 
console.log(ints);  // [5, 6, 7, 8, 9, 5, 6, 7, 8, 9] 
reset(); 
 
// Copy the contents from index 0 to index 3 from ints
// Insert at the beginning of index 4
ints.copyWithin(4.0.3); 
alert(ints);  // [0, 1, 2, 3, 0, 1, 2, 7, 8, 9] 
reset(); 
 
// The JavaScript engine copies the full range of values before interpolation
// Therefore, there is no risk of overwriting during replication
ints.copyWithin(2.0.6); 
alert(ints);  // [0, 1, 0, 1, 2, 3, 4, 5, 8, 9]
reset(); 
 
// Negative index values are supported, in the same way that fill() evaluates a positive index relative to the end of the array
ints.copyWithin(-4, -7, -3); 
alert(ints);  // [0, 1, 2, 3, 4, 5, 3, 4, 5, 6]CopyWithin () silently ignores index ranges beyond array boundaries, zero length, and in opposite directions:let ints,      
    reset = () = > ints = [0.1.2.3.4.5.6.7.8.9]; 
reset(); 
 
// Index too low, ignored
ints.copyWithin(1, -15, -12);
alert(ints);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset() 
 
// Index is too high, ignored
ints.copyWithin(1.12.15); 
alert(ints);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset(); 
 
// Index reversed, ignored
ints.copyWithin(2.4.2);
alert(ints);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset(); 
 
// The index part is available, copy and populate the available part
ints.copyWithin(4.7.10) 
alert(ints);  // [0, 1, 2, 3, 7, 8, 9, 7, 8, 9]; 
Copy the code

6.2.7 Conversion method

As mentioned earlier, all objects have toLocaleString(), toString(), and valueOf() methods.

let colors = ["red"."blue"."green"]; // Create an array of three strings
alert(colors.toString());   // red,blue,green 
alert(colors.valueOf());    // red,blue,green 
alert(colors);              // red,blue,green 
Copy the code

The toLocaleString() method may also return the same result as toString() and valueOf(), but not necessarily. When you call the array’s toLocaleString() method, you get a comma-separated string of array values. The only difference between it and the other two methods is that toLocaleString(), instead of toString(), is called for each value of the array in order to get the final string.

let person1 = {   
  toLocaleString() {   
    return "Nikolaos";  
  }, 
 
  toString() { 
    return "Nicholas"; }};let person2 = {  
  toLocaleString() { 
    return "Grigorios";     
  }, 
 
  toString() {     
    return "Greg"; }};let people = [person1, person2]; 
alert(people);                   // Nicholas,Greg 
alert(people.toString());        // Nicholas,Greg 
alert(people.toLocaleString());  // Nikolaos,Grigorios

let colors = ["red"."green"."blue"];
alert(colors.join(","));     // red,green,blue 
alert(colors.join("| |"));    // red||green||blue 
Copy the code

6.2.8 stack method

ECMAScript provides several methods for arrays that make it look like another data structure. Array objects can act like stacks, which are data structures that restrict insertion and deletion of items. The stack is a LIFO (last-in-first-out) structure, In which the most recently added items are deleted First. Insertion (called push, push) and deletion (called pop, pop) of data items occur only in one place on the stack, at the top. The ECMAScript array provides push() and POP () methods to implement stack-like behavior.

The push() method takes any number of arguments and adds them to the end of the array, returning the new length of the array. The pop() method is used to delete the last item of the array, reducing the length value of the array and returning the deleted item.

let colors = new Array(a);// Create an array
let count = colors.push("red"."green");  // Push two terms
alert(count);                             / / 2
 
count = colors.push("black");  // Push one more term
alert(count);                  / / 3
 
let item = colors.pop();       // Get the last item
alert(item);                   // black 
alert(colors.length);          / / 2
Copy the code

6.2.9 Queue method

Just as stacks restrict access to data structures In the form of LIFO, queues restrict access In the form of FIFO (first-in-first-out). Queues add data at the end of the list, but get data from the beginning of the list. Because of the push() method, which adds data to the end of the data, there is only one way to get data from the beginning of the array to simulate the queue. This array method is called shift(), and it removes the first item in the array and returns it, then the array length is reduced by one. With Shift () and push(), arrays can be used as queues:

let colors = new Array(a);// Create an array
let count = colors.push("red"."green");  // Push two terms
alert(count);                             / / 2
 
count = colors.push("black"); // Push one more term
alert(count);                 / / 3
 
let item = colors.shift();  // get the first item
alert(item);                // red 
alert(colors.length);       / / 2
Copy the code

ECMAScript also provides the unshift() method for arrays. As the name suggests, unshift() does the opposite of shift() : it adds any number of values to the beginning of the array and returns the new array length. By using unshift() and pop(), you can simulate the queue in the opposite direction, adding new data at the beginning of the array and fetching data at the end:

let colors = new Array(a);// Create an array
let count = colors.unshift("red"."green");  // Push two items from the beginning of the array

alert(count);                                / / 2
 
count = colors.unshift("black");  // Push one more term
alert(count);                     / / 3
 
let item = colors.pop();  // Get the last item
alert(item);              // green 
alert(colors.length);     / / 2
Copy the code

6.2.10 Sorting methods

Arrays have two methods that you can use to reorder elements: reverse() and sort(). The reverse() method, as the name suggests, arranges array elements in reverse. Such as:

let values = [1.2.3.4.5]; 
values.reverse(); 
alert(values);  / / 5,4,3,2,1
Copy the code

This method is intuitive, but not flexible enough, hence the sort() method. By default, sort() rearranges array elements in ascending order, with smaller values in front and larger ones behind. To do this, sort() calls the String() transformation function on each item and compares the strings to determine the order. Even if the array’s elements are all numeric, the array is converted to a string and then compared and sorted.

let values = [0.1.5.10.15]; 
values.sort(); 
alert(values);  / / 0,1,10,15,5
Copy the code

This is obviously not what we want, so the sort() method can accept a comparison function to determine which value should come first.

function compare(value1, value2) {  
  if (value1 < value2) {   
    return -1;  
  } else if (value1 > value2) {   
    return 1;  
  } else { 
    return 0; }}let values = [0.1.5.10.15]; 
values.sort(compare); 
alert(values);  / / 0,1,5,10,15
Copy the code

If the array element is a value, or an object whose valueOf() method returns a value (such as a Date object), the comparison function can be written even simpler, since the first value can then be subtracted directly from the second value:

function compare(value1, value2){  
  return value2 - value1; 
}
Copy the code

6.2.11 Operation Methods

There are a number of ways to manipulate elements in an array. For example, the concat() method can create a new array based on all the elements of an existing array. It creates a copy of the current array, appends its arguments to the end of the copy, and returns the newly constructed array. If one or more arrays are passed in, concat() adds each of those arrays to the result array. If the parameters are not arrays, they are appended directly to the end of the result array.

let colors = ["red"."green"."blue"];
let colors2 = colors.concat("yellow"["black"."brown"]); 
 
console.log(colors);   // ["red", "green","blue"] 
console.log(colors2);  // ["red", "green", "blue", "yellow", "black", "brown"] 
Copy the code

The behavior of flattening array parameters can be rewritten by specifying a special Symbol on the array of parameters: symbol.isconcat-spreadable. This symbol prevents concat() from flattening the argument array. Instead, setting this value to true forces a flat array object:

let colors = ["red"."green"."blue"];
let newColors = ["black"."brown"]; 
let moreNewColors = {   
  [Symbol.isConcatSpreadable]: true.length: 2.0: "pink".1: "cyan" 
}; 
 
newColors[Symbol.isConcatSpreadable] = false; 
 
// Forces the array not to be flattened
let colors2 = colors.concat("yellow", newColors); 
 
// Force a flat array object
let colors3 = colors.concat(moreNewColors); 
 
console.log(colors);   // ["red", "green", "blue"] 
console.log(colors2);  // ["red", "green", "blue", "yellow", ["black", "brown"]] console.log(colors3); // ["red", "green", "blue", "pink", "cyan"]
Copy the code

The slice() method is used to create a new array containing one or more elements of the original array. The slice() method can take one or two arguments: the start and end index of the element. If there is only one argument, slice() returns all elements of the index to the end of the array. If there are two arguments, slice() returns all elements from the start index to the end index, with no elements from the end index. Remember, this operation does not affect the original array

let colors = ["red"."green"."blue"."yellow"."purple"]; 
let colors2 = colors.slice(1); 
let colors3 = colors.slice(1.4); 
 
alert(colors2);  // green,blue,yellow,purple 
alert(colors3);  // green,blue,yellow 
Copy the code

The most powerful array method is splice(), which can be used in many ways. The main purpose of splice() is to insert elements in the middle of an array, but there are three different ways to use this method.

  • Delete it. Splice () needs to be passed two parameters: the location of the first element to delete and the number of elements to delete. You can remove any number of elements from an array, such as splice(0, 2), which removes the first two elements.
  • Insert. Splice () needs to be passed three arguments: the start position, 0 (number of elements to delete), and the element to insert, which can be inserted at the specified position in the array. The third argument can be followed by a fourth argument, a fifth argument, or any number of elements to be inserted. For example, splice(2, 0, “red”, “green”) inserts the strings “red” and “green” from array position 2.
  • Replacement. Splice () inserts new elements at specified positions while deleting elements, again passing in three arguments: the starting position, the number of elements to delete, and any number of elements to insert. The number of elements to be inserted is not necessarily the same as the number of elements to be deleted. For example, splice(2, 1, “red”, “green”) removes an element at position 2 and inserts “red” and “green” into the array starting at that position.

The splice() method always returns an array containing the elements that were deleted from the array (or an empty array if no elements were deleted)

let colors = ["red"."green"."blue"];
let removed = colors.splice(0.1);  // Delete the first item
alert(colors);                     // green,blue  
alert(removed);                    // red, an array with only one element
 
removed = colors.splice(1.0."yellow"."orange");   // Insert two elements in position 1
alert(colors);                                       // green,yellow,orange,blue 
alert(removed);                                      / / an empty array
 
removed = colors.splice(1.1."red"."purple");  // Insert two values and delete one element
alert(colors);                                   // green,red,purple,orange,blue
alert(removed);                                  // yellow, an array of one element
Copy the code

6.2.12 Search and Location methods

ECMAScript provides two types of methods for searching arrays: by strict equality and by assertion functions.

1. Strict equality

ECMAScript provides three strictly equal search methods: indexOf(), lastIndexOf(), and includes(). The first two methods are available in all versions, while the third method is new to ECMAScript 7. Each of these methods takes two parameters: the element to look for and an optional starting search location. The indexOf() and includes() methods search backwards from the beginning of the array (the first item), while lastIndexOf() searches forwards from the end of the array (the latter item).

IndexOf() and lastIndexOf() both return the position of the element being searched in the array, or -1 if none is found. Includes () returns a Boolean value indicating whether at least one item matching the specified element was found. When comparing the first argument with each item in the array, congruence (===) is used, meaning that the two items must be exactly equal.

let numbers = [1.2.3.4.5.4.3.2.1]; 
 
alert(numbers.indexOf(4));          / / 3
alert(numbers.lastIndexOf(4));      / / 5
alert(numbers.includes(4));         // true 
 
alert(numbers.indexOf(4.4));       / / 5
alert(numbers.lastIndexOf(4.4));   / / 3
alert(numbers.includes(4.7));      // false 
 
let person = { name: "Nicholas" }; 
let people = [{ name: "Nicholas" }]; 
let morePeople = [person]; 
 
alert(people.indexOf(person));      // -1 
alert(morePeople.indexOf(person));  / / 0
alert(people.includes(person));     // false 
alert(morePeople.includes(person)); // true 
Copy the code

2. Assert functions

ECMAScript also allows arrays to be searched by defined assertion functions, which are called for each index. The return value of the assertion function determines whether the element of the corresponding index is considered a match.

The assertion function takes three arguments: the element, the index, and the array itself. Where the element is the currently searched element in the array, the index is the index of the current element, and the array is the array being searched. The assertion function returns a true value indicating whether a match is made. The find() and findIndex() methods use assertion functions. Both methods start with a small index of the array. Find () returns the first matched element, and findIndex() returns the index of the first matched element. Both methods also accept an optional second argument that specifies the value of this inside the assertion function.

const people = [    
  {     name: "Matt".age: 27   }, 
  {     name: "Nicholas".age: 29}]; alert(people.find((element, index, array) = > element.age < 28)); 
// {name: "Matt", age: 27} 
 
alert(people.findIndex((element, index, array) = > element.age < 28)); 
/ / 0

// After a match is found, both methods stop searching. const evens = [2, 4, 6];
 
// After a match is found, the last element of the array is never checked
evens.find((element, index, array) = > {   
  console.log(element);   
  console.log(index);   
  console.log(array);   
  return element === 4; 
}); // 2 // 0 // [2, 4, 6] // 4/1 // [2, 4, 6]
Copy the code

6.2.13 Iterative method

ECMAScript defines five iterative methods for arrays. Each method takes two parameters: a function to run with each parameter, and an optional scoped object that serves as the context in which the function is run (affecting the value of this in the function). The function passed to each method takes three arguments: the array element, the element index, and the array itself. Depending on the method, the result of this function execution may or may not affect the method return value. The five iterating methods of an array are as follows.

  • Every () : Runs the passed function on each item of the array. This method returns true if it returns true for each function.
  • Filter () : Runs the function passed in for each item in the array. Items that return true are returned as arrays.
  • ForEach () : Runs the passed function on each item of the array, with no return value.
  • Map () : Runs the passed function on each item of the array, returning an array of the results of each function call.
  • Some () : Runs the passed function on each item of the array, returning true if one of the functions returns true.

None of these methods change the array from which they are called

Every () and some() are similar in that they search an array for elements that meet certain criteria. For every(), the function passed must return true for every item; Otherwise, it returns false. For some(), it will return true whenever there is an item that tells the passed function to return true.

let numbers = [1.2.3.4.5.4.3.2.1]; 
 
let everyResult = numbers.every((item, index, array) = > item > 2);
alert(everyResult);  // false 
 
let someResult = numbers.some((item, index, array) = > item > 2); 
alert(someResult);   // true 
Copy the code

The filter () method. This method determines whether an item should be included in the set of numbers it returns based on the given function. For example, to return an array where all values are greater than 2, use the following code:

let numbers = [1.2.3.4.5.4.3.2.1]; 
 
let filterResult = numbers.filter((item, index, array) = > item > 2); 
alert(filterResult);  / / 3,4,5,4,3
Copy the code

The map() method also returns an array. Each entry in this array is the result of running an incoming function on an element at the same position in the original array. For example, you can multiply each item in an array by 2 and return an array containing all the results, as follows:

let numbers = [1.2.3.4.5.4.3.2.1]; 
 
let mapResult = numbers.map((item, index, array) = > item * 2); 
 
alert(mapResult);  / / 2,4,6,8,10,8,6,4,2
Copy the code

The forEach () method. This method only runs the function passed in for each item and returns no value. In essence, the forEach() method is equivalent to iterating through a set of numbers using a for loop. Such as:

let numbers = [1.2.3.4.5.4.3.2.1]; 
 
numbers.forEach((item, index, array) = > {   // perform some operations});
Copy the code

6.2.14 Merging method

ECMAScript provides two merge methods for arrays: reduce() and reduceRight(). Both methods iterate over all the items in the set and build a final return value from that. The reduce() method iterates from the first item of the array to the last. ReduceRight () starts from the latter item and traverses to the first item.

Both methods take two parameters: a merge function that runs for each item, and an optional initial value that is the starting point for the merge. The functions passed to reduce() and reduceRight() take four arguments: the last merge value, the current item, the index of the current item, and the array itself. Any value returned by this function is taken as the first argument of the next call to the same function. If neither method is passed an optional second argument (as the starting value for merge), the first iteration starts with the second item of the array, so the first argument passed to the merge function is the first item of the array, and the second argument is the second item of the array.

let values = [1.2.3.4.5]; 
let sum = values.reduce((prev, cur, index, array) = > prev + cur); 
 
alert(sum);  / / 15
Copy the code

ReduceRight () is similar, but in reverse.

6.3 Stereotype Array

Typed arrays are new ECMAScript constructs designed to increase the efficiency of transferring data to native libraries. In fact, JavaScript does not have a “TypedArray” type; instead, it refers to a special array that contains numeric types. To understand how to use a stereotype array, it is necessary to understand its purpose.

6.3.1 history

The browser provider wanted to develop a JavaScript API that would take full advantage of the 3D graphics API and GPU acceleration to render complex graphics on the <canvas> element. There are two practices: 1, WebGl 2. Formalizing arrays

6.3.2 ArrayBuffer

Float32Array is actually a “view” that allows the JavaScript runtime to access a piece of pre-allocated memory called an ArrayBuffer. An ArrayBuffer is the base unit for all stereotype array and view references.

An ArrayBuffer() is a common JavaScript constructor that can be used to allocate a specific amount of byte space in memory. An ArrayBuffer cannot be resized once created. However, you can copy all or part of it into a new instance using slice().

const buf = new ArrayBuffer(16);  // Allocate 16 bytes in memory
alert(buf.byteLength);            / / 16

const buf1 = new ArrayBuffer(16); 
const buf2 = buf1.slice(4.12); 
alert(buf2.byteLength);  / / 8
Copy the code

ArrayBuffer is somewhat similar to C++ ‘s malloc(), but with a few notable differences.

  • Malloc () returns a null pointer on allocation failure. ArrayBuffer throws an error when allocation fails.
  • Malloc () can leverage virtual memory, so the large allocatable size is limited only by addressable system memory. The memory allocated by ArrayBuffer cannot exceed number.max_SAFE_INTEGER (253-1) bytes.
  • A successful call to malloc() does not initialize the actual address. Declaring an ArrayBuffer initializes all binary bits to 0.
  • Heap memory allocated by malloc() cannot be used by the system unless free() is called or the program exits. Heap memory allocated by declaring an ArrayBuffer can be treated as garbage collection instead of being freed manually.

6.3.3 DataView

The first view that allows you to read and write an ArrayBuffer is the DataView. This view is designed for file I/O and network I/O, and its API supports a high degree of control over buffered data, but also has poor performance compared to other types of views. DataView has no presets for buffered content and cannot iterate. You cannot create a DataView instance unless you are reading or writing to an existing ArrayBuffer. This instance can use all or part of the ArrayBuffer, and maintains a reference to the buffer instance, as well as the view’s starting position in the buffer.

const buf = new ArrayBuffer(16); 
 
DataView uses the entire ArrayBuffer by default
const fullDataView = new DataView(buf); 
alert(fullDataView.byteOffset);      / / 0
alert(fullDataView.byteLength);      / / 16
alert(fullDataView.buffer === buf);  // true 
 
// The constructor accepts an optional byte offset and byte length
// byteOffset=0 indicates that the view starts from the buffer
// byteLength=8 Limits the view to the first 8 bytes
const firstHalfDataView = new DataView(buf, 0.8); 
alert(firstHalfDataView.byteOffset);      / / 0
alert(firstHalfDataView.byteLength);      / / 8
alert(firstHalfDataView.buffer === buf);  // true 
 
// If not specified, the DataView will use the remaining buffer
// byteOffset=8 indicates that the view starts from the 9th byte of the buffer
// byteLength is not specified, default is residual buffer
const secondHalfDataView = new DataView(buf, 8);
alert(secondHalfDataView.byteOffset);      / / 8
alert(secondHalfDataView.byteLength);      / / 8
alert(secondHalfDataView.buffer === buf);  // true 
Copy the code

To read the buffer through the DataView, you need a few more components.

  • The first is the byte offset to read or write. You can think of it as some sort of “address” in DataView.
  • DataView should use ElementType to implement the conversion of JavaScript’s Number type to the binary format in the buffer.
  • Followed by the byte order of the value in memory. The default is big endian.

1. ElementType

DataView has no preset for the data types stored in the buffer. The exposed API forces developers to specify an ElementType when they read and write, and DataView faithfully converts it for read and write.

DataView exposes get and set methods for each of the types in the table above, which use byteOffset (byteOffset) to locate a value to read or write. Types are interchangeable, as shown in the following example:

// Allocate two bytes in memory and declare one
DataView const buf = new ArrayBuffer(2);
const view = new DataView(buf); 
 
// Indicates that all bits of the buffer are indeed zeros
// Check the first and second characters
alert(view.getInt8(0));  / / 0
alert(view.getInt8(1));  / / 0
// Check the entire buffer
alert(view.getInt16(0)); / / 0
 
// Set the entire buffer to 1
// The binary representation of 255 is 11111111 (2^ 8-1).
view.setUint8(0.255); 
 
// DataView automatically converts the data to a specific ElementType
// The hex representation of 255 is 0xFF
view.setUint8(1.0xFF); 
 
// Now the buffer is all 1's
// If it is a signed integer with a binary complement, it should be -1
alert(view.getInt16(0)); // -1 
Copy the code

2. The byte order

. DataView supports only two conventions: big-endian and small-endian. Big-endian byte order is also called “network byte order”, meaning that the high significant bits are stored in the first byte and the low significant bits in the next byte. The small endian byte order is reversed, that is, the low significant bits are stored in the first byte and the high significant bits in the next byte.

The native byte order of the system on which the JavaScript runs determines how bytes are read or written, but DataView does not adhere to this convention. DataView is a neutral interface for a piece of memory that follows the byte order you specify. All API methods of DataView take big-endian as the default, but accept an optional Boolean parameter that is set to true to enable small-endian.

// Allocate two bytes in memory and declare a DataView
const buf = new ArrayBuffer(2); 
const view = new DataView(buf); 
 
// Fill the buffer so that the first and last bits are both 1
view.setUint8(0.0x80); // Set the leftmost bit equal to 1
view.setUint8(1.0x01); // Set the rightmost bit to 1
 
// Buffer content (artificially added Spaces for easy reading)
// 0x8 0x0 0x0 0x1
// 1000 0000 0000 0001 
 
// Read Uint16 in big-endian order
// 0x80 is the high byte and 0x01 is the low byte
// 0x8001 = 2^15 + 2^0 = 32768 + 1 = 32769 
alert(view.getUint16(0)); / / 32769
 
// Read Uint16 in small endian order
// 0x01 is the high byte and 0x80 is the low byte
// 0x0180 = 2^8 + 2^7 = 256 + 128 = 384 
alert(view.getUint16(0.true)); / / 384
 
// Write the Uint16 in big-endian order
view.setUint16(0.0x0004); 
 
// Buffer content (artificially added Spaces for easy reading)
// 0x0 0x0 0x0 0x4
// 0000 0000 0000 0100 
 
alert(view.getUint8(0)); / / 0
alert(view.getUint8(1)); / / 4
 
// Write the Uint16 in small endian order
view.setUint16(0.0x0002.true); 
 
// Buffer content (artificially added Spaces for easy reading)
// 0x0 0x2 0x0 0x0
// 0000 0010 0000 0000 
 
alert(view.getUint8(0)); / / 2
alert(view.getUint8(1)); / / 0
Copy the code

3. Boundary case

DataView must have sufficient buffers to complete reads and writes, otherwise RangeError will be raised. DataView will do its best to convert a value to the appropriate type in the write buffer, with a backup of 0. If the conversion is not possible, an error is thrown.

6.3.4 Stereotype arrays

Stereotype arrays are another form of ArrayBuffer view. Although conceptually similar to DataView, stereotype arrays differ in that they are specific to an ElementType and follow the system’s native byte order. Stereotyped arrays, in turn, provide a broader API and higher performance. Stereotyped arrays are designed to improve the efficiency of exchanging binary data with native libraries such as WebGL. Because the binary representation of stereotyped arrays is an easy-to-use format for an operating system, the JavaScript engine can heavily optimize arithmetic, bitwise, and other common operations on stereotyped arrays, making them extremely fast to use.

You can create stereotyped arrays by reading existing buffers, using your own buffers, populating iterable structures, and populating stereotyped arrays of any type. In addition, <ElementType>.from() and <ElementType>.of() can also create stereotype arrays:

// Create a 12-byte buffer
const buf = new ArrayBuffer(12); 
// Create an Int32Array that references the buffer
const ints = new Int32Array(buf); 
// This stereotype array knows that each of its elements needs 4 bytes
// So the length is 3
alert(ints.length); // 3 javaScript
// Create an Int32Array of length 6
const ints2 = new Int32Array(6);
// Each value uses 4 bytes, so ArrayBuffer is 24 bytes
alert(ints2.length);             / / 6
// Like DataView, stereotype arrays also have a reference to the associative buffer
alert(ints2.buffer.byteLength);  / / 24
 
// Create an Int32Array containing [2, 4, 6, 8]
const ints3 = new Int32Array([2.4.6.8]);
alert(ints3.length);            / / 4
alert(ints3.buffer.byteLength); / / 16
alert(ints3[2]);                / / 6
 
Create an Int16Array by copying the value of ints3
const ints4 = new Int16Array(ints3); 
// This new type array allocates its own buffer
// Each value of the corresponding index is converted to the new format accordingly
alert(ints4.length);            / / 4
alert(ints4.buffer.byteLength); / / 8
alert(ints4[2]);                / / 6
 
// Create an Int16Array based on a normal array
const ints5 = Int16Array.from([3.5.7.9]);
alert(ints5.length);            / / 4
alert(ints5.buffer.byteLength); / / 8
alert(ints5[2]);                / / 7
 
// Create a Float32Array based on the parameters passed in
const floats = Float32Array.of(3.14.2.718.1.618);
alert(floats.length);            / / 3
alert(floats.buffer.byteLength); / / 12
alert(floats[2]);                / / 1.6180000305175781

// The constructor and instance of stereotyped arrays have a BYTES_PER_ELEMENT property that returns the size of each element in the array of that type:
 alert(Int16Array.BYTES_PER_ELEMENT);  / / 2
 alert(Int32Array.BYTES_PER_ELEMENT);  / / 4
 
const ints = new Int32Array(1),       
floats = new Float64Array(1);  
 
alert(ints.BYTES_PER_ELEMENT);        / / 4
alert(floats.BYTES_PER_ELEMENT);      / / 8

// If the stereotype array is not initialized with any value, its associated buffer is filled with 0:
const ints = new Int32Array(4); 
alert(ints[0]);  / / 0
alert(ints[1]);  / / 0
alert(ints[2]);  / / 0
alert(ints[3]);  / / 0
Copy the code

1. Stereotype array behavior

In many ways, stereotyped arrays are similar to regular arrays. Stereotyped arrays support the following operators, methods, and properties:

  • []
  • copyWithin()
  • entries()
  • every()
  • fill()
  • filter()
  • find()
  • findIndex()
  • forEach()
  • indexOf()
  • join()
  • keys()
  • lastIndexOf()
  • length
  • map()
  • reduce()
  • reduceRight()
  • reverse()
  • slice()
  • some()
  • sort()
  • toLocaleString()
  • toString()
  • values()

2. Merge, copy, and modify stereotype arrays

Stereotyped arrays also use array buffers to store data, and array buffers cannot be resized. Therefore, the following methods do not apply to stereotyped arrays:

  • concat()
  • pop()
  • push()
  • shift()
  • splice()
  • unshift()

Stereotype arrays also provide two new methods to quickly copy data outward or inward: set() and subarray(). Set () copies a value from the supplied or stereotype array to the index position specified in the current stereotype array:

// Create an int16 array of length 8
const container = new Int16Array(8);
// Copy the first four values of the stereotype array
// The offset defaults to index 0
container.set(Int8Array.of(1.2.3.4)); console.log(container);  Container. Set ([5,6,7,8], 4); console.log(container); / /,2,3,4,5,6,7,8 [1]
 
Container. Set ([5,6,7,8], 7); // RangeError
Copy the code

3. Underflow and overflow

Underflows and overflows of values in an array do not affect other indexes, but you still need to consider what type the elements of the array should be. Stereotyped arrays accept only one correlation bit for each index that can be stored, regardless of their effect on the actual value.

// An array of signed integers of length 2
// Each index holds a signed integer in the form of a binary complement
// Range is -128 (-1 * 2^7) ~127 (2^ 7-1)
const ints = new Int8Array(2); 
 
// An unsigned integer array of length 2
// Each index holds an unsigned integer
// The range is 0 to 255 (2^ 7-1)
const unsignedInts = new Uint8Array(2); 
 
// Overflowed bits do not affect adjacent indexes
// The index takes only 8 bits of the least significant bit
unsignedInts[1] = 256;      // 0x100 
console.log(unsignedInts);  / / [0, 0]
unsignedInts[1] = 511;      // 0x1FF
console.log(unsignedInts);  / / [0, 255]
 
// The overflowed bit is converted to its unsigned equal value
// 0xFF is binary 1 (truncated to 8 bits),
But 255 is an unsigned integer
unsignedInts[1] = -1        // 0xFF(truncated to 8 bits)
console.log(unsignedInts);  / / [0, 255]
 
// Overflow automatically becomes binary complement
// 0x80 is an unsigned integer of 128, which is the binary complement of -128
ints[1] = 128;        // 0x80 
console.log(ints);    // [0, -128] 
 
// Underflow automatically becomes binary complement //
0xFFIs an unsigned integer255Lambda is the binary complement of lambda1 
ints[1] = 255;        // 0xFF 
console.log(ints);    // [0, -1] 
Copy the code

6.4 the Map

A new feature of ECMAScript 6, Map is a new collection type that brings true key/value storage to the language. Most of Map’s features can be implemented with the Object type, but there are some subtle differences. Which one to use in specific practice is still worth careful screening.

6.4.1 basic API

const m = new Map(a);Copy the code

If you want to initialize the instance while creating it, you can pass the Map constructor an iterable containing an array of key/value pairs. Each key/value pair in the iterable is inserted into the new mapping instance in iteration order:

// Initialize the map with a nested array
const m1 = new Map([["key1"."val1"],   
  ["key2"."val2"],  
  ["key3"."val3"]]); alert(m1.size);/ / 3
 
// Initializes the map using a custom iterator
const m2 = new Map({[Symbol.iterator]: function* () {  
    yield ["key1"."val1"];    
    yield ["key2"."val2"];   
    yield ["key3"."val3"]; }}); alert(m2.size);/ / 3
 
// Map expected key/value pairs, whether provided or not
const m3 = new Map([[]]); 
alert(m3.has(undefined));  // true
alert(m3.get(undefined));  // undefined 
Copy the code

After initialization, you can add key/value pairs using the set() method. In addition, you can use get() and has() for queries, the size attribute to get the number of key/value pairs in the map, and delete() and clear() to delete values.

const m = new Map(a); alert(m.has("firstName"));  // false
alert(m.get("firstName"));  // undefined 
alert(m.size);              / / 0
 
m.set("firstName"."Matt")  .set("lastName"."Frisbie"); 
 
alert(m.has("firstName")); // true
alert(m.get("firstName")); // Matt 
alert(m.size);             / / 2
 
m.delete("firstName");     // Delete only one key/value pair
 
alert(m.has("firstName")); // false
alert(m.has("lastName"));  // true 
alert(m.size);             / / 1
 
m.clear(); // Clears all key/value pairs in this mapping instance
 
alert(m.has("firstName")); // false 
alert(m.has("lastName"));  // false 
alert(m.size);             / / 0The set() method returns an instance of the map, so you can concatenate multiple operations, including initialization declarations:const m = new Map().set("key1"."val1"); 
 
m.set("key2"."val2")   .set("key3"."val3"); 
 
alert(m.size); / / 3
Copy the code

Unlike Object, which can only use numeric values, strings, or symbols as keys, Maps can use any JavaScript data type as keys. Map uses the SameValueZero comparison operation internally (defined internally by the ECMAScript specification and not available in the language), essentially using strict object equality criteria to check key matches. Like Object, there is no limit to the value of the map.

const m = new Map(a);const functionKey = function() {};
const symbolKey = Symbol(a);const objectKey = new Object(a); m.set(functionKey,"functionValue");
m.set(symbolKey, "symbolValue");
m.set(objectKey, "objectValue"); 
 
alert(m.get(functionKey));  // functionValue 
alert(m.get(symbolKey));    // symbolValue
alert(m.get(objectKey));    // objectValue 
 
// SameValueZero comparison means that individual instances do not conflict
alert(m.get(function() {})); // undefined 
Copy the code

6.4.2 Sequence and Iteration

One major difference from the Object type is that Map instances maintain the insertion order of key-value pairs, so they can iterate based on the insertion order.

A mapping instance can provide an Iterator that generates an array of [keys, values] in insert order. This iterator can be obtained through the entries() method (or the symbol.iterator property, which references entries()) :

const m = new Map([["key1"."val1"],    
  ["key2"."val2"],  
  ["key3"."val3"]]); alert(m.entries === m[Symbol.iterator]); // true 
 
for (let pair of m.entries()) {  
  alert(pair); 
} // [key1,val1] // [key2,val2] // [key3,val3] 
 
for (let pair of m[Symbol.iterator]()) { 
alert(pair);
} // [key1,val1] // [key2,val2] // [key3,val3]Because entries() are the default iterator, we can extend maps directly to turn them into arrays:console.log([...m]); // [[key1,val1],[key2,val2],[key3,val3]] 
Copy the code

If, instead of iterators, you use the callback approach, you can call the mapped forEach(callback, opt_thisArg) method and pass in the callback, iterating over each key/value pair in turn. The incoming callback takes an optional second argument that overrides the value of this inside the callback:

const m = new Map([["key1"."val1"],  
  ["key2"."val2"],   
  ["key3"."val3"]]); m.forEach((val, key) = > alert(`${key} -> ${val}`)); 
// key1 -> val1 
// key2 -> val2 
// key3 -> val3 Keys () and values() return iterators that generate keys and values in insertion order, respectively:const m = new Map([["key1"."val1"],  
  ["key2"."val2"], 
  ["key3"."val3"]]);for (let key of m.keys()) {   alert(key); } // key1 // key2 // key3 
 
for (let key of m.values()) {   alert(key); } // value1 // value2 // value3
Copy the code

Keys and values can be modified during iterator traversals, but references inside the map cannot. Of course, this does not prevent you from modifying attributes inside objects that are keys or values, since this does not affect their identity within the mapping instance:

const m1 = new Map([["key1"."val1"]]);// The original value of the string as a key cannot be modified
for (let key of m1.keys()) {  
  key = "newKey";   
  alert(key);             // newKey   
  alert(m1.get("key1"));  // val1 
} 
 
const keyObj = {id: 1}; 
 
const m = new Map([   [keyObj, "val1"]]);// Changed the attributes of the object as the key, but the object still references the same value inside the map
for (let key of m.keys()) { 
  key.id = "newKey";   
  alert(key);            // {id: "newKey"}   
  alert(m.get(keyObj));  // val1
} 
alert(keyObj);           // {id: "newKey"} 
Copy the code

6.4.3 Selecting An Object or A Map

For most Web development tasks, the choice between Object and Map is a matter of personal preference, with little impact. However, for developers who care about memory and performance, there are significant differences between objects and maps.

  1. Memory footprint

The engineering-level implementation of Object and Map varies significantly between browsers, but the amount of memory used to store a single key/value pair increases linearly with the number of keys. Adding or removing key/value pairs in batches depends on the engineering implementation of the type of memory allocated by each browser. This varies by browser, but given a fixed size of memory, Map can store approximately 50% more key/value pairs than Object. 2. Insert performance The cost of inserting new key/value pairs into Object and Map is roughly the same, although Map is generally a little faster in all browsers. For both types, the insertion speed does not increase linearly with the number of key/value pairs. If the code involves a lot of inserts, then clearly the Map performs better. 3. Lookup speed is different from insertion. Finding key/value pairs from large objects and maps has minimal performance differences, but objects are sometimes faster if only a small number of key/value pairs are included. In cases where objects are used as arrays (such as contiguous integers as attributes), the browser engine can be optimized to use a more efficient layout in memory. This is not possible for Map. For both types, lookup speed does not increase linearly with the number of key/value pairs. If your code involves a lot of lookups, it might be better to choose Object in some cases. The performance of deleting the Object property with delete has long been criticized, and still is in many browsers. For this reason, there are some pseudo-deletions of object attributes, including setting the attribute value to undefined or NULL. But all too often, this is a nasty or inappropriate compromise. For most browser engines, Map’s delete() operation is faster than insert and lookup. If your code involves a lot of deletions, you should definitely choose Map.

6.5 WeakMap

WeakMap is a “sibling” type of Map and its API is also a subset of Map. The “weak” in WeakMap describes how the JavaScript garbage collector treats the key in the “weak map”.

6.5.1 basic API

const wm = new WeakMap(a);Copy the code

A key in a weak map can only be Object or of a type inherited from Object. Attempting to set a key with a non-object type raises TypeError. There is no restriction on the type of value. The method is similar to map.

Tactical fix packs for 6.5.2 weak bond

“Weak” in WeakMap means that the key of weak mapping is “held weakly”. This means that these keys are not formal references and do not prevent garbage collection. Note, however, that a reference to a value in a weak map is not “weakly held.” As long as the key exists, the key/value pair exists in the map and is treated as a reference to the value, so it is not garbage collected.

const wm = new WeakMap(a); wm.set({},"val"); 
The set() method initializes a new object and uses it as the key of a string.
// Since there is no other reference to the object, the object key will be garbage collected when this line of code completes.
// The key/value pair then disappears from the weak map, making it an empty map.
// In this case, since the value is also not referenced, the value itself becomes a target for garbage collection after the key/value pair is destroyed.
const wm = new WeakMap(a);const container = {   key: {}}; wm.set(container.key,"val"); 
 
function removeReference() {   container.key = null; } 
// This time, the Container object maintains a reference to a weakly mapped key, so the object key is not a target for garbage collection.
// However, if removeReference() is called, the next reference to the key object is destroyed and the garbage collector can clean up the key/value pair.
Copy the code

6.5.3 Non-iterable keys

Because key/value pairs in a WeakMap can be destroyed at any time, there is no need to provide the ability to iterate over their key/value pairs.

6.5.4 Using weak Mapping

1. Private variables

Weak mappings create a new way to implement truly private variables in JavaScript. The premise is clear: private variables are stored in a weak map, with the object instance as the key and the dictionary of the private member as the value.

const wm = new WeakMap(a);class User {  
  constructor(id) {    
    this.idProperty = Symbol('id');  
    this.setId(id);  
  } 
 
  setPrivate(property, value) {   
    const privateMembers = wm.get(this) | | {}; privateMembers[property] = value; wm.set(this, privateMembers);   
  } 
 
  getPrivate(property) {   
    return wm.get(this)[property]; 
  } 
 
  setId(id) {  
    this.setPrivate(this.idProperty, id);  
  } 
 
  getId() {   
    return this.getPrivate(this.idProperty); }}const user = new User(123);
alert(user.getId()); / / 123
user.setId(456); 
alert(user.getId()); / / 456
 
// Not really private
alert(wm.get(user)[user.idProperty]); / / 456
Copy the code

For the above implementation, the external code only needs to get the reference and weak mapping of the object instance to get the “private” variable. To avoid such access, WeakMap can be wrapped around a closure so that the WeakMap is completely isolated from the outside world.

2. DOM node metadata

Because the WeakMap instance does not interfere with garbage collection, it is a good fit for storing associated metadata.

const m = new Map(a);const loginButton = document.querySelector('#login'); 
 
// Associate some metadata with this node
m.set(loginButton, {disabled: true}); Suppose that after the above code is executed, the page has been changed by JavaScript and the original login button has been removed from the DOM tree. But because the reference to the button remains in the map, the corresponding DOM node remains in memory unless it is explicitly removed from the map or until the map itself is destroyed.const wm = new WeakMap(a);const loginButton = document.querySelector('#login'); 
 
// Associate some metadata with this node
wm.set(loginButton, {disabled: true}); If a weak mapping is used here, as shown in the following code, the garbage collector can immediately free the memory of the node as soon as it is removed from the DOM tree (assuming there is no other reference to the object) :Copy the code

6.6 the Set

ECMAScript 6’s new Set is a new collection type that brings collection data structures to the language. Sets are in many ways like enhanced maps because most of their apis and behaviors are common.

6.6.1 basic API

const m = new Set(a); Initialization time:// Initialize the collection with an array
const s1 = new Set(["val1"."val2"."val3"]); 
 
alert(s1.size); / / 3
 
// Initialize the collection with a custom iterator
const s2 = new Set({[Symbol.iterator]: function* () {  
    yield "val1";    
    yield "val2";     
    yield "val3"; }}); alert(s2.size);/ / 3
Copy the code

After initialization, you can add values using add(), query with has(), get the number of elements by size, and delete elements using delete() and clear() :

const s = new Set(a); alert(s.has("Matt"));    // false
alert(s.size);           / / 0
 
s.add("Matt")  .add("Frisbie"); 
 
alert(s.has("Matt"));    // true 
alert(s.size);           / / 2
 
s.delete("Matt"); 
 
alert(s.has("Matt"));    // false 
alert(s.has("Frisbie")); // true 
alert(s.size);           / / 1
 
s.clear(); // Destroy all values in the collection instance
 
alert(s.has("Matt"));    // false 
alert(s.has("Frisbie")); // false 
alert(s.size);           / / 0
Copy the code

Like a Map, a Set can contain any JavaScript data type as a value. Collections also use the SameValueZero operation (defined internally by ECMAScript and not available in the language), which basically amounts to a strict object equality standard for checking value matches.

6.6.2 Sequence and Iteration

Set maintains the order in which values are inserted, and therefore supports sequential iteration. A collection instance can provide an Iterator that generates the contents of the collection in insertion order. This iterator can be obtained via the values() method and its alias method keys() (or the symbol.iterator property, which references values()) :

const s = new Set(["val1"."val2"."val3"]); 
 
alert(s.values === s[Symbol.iterator]); // true
alert(s.keys === s[Symbol.iterator]);   // true 
 
for (let value of s.values()) {  
  alert(value);  
} // val1 // val2 // val3 
 
for (let value of s[Symbol.iterator]()) { 
  alert(value);
} // val1 // val2 // val3 Because values() is the default iterator, we can use the extension operation directly on the collection instance to convert the collection to an array:const s = new Set(["val1"."val2"."val3"]); 
 
console.log([...s]); // ["val1", "val2", "val3"] 
Copy the code

The entries() method of the collection returns an iterator that produces an array of two elements, repeated occurrences of each value in the collection, in insertion order.

6.6.3 Defining formal collection operations

In every way, Set and Map are similar, with a slight API adjustment. The only thing that needs to be emphasized is the simple manipulation of the collection API on itself. Many developers prefer to use the Set operation, but you need to implement it manually, either by subclassing Set or by defining a library of utility functions. To combine the two approaches, implement static methods on subclasses and then use them in instance methods. There are several things to consider when implementing these operations.

  • Some Set operations are associative so that the implemented methods can handle any number of collection instances.
  • Set preserves the insertion order, and the order of the collection returned by all methods must be maintained.
  • Use memory as efficiently as possible. The syntax of the extension operator is concise, but avoiding conversions between collections and arrays as much as possible can save on object initialization costs.
  • Do not modify existing collection instances. Union (a, b) or a.ion (b) should return a new collection instance with the result.

6.7 WeakSet

Weak type Set.

6.8 Iteration and Expansion operations

ECMAScript 6’s new iterators and extension operators are particularly useful for collection reference types. These new features make it easy to interoperate, copy, and modify collection types. As shown earlier in this chapter, there are four native collection types that define default iterators:

  • Array
  • All stereotype array
  • Map
  • Set

Chapter 7 covers iterators and generators in more detail.

6.9 summary

Objects in JavaScript are reference values, and you can create objects of a specific type from several built-in reference types.

  • Reference types are similar to classes in traditional object-oriented programming languages, but their implementation is different.
  • The Object type is a base type from which all reference types inherit their basic behavior.
  • The Array type represents an ordered set of values and provides the ability to manipulate and convert values.
  • Stereotype arrays contain a different set of reference types that manage the types of values in memory.
  • The Date type provides information about the Date and time, including the current Date and time and calculations.
  • The RegExp type is an interface to regular expressions supported by ECMAScript, providing most basic regular expressions as well as some advanced regular expression capabilities.

One of the things that makes JavaScript unique is that functions are actually instances of type Function, which means functions are also objects. Since functions are objects, they also have methods that enhance their behavior. Because primitive value wrappers exist, primitive values in JavaScript can have object-like behavior. There are three primitive value wrapper types: Boolean, Number, and String. They all have the following characteristics.

  • Each wrapper type maps to a primitive type of the same name.
  • When the raw value is accessed in read mode, the background instantiates a raw value wrapper object through which the data can be manipulated.
  • The wrapped object is destroyed as soon as the statement involving the original value is executed.

JavaScript also has two built-in objects that are present at the beginning of code execution: Global and Math. Of these, the Global object is not directly accessible in most ECMAScript implementations. However, browsers implement Global as a Window object. All Global variables and functions are properties of the Global object. The Math object contains properties and methods that aid in complex mathematical calculations.

ECMAScript 6 adds a new Set of reference types: Map, WeakMap, Set, and WeakSet. These types provide new capabilities to organize application data and simplify memory management.

JS Set, Map, WeakSet, WeakMap usage summary and difference

References:

JavaScript Advanced Programming (Version 4)