preface

ECMAScript 6.0 (ES6), the language standard for the next generation of JavaScript, was officially released in June 2015. It has been more than three years since it was released. However, due to the wide range of syntax contained in IT, it will take some time to fully digest it. Use scenarios, hope to help you

This article will focus on complementations to the syntax features of ES6. It will not cover some of the API level syntax, but more on exploring the underlying principles, and what problems does ES6 solve

If there is any mistake, you are welcome to point it out. We will revise it at the first time. Suggestions and suggestions are welcome

Without further ado, start your ES6 journey

Let /const

Let,const is used to declare variables. It is used in place of the var keyword in the old syntax. Unlike var, let/const creates a block-level scope (colloquial meaning a new scope within a curly brace).

Here the external console.log(x) cannot pick up the first two block-level scope declarations let:

In everyday development, block-level scopes are often created using the if/for keyword combined with let/const. It is worth noting that for loops that use the let/const keyword are somewhat different from var declarations

For loop is divided into three parts, the first part contains a variable declaration, the second part contains a loop exit criteria, the third part contains each loop finally to perform expression, that is to say, the first part in the for loop will only perform a var I = 0, and the two parts at the time of each loop will execute it again

In addition to creating the block-level scope, let/const binds it to each loop, ensuring that the value at the end of the last loop is reassigned

What does that mean? In short, each loop is declared once (var for is declared only once). This is the way to think about for loops in let/const

Create a block-level scope for each loop:

Temporary dead zone

Variables declared with let/const are enclosed from the start and cannot be used until they are declared. This feature is also designed to compensate for the shortcoming of var (which has variable enhancement).

Let /const also has the same effect, but is different from var

  • Var is initialized at creation time and assigned to undefined

  • Let /const is created when it enters the block-level scope for promotion reasons, but is not initialized until the declaration is executed. If a variable is not assigned to a let, it is assigned to undefined by default, while const must be assigned during initialization. The code snippet between creation and initialization forms a temporary dead zone

Quote from a blog translation of ES6 standards

Variables declared by let/const are created when the Lexical Environment they contain is instantiated, but can only be accessed after the LexicalBinding has been evaluated

Back to the example, since the let variable name is declared, the code is precompiled before the if statement, creating the block-level scope, the lexical environment, the name variable (not initialized), and then the code is executed. The variable is initialized only when the let name statement is run and is assigned undefined by default, but a temporary dead zone causes the name variable to be used before the declaration statement is run, so an error is reported

The above example, because use the var statement variables, there will be variable to ascend, also occurred in the precompiled stage, var will ascend to the top of the current function scope and the default value is undefined, if this is a few lines of code in the global scope, the name variable will directly promote to the global scope, then execute the code into the execution stage ,name is assigned the value “ABC “, and the string ABC can be successfully printed

This is equivalent to

The temporary dead zone is intended to prevent ES5 from using the variable before it is declared. This is because the variable improvement feature of var causes some developers unfamiliar with var to take it for granted that the variable can be used first in the declaration, thus causing some problems

About JS precompile and JS 3 scope (global, function, block level) here also don’t repeat, otherwise can write thousands of words blog, interested friends to understand themselves, also help to understand JavaScript this language

const

Declare a constant using the const keyword. Constant means a variable that does not change. Some differences between const and let are

  1. If a variable is declared as const, it must be assigned; otherwise, an error will be reported. Similarly, if a variable declared as const is modified, an error will be reported

  1. A const declaration variable cannot be changed, and if a reference type is declared, its memory address cannot be changed.

Some people wonder why block-level scopes are not explicitly declared in everyday development, and let/const variables are not made global

This is also a let/const feature. ES6 states that they are not properties of top-level global variables

As you can see, the let variable x is in a scope called script, and the var variable is promoted to the global variable window object. This allows us to use the new syntax without worrying about polluting the global window object

advice

In daily development, my advice is to fully embrace the let/const, general use variable declarations let keyword, and when declare some configuration items (similar to the interface address, NPM depend on package, pager once the default number of pages and some other statement after he won’t change the variables) can use const, when other developers to explicitly tell the project, This variable cannot be changed (use all uppercase for const declarations, and underline between words). It is also recommended that you be aware of the flaws of the var keyword (promoting variables, polluting global variables, etc.) so that you can use the new syntax better

