Cast type
Value type conversion
var a = 42; var b = a + ""; // Implicit cast var c = String(a); // Explicit cast Copy code
Abstract value operation
document.all Is a false value object. that is !! document.all Value is false.
Show cast
Convert date display to number:
use Date.now() To get the current timestamp, use new Date(..).getTime() To get the timestamp of the specified time.
Strange ~ operator:
~x Roughly equivalent to - (x+1). Strange, but relatively easy to explain: ~ 42; // \- (42+1) ==> \-43
String in JavaScript indexOf(..) Method also follows this Convention. This method searches the specified substring in the string. If it is found, it returns the position of the substring (starting from 0), otherwise it returns - 1.
~ and indexOf() Together, you can cast the result cast type (actually just cast) to true / false values:
var a = "Hello World"; ~a.indexOf("lo"); // - four <-- It's worth it! if (~a.indexOf("lo")) { // true // Find a match! } Copy code
Parse non string:
Someone once make complaints about Tucao. parseInt(..) One of the pits:
parseInt( 1/0, 19 ); // 18 Copy code
parseInt(1/0, 19) Actually parseInt("Infinity", 19). The first character is "I", and when the base number is 19, the value is 18.
In addition, there are some examples that seem strange but actually make sense:
parseInt(0.000008); // 0 ("0" From "0.000008") parseInt(0.0000008); // eight ("8" From "8e-7") parseInt(false, 16); // two hundred and fifty ("fa" From "false") parseInt(parseInt, 16); // fifteen ("f" From "function..") parseInt("0x10"); // 16 parseInt("103", 2); // 2 Copy code
Implicit cast
Implicit cast between string and number
For example:
var a = "42"; var b = "0"; var c = 42; var d = 0; a + b; // "420" c + d; // 42 Copy code
Another example:
var a = [1,2]; var b = [3,4]; a + b; // "1,23,4" Copy code
According to section 11.6.1 of the ES5 specification, if an operand is a string or can be converted to a string through the following steps, + will be spliced. If one of the operands is an object (including an array), first call the ToPrimitive abstract operation (section 9.1 of the specification), and then call the [[DefaultValue]] (section 8.12.8 of the specification), with numbers as the context.
You may have noticed that this is related to ToNumber Abstract operations treat objects the same way (see section 4.2.2). Because of the array valueOf() The operation cannot get a simple base type value, so it calls instead toString(). Therefore, the two arrays in the above example become "1,2" and "3,4"+ Splice them and return to "1,23,4".
In short, if one of the operands of + is a string (or a string can be obtained through the above steps), string splicing is performed; Otherwise, digital addition is performed.
Cast of symbols
ES6 allows explicit cast from symbol to string, but implicit cast will produce errors. The specific reasons are beyond the scope of this book.
For example:
var s1 = Symbol("cool"); String(s1); // "Symbol(cool)" var s2 = Symbol("not cool"); s2 + ""; // TypeError Copy code
Symbols cannot be cast to numbers (both explicit and implicit errors), but they can be cast to Booleans (both explicit and implicit results are false) true).
Due to the lack of consistency of rules, we should be more careful about the cast of symbols in ES6.
Fortunately, due to the special use of symbols, we will not often use its cast.
Loose equality and strict equality
The common misconception is "== Check whether the values are equal=== Check that values and types are equal. It sounds reasonable, but it's not accurate enough. Many JavaScript books and blogs explain this, but unfortunately they are all wrong.
The correct explanation is:== Allow cast in equality comparison, and === Not allowed. "
Equality comparison between strings and numbers:
-
If Type(x) is a number and Type(y) is a string, the result of x == ToNumber(y) is returned.
-
If Type(x) is a string and Type(y) is a number, the result of ToNumber(x) == y is returned.
Equality comparison between other types and Boolean types:
-
If Type(x) is Boolean, the result of ToNumber(x) == y is returned;
-
If Type(y) is Boolean, the result of x == ToNumber(y) is returned.
null and undefined Equality comparison between:
-
If x is null and y is undefined, the result is true.
-
If x is undefined and y is null, the result is true.
Equality comparison between object and non object:
-
If Type(x) is a string or number and Type(y) is an object, the result of x == ToPrimitive(y) is returned;
-
If Type(x) is an object and Type(y) is a string or number, the result of ToPromitive(x) == y is returned.
grammar
error
Use variables in advance
The ES6 specification defines a new concept called TDZ (temporary dead zone).
TDZ refers to the case where a variable in the code cannot be referenced because it has not been initialized.
The most intuitive example of this is in the ES6 specification let Block scope:
{ a = 2; // ReferenceError! let a; } Copy code
a = 2 Try in let a initialization a This variable was previously used (its scope is { .. } Inside), this is a TDZ, will produce an error.
Interestingly, using typeof for undeclared variables will not produce an error (see Chapter 1), but an error will be reported in TDZ:
{ typeof a; // undefined typeof b; // ReferenceError! (TDZ) let b; } Copy code
Callback
Save point callback
Construct a timeout verification tool:
function timeoutify(fn, delay) { var intv = setTimeout(function() { intv = null fn(new Error('Timeout!')) }, delay) return function() { // Not timed out yet? if (intv) { clearTimeout(intv) fn.apply(this, arguments) } } } Copy code
Here is how to use it:
// use ‘ error-first Style ' Callback design function foo(err, data) { if (err) { console.error(err) } else { console.log(data) } } ajax('http://some.url.1', timeoutify(foo, 500)) Copy code
What if you're not sure if the API of interest will always execute asynchronously? You can create a version similar to this "proof of concept" version asyncify(..) Tools:
function asyncify(fn) { var orig_fn = fn, intv = setTimeout(function() { intv = null if (fn) fn() }, 0) fn = null return function() { // Trigger too fast, before the timer intv trigger indicates that the asynchronous conversion occurs? if (intv) { fn = orig_fn.bind.apply( orig_fn, // Add this of the wrapper to the parameter of the bind(..) call, // And currying all incoming parameters [this].concat([].slice.call(arguments)) ) } // Already asynchronous else { // Call the original function orig_fn.apply(this, arguments) } } } Copy code
It can be used like this asyncify(..):
function result(data) { console.log(a) } var a = 0 ajax('..pre-cached-url..', asyncify(result)) a++ Copy code
Whether the Ajax request is already in the cache and attempts to call the callback immediately, or it needs to be obtained from the network and completed asynchronously in the future, this code will always output 1 instead of 0 -- result(...) can only be called asynchronously, which means that a + + has the opportunity to run before result(...).
Promise
Promise trust issues
Callback not called
Provide a solution for timeout processing:
// A Promise tool for timeout function timeoutPromise(delay) { return new Promise(function(resolve, reject) { setTimeout(function() { reject('Timeout!') }, delay) }) } // Set foo() timeout Promise.race([ foo(), timeoutPromise(3000) ]) .then( function() { // foo(..) completed in time! }, function(err) { // Or foo() was rejected, or it just didn't finish on time // Check err to see what happens } ) Copy code
Chain flow
To further illustrate the link, let's generalize the delay Promise creation (no resolution message) process into a tool to reuse in multiple steps:
function delay(time) { return new Promise(function(resolve, reject) { setTimeout(resolve, time) }) } delay(100) // Step 1 .then(function STEP2() { console.log("step 2 (after 100ms)") return delay(200) }) .then(function STEP3() { console.log("step 3 (after another 200ms)") }) .then(function STEP4() { console.log("step 4 (next Job)") return delay(50) }) .then(function STEP5() { console.log("step 5 (after another 50ms)") }) Copy code
call delay(200) Create a promise that will be completed in 200ms, and then we start from the first one then(..) This promise is returned in the completion callback, which will result in a second error then(..) Wait for the 200ms promise.
Promise limitations
Sequence error handling
Promise's design limitations (chain call) cause a trap that people can easily get caught, that is, errors in promise chain can easily be inadvertently ignored.
There are other things to consider about Promise errors. Because a Promise chain is only a member Promise connected together, it does not identify the whole chain as an individual entity, which means that there is no external method to observe possible errors.
If a promise chain without an error handler is built, any error anywhere in the chain will continue to propagate in the chain until the reject handler is registered at a certain step. In this particular example, it is sufficient to have a reference to the last promise in the chain (p in the following code), because you can register the rejection handler there, and this handler can be notified of all propagated errors:
// foo(..), STEP2(..) and STEP3(..) are tools that support promise var p = foo(42) .then(STEP2) .then(STEP3); Copy code
Although it may be confusing here, the p Does not point to the first promise() call in the chain foo(42) The generated one), but points to the last promise, that is, from the call then(STEP3) The one in the.
Also, no step in the Promise chain explicitly handles its own errors. That means you can p Register a reject error handler on the. This handler will be notified of any error at any position in the chain:
p.catch(handleErrors); Copy code
However, if any step in the chain actually carries out its own error handling (possibly in a hidden or abstract invisible way), your handleErrors(..) You won't be notified. This may be what you want - after all, it's a "handled rejection" - but it may not be. Failure to get clear error notification (for a specific "processed" rejection) is also a defect, which limits the functionality of some use cases.
Basically, this is equivalent to the limitation of try... Catch: try... Catch may catch an exception and simply swallow it. So this is not a unique limitation of Promise, but it may be a trap we want to bypass.
Unfortunately, many times there are no references reserved for intermediate steps of Promise chain sequence. Therefore, without such a reference, you cannot associate an error handler to reliably check for errors.
Single value
Promise can only have one completion value or one rejection reason by definition. In simple examples, this is not a problem, but in more complex scenarios, you may find that this is a limitation.
The general recommendation is to construct a value encapsulation (such as an object or array) to hold such multiple information. This solution can work, but it is very ugly and cumbersome to encapsulate and unseal every step of the Promise chain.
- Split value
Sometimes you can use this as a signal that you should break down the problem into two or more promises.
Imagine you have a tool foo(..), which can asynchronously generate two values (x and y):
function getY(x) { return new Promise(function(resolve, reject){ setTimeout(function(){ resolve((3 * x) - 1); }, 100); }); } function foo(bar, baz) { var x = bar * baz; return getY(x).then(function(y){ // Encapsulate the two values into a container return [x, y]; }); } foo(10, 20).then(function(msgs){ var x = msgs[0]; var y = msgs[1]; console.log(x, y); // 200 599 }); Copy code
First, let's reorganize foo(..) Returns the content of the, so that there is no need to x and y Encapsulated into an array value for transmission through promise. Instead, we can encapsulate each value into its own promise:
function foo(bar, baz) { var x = bar * baz; // Return two promise return [ Promise.resolve(x), getY(x) ]; } Promise.all( foo(10, 20) ).then(function(msgs){ var x = msgs[0]; var y = msgs[1]; console.log(x, y); }); Copy code
Is a promise array really better than a value array passed to a single promise? From a grammatical point of view, this is not an improvement.
However, this method is more in line with promise's design concept. If you need to refactor the code later x and y This method is much simpler if the calculations are separated. It is up to the calling code to decide how to arrange the two promises, rather than putting such details in foo(..) Internal abstraction, which is cleaner and more flexible. Used here Promise.all([..]), of course, this is not the only option.
- Transfer parameters
var x = .. and var y = .. The assignment operation is still a troublesome overhead. We can use some kind of function technique in auxiliary tools:
function spread(fn) { return Function.apply.bind(fn, null); } Promise.all( foo(10, 20) ).then(spread(function(x, y){ console.log(x, y); // 200 599 })) Copy code
This will be better! Of course, you can make this function trick online to avoid additional AIDS:
Promise.all( foo(10, 20) ).then(Function.apply.bind( function(x, y){ console.log(x, y); // 200 599 }, null )); Copy code
These techniques may be clever, but ES6 gives a better answer: deconstruction. The array deconstruction assignment form looks like this:
Promise.all( foo(10, 20) ).then(function(msgs){ var [x, y] = msgs; console.log(x, y); // 200 599 }); Copy code
Best of all, ES6 provides the deconstruction form of array parameters:
Promise.all( foo(10, 20) ) .then(function([x, y]){ console.log(x, y); // 200 599 }); Copy code
Now, we are in line with the concept of "one value for each Promise" and keep the amount of duplicate template code to a minimum!
Single resolution
The most essential feature of Promise is that Promise can only be decided once (completed or rejected). In many asynchronous situations, you only get a value once, so this works well.
However, there are many asynchronous situations that are suitable for another pattern - a pattern similar to events or data flows. On the surface, it is unclear whether promise can be well used for such use cases, if not completely unavailable. Promise cannot support multi value resolution processing at all without building a significant abstraction on promise.
Imagine a scenario where you might start a series of asynchronous steps in response to a stimulus that may occur many times (like an event), such as a button click.
This may not work as you expect:
// click(..) Bind the "click" event to a DOM element // request(..) Is the support defined earlier Promise of Ajax var p = new Promise(function(resolve, reject){ click("#mybtn", resolve); }); p.then(function(evt){ var btnID = evt.currentTarget.id; return request("http://some.url.1/?id=" + btnID); }).then(function(text){ console.log(text); }); Copy code
This works only if your application only needs to respond to one click of the button. If this button is clicked a second time, promise p has been decided, so the second one resolve(..) The call is ignored.
Therefore, you may need to transform this example to create a new Promise chain for each event:
click("#mybtn", function(evt){ var btnID = evt.currentTarget.id; request("http://some.url.1/?id=" + btnID).then(function(text){ console.log(text); }); }); Copy code
This method works because a new Promise sequence is started for each "click" event on the button.
This is ugly because you need to define the entire Promise chain in the event handler. In addition, this design destroys the idea of separation of concerns and functions (SoC) to some extent. You probably want to put the definition of the event handler function and the definition of the response to the event (the Promise chain) in different places in the code. If there is no auxiliary mechanism, it is difficult to achieve this in this mode.
Click on the bottom card / WeChat search to pay attention to the official account of "ID:gh_cc865e4c536b" (Tian Yuwen).
It's said that those who praise and pay attention to this number have found a beautiful little sister, and they will enter a million years later!!
Previous recommendation
[
Spark JS open source low code scenario workbench, which automatically generates code for you
](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487682&idx=2&sn=26d15f07ffccf6454c6aa08c04e00230&chksm=ebb6f3a1dcc17ab7973d3844f15161bcbec19c7ef85a32ce0ae590dd96df76a5d3b6327e3097&scene=21#wechat_redirect)
[
Front end code optimization, and daily use skills
](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487682&idx=4&sn=7dc9a838847145f31ebfb2a8cca46960&chksm=ebb6f3a1dcc17ab7e58fc1a20f60843e3ff656c066f22fc616f9ecf5adbc1dce8baa5523cfae&scene=21#wechat_redirect)
[
nvm installation and use
](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487379&idx=1&sn=fb06b04c517731e2ec2b2baa43635a9f&chksm=ebb6ecf0dcc165e6ef1c9b889a3f42ede75c84f9c3b8359d9e39587aed6c1534f7119631e316&scene=21#wechat_redirect)
[
One click to generate the design draft of the UI little sister, and the development efficiency is improved by 100%
](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247487088&idx=3&sn=e977dc4c7a2b5e681bd621b20023caaf&chksm=ebb6ed13dcc164056f688f7c4fd2cfc4ed045bec27049efc2c75c01189dcb992ee61c781098a&scene=21#wechat_redirect)
[
Spring Boot + Vue development example music playback app
](http://mp.weixin.qq.com/s?__biz=MzI4MDQ5MTUzMg==&mid=2247486140&idx=2&sn=db97688d38832894fb5fc4cc1808461a&chksm=ebb6e9dfdcc160c9a6b83afc98e98d21ce1e1ae835c2ac2da3749b32f4c5b0e4310a86f4f33c&scene=21#wechat_redirect)