Since the ball is round, anything is possible, I’m sure of that, but lately I’ve been wondering if JavaScript is round too!

What is slang?

Slang, refers to the old jianghu gang characters of the code, code, often seen in novels, refers to popular in a special industry, not outsiders can understand the language. JavaScript syntax is very simple and flexible. In the project, it is suggested that we follow the ESLint specification to write maintainable code. Each immortal should also exercise self-restraint. After all, “slang” is not all good things, if a lot of words can be directly said, why beat around the bush to say?

“Arithmetic”

Bitwise arithmetic has been banned by the authors, so make sure you have a good reason to use bitwise arithmetic in your projects, and write Hack comments if necessary.

! With!!!!!

! Is a logical non-operator that can be applied to any value in ECMAScript. No matter what type the value is, it is forced to be converted to a Boolean variable and its value inverted.

!!!!! It simply executes the operand twice. It can convert any type of value into the corresponding Boolean value. It contains the following steps:

  1. Convert a value to a Boolean value;
  2. Take it inversely;
  3. Take the inverse again.

If you need a Boolean variable to indicate whether there is an ID value, the last method is recommended:

const enable1 =!!!!! Id; constenable2 = id ? true : false;
const enable3 = Boolean(id);
Copy the code

~ ~ ~

~ indicates the reverse bit. The operation procedure of ~5 is as follows:

  1. Convert to a binary representation of a byte: 00000101,
  2. Reverse by bit: 11111010
  3. Take its inverse code: 10000101
  4. Take its complement code: 10000110
  5. To decimal: -6

It stands for the double non-bitwise inverse operator, and if you want something faster than math.floor (), that’s it. Notice, for positive numbers, it rounds down; For negative numbers, round up; The non-numeric value is 0, which is expressed as follows:

~~null; // => 0 ~~undefined; // => 0 ~~Infinity; // => 0 --NaN; / / = > 0 ~ ~ 0; / / = > 0 ~ ~ {}; / / = > 0 ~ ~ []; / / = > 0 ~ ~ (1/0); / / = > 0 ~ ~false; / / = > 0 ~ ~true; / / = > 1 ~ 1.9; / / = > 1 ~ 1.9; / / = > 1Copy the code

+

The use of + before a variable value is intended to convert a variable to a number, and is particularly useful when a function accepts arguments of numeric type:

+'1'/ / + 1'1' // '-1
+[] // 0
+{} // NaN
Copy the code

From observation, +a is similar to a * 1. +function() {}(), equivalent to (function(){})().

The default value is converted to a string when a string is added to a number, so here’s a quick way to convert a number to a string: ‘+ 1’.

& with &&

If you’re from a C-like language, discard the previous stereotype that & can act as a logical manipulation symbol. In JavaScript, & is bitwise only.

Ampersand, which represents bitwise and, this operator takes two digits and returns one. If they are not numbers, they are converted to numbers. If you execute 7 & 3, the following steps will follow:

  1. First convert to base 2:111 & 11
  2. The comparison results are as follows:011
  3. Convert binary back to decimal, so:7 times 3 is 3

It can also be used for base even judgments: const isOdd = num =>!! (num & 1);

&& is used for if conditions. Contrary to what you might expect, && does not simply return true or false, but depends on:

  1. If the first expression is false, return the first expression;
  2. If the first expression is true, return the second expression. Here are a few examples:
0 && false          0 (both are false-y, but 0 is the first)
true && false       false (second one is false-y)
true && true        true (both are true-y)
true && 20          20 (both are true-y)
Copy the code

&& can concatenate multiple operators, such as: A && b && c && d, and return the same rules as above. In addition, it is often used as short-circuit logic: if the preceding expression is not truthy, the subsequent expression will not continue. If an object attribute is empty, Uncaught TypeError will be raised. If an object attribute is empty, Uncaught TypeError will be raised. If an object attribute is empty, Uncaught TypeError will be raised.

const value = obj && obj.value || false
Copy the code

When JavaScript compression tools encounter if judgments, they also use && short-circuiting logic to save memory:

// before
if (test) { alert('hello') }
// after
test && alert('hello')
Copy the code

| and | |

Them with & and && use method is very similar, or different is that they said is logic, so using | will perform bitwise or operations, and | | will return the first Truthy value.

Using | | for the default value assignment in JavaScript are very common, it can omit a lot of unnecessary if statements, such as:

