Dig a hole, JavaScript Rhino Book English 7th edition The Definitive Guide, 7th Edition was actually released in May of last year (2020). At that time, I bought a book to try it. However, it was a thick English book, so I was told not to read it for a long time. But now I’m going to take advantage of the holiday to read it well, so I’m going to write down some reading notes. Let’s just get started.
GitHub link: javascript-the- Definitive – Guide
Lexical Structure
The first chapter is mainly a brief introduction to the history of JavaScript, the development trend and some simple usage, here will not expand, interested friends can go to search to have a look. So here we go straight from the lexical structure of Chapter 2.
What does lexical structure mean? Lexical structures are the lowest ground rules in JavaScript, including things like how to define variables, how to separate comments from programs, and how to separate statements.
Text and Literals
JavaScript is a case sensitive language, which means that keywords, variable names, function names, and so on are case sensitive.
let a = 1; // Ok
Let a = 1; // SyntaxError
let b = 1, B = 2; // Ok
Copy the code
Comments
Comments can be expressed in two ways. The first is //, which represents a single line comment, and the second is /*… */ used to indicate block comments.
// This is a comment
/* This is also a comment */
Copy the code
Types, Values and Variables
There are two types of JavaScript, primitive types and object types. The basic types include Number, String, Boolean, null, undefined, Symbol for ES6, and BigInt for ES2020. All other types are reference types, or objects.
//primitive types
let a = 1; // typeof a = number
let b = 'hi'; // typeof b = string
let c = true; // typeof c = boolean
let d = null; //typeof d = object * is object for reasons discussed later
let e; // typeof e = undefined
let f = Symbol(a);// typeof f = symbol
let g = 1n; //typeof g = bigint
Copy the code
Numbers
In JavaScript, numbers are represented in 64-bit floating point form defined by IEEE754 standard. This allows us to accurately represent all integers from -253 all the way up to 253. If you use larger integers, you may lose some accuracy because the mantissa is truncated.
In JavaScript, a decimal number can be represented directly by a string of digits, and both integers and floating-point numbers can be represented, such as:
1
13
1300000
1.3
133.3333
Copy the code
We can also add 0x to a hexadecimal number and 0b or 0o to an octal number, for example:
0xff / / 255
0x8f3e / / 36670
0b77 / / 63
0o11771 / / 5113
Copy the code
Arithmetic
The most basic operations include addition, subtraction, multiplication and division: +, -, *, /; Modulo also includes modulo: %; And find the square: **
3+1 / / 4
2.5-1.1 / / 1.4
2*3 / / 6
8/2 / / 4
7%2 / / 1
2**3 / / 8
Copy the code
In JavaScript, you can do more Math with Math Object than basic operations. Here are some common examples:
Math.pow(2.8) / / 256 square, is equivalent to * *
Math.round(0.67) // 1 is rounded to the nearest integer
Math.floor(0.67) // 0 is rounded down to the nearest integer
Math.ceil(0.67)// 1 is rounded up to the nearest integer
Math.abs(-3) // 3 take the absolute value
Math.max(1.2.3) // 3
Math.min(1.2.3) // 1
Math.random() // 0 <= x <1 random number from 0 to 1
Math.PI // the value of PI, approximately 3.141592653589793, is capitalized
Math.E // The base of the natural logarithm of e is approximately 2.718281828459045
Math.sqrt(25) Take the square root of 5
Math.cbrt(125) // take the cube root of 5
Math.hypot(3.4) // 5 Take the square root of the sum of squares of each input value
Math.log(Math.E**100) // Take the natural log of 100
Math.log10(100) // 2 find the log base of 10
Math.fround(1.2)// 1.2000000476837158 Accurate to the nearest 32-bit floating point number
Copy the code
Operations in JavaScript also don’t fail because they overflow or divide a number by zero. If that happens, Infinity is returned. An Infinity operation still returns Infinity. There is, however, a special case where NaN (Not a Number) is returned:
- At 0 divided by 0
- Infinity divided by Infinity
- When you take the square root of a negative number
- When a mathematical operation on a non-numeric type cannot be converted to a number
0/0 // NaN
Infinity/Infinity // NaN
Math.sqrt(-3) // NaN
'a' - 'b' // NaN
Copy the code
In addition to Infinity and NaN on global objects, Number objects also have the following common properties:
Number.isNaN(x) // Determine if x is NaN
Number.isFinite(x) // Determine if x is finite
Number.isInteger(x) // Check whether x is an integer
Number.isSafeInteger(x) // Determine if x is a safe integer (safe integer)
Number.MIN_SAFE_INTEGER // Return the minimum safe integer -2**53
Number.MAX_SAFE_INTEGER // Return the maximum safe integer 2** 53-1
Number.EPSILON // Return the minimum difference between two numbers 2**-52
Number.MAX_VALUE // The maximum value that can be expressed in JavaScript, greater than that will be converted to Infinity
Number.MIN_VALUE // The minimum value that can be expressed in JavaScript, less than which is converted to 0
Copy the code
NaN sea oh a peculiar property, it does not equal any value, including his own. To determine if a value isNaN, you can only use the isNaN() function:
let a = NaN;
a === NaN // false
a == NaN // false
isNaN(a) // true
Copy the code
BigInt
BigInt is a new numeric type added in ES2020. It can represent a 64-bit Integer. BigInt can be declared by adding n to the Integer, or by using BigInt () :
let a = 123n // typeof a = bigint
let b = BigInt(123) // typeof b = bigint
a === b // true
Copy the code
Ordinary mathematical operations can also be used for BigInt:
1000n + 1000n // 2000n
1000n - 1000n // 0n
1000n * 1000n // 1000000n
1000n / 95n // 10n
1000n % 95n // 50n
2n ** 10n // 1024n
Copy the code
BigInt and other types cannot be used because Number and BigInt do not say which is more general. Number contains decimals and BigInt contains more integers. Therefore, there is no definite answer to the return value. So JavaScript decided that BigInt could not be mixed with other types.
100n + 100 // Uncaught TypeError: Cannot mix BigInt and other types
Copy the code
But conversely, the comparison algorithm can still be executed correctly:
1 > 2n // false
1 < 2n // true
0= =0n // true
0= = =0n // false
Copy the code
Text
In JavaScript, text is represented as strings, which are immutable sequences of 16-bit values. Each 16-bit value usually represents a Unicode character. The length of a string is the number of 16-bit values it contains. There is no such thing as a Char type for a 16-bit value in JavaScript; we can do this by maintaining a string of length 1.
String (Stirng)
In JavaScript programs, characters can be enclosed by single, double, or backquotes. In single quotes, double and backquotes can be expressed in strings, and the same is true for double and backquotes:
let a = 'a'; // Ok
let b = "b"; // Ok
let c = `c`; // Ok
let d = 'this is "cool"! '; // Ok
let e = "it is 10 o'clock"; // Ok
let f = `"something", 'something else'`; // Ok
Copy the code
Unlike single and double quotes, JavaScript expressions can also be used inside backquotes:
let a = 1 / / 1
let b = 2 / / 2
let c = `${a} + ${b} = ${a+b}` // 1 + 2 = 3
Copy the code
Working with Strings
One of the most commonly used string built-in features is the concatenation string. If we are to + operation of two Numbers, it will return them and, if to + string operations, will connect the two strings, if is Numbers and strings are connected by means of a string and returns a string (because the string is more general, the package of all the Number of characters expression) :
1 + 1 / / 2
'1' + '1' / / '11'
'1' + 1 / / '11'
Copy the code
Strings can also be passed by === or! ==, < or > can also be used to compare strings by comparing the size of each 16-bit value in a string one by one:
'a'= = ="a" // true
'a'! = ="b" // true
'a' > 'b' // false
'aaa' > 'b' // false
Copy the code
There are many apis for strings in JavaScript, here are some commonly used ones:
let s = 'Hello, world';
s.length // 12 Contains a string of characters
s.substring(0.4) The first argument is the index at the beginning of the substring, and the second argument is the index that is not included in the substring after this index
s.slice(0.4) / / same as above
s.slice(-3) // 'RLD' takes the last three 16-bit values
s.split(', ') // ["Hello", "world"] splits the string with ','
s.indexOf('l') // 2 Find the first occurrence of 'l'
s.lastIndexOf('l'.2) // 10 Locate the last occurrence of 'l'
s.startsWith('He') // true determines whether the string begins with 'He'
s.endsWith('ld') // true checks whether the string ends in 'ld'
s.includes('or') // check whether the string contains 'or'
s.replace('hello'.'hi') // 'hi, world' replaces the first substring that occurs
s.replaceAll('l'.'L') // 'HeLLo, worLd' replaces all substrings that match
s.toLowerCase() // 'hello, world' becomes lowercase
s.toUpperCase() // 'HELLO,WORLD' becomes uppercase
s.charAt(1) // 'e' finds a 16-bit value based on index
s.charCodeAt(1) // '101' finds a 16-bit value based on index
s.codePointAt(1) // '101' finds a sequence of 16-bit values based on index
's'.padStart(3.'! ') / / '!!!!! S prime fills on the left side! All the way to length 3
's'.padStart(3.'! ') // 's!! 'Fill on the right side! All the way to length 3
' s '.trim() // 's' trim Spaces on both sides
' s '.trimStart() 's 'Trim left space' s '.trimEnd() ' s'Trim space on the rightCopy the code
Because string values in JavaScript are immutable, functions like replace that modify strings do not change the original string itself, but simply create a new string.
We can also treat a string as if it were a read-only array:
let s = 'Hello, world';
s.charAt(1) // 'e'
s[1] // 'e' can be used instead of charAt()
Copy the code
Boolean Values
A Boolean value can be used to represent truth or falsehood, on or off, yes or no. Booleans in JavaScript have two values, true and false. Booleans are usually the result of comparisons in JavaScript. Boolean values have the toString() function, which can be converted to ‘true’ or ‘false’.
Null and Undefined (Null and Undefined)
Null is a missing keyword commonly used to indicate a value. Using typeof on NULL returns ‘object’, which means that NULL can be thought of as a special object representing no object. But in most cases, NULL is treated as a unique member of its type and can be used to represent strings or numbers that have no value. Most languages have nulls similar to JavaScript null, nil, Node.
Undefined is used to indicate the loss of a deeper value, which indicates that the value of a variable has not been initialized at the time of the query, or that an array element or object attribute does not exist. When a function does not have an explicit return value, undefined is used as the return value. The value is undefined even if the parameter of the function is not defined. Undefined is a global constant object that is a unique member of its type.
Undefined usually denotes the absence of an error-like value at the system level; Null is used to indicate the absence of a common value at the programming level.
null= =undefined // true
null= = =undefined // false
Copy the code
Symbols
Symbol was introduced in ES6 to represent identifiers for attributes that are not represented by strings. The most basic object in JavaScript is an unordered collection of properties. Each property usually has a key and a value. The identifier is usually a string (symbol can now also be used as an identifier).
Symbols can be constructed using Symbol(). This function never returns two identical values. So the value of Symbol obtained by this method is absolutely different. When constructing Symbol, you pass in a string that appears in the return value of toString().
let a = Symbol(a)let b = Symbol()
a == b // false
a.toString() // "Symbol()"
let c = Symbol('notShared')
let d = Symbol('notShared')
c == d // false
c.toString() // "Symbol(notShared)"
Copy the code
Use symbol.for () when you want to define the value of a Symbol that will be widely used. It takes a string as an argument to which the Symbol is bound. If the string is not bound, this returns a new Symbol; But if the string is already bound, it returns the bound Symbol. We can also use symbol.keyfor () to return the string bound to a Symbol.
let a = Symbol.for('shared')
let b = Symbol.for('shared')
a === b // true
a.toString() // "Symbol(shared)"
Symbol.keyFor(a) // "shared"
let c = Symbol('shared') // Do not use symbol.for () construct
c === a // false
Copy the code
The Global Object
The main purpose of a global object is to hold properties that are available globally. Whenever JavaScript is compiled, or whenever a new browser window is opened, a new global object is created that contains:
- Global constants such as undefined, Infinity, NaN
- Global functions such as isNaN(), parseInt()
- Constructors, such as Object(), String(), Date()
- Global objects such as Math and JSON
Properties defined in global objects are not reserved words; they can be overridden, but this is not recommended. GlobalThis, added in ES2020, allows us to point to a global object and access its contents.
Immutable Primitive Values and Mutable Object References
In Javascript, base types (Number, String) and reference types (Object) are fundamentally different. Primitive types are Immutable, which means that we cannot change the value of a primitive type; In contrast, reference types (objects) are mutable.
let s = 'string';
s.slice(0.3); // returns "STR" without changing the value of s
s // Still "string"
s = s.slice(0.3) // s = "STR" assignment gives it a new value instead of changing it
let a = [];
a.push(1); // A adds a new element, he is changed
a / / [1]
Copy the code
In addition, primitive types are compared by value: we can only say that two values are the same if they are; In contrast, reference types are compared by reference: two values are the same only if they refer to the same underlying object.
let a = 'str'
let b = 'str'
a === b // true
let c = [1.2.3]
let d = [1.2.3]
c === d // false c and d look the same, but reference two different arrays, so not the same
let e = Object(a)let f = e
e === f // e and f refer to the same object, so they are identical
e.name = 'jake' // e = {name: "jake"}
f // f = {name: "jake"} changes to e are mapped to f
Copy the code
In the e and F examples above, we can see that when an assignment is made to an object, it only assigns its reference value (a shallow copy), and it does not create a new object. If you want to copy a brand new object, you must explicitly copy every property of the object, or every element of the array (deep copy). This way, changes to the original array do not affect the new array:
let a = [1.2.3.4];
let b = [];
for (let i = 0; i < a.length; i++) {
b[i] = a[i];
}
a[0] = 4; / / a = [4, 2, 3, 4]
b // b = [1,2,3,4
Copy the code
Similarly, if we want to compare two separate arrays or objects, we need to compare all of their elements or attributes. The following example can be used to compare two arrays:
function areArraysEqual(a,b) {
if (a === b) { // Return true if the same array is referenced
return true;
}
if(a.length ! == b.length) {// Return false if the length is different
return false;
}
for (let i = 0; i< a.length; i++) {
if(a[i] ! == b[i]) {return false; // If there are different elements, return false}}return true; // All elements are the same, return true
Copy the code
Type Conversions
JavaScript is very flexible about value types. When expecting a Boolean value, we can fill in a non-Boolean value and JavaScript will automatically convert it to a Boolean value for us. Similarly, when a string is needed, it converts the received value, no matter what, to the old string; The same is true for numbers:
10 + 'hello' // '10hello' converts a number to a string
'7' * '4' // 28 Converts strings to numbers
1 - 'x' // NaN converts a string to an old number, and returns NaN if no meaningful conversion is possible
Copy the code
The following table lists how JavaScript performs type conversions:
Value | to String | to Number | to Boolean |
---|---|---|---|
undefined | “undefined” | NaN | false |
null | “null” | 0 | false |
true | “true” | 1 | – |
false | “false” | 0 | – |
“” | – | 0 | false |
“1.2” | – | 1.2 | true |
“str” | – | NaN | true |
0 | “0” | – | false |
1.2 | “1.2” | – | true |
Infinity | “Infinity” | – | true |
NaN | “NaN” | – | false |
Conversions and Equality
There are two operators in JavaScript to compare whether two values are equal. The first is strict equality operator: “===”. This comparison returns false if the two values are of different types rather than converting them. In programming, this should also be the comparison operation used most of the time. Second, because JavaScript has flexible type conversions, there are abstract (non-strict) equality comparisons: “==”. This converts the two values to the same type before the comparison (the conversion pattern is shown below) and the congruent operator comparison (===) after the conversion.
(From MDN)
Note that although we can type undefined whenever we need a Boolean value and then convert it to false, this does not mean undefined == false:
if (!undefined) { / /! Undefined is converted to! False is true
console.log("hello");
} // "hello" will be printed to the console
undefined= =false // false, unequal
Copy the code
Explicit Conversions Explicit Conversions
Although JavaScript does a lot of automatic type conversions, at some point we still need to explicitly convert them:
Number('3'); // string to number
String(false); // boolean to string
Boolean([]); // objet to boolean
Copy the code
All values except null and undefined have a toString() method, whose result is equivalent to the String() function in most cases.
In addition, we can sometimes write explicit new Boolean(), Number(), or String() to create a new object package for raw data that behaves as if it were the original value, but these are forgotten in JavaScript history. There is little reason to use them now.
Some operators perform implicit type conversions automatically:
x + ' ' // Same as String(x)
+x // Equivalent to Number(x)
x-0 // Equivalent to Number(x)!!!!! x// Same as Boolean(x)
Copy the code
The toStirng() method defined on a number can take an extra argument to indicate the base (base) of the number, supporting numbers from 2 to 36, ignoring numbers after the decimal point:
let n = 17;
n.toString(2); / / "10001"
n.toString(8); / / "21"
n.toString(16); / / "11"
Copy the code
Sometimes for statistical purposes, the following methods can be used:
let n = 12345.6789
n.toFixed() / / "12346"
n.toFixed(3) // "12345.679" only three decimal places are reserved
n.toExponential() 1.23456789 e+4 "/ /"
n.toExponential(4) // "1.2346e+4"; // "1.2346e+4"
n.toPrecision(4) 1.235 e+4 "/ /"
n.toPrecision(11) // "12345.678900" The number of digits for a given result, if not numbered
Copy the code
Number(), parseInt(), parseFloat()
Number('123.45 asd') // NaN Returns NaN as long as the input value is a string that contains no numbers or Spaces
parseInt('123.45 asd') // 123 is more flexible and returns an integer
parseInt('123.45 asd') // 123.45 is more flexible and returns floating point numbers
parseInt('111'.2) // 7 can be passed a second argument to specify the base
Copy the code
Object to Primitive Conversions
Conversion from a reference value to a base value is not as simple as conversion from base value to base value, it can be more complex and obscure. One reason for this is that JavaScript objects sometimes contain more than one base value. The most basic object-to-base conversion algorithm in JavaScript is as follows:
- Prefer-string: This algorithm returns a raw value and prefers to convert to a string when it can be converted to a string
- Prefer-number: This algorithm returns a raw value and prefers to convert to a number when it can be converted to a number
- No-preference: This algorithm has no preferred raw values, and classes can define their own conversion methods. For JavaScript embedded object types, Date uses prefer-number, and prefer-string is used for all but Date.
Object to string conversion
In the case of converting an object to a string, JavaScript will use prefer-string algorithm to convert the converted value to a string.
This conversion occurs when, for example, an object is passed as an argument to an embedded function that requires an argument string; Or passing an object to the String() function; Or when an object is inserted in a backquote.
Object to number conversion
In the case of converting an object to a string, JavaScript converts it using the prefer-number algorithm and converts the converted value to a number.
This conversion occurs when an object is passed as an argument to an embedded function that requires a number; This conversion is also performed for the vast majority of operators that require numeric execution.
ToString () and valueOf() methods
All objects inherit the toString() and valueOf() conversion methods.
The first is toString(), which returns an object represented by a string, but its default value is quite special:
let a = {
name: 'jake'.age: 22
}
a.toString() // "[object Object]"
String(a) // "[object Object]"
Copy the code
Many defining classes have more precise toString() methods, such as array toString(), which returns strings linked to all elements by ‘,’; Date’s toString() returns a readable Date and time; For functions the JavaScript source code is returned:
let a = [1.2.3.4];
a.toString(); / / "1, 2, 3, 4"
let b = () = > {
console.log(1);
}
b.toString();
/ / "() = > {
// console.log(1);
// }"
let c = new Date(2021.1.12)
c.toString() // "Fri Feb 12 2021 00:00:00 GMT+1100 (Australian Eastern Daylight Time)"
Copy the code
The second is the valueOf() method, whose purpose is to return a base valueOf this object, if one exists. Most of the time, however, an object is a combination of many base values, so there is no way to represent it directly as a base value, so the default value for valueOf() returns the object itself:
let a = {
name: 'jake'.age: '22'
}
a.valueOf() // {name: "jake", age: "22"}
Copy the code
Variable Declaration and Assignment
In JavaScript, variables can be declared using lets and const. Before ES6, var was used, but var is a bit special.
Use let and const declarations
In today’s mainstream JavaScript, we use let and const to declare variables and assign them an initial value whenever possible. If you do not assign an initial value to a variable, the value of the variable will be undefined until it is assigned. If you declare a constant, you use const instead of let. The value of a constant cannot be changed, and any attempt to change it will result in TypeError. Usually we declare a constant in all uppercase.
let num1 = 1;
num1 = 2 // Ok
const NUM2 = 1;
NUM2 = 2; // TypeError
Copy the code
VARIABLE AND CONSTANT SCOPE
The scope of a variable is the area in which it is defined and applied in the code. Variables and constants declared using lets or const are block-scoped. This means that they only work within the block they define. Simply put, if a variable is defined within a pair of curly braces {… }, the variable can only be accessed within the braces. Of course, accessing it before the declaration is also invalid.
If a variable is defined in toplevel, it is a global variable and has a global scope.
let a = 1;
{ let b = 2; }
a // Ok
b // ReferenceError
Copy the code
Repeat statement
Repeating the same variable/constant name in the same scope raises a SyntaxError. It is possible (but not recommended) in different scopes:
let a = 1;
let a = 2; // SyntaxError
let b = 1;
{ let b = 2; } // Ok but not recommended
Copy the code
Declarations and types
In JavaScript, variables or constants are declared untyped. A variable can hold any type of value:
let a = 10;
typeof a // "number"
a = 'ten';
typeof a // "string"
Copy the code
Variable Declarations with var
Prior to ES6, the only way to define variables was var, and constants were not possible. Although var and let are similar, they have the following differences:
- The scope of variables declared by var is function scope, while let is block scope.
- If you declare var outside of a function, you declare a global variable. However, there are a number of differences between this and the variables declared by let. The variables declared by var are defined as properties of the global object (var a = 1 equals globalThis.a = 1), while let is not a property of the global object. And variables declared using var cannot be deleted by the DELETE operator.
- It is legal to declare the same variable name repeatedly with var.
- Var life variables also have the property of reactive. After var declares a variable, it is promoted to the top of the function scope (but assignment is not promoted). So variables defined by var in functions are not reported as errors, but if they are not assigned, the value will be undefined.
In non-strict mode, variables are defined as properties of the global object if they are not directly assigned with let, var, or const. The difference is that variables declared this way can be deleted using the delete operator, but var declarations cannot.
Destructuring Assignment
Destruct assignment is a new declaration and assignment syntax added to ES6. The syntax of deconstructed assignment mimics the syntax of an array or object (a structured value) to the right of the equals sign and one or more variable names to the left. When a deconstruction assignment is performed, the structure values on the right are deconstructed and then assigned to the variables on the left.
let [a,b] = [1.2]; // let a = 1, b = 2
[a,b] = [a+1, b+1]; // a = a+1, b = b+1
[a,b] = [b,a]; // Swap a and b
[a,b] / / (3, 2]
Copy the code
Functions that return arrays when destructed can be assigned more easily. It is not necessary to have the same number of variables on the left and right. The extra variables on the left will be assigned as undefined and the extra values on the right will be ignored:
let [a,b] = [1]; // a = 1, b = undefined
let [a] = [1.2]; // a = 1
Copy the code
This is also possible if you don’t want to get every value of the structure value; You can also do this by prefixing the variable with three dots (…). Make the last variable get all the remaining values; Destructed arrays can also be used with nested arrays:
let [a, , , ,b] = [1.2.3.4.5]; // a = 1, b = 5
let [a, ...b] = [1.2.3.4.5]; // a = 1, b = [2,3,4,5]
let [a,[b,c],[d,e]] = [1[2.3], [4.5]] // a=1, b=2, c=3, d=4, e=5
Copy the code
A struct value doesn’t have to be an array, it can also be a traversable object, so any object that can be used by a for/of loop can be a struct value.
let [first,...rest] = "Hello"; // first = "H"; rest = ["e","l","l","o"]
let {name, age} = {name: "jake".age: 22}; // name = "jake", age = 22
Copy the code
Destruct assignment can also be used for multiple nested objects/arrays, but it can become difficult to read and maintain, so it is not recommended.
The small knot
The main points of this chapter:
- How do I manipulate and change characters/numbers in JavaScript
- How other basic types in JavaScript work: Booleans, Symbols, numm, undefined
- The difference between immutable base types and mutable reference types
- Explicit and implicit type conversions
- Declaration and assignment of variables/constants and their scope
Next chapter links: 2. Expressions and operators