Author: Senior development engineer of navigation Division of Wang Ming 360 Group

Overview of new features:

  • At (): Arrays support index queries

  • Array Group: Array element classification;

  • Array find from last: Array reverse query

  • Resizable and Growable Array Buffer: The Array Buffer supports size scaling

Before you begin, briefly describe the context.

These new features were incorporated into stagE3 and STAGe4 status by TC39, an official organization that promotes the advancement of the EMCAScript language. Some of the best proposals, such as Promises, arrow functions, etc., came from this organization. 360 Group, as a member of TC39, is participating in the EMCAScript language promotion activity, so I would like to share the latest language features with you here. Finally, explain stage3 status; Each proposal goes through four stages, stagE1 through STAGe4, before being released into the formal environment of the EMCAScript language. At the stage of StagE3, it was basically recognized by members of the community, and then it was supported by various browser manufacturers, Chrome, Firefox, Safari and so on. Stage4 is now available in all browsers and ready for us to use. This stage concept is usually encountered when configuring the Babel file;

{ "presets": ["es2015"."react"."stage-3"]}Copy the code

A proposal at ()

Arrays support indexed queries

In the past, when we used the [] syntax, we thought arrays and strings supported querying elements by index. Such as:

const arr = [1.2.3.4.5];

console.log(arr[4])  / / 5
Copy the code

It is misleading to think that arr[4] is the fourth element of the query array, but if we try arr[-1], we will find undefined. In EMCAScript, the [] syntax was originally designed to apply to all objects, such as arr[1], and actually only refers to properties of objects with a key of “1”, which any object can have. So writing code like arr[-1] looks valid, but it returns the value of the object’s “-1” attribute.

const arr = [1.2.3.4.5];

console.log(arr[-1])  // undefined
Copy the code

The at() proposal resolves that arrays, strings, and TypedArray cannot be queried directly through indexes. The proposal is currently in SATGE4 and implemented by major browsers. You can copy the code below and try it directly in the browser.

const arr = [1.2.3.4.5];

console.log(arr.at(-2))  / / 4
Copy the code

Comments: The use of arr[arr.Length-1] in past code would have been odd for beginners or developers of other languages, but arr.at(index) is obviously more semantic and a good proposal.

Proposal 2 Array Group

Used for sorting array elements

Add two methods to the Array prototype, groupBy and groupByToMap. The content is relatively simple, directly look at the use case can understand, here is not repeated.

  • groupBy

Use cases:

Sort the elements of the array by the number ’40’

let array = [23.56.78.42.11.49]
array.groupBy((item,index) = > {
    return item > 40 ? 'Greater than 40' : "Less than 40."
})
// {' less than 40 ': [56, 78, 42, 49], 'less than 40 ': [23,11]}
Copy the code

The groupBy method returns a new anonymous object whose key is the return value of the groupBy callback.

Without the groupBy proposal, we used to implement this:

Array.prototype.groupBy = function(callback) {
    const object = {};
    for(let i =0; i < this.length; i++) {
        let key = callback(this[i],i,this);
        if(object[key]) {
            object[key].push(this[i])
        } else {
            object[key] = [this[i]]
        }
    }
    return object;
}
Copy the code
  • groupByToMap

The groupByToMap method returns a regular Map with the key of the Map being the value returned by the groupBy callback.

Use cases:

const array = [1.2.3.4.5];
const odd  = { odd: true };
const even = { even: true };
array.groupByToMap((num, index, array) = > {
  return num % 2= = =0 ? even: odd;
});

// => Map { {odd: true}: [1, 3, 5], {even: true}: [2, 4] }
Copy the code

Without the groupByToMap proposal, we used to implement this:

Array.prototype.groupByToMap = function(callback) {
    const mapObject = new Map(a);for(let i =0; i < this.length; i++) {
        let key = callback(this[i],i,this);
        if(mapObject.get(key)) {
            mapObject.get(key).push(this[i])
        } else {
            mapObject.set(key,[this[i]])
        }
    }
    return mapObject;
}
Copy the code

Evaluation: In real development, arrays like [1, 2, 3, 4, 5] need to be redeclared to store even and odd numbers.

const obj = { odd: [1.3.5].even: [2.4]}Copy the code

The groupBy proposal returns a null-prototype object, {odd: [1, 3, 5], even: [2, 4]}, without declaring a named object. GroupByToMap returns a regular Map object, which is generally useful.

Proposal 3 Array Find from last

The method of finding an element from the end of an array to the first

Use cases:

In the old JS style, we want to query the elements of the array backwards.

Reverse the array once, then use find to query.