// before
let res;
if (a) {
  res = a;
} else if (b) {
  res = b;
} else if (c) {
  res = c;
} else {
  res = 1;
}

// after
const res = a || b || c || 1;
Copy the code

= = = = =

== is the equality operator. The operator forces the left and right operands to the same operand and then performs an equality comparison.

=== is the congruent operator, which does not force the transformation of operands during comparison, and the equality judgment is consistent with ==.

In short, == is used to determine whether values are equal, and === is used to determine whether values and types are equal. Therefore, it is more accurate to use the congruence operator to determine operands. The first few Tips for beginners learning JavaScript are to avoid using the equality operator. Yes, this ensures that you don’t make as many mistakes as possible if you’re not thoroughly familiar with the language, but it’s also important to know when to use the equality operator. Rules are usually for beginners, and it’s important to know what you’re doing if you’re smart.

The equality operator compares different types of values as follows:

  B
    Undefined Null Number String Boolean Object
A Undefined true true false false false IsFalsy(B)
Null true true false false false IsFalsy(B)
Number false false A === B A === ToNumber(B) A=== ToNumber(B) A=== ToPrimitive(B)
String false false ToNumber(A) === B A === B ToNumber(A) === ToNumber(B) ToPrimitive(B) == A
Boolean false false ToNumber(A) === B ToNumber(A) === ToNumber(B) A === B ToNumber(A) == ToPrimitive(B)
Object false false ToPrimitive(A) == B ToPrimitive(A) == B ToPrimitive(A) == ToNumber(B) A === B

For undefined and NULL: undefined is equal to null and is not equal to any other object, so in some libs you might see this:

if (VAR == undefined) {}
if (VAR == null) {}
Copy the code

It is equivalent to:

if (VAR === undefined || VAR === null) {}
Copy the code

For ”, false, and 0, they are all types of Falsy. Boolean objects are all cast to false, and Boolean objects are always equal because they are all cast to false when comparing values:

console.log((false == 0) && (0 == ' ') && (' '= =false)) / /true
Copy the code

Or sometimes we want to compare strings with numbers using strong transitions:

console.log(11 == '11') / /true
console.log(11 === '11') / /false
Copy the code

^

The xOR operator compares each bit bit by bit, returning 1 if the bit bit is different, and 0 otherwise. Few people use this operator in Web development, except for one legendary scenario: swapping values.

To swap the values of a and B, it is recommended that you use:

[a, b] = [b, a];
Copy the code

Or create a new C to store temporary variables, if you encounter someone who writes:

A ^ b ^ b = a, a ^ a ^ b = b a = a ^ b b = a ^ b a = a ^ b b = a ^ b a = a ^ bCopy the code

Forgive him and ignore it. He can only be a beginner who loves magic, and wish him luck as he discovers that simple and readable functions are best practice.

.

JavaScipt integers and floating point numbers are of type Number, and all numbers are stored as 64-bit floating point numbers. Therefore, parsing statements allows numbers to be followed by a decimal point (1. === 1). This actually causes a problem. Uncaught SyntaxError, in this expression. Instead of treating it as a property accessor, it is combined with 1to the float 1., so the program reports an error. 1.toString() equals 1toString().

To make things easier to understand, remember this rule: The first occurrence of a Number expression in the interpreter’s eyes. Is the decimal separator for a floating point number, the second. Is a property accessor. Such as 1.0.tostring () and 1.. Syntax like toString() works fine. It is important to note the difference between a variable and an expression. If you assign a Number expression to a variable, you can call the prototype method directly from the variable. There is no ambiguity.

This loose type structure is really misleading, and we should all avoid such ambiguities in our programs by disambiguating numeric expressions with parentheses (1).tostring () instead of using 1 for the sake of cool. The toString ().

void

Void evaluates a given expression and returns undefined, as defined in MDN. There are several ways to understand this.

First of all, it can be used as a substitute for undefined. Since undefined is not a reserved word, it is actually a global variable value, so we can change it, and the program can be unstable. In ES5, it is already a read-only property, but in local scopes, There is still the possibility of being reloaded (you may also have persecutory delusions) :

(function() {
  const undefined = 'hello';
  console.log(undefined); // hello
})();
Copy the code

Second, we can use the void keyword in front of a function to indicate that the function does not return a value, but not in every function. This is not JavaScript code style, and we can use this feature to execute IIFE (execute the function immediately). Let’s take a look at the following example:

const arrs = []
(function() {
  console.log('hello')
})()
Copy the code

If you are not used to writing semicolons, you may have encountered this error: Uncaught TypeError: [] is not a function because minify does not have the correct word segmentation, void can solve the problem of word segmentation, and make the immediate execution of the function call more elegant:

const arrs = []
void function() {
  console.log('hello')
}()
Copy the code

In cases where we don’t want the a tag to jump, here are some common methods:

<! -- use preventDefault -->
<a id="a" href="">hello</a>
<script>
  a.addEventListener('click', e => e.preventDefault());
</script>

<! -- use return false -->
<a href="" onclick="return false;">hello</a>
Copy the code

We can also avoid the default jump behavior of the a tag when we set the href value to undefined:

<a href="javascript: void 0;" onclick="return false;">hello</a>
Copy the code

> > > 0

Unsigned right shift operation, no special treatment is made to the sign bit of the highest bit, the whole binary code is moved to the right, discarding the low bit, and the high bit is supplemented with 0. Let’s take an integer of 1 byte size as an example:

  • 3 > > > 1Is equivalent to0000, 0011,Move 1 bit to the right:000, 0001,, and then fill 0 in the high order:0000, 0001,, i.e.,3 >>> 1 = 1

In js, we can always find some Hack code that uses >>> 0. The core feature is that unsigned 0 right shift can convert all types of value to number type for uniform processing, and non-number type to 0.

type operation The sample
Positive integer Do nothing 1 >>> 0 = 1
Negative integer Take the value of the complement of a negative number -1 >>> 0 = 4294967295
Boolean value That translates to 1 or 0 true >>> 0 = 1,false >>> 0 = 0
string The value is 0 if isNaN(STR); Otherwise, a value ofparseInt(str) >>> 0 '1' >>> 0 = 1,'s' >>> 0 = 0
Floating point Numbers Discard the decimal place and then perform the integer shift operation 1.1 >>> 0 = 1 >>> 0,-1.1 >>> 0 = -1 >>> 0
Other objects The unified value is 0 [] >>> 0 = 0,undefined >>> 0 = 0And…

Numerical representation

3e9

Scientific notation is a mathematical term that expresses a number as a multiplied by 10 to the power of n. For example, the speed of light is 300,000 kilometers per second. In calculation, meters are usually used as units, and it is written as 300000000m/s.

Here are a few examples of scientific notation:

1e5; // 100000 2e-4; / / 0.0002 3 e3. / / - 3000Copy the code

The Number object has a toExponential(fractionDigits) method that returns a string representation of the Number in scientific notation. The optional fractionDigits parameter is used to specify the Number of digits behind the decimal point, for example: (179000).toExponential(); 1.79 e+5 “/ /”.

JavaScript automatically converts numeric values to scientific notation when:

  1. There are more than 21 digits before the decimal point.
  2. The value is less than 1 with more than 5 zeros after the decimal point, as in0.0000001.

.5px

For example, 0.5px can be written as.5px, and 0.2 * 0.3 can be written as.2 *.3

0x, 0O and 0b

After spending a lot of time in the world of the decimal system, remember that there are other bases, and they have equal status in computers. JavaScript provides the following notation in base:

  • Binary: uses only 0 and 1 digits. The prefix is0b, decimal 13 can be expressed as0b1101
  • Octal: uses only eight digits from 0 to 7, prefixed with0 o, 0, decimal 13 can be expressed as0 o15, 015
  • Hexadecimal: contains only ten digits from 0 to 9 and six letters from A to F0x, decimal 13 can be expressed as0xd

By default, JavaScript automatically converts octal, hexadecimal, and binary to decimal for processing. To convert from decimal to another base, refer to the toString method. To convert from another base, refer to the parseInt method. To convert from another base, first convert to decimal before converting to another base.

“Words”

Array.prototype.sort

Array.prototype.sort() defaults to sort by the Unicode encoding of the string, depending on the browser that implements it. In V8, subinsertion sort is used if the Array length is less than 10, and quicksort is used if the Array length is greater than 10.

CompareFunction (a, b) : compareFunction(a, b) : compareFunction(a, b);

  • The return value is less than 0, and A is placed to the left of B
  • The return value is 0, and the positions of A and B remain the same
  • The return value is greater than 0, and A is placed to the right of B