Arrow functions (common)

ES6 allows functions to be defined using arrows (=>)

Arrow functions have the following differences for functions created using the function keyword

  1. Arrow function does not have arguments (suggest better syntax, remaining operator instead)

  2. Arrow functions have no Prototype property and cannot be used as constructors (cannot be called with the new keyword)

  3. The arrow function does not have its own this, its this is lexical, it refers to the context this, When you write this line of code, the “this” function is bound to the “this” context in which the arrow is executed. (This does not mean that the “this” function is completely static, because the “this” context is still dynamic and can be modified using call,apply, or bind. Of this,

Because setTimeout pushes an anonymous callback function onto an asynchronous queue, and the callback function is global, that is, in non-strict mode this points to window, there is a problem with losing the variable a, whereas if you use the arrow function, At the time of writing it was determined that its this equals its context (here is the execution context of makeRequest’s function, Since the makeRequest function is called by the controller object, this refers to a variable in the Controller object

The “this” point of an arrow function cannot be changed even if call,apply, or bind is used.

advice

The arrow function replaces the previous need to explicitly declare a variable to hold this, making the code much cleaner

ES5 writing:

ES6 arrow function:

Let’s do another example

It is important to note that function after makeRequest cannot use the arrow function, because then it will use the upper layer of this, which is the global execution context, and the value of this will point to window, so the variable a will not be found and undefined will be returned

Using the arrow function in the array iteration is more concise and omits the return keyword

Do not use the arrow function in functions that may change the point of this, such as Vue’s methods,computed, and lifecycle functions. Vue binds this to the vm instance of the current component, and using the arrow function forces this to change. Because the arrow function has the highest priority (can’t change the point using call,apply,bind)

Before using arrow functions as a grammar for everyday development, my advice is to understand how arrow functions are bound to this, rather than just omit the word function, which is what ECMAScript is really trying to solve

The iterator iterator

Iterator is a very important ES6 concept that many people don’t know much about, but it underlies the implementation of four other common ES6 features (deconstruct assignment, residual/extension operators, generators, and for of loops). Understanding iterators helps you understand the other four core syntax principles. In addition, ES6’s new Map,Set data structure also uses it, so I put it in the first

For iterable data destruction, ES6 internally deploys a [Symbol. Iterator] property, which is a function that returns an iterator (also called an iterator object), The iterator object [Symbol. Iterator] property is called the iterator interface. Data structures with this interface are considered iterable

The Symbol. Iterator method in an array (the iterator interface) is deployed on the array prototype by default:

By default, the iterator interface is deployed in the following data structures. Note that normal objects do not have an iterator interface by default (you can create your own iterator interface to allow normal objects to iterate).

  • Array
  • Map
  • Set
  • String
  • TypedArray (array of classes)
  • The Arguments object for the function
  • The NodeList object

Iterator An iterator is an object that has a next method so it can be called this way

The next method returns an object with two properties: value and done. Value is the value returned after each iteration, and done indicates whether the loop needs to be repeated. As you can see, when value is undefined, done is true to terminate the loop

comb

  • Iterable data structures have a [Symbol. Iterator] method
  • [Symbol. Iterator] returns an iterator object after execution
  • Iterator objects have a next method
  • Executing the next method once (consuming an iterator) returns an object with the value,done properties

Borrow Hu feather blog in ES5 implementation of iterators can be more profound understanding of how to generate and consume iterators

Deconstruct assignment (common)

Destructuring assignment can directly use a property of an object, without using the form of property access. Personally, object destructuring principle is to find the same property name of the original object, and then assign the value of this property name to the corresponding property of the new object

So what we’re really declaring on the left is titleOne,titleTwo, and we’re looking for the title in the object on the right and the title in test[0], Find the strings ABC and test and assign to titleOne,titleTwo (returns undefined if not found)

The principle of array destructuring is to consume the iterator of the array, and assign the value of the value property of the generated object to the corresponding variable

One use of array destructuring is to swap variables, avoiding the need to declare a temporary variable value to store the value

ES6 Exchange variables:

advice

It is also recommended because the assignment is more semantic. For function parameters that are objects, you can reduce the declaration of parameters and directly use the properties of the object (if there are too many nesting levels, I personally think it is not suitable to use object destructuring, which is not very elegant).

A common example of this is the Vuex method in actions that takes two arguments. The first argument is an object, which you can name, and then call the commit function using the < name >.commit method, or use the object destruct to use the commit function directly

Without object destructuring:

Using object deconstruction:

You can also deconstruct responses using Axios (by default, Axios puts the actual responses in the data attribute)

Residual/extension operators (common)

The remainder/extension operator is also a very important syntax in ES6, using three points (…) , followed by a data structure containing the iterator interface

Extension operator

In the case of an array, using the extension operator makes it possible to “expand” the array, so you can think of an array as a container for a collection of elements, and using the extension operator makes it possible to break the container apart, so that all that’s left is a collection of elements, and you can put that collection of elements into another array

The extension operator can replace the CONCat method of the array prototype in ES3

This expands ARR1 and ARR2 using the extension operator, and then places the elements in a new array, which is more semantically significant than the concat method

Residual operator

The most important feature of the residual operator is that it replaces arguments

Access functions of the arguments object is a very expensive operation, before the arguments of the callee, the arguments. The caller was abolished, advice in support ES6 syntax circumstances do not use the arguments in the object, Use the residual operator instead (the arrow function has no arguments, you must use the residual operator to access the parameter collection)

Remaining operator can be used together with an array of deconstruction assignment, but must be on the last one, because the rest of the principle of the operator is using the array iterators, it consumes three points behind an array of all the iterator that reads all iterators to generate the value attribute of an object, remaining after the operator cannot in deconstruction assignment, Because the remaining operator consumes all iterators, destructuring an array also consumes iterators, but there are no iterators left, so an error is reported

Here first consumes an iterator from the array on the right… Arr consumes all the remaining iterators, and the second example… Arr directly consumes all iterators, leaving last with no iterators left to consume, so an error is reported because it is meaningless

The difference between the residual operator and the extension operator is that the residual operator collects the collection and puts it in the array on the right, and the extension operator breaks the array on the right into a collection of elements, which is the opposite

Use extension operators in objects

This is ES9 syntax, supports the use of extension in the object in the ES9 operator, said before the array principle is the extension of the operators consume all iterators, but not an iterator object, I personally think that principle may be different, but still can be understood as the key/value pair from the object in the open, it can be on another common object

It’s similar to another new API in ES6, the object.assign API. They both merge objects, but there are a few differences. The object.assign API fires setters for the target Object, while the Object extension operator does not

advice

Use the extension operator to quickly turn a class array into a real array

Merging multiple arrays

The function is curialized

Shorthand for object properties/methods (common)

Shorthand for object property

Es6 allows you to omit the property name if the object’s property and value are the same

One caveat

  • Omit the property name instead of the value
  • The value must be a variable

Shorthand for object properties is often used in conjunction with destructive assignments

In combination with the above destructuring assignment, the code here will actually declare the variables x,y, and z, because bar returns an object that has three properties x,y, and z. Destructuring assignment will look for the x,y, and z properties of the expression to the right of the equal sign, and then assign the values to the declared variables x,y, and z

Methods shorthand

Es6 allows the shorthand form to be used when the value of an object’s property is a function (that is, a method)

In Vue, since methods are written in VM objects, it is entirely possible to write functions using method shorthand

for … Of circulation

for … Of is a new iterator in ES6 that allows you to iterate over a data structure with an iterator interface and return the values of each item, as well as for… The difference in is as follows

  1. for … “Of” can only be used on iterables. It is the value returned by the iterator. In can get the key names of all objects

  2. for … In traverses the entire prototype chain of objects and is not recommended for poor performance, whereas for… Of only traverses the current object and not its prototype chain

  3. For an array traversal,for… In returns all enumerable properties in the array (including those on the prototype chain),for… Of only returns the value of the attribute corresponding to the index of the array

for … The principle of the “of” loop is also the use of iterator interface internally deployed iterable, if the for… The “of” loop is broken down into the original “for” loop

As you can see, the loop continues as long as the second condition (iterator.next() exists and res.done is true) is met, and each time the object generated by the iterator’s next method is assigned to res, and the value property of res is assigned to for… The done property of res controls whether to continue traversal

for… The of loop also supports break,continue,return(if called in a function) and can be used with object destructuring assignments

Arr array is used every time for… Each of the “of” loops returns an object ({a:1},{a:2},{a:3}), which is then destructed to find the value of attribute a and assign it to obj.a

I Promise you.

As a new concept introduced in ES6, Promise has changed JS asynchronous programming. Most asynchronous requests in modern front-end are implemented using Promise. Fetch, a Web API, is also based on Promise. What does Promise do to ameliorate these weaknesses

The callback function

As is known to all, JS is single-threaded, because multiple threads to change the DOM can lead to a page is disorder, so design for a single thread of language, but the browser is multithreaded, this makes the JS has asynchronous operation at the same time, the timer, request, event listeners, etc., and this time you need a set of event processing mechanism to determine the sequence of these events, The Event Loop (Event Loop), not detail Event Loop here, just need to know, the front of the request, is usually get into the browser’s HTTP request thread, when the response is received by the callback function into asynchronous queue, finish the task of the main thread will read such as asynchronous tasks in the queue, implement the callback

In the next volume of JavaScript you Don’t Know

Using a callback function to handle asynchronous requests puts your callback function in a black box. Although you have declared that you will execute the callback function when you receive a response, you do not know exactly how the third-party library will execute the callback function

Using a third-party request library you might write something like this:

After receiving the response, the following callback is executed to print the string, but if the third-party library has a function like timeout retry, you may execute your callback multiple times. If it is a payment function, you may find that you have been charged more than $1000

The second well-known problem is that nesting callbacks inside callbacks makes the code very difficult to maintain, which is often referred to as “callback hell.”

In addition, the third-party Ajax library you are using may not provide any error callbacks, and the error messages may be swallowed if the request fails. Nodejs provides err-first style callbacks, i.e. the first callback for an asynchronous operation is always an error callback, but you can’t guarantee that all libraries provide an err-first callback for sending an error.

Let me summarize some of the drawbacks of callback functions

  1. Multiple nesting, resulting in callback hell

  2. Code jumping is not the way humans are used to thinking

  3. Trust issues, you can’t trust your callback to a third party library, because you don’t know exactly how the third party library will perform the callback (multiple times)

  4. Third-party libraries may not provide error handling

  5. It is not clear if all callbacks are called asynchronously (you can call Ajax synchronously and block the entire thread until you receive a response, which can lead to suspended animation and is highly recommended)

xhr.open("GET"."/try/ajax/ajax_info.txt".false); // By setting the third async tofalseYou can call Ajax synchronouslyCopy the code

Promise

A Promise is a constructor that uses the new keyword to create an instance of a Promise. How can a Promise solve these problems

Promise is not a derivative of the callback function, but is a concept of two, so you need to change the previous callback function to a version that supports the Promise. This process is called “promotion”, or “promisory”. Axios, a third-party request library commonly used in modern MVVM frameworks, is a typical example. In addition, NodeJS also has Bluebird, Q and so on

  1. Multiple nesting, resulting in callback hell

Promise was designed to introduce the concept of chained calls, and each THEN method is also a Promise, so it can be chained indefinitely

Together with the arrow function, it is significantly more elegant than the multiple layers of nesting of previous callback functions

  1. Code jumping is not the way humans are used to thinking

Promise makes it possible to write code in sync with your mind. The code asks for port 3000, gets a response, then asks for 3001, then 3002, then 3003, and the writing format is human, first to last

  1. Trust issues, you can’t trust your callback to a third party library, because you don’t know exactly how the third party library will perform the callback (multiple times)

The Promise itself is a state machine with three states

  • Pending (waiting)
  • This is very depressing.
  • I don’t like it.

If a request is sent and no response is received, it will be in the pending state. When a response is received, it will resolve the current Promise instance and become fulfilled/ fulfilled.

When an error occurs on the request, a reject is executed to put the Promise instance into the Rejected state

The state of a Promise instance can only be changed from pending => fulfilled or from pending => Rejected. This will be very depressing. This will be very depressing.

A Promise instance must call the THEN method to retrieve the value from the Promise instance (provided that the Promise is not pending). This “active” operation is the key to solving this problem, that is, all the third-party libraries do is change the Promise state, but what to do with the response value. This is actively controlled by the developer. This is an inversion of control, transferring control of the third-party library to the developer

  1. Third-party libraries may not provide error handling

The Promise then method accepts two functions. The first is a callback for the Promise instance to be resolved, and the second is a callback for the Promise instance to be resolved, which is called by the developer

When an asynchronous request sends an error, Promise does not block the main thread, even if the error is not caught.

  1. It is not clear whether the callbacks are all invoked asynchronously

Promise is designed to ensure that all response callbacks are called asynchronously and do not block code execution. Promise queues the then callback to a MicroTask and ensures that these callbacks are not executed until the synchronization task has completed. This part is also the knowledge point of the event cycle, interested friends can in-depth research

For the third question, why is it that “in most cases” the state will be fulfilled after executing the resolve function? Consider the following

(Here we use a timer to print the state of the Promise instance in the next event loop, otherwise it would be pending)

This is very depressing. Many people assume that a promise must be fulfilled if the resolve function is called, but you can see here that even if the resolve function is called, a promise is still returned with a rejected state, because if it is in the resolve state of a promise, it will be fulfilled If another Promise is passed into the function, it expands the passed Promise

The resolve function expands the promise to become a promise with a rejected state, so it is better to think of resolve as a resolution

That’s the same thing

advice

In daily development, it is recommended to fully embrace the new Promise syntax. In fact, today’s asynchronous programming is basically using promises

It is recommended to further optimize the writing of promises using ES7’s async/await function. Async functions always return a Promise, and await implements a “wait” function. Async /await is considered the ultimate solution for asynchronous programming, that is, to write asynchronous code synchronously. And can more elegant implementation of asynchronous code sequence execution and asynchronous error in the occurrence of error to provide more accurate error information, detailed usage can see Mr. Ruan’s ES6 standard introduction

There’s a lot more to say about promises, including the static methods all, Race, resolve, reject, the execution order of promises, the nesting of promises, the handling of thenable objects, and so on. Because of the space, there is only a brief description of why Promise is needed. However, many developers only understand these apis in daily use, but do not know how to implement the Promise internal specific, encountered complex asynchronous code is impossible to start, it is highly recommended to understand Promise A+ specification, their own implementation of A Promise

ES6 Module(Common)

Before the emergence of ES6 Module, modularity has always been the focus of discussion among front-end developers. Faced with the increasing demand and code, there is a need for a solution to break the bloated code into small modules, so there are three modular solutions of AMD,CMD and CommonJs. The former is used on the browser side, and the latter two are used on the server side. Until ES6 Module

The ES6 Module is currently not supported by the browser by default and requires the use of Babel. This error is often displayed when writing demos

– tpye=”module” can be used in the script tag to solve the problem if the server is in the same domain (if the server is not in the same domain, it will be blocked by the same origin policy, webstorm will open a server in the same domain and this problem does not exist, vscode does not work)

ES6 Module uses the import keyword to import modules and the export keyword to export modules. It also has the following features

  1. The ES6 Module is static, which means it runs at compile time and is improved just like var and function (this makes it possible for Tree shaking)

  2. Automatically adopt strict mode (the top-level this returns undefined)

  3. The ES6 Module can use export {< variable >} to export a named interface or export default to export an anonymous interface

The module. Js export:

A. s import:

The difference is that export {< variable >} exports a reference to a variable, while export Default exports a value

In a.js, if the x variable is changed for some reason, it will be immediately reflected in a.js. In a.js, the y variable in a.js will remain the same value

module.js:

a.js:

You can see that we have set a timer for module.js to change variables x and y after one second. We can also observe the values of the variables at the time of import after one second. We can see that x has changed, but y is still 20, because y is exported X is a reference to a variable. A. js imports the current value of x and only cares about the current value of x. A. js imports the current value of x and only cares about the value of x.

In other words, rename the module export to default. You can also use the syntax import < variable > from < path > to import

The module. Js export:

A. s import:

However, a module exported using the form export {< variable >} will still export a reference to a variable even if it is renamed default

Here are some of the differences between ES6 Module and CommonJs, the most popular modularity solution so far

  1. CommonJs outputs a copy of a value,ES6 Module outputs a reference to a variable through export {< variable >}, and export Default outputs a value

  2. CommonJs runs on the server and is designed to be loaded at runtime, that is, the code executes at that line and then goes back to load the Module, whereas ES6 Module is a static output interface that occurs during compilation

  3. CommonJs is run once on the first load and generates a cache, and later loads return the contents of the cache

import()

About the ES6 The static compilation of modules makes it impossible to load modules dynamically, but there will always be some requirement to load modules dynamically, so there is a proposal to do this by using import as a function that returns a Promise with the value of the output Module when the Promise is resolved

Rewrite a.js above with the import method so that it can be loaded dynamically (using statically compiled ES6 modules in conditional statements will report an error because of the improved effect, which is also not allowed), and you can see that a variable x and a default output are printed for module.js

The ES6 notation for lazy loading of routes in Vue uses this technique to dynamically load component rendering views when switching routes

Function default

ES6 allows default values to be set in function arguments

ES5 writing:

ES6 writing:

In ES6, the default value is written directly on the parameter, which is more intuitive than in ES5

If a function’s default argument is used, it is scoped as a separate block-level in the function’s argument region (inside parentheses) and has some of the features of let/const methods, such as temporary dead zones

Here when running func, since no arguments are passed, using the default function arguments,y looks for the value of x, and finds the variable x with the value 1 in the outer layer of the lexical scope

Let’s do another example

X finds the variable w through the lexical scope, so x defaults to 2. Y also finds the variable x just defined through the lexical scope,y defaults to 3, but after parsing to z = z + For the first line, the JS interpreter will parse z+1 to find the corresponding value and then assign it to the variable z, but because of a temporary dead zone (let/const” hijacks “the block-level scope and cannot use the variable before the let declaration, as explained above), the variable z will be used before the let declaration

This makes it relatively easy to understand the default value of the function

The default value of the function is used only if the argument passed is undefined (explicit passing of undefined also triggers using the default value of the function, null does not)

Here’s an example:

Here to borrow nguyen other teacher in the book an example, the default value as a function of func, perform return foo variables, and performed within the function, the equivalent of the foo variables in a query (LHS query), and the starting point of the query is the single block level scope, namely the JS interpreter won’t function to query the internal query variable foo, Instead, it looks for foo in the same scope (the previous function argument), and then looks outside the function to see if it can’t find foo. This is also a feature of the default value of the function

Func,foo, and this can be accessed from the lexical scope of the function. However, when you look at the lexical scope of func, you can see that it can only access the Global scope. Foo does not exist in its lexical scope

Function defaults work with destruct assignments

The first line to func function is introduced into two empty object, so the function of the first the second parameter is not using the function default values, then the function of the first parameter will try to deconstruct object, extract variable x, because the first parameter to an empty object, so couldn’t deconstruct the variable x, but here again, a layer set a default value, so the value of x to 10, and the first We also pass an empty object for the two arguments. We don’t use the default value of the function, and then we try to deconstruct the variable y. We find that there is no variable y in the empty object, but y is not set by default, so the value of y is undefined

The first argument in the second line explicitly passes in undefined, so the function defaults to an empty object. Then it attempts to deconstruct x as in the first line and finds that x is undefined, but sets the default value so x is 10 and y is undefined as in the previous line

In the third line, both arguments are undefined. The first argument is the same as above, and the second argument calls the default value of the function, assigning it {y:10}, and then attempts to deconstruct the variable y, where y is 10

The fourth line is the same as the third line. One is explicitly passed in undefined and the other is implicitly passed out

Line 5 uses the passed arguments, does not use the default function values, and is able to smoothly deconstruct the variables x and y

Proxy

Proxy as a “interceptor”, can be in front of the target object erect a blocker, others access object, must through the first layer of the interceptor, the Proxy is also a constructor, use the new keyword to generate a blocking object instance, ES6 provides a very much object to intercept operation, covering almost all may modify the target object ( Proxy is used with Reflect. The former intercepts the object and the latter returns the intercepting result.

Object.definePropery

Object. DefineProperty is an API that allows you to add properties to an Object and the property descriptor/accessor of the property (the two cannot coexist; only one of the same property can be added). Property descriptors can be freely configured, freely configured, or writab Le, enumerable, value the four attributes, respectively is configurable, whether read-only, whether can be enumerated and attribute values, the visitor is configurable, enumerable, get, set, the former two functions and attributes descriptor, after two function, defines the get and set later All reads and writes to an element execute the following getter/setter functions and override the default read/write behavior

Obj2 defines get but does not define set as read-only, and the value returned by reading obj2’s b property is the return value of the getter function

Proxy is an enhanced version of Object. DefineProperty. ES5 only specifies property descriptors or accessors that can define properties. Proxy has been enhanced to 13, so I won’t show you all the details, but let me give you some interesting examples

handler.apply

The apply function allows us to intercept the execution of a function (which is also an object in JS, and can also be intercepted by a Proxy). We can use it in function throttles

Call the intercepted function:

handler.contruct

Contruct can intercept calls to this function using the new keyword, which we can use in singletons

The instance variable is stored in a closure. Each time the intercepted function is called using the new keyword, the instance variable is checked. If it exists, the instance variable stored in the closure is returned

handler.defineProperty

DefineProperty intercepts the object.defineProerty operation on this Object

Note that the default [[SET]] operation inside an object (that is, assigning values to properties of the object) indirectly triggers defineProperty and getOwnPropertyDescriptor interceptors

So here’s a couple of things

  1. Recursive operations are used here. When an object property needs to be accessed, it is determined that the value of the object property of the proxy is still a surrogate object, otherwise the default GET operation is performed through error capture
  2. An interception method with defineProperty is assigned to a property of the proxy object using the default [[SET]] operation within the object, which indirectly triggers the defineProperty method and then executes the defined callback

This enables the get method to be triggered whenever a property is assigned to the object regardless of how many layers the object is nested, which then triggers defineProperty to perform the callback function

Other usage scenarios

Proxy also has many functions, such as separating business logic and validator to achieve decoupling when implementing validators, intercepting access to private variables through GET to realize private variables, intercepting objects for logging, realizing the promise of wechat API, etc

Vue

Vue3.0 is expected to be released in the second half of 2019, and one of its core features is the use of Proxy instead of Object.defineProperty

I’m sure those of you who have seen a little of Vue’s reactive principles are aware of some of the shortcomings of the Vue framework in object interception

<template>
   <div>
       <div>{{arr}}</div>
       <div>{{obj}}</div>
       <button @click="handleClick"> Alter arR subscript </button> < button@click ="handleClick2"</button> </div> </template> <script>export default {
        name: "index".data() {
            return{arr:[1,2,3], obj:{a:1, b:2}}}, methods: {handleClick() {
                this.arr[0] = 10
                console.log(this.arr)
            },
            handleClick2() {
                this.obj.c = 3
                console.log(this.obj)
            }
        },
   }
</script>
Copy the code

As you can see, the data changes here, the console prints out the new value, but the view is not updated. This is due to data hijacking within Vue using object.defineProperty, and the API cannot detect the addition and removal of the Object root property, and assigns the array index directly, so rendering WAT is not notified Cher does view updates, and in theory the API cannot detect a series of array methods (push,splice,pop), but the Vue framework has modified the array prototype so that the view updates are performed when these methods are called to modify the data

/ / source location: SRC/core/observer/array. Js methodsToPatch. ForEach (function (method) {
  // cache original method
  var original = arrayProto[method];
  def(arrayMethods, method, function mutator () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    var result = original.apply(this, args);
    var ob = this.__ob__;
    var inserted;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break
      case 'splice':
        inserted = args.slice(2);
        break
    }
    if(inserted) { ob.observeArray(inserted); } // notify change ob.dep.notify(); // This line calls the notify method and notifies the rendering watcher to update the viewreturn result
  });
});
Copy the code

Wrote in the nuggets translation of the Vue3.0 project

3.0 will bring a proxy-based observer implementation that provides a full range of responsive capabilities covering the language, eliminating some of the current limitations of the Object.defineProperty based Vue 2 series, such as: Monitoring for attribute add and delete actions on array subscription-based changes, monitoring for.length changes on Map, Set, WeakMap, and WeakSet support

Proxy does not have this problem and provides more intercepting methods to replace object.defineProperty. The only drawback is browser support (IE: who’s talking about me?).

Therefore, in order to understand the implementation mechanism of Vue3.0, it is essential to learn Proxy

Object.assign

The Object static method added in ES6 allows us to merge multiple objects

Assign iterates through {a:1} to assign attribute a and the number 1 to target, and then iterates through {b:2} to assign attribute b and the number 2 to Targe T object

Here are some of the things you need to know about the API

  1. Assign is a shallow copy. For attributes whose value is a reference type, the copy is still the reference

  2. You can copy the Symbol attribute

  3. Cannot copy non-enumerable properties

  4. Assign guarantees that the target is always an Object. If passed a primitive type, it is converted to the primitive wrapper type. Null /undefined has no primitive wrapper type, so it is passed with an error

  5. (String types are considered enumerable because of the internal iterator interface).

  6. Since we assign with an equal sign, if the property of the assigned Object has a setter, it triggers the setter. Similarly, if there is a getter, it calls the getter of the property of the assigned Object. (This is why object. assign cannot merge the accessors of the property of the Object Getter/setter function rather than merge them, if you need to merge the attributes of the Object getter/setter function, you can use the ES7 provide Object. GetOwnPropertyDescriptors and Object defineProperties these 2 API implementation)

You can see that the getter/setter for the a property in the obj object is successfully copied

In order to deepen the understanding of my own simulation Object. Assign implementation, for reference

The catch here is that passing a string to the target argument internally converts it to the base wrapper type, and the string base wrapper type’s properties are read-only (the writable attribute of the property descriptor is false)

The property descriptor for the print object property can see that the values for the subscript property are read-only, that is, they cannot be assigned again, so attempting the following will result in an error

The string ABC is converted to the primitive wrapper type, and the string def is expanded when the string def is merged into the primitive wrapper type. The string def is assigned to the 0,1, and 2 attributes of the primitive wrapper type ABC, respectively, and an error is reported during the assignment (non-strict mode is handled silently,ES6 object.as) Sign has strict mode enabled by default)