const array = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }];

[...array].reverse().find(n= > n.value % 2= = =1); 
// { value: 3 }
Copy the code

Or hang a method on the prototype to implement it:

Array.prototype.findLast = function(callback) {
    const len = this.length;
    for(let i = len - 1; i > 0; i--) {
        if(callback(this[i],i,this)) {
            return this[i]
        }
    }
    return -1
}

Copy the code

Array Find from Last

The proposed method, findLast, supports direct reverse lookup of arrays.

array.findLast(n => n.value % 2 === 1); // { value: 3 }
Copy the code

Evaluation: In terms of performance, there is one less array inversion, which is a really impressive optimization. Semantically, there should be a forward query should be reverse query, in line with people’s habits of thinking, generally more practical.

Proposal 4 Resizable and Growable Array Buffer

Extends the ArrayBuffer constructor to take an extra maximum length to allow the buffer to grow and shrink in place.

Before introducing the proposal, you need to take a look at what an ArrayBuffer is, what problems it solves, how and where it can be used. This will make it easier to understand the proposal. The following introduction to ArrayBuffer I have filtered out a lot of the details of the theory, and HOPE to be able to do so without prior knowledge of the students can also read ArrayBuffer at one time.

Array Buffer is a concept that you rarely see in your daily development, but it’s very relevant to us.

An easy understanding of Array buffers is that they allow EMCAScript to manipulate memory space. It was born out of a project, WebGL, which required frequent communication between the browser and the graphics card, whereas the traditional text transfer required the text ‘Hello word’ to be translated into 01010111… Machine languages like this were very performance-intensive, and it was necessary to communicate directly in binary form. Array buffers were born.

 const buffer = new ArrayBuffer(32);
Copy the code

This creates a 32-byte memory space called buffer. Some of the conventions, such as the generated ArrayBuffer, cannot be modified directly. Instead, they need to be modified through TypedArray views and DataView views. Uint32Array, DataView is a more flexible view for manipulating ArrayBuffer. Probably because it was originally designed to solve the problem of drawing images, it was called Various views, just remember.

The following are some of the data types supported by TypedArray views

The data type bytes meaning The corresponding C language type
Int8 1 An 8-bit signed integer signed char
Int16 2 16 – bit signed integer short
Int32 4 32 – bit signed integer int
// Generate 12 bytes of memory
const buffer = new ArrayBuffer(12);
// Read the memory space with the Int32Array view
const x1 = new Int32Array(buffer);
// Modify directly
x1[0] = 1;/ / 1
// Read the memory space with the Int8Array view
const x2 = new Uint8Array(buffer);
x2[0]  = 2;/ / 2
// the value of x1 changes
x1[0] / / 2
Copy the code

The above code creates two views of the same segment of memory: the 32-bit signed integer (Int32Array constructor) and the 8-bit unsigned integer (Uint8Array constructor). Since the two views correspond to the same memory segment, the modification of the underlying memory by one view affects the other view.

Some understanding of ArrayBuffer

An Array Buffer is a binary Array, or a constructor, but essentially a cache of zeros and ones that serves as a Buffer in memory. Simple understanding is the computer to run, is CPU 0 1 of binary data to be processed, but sometimes the binary data to more quickly and the CPU is in dealing with other tasks, that these data have to back to wait, if the CPU processing tasks quickly, but the data transfer is slow, have to let the CPU idle waiting for data, cause the waste of resources, An ArrayBuffer acts as a buffer memory space. Make this memory space always available for data, not for data to wait with the CPU.

Bottlenecks encountered by the ArrayBuffer

Existing ArrayBuffer there is a problem, when you’re ArrayBuffer space is not enough, need to increase the space you need to call ArrayBuffer a method on the prototype, ArrayBuffer. Prototype. The slice (), use the following

const buffer = new ArrayBuffer(8);
const newBuffer = buffer.slice(0.3);
Copy the code

The above code copies the first three bytes of the buffer object (starting at 0 and ending before the third byte), generating a new ArrayBuffer object.

The slice method actually consists of two steps. The first step is to allocate a new portion of memory and the second step is to copy the original ArrayBuffer object.

This approach works fine once, but in some drawing scenarios, frequently creating new memory and then copying the past can take a significant toll on performance.

Transform ArrayBuffer

That concludes ArrayBuffer and its current bottlenecks. Let’s talk about the proposal to enter stage3.

This proposal extends the constructor of ArrayBuffer by adding the configuration of maximum and minimum scaling length, maximumByteLength. Rewrite the transfer method, can be used to directly transfer the memory space, do not have to copy a copy of memory and move.