So we can use sort to write a method that scrambles arrays:

[1,2,3,4].sort(() => .5 - Math.random())
Copy the code

However, the above implementation is not completely random. The reason is that some elements are not compared due to the instability of the sorting algorithm. For details, please refer to the question.

function shuffle(arrs) {
  for (leti = arrs.length - 1; i > 0; i -= 1) { const random = Math.floor(Math.random() * (i + 1)); [arrs[random], arrs[i]] = [arrs[i], arrs[random]]; }}Copy the code

Array.prototype.concat.apply

Apply takes arguments of an array type to call a function, while concat takes multiple arguments of a string or array, so you can use this technique to flatten a two-dimensional array directly:

Array. The prototype. Concat. Apply ([], [1, [2, 3], [4]])Copy the code

This method can also be used to write a deeper traversal method:

function flattenDeep(arrs) {
  let result = Array.prototype.concat.apply([], arrs);
  while (result.some(item => item instanceof Array)) {
    result = Array.prototype.concat.apply([], result);
  }
  return result;
}
Copy the code

After testing, the efficiency is compared with lodash as follows:

For an Array of the above methods. The prototype. Concat. Apply ([], target) also can be written as: [] concat (… The target).

Array.prototype.push.apply

In ES5, if we want to concatenate arrays, we use the concat method in arrays:

letarrs = [1, 2, 3]; Arrs = arrs. Concat ((4 and 6));Copy the code

But there is a cool way to do concatenation more concatenated using the array pass feature of the Apply method:

const arrs = [1, 2, 3];
arrs.push.apply(arrs, [4, 5, 6]);
Copy the code

Array.prototype.length

It is usually used to return the length of an array, but it is also a property with complex behavior. First, it is not used to count the number of elements in the array, but to represent the highest index in the array:

const arrs = []; arrs[5] = 1; console.log(arrs.length); / / 6Copy the code

In addition, the length length changes as the array changes, but only when the highest index value of the child element changes. If you delete the highest element using the delete method, the length does not change, because the highest index value also does not change:

const arrs = [1, 2, 3]; delete arrs[2]; // The length is still 3Copy the code

Another important feature of length is that it allows you to change its value. If the value changed is less than the maximum index of the array itself, the array is partially truncated:

const arrs = [1, 2, 3, 4];
arrs.length = 2; // arrs = [1, 2]
arrs.length = 0; // arrs = []
Copy the code

If the value assigned is greater than the current maximum index, we get a sparse array:

const arrs = [1, 2];
arrs.length = 5; // arrs = [1, 2,,,,]
Copy the code

If the value is set to 0, the array is emptied:

const arrs = [1, 2, 3, 4];
arrs.length = 0; // arrs = []
Copy the code

Using this method removes all indexes from the array, and therefore affects other values that reference the array. This is very different from using arrs = [] :

letA = [1, 2, 3];letB = [1, 2, 3];let a1 = a;
let b1 = b;
a = [];
b.length = 0;
console.log(a, b, a1, b1); // [], [], [1, 2, 3], []
Copy the code

When modifying length, note that:

  • The value must be a positive integer
  • The pass string is attempted to be converted to a numeric type

Object.prototype.toString.call

Each object has a toString(), which is called automatically when the object is referenced as a string. If this method is not overridden, toString returns [Object type], So Object. The prototype. ToString. Call just to call on native objects are covered in a way, the call will be scope refers to the Object need to decide, so that can pass the native toString method to print the Object’s type string: Object. The prototype. ToString. Call ([]) = > “[Object Array]”, using this feature, you can achieve more accurate type of judgment.

In ES3, the type obtained is the internal attribute [[Class]] attribute, which can be used to determine which built-in value a native attribute belongs to. Two new rules are added in ES5: If this value is null, undefined return [object null], [object undefined]; [[Class]] does not exist in ES6. Instead, there is an internal attribute: [[NativeBrand]], which is a tag value used to distinguish the attributes of native objects. Specific judgment rules are as follows:

19.1.3.6 Object. The prototype. The toString () When the toString method is called, the following steps are seems: If the this value is undefined,return "[object Undefined]".
If the this value is null, return "[object Null]".
Let O be ! ToObject(this value).
Let isArray be ? IsArray(O).
If isArray is true.let builtinTag be "Array".
Else if O is a String exotic object, let builtinTag be "String".
Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
Else if O has a [[Call]] internal method, let builtinTag be "Function".
Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
Else, let builtinTag be "Object".
Let tag be ? Get(O, @@toStringTag).
If Type(tag) is not String, set tag to builtinTag.
Return the string-concatenation of "[object ", tag, and "]".
This function is the %ObjProto_toString% intrinsic object.