Compare this to ES9’s object extension operator

ES9 supports the use of extension operators on objects, which are similar to object. assign except for the properties of objects that contain getter/setter functions

(You can ignore the last string get, which is the getter function that the console fires to display the variable a.)

Consider this example

ES9:

  • It merges two objects and fires getters for the properties of only two objects
  • The latter of the same property overrides the former, so the value of the a property is the value of the second getter function return

ES6:

  • It also combines the two objects, and only fires the setter for property A on obj, not its getter (it’s easier to understand with the internal implementation of object.assgin above).
  • The setter function for the a property on obj replaces the default assignment behavior, so that the a property of obj2 will not be copied

With the exception of getters/setters for Object properties, object. assgin and the Object extension operator function are identical. You can use both, and both are shallow copies

advice

  1. Vue resets data in data

This is one of my most common techniques. Using object. assign, you can merge the data Object in your current component with the data Object in the default initialization state of the component

The $data property in the current component instance holds the data object of the current component. The $options property is the property of the initialization of the current component instance. The data method, which is written in the component, returns an initialized data object Current data to initialize all data

  1. Give the object the default attributes needed for the merge

You can encapsulate a function with a default constant declaring that options is the dynamic configuration that is passed to it each time it is executed

  1. When passing parameters, multiple data can be merged into a single object and passed to the back end

The resources

  1. Ruan Yifeng: Introduction to ES6 standards

  2. Moocs: ES6 zero foundation teaching

  3. You don’t know the JavaScript next volume