The modified ArrayBuffer constructor

class ArrayBuffer {
    //options indicates the configuration item
    //maximumByteLength Configures the memory scaling length
    constructor(byteLength,[ options ]);
    
    // Move the memory
    transfer(newByteLength);
    
    // Change the memory size
    resize(newByteLength);
    
    // Separate out a non-resizable ArrayBuffer
    slice(start, end);
    
    // Determine whether it can be scaled
    resizable();
    
    // Get the maximum memory size configured
    maximumByteLength();
    
    // Get the current memory size
    byteLength();
}
Copy the code

Use cases:

Declare a 1024 byte memory and set the maximum memory size to 1024 square

// Add a configuration item, maximumByteLength, to allow maximum memory length up to 1024 square
let rab = new ArrayBuffer(1024, { maximumByteLength: 1024 ** 2 });

// The current memory length is 1024 bytes
assert(rab.byteLength === 1024);

// Contains a maximum of 1024 square bytes
assert(rab.maximumByteLength === 1024 ** 2);

// The length can be changed in place
assert(rab.resizable);

// Change the memory size
rab.resize(rab.byteLength * 2);

// The memory length changed
assert(rab.byteLength === 1024 * 2);
Copy the code

Transfer is to move the memory

let ab = rab.transfer(1024);
Copy the code

It’s 1024 bytes starting at 0

assert(rab.byteLength === 0);
assert(rab.maximumByteLength === 0);
Copy the code

Above is the original memory (RAB) being separated and the memory cleared.

// Memory is transferred to ABassert(! ab.resizable); assert(ab.byteLength ===1024);
Copy the code

The memory was successfully transferred to AB

SharedArrayBuffer

SharedArrayBuffer is, as its name implies, an ArrayBuffer that can be shared

In the ArrayBuffer proposal, SharedArrayBuffer has also been changed to support memory growth.

Let’s take a look at what SharedArrayBuffer is.

There are some calculation tasks in EMCAScript that are usually placed in worker threads. Each worker thread is isolated from each other and communication between them needs to be completed via postMessage.

In addition to the number of strings, binary data can also be communicated, but the binary data needs to be copied and then used as postMessage to pass to another thread. The efficiency will be low when there is a large amount of data. Therefore, someone proposed SharedArrayBuffer that can be shared in ES2017. The main thread of JS and worker thread can share this memory space and simultaneously read and write data, avoiding performance loss caused by binary data replication.

In the actual scenario, a SharedArrayBuffer is created in the main thread and the memory address is sent to the worker thread via postMessage

/ / main thread
// create 1KB shared memory
const sharedBuffer = new SharedArrayBuffer(1024);
// The main thread sends the address of shared memory
w.postMessage(sharedBuffer);
Copy the code

The worker thread accepts the shared address —- SharedArrayBuffer from the host thread without having to copy the same binary address. Improved performance by sharing addresses.

/ / Worker thread
onmessage = function (ev) {
  // The data shared by the main thread is 1KB of shared memory
  const sharedBuffer = ev.data;
  // Create views on shared memory for easy reading and writing
  const sharedArray = new Int32Array(sharedBuffer);
  // ...
};
Copy the code

The ArrayBuffer proposal also extends SharedArrayBuffer to allow it to increase memory. Unlike ArrayBuffer, SharedArrayBuffer does not support reducing memory. Obviously, if you reduce memory, It is likely to affect other programs that are sharing the memory.

Here are the constructors for SharedArrayBuffer. The resize in the ArrayBuffer constructor, named grow, also shows the difference.

class SharedArrayBuffer {
    // Configure maximumByteLength like ArrayBuffer
    constructor(byteLength, [ options ]);
    
    // Memory can only be increased, not shortened
    grow(newByteLength);
    
    // Separate a non-growing SharedArrayBuffer.
    slice(start, end);

    // Determine whether memory can be expanded
    growable();
    
    // Get the maximum memory size
    maximumByteLength();
    
    // Get the current memory size
    byteLength();
  }
Copy the code

At present, the ArrayBuffer proposal encounters one obstacle. In real scenarios, there may be multiple threads that need to expand the memory. According to the current design, the memory needs to be occupied first and then expanded or reduced to the target memory, but in real scenarios, there may be insufficient memory, which will cause resource robbery. So the problem of how to allocate memory resources still needs to be solved.

The end of the

If you are interested in these proposals, please leave a comment.

So those are the four proposals.

If it is helpful to you, I hope every friend points like forwarding, your encouragement is we continue to promote the new features of JS language power.

Xiaoming

Censor: Jay