NOTE
Historically, this function was occasionally used to access the String value of the [[Class]] internal slot that was used in previous editions of this specification as a nominal type tag for various built-in objects. The above definition of toString preserves compatibility for legacy code that uses toString as a test for those specific kinds of built-in objects. It does not provide a reliable type testing mechanism for other kinds of built-in or program defined objects. In addition, programs can use @@toStringTag in ways that will invalidate the reliability of such legacy type tests.

Copy the code

Object.create(null)

It is used to create objects that have no “side effects”, that is, it creates an empty object that does not contain the stereotype chain or other properties. Const map = {} creates an Object equivalent to Object.create(Object.prototype), which inherits the prototype chain of the Object.

JSON.parse(JSON.stringify(Obj))

A common way to deep-copy an object is to format the object as a JSON string and then parse it to get a new object. Note that it does not perform very well and cannot handle closed loop references, such as:

const obj = {a: 1};
obj.b = obj;
JSON.parse(JSON.stringify(obj)) // Uncaught TypeError: Converting circular structure to JSON
Copy the code

If the object can be copied using a shallow copy, please use the shallow copy method regardless of whether you use {… Object. Assign ({}, obj). If you need to create a new wheel, use NPM: Clone or something else.

Generate [0, 1… n-1]

Remember how neat the syntax for generating lists in Python was: [x for x in range(1, 10)]. How do you initialize an ordered sequence of 1 to 10 in JavaScript?

It is not desirable to use new Array(10) to initialize the Array and map it because only the Array length field is set:

Object.getOwnPropertyNames([1, 2, 3]) // ["0"."1"."2"."length"]

const a = new Array(3) // [undefined, undefined, undefined]
Object.getOwnPropertyNames(a) // ["length"]
Copy the code

This invalidates iteration methods such as map, filter, etc. Of course, you can manipulate the array entry by filling it with fill, but in this case you will use other methods.

Array.apply(null, {length: 3}); {length: 3} Array.prototype.apply () {array.prototype. apply ();

for (let index = 0; i < arguments[1].length; index++) {
  // pass arguments[1][index]
}
Copy the code

Array(undefined, undefined, undefined) = Array(undefined, undefined, undefined)

Array.apply(null, { length: 10 }).map((v, k) => k)
Copy the code

For ES6, you can use array. from instead of the above statement:

Array.from(new Array(10), (k, v) => v)
Copy the code

Array.from takes not only strings, sets, maps, and array-like objects as arguments, but anything that can be iterated over, as we did with generators for entertainment purposes:

function* range(start, end) {
  for (let i = start; i < end; i++) {
    yield i
  }
}
Array.from(range(1, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
[...range(1, 10)] // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Copy the code

“Theory”

With Falsy Truthy

For each type of value, each object has a Boolean value. Falsy represents a value that behaves as false in a Boolean object. JavaScript forces any type to be a Boolean object in a conditional judgment and loop. These objects all behave as Falsy when encountering an if statement:

if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if (' ')
if ("")
if (document.all)
Copy the code

Document. all is a relic of the past, so it is false. It violates the JavaScript specification and can be ignored, while NaN is a variable that should never be judged as congruent or equal.

console.log(NaN === 0) // false
console.log(NaN === NaN) // false
console.log(NaN == NaN) // false
Copy the code

We can, however, use the object. is method to determine if a value is NaN, which is a new syntax added in ES6 for comparing two values. It can be considered a stricter method than the congruent identifier, but should not be confused:

Object.is(NaN, NaN) // true
Object.is(+0, -0) // false
Copy the code

All values except Falsy are Truthy values, which are true in Boolean context.

other

This is a summary of some of JavaScript’s quirks, but only a few of them are worth digging into, and we should focus more on the principles than the syntax.

The resources

  • Modernweb.com/45-useful-j…
  • Dmitripavlutin.com/the-magic-b…
  • Medium.freecodecamp.org/9-neat-java…
  • Stackoverflow.com/questions/7…
  • Javascript.ruanyifeng.com/grammar/num…
  • Medium.freecodecamp.org/https-mediu…