In order to consolidate the basic knowledge of JavaScript, I reread the book “JavaScript Advanced Programming” and summarized some knowledge points that are seldom used in ordinary programming (especially browser side programming). The first part mainly summarized the content of the first five chapters, including basic concepts, variables, scope, memory and reference type related issues.

1. Basic concepts

1. <script>Of the labelasnycwithdefer

This is the sequence diagram of async and defer when rendering the page. The blue line represents the network read and the red line represents the execution time, both of which are specific to the script. The green line represents HTML parsing.

That means async is out of order and defer executes sequentially, which makes Async more suitable for libraries like Baidu Analytics or Google Analytics that don’t rely on other scripts. As you can see from the diagram, a normal

2. Values of all data types have andtrueandfalseThe value of the equivalent

The data type The value converted to true Convert to a value of false
Boolean true true
String Any non-empty string An empty string
Number Any non-zero numeric value (including infinity) 0 and NaN
Object Any object null
Undefined n/a undefined

If an object is initialized, var obj = null is used. This is convenient to determine the null state of the variable, which is conducive to the type management of weakly typed JavaScript languages.

3. Infinity of numbers

The maximum Number in js is stored in number.max_value, and the minimum Number is stored in number.min_value. Infinity cannot be used in the calculation. IsFinite () method is used to determine whether the Number is infinite.

4. Relational operators<,>,> =,< =Judgment string

The size of the string depends on the character number of the first character. This can be applied to the position of English letters (must be uppercase or lowercase, can be applied to alphabetical sorting).

var result = 'Brick'.toLowerCase() > 'alphabet'.toLowerCase() // true
Copy the code

B comes after A in the alphabet

5. for-incycle

ECMAScript object properties have no order, so the order of property names printed through a for-in loop is theoretically unpredictable, and the order returned may vary from browser to browser.

6. forThe cycle oflabel

Marks the for loop so that it can break or continue the specified loop

var a = 1;
label : for ( var i = 0; i < 5; i++ ) {
    for ( var j = 0; j < 5; j++ ) {
        if ( i == 3 ) {
            breaklabel; } a++; }}console.log( a ); / / 16
// Break breaks out of the specified outer loop, the entire loop is nested, so only three inner loops are executed, +5+5+5 equals 16
var a = 1;
for ( var i = 0; i < 5; i++ ) {
    for ( var j = 0; j < 5; j++ ) {
        if ( i == 3 ) {
            break; } a++; }}console.log( a ); / / 21
/ / at this point do not specify a label, break out of the inner loop, namely skip a when I = 3 external loop, therefore carried out 5-1 = 4 times in total internal circulation, + 5 + 5 + 5 + 5 results for the 21st
Copy the code

7. switch

The switch statement uses the congruent operator when comparing values, so no type conversion occurs.

8. Function

  1. Inside the function you can passarguments[0].arguments[1]To access the parameters of the function,argumentsIs an array-like object (noArrayThe instance)
  2. usearguments.lengthJudgment parameter lengths are often used for method encapsulation, such as:
function doSth () {
    if (arguments.length === 1) {
        alert(arguments[0])}else if (arguments.length === 2) {
        alert(arguments[0].arguments[1])}}Copy the code

Put it another way: by examining the passed in the parameter type and quantity and make a different reaction, can be used to simulate the overloading of methods (such as Java language to write two definitions for function, as long as the two signature – accept the parameters of the definition of the type and amount of different, the way of function calls, called function overloading).

2. Variables, scope, memory

1. Variable assignment

In variable assignment, the parameter is passed by value only. In reference assignment, the memory pointer is passed to the value in the memory stack, which is still passed by value.

function setName (obj) {
    obj.name = 'Nicholas'
    obj = new Object()
    obj.name = 'Greg'
}
var person = new Object()
setName(person)
alert(person.name) // Nicholas
Copy the code

Since a new object is created for the parameter obj inside the function, obj has a new memory address (which is a local variable in the function block), so the subsequent changes to the object are made to the object stored in the new memory address, so the object referred to by the parameter obj will not be modified. So obj.name will return the result of the assignment without declaring a new memory pointer, Nicholas.

2. Scope of function parameters

Arguments to a function are treated as variables, and therefore have the same access rules as any other variable in the execution environment (function arguments are local variables within a function).

3. Garbage collection strategy

  1. Mark clear

First mark all variables in memory; Then clear the tags of the variables in scope and those referenced by the variables in scope; Treat the remaining tags as variables to be cleared; Perform cleanup — destroy the tag and reclaim memory.

  1. Reference counting

Reference counting tracks the number of times each variable is referenced and waits until the number of references to a variable reaches zero before clearing. This can cause problems with circular references:

function problem () {
    var objA = new Object(a)var objB = new Object()
    
    objA.someOtherObject = objB
    objB.anotherObject = objA
}
Copy the code

In this case, objA and objB reference each other through their own attributes, and the number of references is always 2. Therefore, under the reference-counting policy, the memory of these two variables is never reclaimed, resulting in performance degradation or even crash.

In versions of IE below IE9, BOM objects and DOM objects are not native JavaScript objects, but are implemented in C++ in the form of Component Model objects (COM), which use reference counting for garbage removal. So in these versions, circular references can also occur if a DOM or BOM object is cross-referenced with a native JavaScript object:

var element = document.getElementById('some_element')
var myObject = new Object()
myObject.element = element
element.someObject = myObject
Copy the code

In the example above, a DOM element has a circular reference to a native JavaScript object, and the DOM object remains in memory even if the DOM node is removed from the HTML

  1. Performance issues

IE’s garbage collection frequency is defined by memory allocation, and collection is triggered when memory usage exceeds a certain threshold. Prior to IE7, this threshold was fixed (256 variables, 4096 object literals and array elements, or 64KB strings), but if a program has that many variables, The memory ratio of other variables must remain at this high level for a long time, thus triggering garbage collection and causing a performance explosion. IE7 optimizes this by dynamically setting thresholds based on memory usage.

  1. Remove references

Manually dereference global variables to avoid unnecessary memory usage.

// Dereference
var asd = { a: 213 }
asd = null
Copy the code

Dereference does not immediately free the variable, but marks it up so that it can be collected when the next garbage collection operation runs.

3. Reference types

1. Object literals

An Object can be declared either by creating an instance of the Object constructor with the new operator or by creating an instance with the Object literal syntax

// new
var obj1 = new Object(a)// Object literals
var obj2 = {}
Copy the code
  1. When an object is created using an object literal, it is not actually calledObjectThe constructor
  2. In general, although named parameters are easy to handle when wrapping functions, they are less flexible when dealing with large amounts of data, so arguments that must be passed in are handled with named parameters, while a large number of optional parameters are passed in with object literals.

2. Array operators

  1. arr.push(): into the stack. Multiple values can be pushed simultaneously, such asarr.push(1, 2, 3), appends 1, 2, and 3 to the end of the array.
  2. arr.pop(): out of the stack. An unstack operation is performed on the last value of the array and returns that value. The unstack operation modifies the array.
  3. arr.unshift(): Enters the queue. Push the value to the front of the array. It is also possible to push multiple values:arr.unshift(1, 2, 3).
  4. arr.shift(): Exits the queue. Dequeue the first item of the array and return this value. Dequeue modifies the array.
  5. arr.reverse(): inversion. Used simply to reverse array order, as in[1, 3, 2, 5, 4].reverse()Will get[4, 5, 2, 3, 1]
  6. arr.sort(): sorting. By default,sort()Sort an array in ascending order that calls each itemtoString()Method transformation is then compared, even though all array items are numbers, so generally insort()To implement the correct sort

The comparison function takes two arguments and returns a negative number if the first argument should come before the second, 0 if the two arguments are equal, and a positive number if the first argument should come after the second.

// This comparison function works for most data types and is called by passing the whole function as an argument to sort()
function compare (val1, val2) {
    if (val1 < val2) {
        return - 1
    } else if (val1 > val2) {
        return 1
    } else {
        return 0}}var arr = [1.3.2.5.4]
arr.sort(compare) // [1, 2, 3, 4, 5]

// You can use this simpler function for numeric types or for variable types whose valueOf() method returns a value
function compareEasy (val1, val2) {
    return val2 - val1
}
Copy the code
  1. arr1.concat(): joining together. Create a copy of the array and concatenate the parameters to the copy
// If no argument is received, the copy is returned. This method can be used for deep array copying
var copy = [1.2.3].concat() / / [1, 2, 3]
// If one or more arrays of parameters are received, all items of the array are appended to the end of the result array
var arr1 = [1.2.3].concat([4.5], [6]) // [1, 2, 3, 4, 5, 6]
// If the parameters received are not arrays, the values are simply appended to the result array
var arr2 = [1.2.3].concat(4.5) // [1, 2, 3, 4, 5]
Copy the code
  1. arr.split(): Creates a new array based on one or more items in the array
// Pass in an argument that returns a new array containing the contents of this location to the end of the array
var arr1 = [1.2.3.4.5].slice(1) // [2, 3, 4, 5]
// Take two arguments to return a new array containing the contents from the start position to the end position
var arr2 = [1.2.3.4.5].slice(1.4) / / [2, 3, 4]
// If the argument is negative, the start and end positions are calculated from the last item in the array, and a new array is returned
var arr3 = [1.2.3.4.5].slice(2 -.- 1) / / [4]
// If the end position is less than the start position, an empty array is returned
var arr4 = [1.2.3.4.5].slice(- 1.2 -) / / []
Copy the code
  1. arr.splice(): Provides the insert, delete, and replace functions for arrays. When called, this function always returns an array containing items deleted from the original array (or an empty array if not deleted).
// Delete: specifies the location of the first item to be deleted and the number of items to be deleted
var arr1 = [1.2.3.4.5].splice(0.2) // Return: [1, 2], the original array: [3, 4, 5]
// Insert: Specifies the position of the item to be inserted, 0 (the number of deleted items) and the item to be inserted
var arr2 = [1.2.3.4.5].splice(2.0.'a'.'b') // Return: [], array: [1, 2, 'a', 'b', 3, 4, 5]
// Replace: Specifies the position of the item to be inserted, the number of items to be deleted, and the item to be inserted
var arr2 = [1.2.3.4.5].splice(2.1.'a'.'b') // Return: [3], the original array: [1, 2, 'a', 'b', 4, 5]
Copy the code
  1. arr.indexOf() & arr.lastIndexOf(): Is used to find the position of an item in an arrayindexOf()Starting at the beginning of the array,lastIndexOf()The search starts at the end of the array. Both of them take two arguments, the item to be searched and the starting point of the search. It uses the congruent operator to find comparisons, so it must ensure that the search item is of the same data type as the target
var arr = [1.2.3.2.1]
arr.indexOf(0) // -1
arr.indexOf(1) / / 0
arr.lastIndexOf(1) / / 4
arr.indexOf(1.2) / / 4
// It compares by reference when looking for reference type variables
var obj = { attr: 1 }
var arr1 = [{ attr: 1 }]
var arr2 = [obj]
arr1.indexOf(obj) // -1
arr2.indexOf(obj) / / 0
Copy the code
  1. arr.every() & arr.filter() & arr.forEach() & arr.map() & arr.some(): iteration. Each method takes two parameters: the function to run on each item and the scoped object that runs the function — affecting the value of this. The functions passed into these methods take three arguments: the value of the array item, the position of the item in the array, and the array itself.

Every () : Runs the given function on each item in the array, and returns true if the function returns true for each item. Filter () : Returns an array of items that return true by running the given function on each item in the array. ForEach () : Runs the given function on each item in the array, with no return value. Map () : Runs the given function on each item in the array, returning an array of the results of each function call. Some () : Runs the given function on each item in the array, returning true if the function returns true for any item

  1. arr.reduce() & arr.reduceRight(): merge. Iterating over all of the array items, building a return value, each of which takes two arguments, a function called on each item and an initial value (optional) as the basis for merging. The function passed in takes four arguments: the previous value, the current value, the item’s index, and the array object.
// Sum all the entries
var values = [1.2.3.4.5]
var sum = values.reduce(function (prev, cur, index, array) {
    return prev + cur
}) / / 15
Copy the code

3. Dateobject

  1. Date.parse(): can be used to convert a fixed date format string to a timestamp. The following date formats are supported

Month/day/year: 6/13/2018 English month name day, year: January 12,2018 English week day English month name day year: minute: second time zone: Tue May 25 2018 00:00:00 GMT-0700 ISO8601 Extended format YYYY-MM-DDTHH: MM: SS :sssZ: 2018-05-25T00:00:00

  1. Date.now(): Facilitates obtaining the current system timestamp
  2. DateAn instance of a type can be directly compared with a greater-than – less sign, which it callsvalueOf()Method converts a date object to a timestamp and compares values.

4. RegExpobject

  1. Create a re with a literal approach:var expression = / pattern / flags. Each re has one or more identifiers (flags) to indicate the behavior of the regular expression.

G: represents the global pattern, meaning that the re matches all characters, rather than ending at the first character. I: Insensitive mode, which is case insensitive during matching. M: indicates multiline mode, that is, after reaching the end of a line of text, the search continues to see if there is a regular item in the next line.

The RegExp properties left left left left left left left left left left down down down down down down down down down left left left left left left down down down down down

  1. global: Boolean value indicating whether it is setglogo

  2. ignoreCase: Boolean value indicating whether it is setilogo

  3. multiline: Boolean value indicating whether it is setmlogo

  4. lastIndex: integer, indicating the next position of the last successful match. The next search starts from this position

  5. source: A string representation of the regular expression, returned as a literal rather than the string pattern passed in to the constructor

The RegExp methods left left left left left left left down down down down down down left left left left left left left left left down down down down down down down down

  1. exec(): It takes an argument to match the string of the re and returns an array containing information about the first match. Continuous exec() of the same re will match to the next position that satisfies the rule (as opposed to returning only the first position that matchestest())
var text = 'cat, bat, sat, fat'
var pattern = /.at/g
var matches = pattern.exec(text)
console.log(matches.index) / / 0
console.log(matches[0]) // cat
console.log(matches.lastIndex) / / 3
matches = pattern.exec(text)
console.log(matches.index) / / 5
console.log(matches[0]) // bat
console.log(matches.lastIndex) / / 8
Copy the code
  1. RegExpStereotype properties: These properties are based on the most recent regular expression generation
Long attribute name Short attribute name instructions
input The $_ String that was last matched. Opera does not implement this property
lastMatch $& The content of the last match. Opera does not implement this property
lastParen + $ The capture group that was last matched. Opera does not implement this property
leftContext $` The text before lastMatch in the input string
multiline $* Boolean value indicating whether all res use multi-line mode. IE and Opera do not implement this property
rightContext $’ The text after lastMatch in the input string
var text = 'this has been a short summer'
var pattern = / (.). hort/g
if (pattern.test(text)) {
    console.log(RegExp.input) // this has been a short summer
    console.log(RegExp.leftContext) // this has been a
    console.log(RegExp.rightContext) // summer
    console.log(RegExp.lastMatch) // short
    console.log(RegExp.lastParen) // s
    console.log(RegExp.multiline) // false
}
Copy the code
  1. RegExpAlso providesRegExp.$1Such nine are used to store the first to ninth capture groups in the callexec()ortest()These capture groups are automatically populated.
var text = 'this has been a short summer'
var pattern = / (..) or(.) /g

if (pattern.test(text)) {
    alert(RegExp. $1)
    alert(RegExp. $2)}Copy the code

5. Functiontype

  1. arguments.calleeGet the function itself using this argument inside the function:
// Factorial, usually implemented recursively, but coupled to the function name
function factorial (num) {
    if (num <= 1) {
        return 1
    } else {
        return num * factorial(num - 1)}}// Use arguments.callee to decouple
function factorial (num) {
    if (num <= 1) {
        return 1
    } else {
        return num * arguments.callee(num - 1)}}Copy the code
  1. arguments.callee.caller: using functionscallerProperty has access to the environment in which the function was calledarguments.callee.callerAccess,ES5It also defines a value ofundefinedthearguments.callerField and function itselfcallerField to distinguish. In strict mode, accessarguments.calleeandarguments.callerAll lead to errors.
  2. length & prototype: holds the number of arguments the function wants to receive and the prototype object of the function, respectively.

In ES6, length returns only the number of arguments without a default value

(function (a) {}).length / / 1
(function (a = 5) {}).length / / 0
Copy the code
  1. call() & apply(): You can change the scope of a function to make the callcall()Method by concatenating arguments with commas,apply()The method takes arguments in an array:
function sum (num1, num2) {
    return num1 + num2
}
function callSum1 (num1, num2) {
    return sum.apply(this.arguments)}function callSum2 (num1, num2) {
    return sum.apply(this, [num1, num2])
}
function callSum3 (num1, num2) {
    return sum.call(this, num1, num2)
}
console.log(callSum1(10.10)) / / 20
console.log(callSum2(10.10)) / / 20
console.log(callSum3(10.10)) / / 20
Copy the code

The first argument passed to apply() and call() above refers to the scope to which the call is made:

window.color = 'red'
var o = { color: 'blue' }

function sayColor () { alert(this.color) }

sayColor() // red: The calling environment is global scoped, so window.color
sayColor.call(this) // red: Pass this as the global scope and get window.color
sayColor.call(window) // red: the scope is directly specified as window to get window.color
sayColor.call(o) // blue: scope is o to get O.color
Copy the code

Expand the result parsing of ES6 declaration

// The arrow function is already defined to refer to this; The arrow function does not have its own this; it always refers to the this of the outer function
window.color = 'red'
var o = {
    color: 'blue'.sayColor: (a)= > { alert(this.color) }
}

o.sayColor() // red: the arrow function this refers to this of the declared outer function, which has only global scope, so window.color
o.sayColor.call(this) // red: specifies the global scope, window.color
o.sayColor.call(window) // red: specifies the global scope, window.color
o.sayColor.call(o) // red: the arrow function this refers to this of the declared outer function, which has only global scope, so window.color
Copy the code
SayColor: function () {alert(this.color)}
window.color = 'red'
var o = {
    color: 'blue',
    sayColor () { alert(this.color) }
}

o.sayColor() // blue: when called from o, o is used as the current scope by default and O.color is used
o.sayColor.call(this) // red: specifies the global scope, window.color
o.sayColor.call(window) // red: specifies the global scope, window.color
o.sayColor.call(o) // blue: specify o as scope, take O.color
Copy the code
  1. bind(): can also be used to change the scope of a function, but this method returns a rescoped function declaration that requires a pair of parentheses to execute
window.color = 'red'
var o = { color: 'blue' }

function sayColor () { alert(this.color) }

sayColor.bind(o)() // blue
Copy the code

6. Basic packaging typesBoolean,String,Number

  1. In fact, when accessing a method or property of an underlying type value,jsThe engine enters a “read mode” where it creates an instance object of the underlying type, calls properties or methods in the instance object, and unregisters the instance object:
// When executing this code
var s1 = 'abc'
var s2 = s1.substring(s1)
// This is how the engine works
var s1 = new String('abc')
var s2 = s1.substring(s1)
s1 = null
Copy the code

Use the new operator to create the object instance and basic packing type difference between its statement cycle, object instance before execution flow away from the current scope will always exist (as a reference type variable), while the basic packing type in only one line of code to access it’s time to create, to leave this line of code will be destroyed immediately:

var s1 = 'abc'
s1.color = 'blue'
console.log(s1.color) // undefined
Copy the code

Number Type method ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

  1. toExponential(): Returns a scientific notation for a number that accepts a single, parameter representing the number of digits reserved to the decimal point
var num = 10
console.log(num.toExponential(1)) / / '1.0 e+1'
Copy the code
  1. toPrecision(): Returns the most reasonable representation of a number, either scientific notation or numerical value, which takes a single argument representing the number of bits of all numeric values
var num = 99
console.log(num.toPrecision(1)) // '1e+2' : round off when the mantissa needs to be discarded
console.log(num.toPrecision(2)) / / '99'
console.log(num.toPrecision(3)) / / '99.0'
Copy the code

String Method ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

  1. Concat () : concatenation string

  2. Slice () & substr() & subString () : These three methods return a substring based on the operated string, and they take one or two arguments: The first points to the start of the operation, the second to the end of the operation (where the two arguments to slice() and substring() refer to the position of the last character of the operation, and the two arguments to substr() refer to the number of characters in the operation)

var str = 'hello world'
str.slice(3) // 'lo world'
str.substring(3) // 'lo world'
str.substr(3) // 'lo world'
str.slice(3.7) // 'lo w'
str.substring(3.7) // 'lo w'
str.substr(3.7) // 'lo worl'
// When passing negative numbers, the three methods handle them differently: slice() adds the negative number to the string length; Substr () adds the first argument to the length of the string and converts the second argument to 0; Substring () converts all negative arguments to 0
var str = 'hello world'
str.slice(- 3) // 'rld'
str.substring(- 3) // 'hello world'
str.substr(- 3) // 'rld'
str.slice(3.4 -) // 'lo w'
str.substring(3.4 -) // 'hel'
str.substr(3.4 -) / /"
Copy the code
  1. indexOf() & lastIndexOf(): Takes two arguments, the string to match and the position where the match started, and returns the position of the first matched content
  2. trim(): Deletes all Spaces before and after the string
  3. toUpperCase() & toLocaleUpperCase() & toLowerCase() & toLocaleLowerCase()All characters are replaced with lowercase and uppercase characters. Locale implementations are specific to different locales. It is generally safer to use the Locale series if you do not know the Locale
  4. match() & search(): Common: Both accept a parameter and return a value that matches the first string. Difference: Search () can only accept a re, match() can accept either a string or a re. Search () can only look for the first matching position from the beginning of the string (ignoring the global flag of G). Match () performs the same as search() if it does not have a G flag. If it does, it performs a global search. Returns an array of capture groups (as returned by the exec() method of the RegExp object)
var text = 'cat, bat, sat, fat'
var pattern = /.at/

var matches = text.match(pattern)
console.log(matches.index) / / 0
console.log(matches[0]) // 'cat'
console.log(pattern.lastIndex) / / 0

var pos = text.search(/at/)
console.log(pos) / / 1
Copy the code
  1. replace()It takes two arguments, either a string or a re, specifying what to replace; The second is a string specifying what to replace. If you want to replace all matched characters, you must pass the re and the argument to the first argument
var text = 'cat, bat, sat, fat'
var result = text.replace('at'.'ond') // 'cond, bat, sat, fat'
result = text.replace(/at/g.'ond') // 'cond, bond, sond, fond'
Copy the code

Special character sequences can also be used in the second argument to insert the value matched by the re into the result string

Character sequence Replace text
? $
$& Matches the substring of the entire pattern, with the same value as RegExp. LastMatch
$’ The substring following the matched substring has the same value as RegExp. LeftContext
$` The substring before the matched substring has the same value as RegExp. RightContext
$n Matches the NTH capture group substring, from 1 to 9, such as $1
$nn Matches the NNTH capture group substring, from 01 to 99, such as $01
var text = 'cat, bat, sat, fat'
var result = text.replace(/(.at)/g.'word($1)') // 'word(cat), word(bat), word(sat), word(fat)'
result = text.replace(/(.at)/g.'word($&)') // 'word(cat), word(bat), word(sat), word(fat)'
result = text.replace(/(.at)/g.'word($\')') // 'word(, bat, sat, fat), word(, sat, fat), word(, fat), word()'
result = text.replace(/(.at)/g.'word($\`)') // 'word(), word(cat, ), word(cat, bat, ), word(cat, bat, sat, )'
Copy the code

The second argument can also be passed to a function that returns the value to be replaced. When there is only one match, three arguments are passed to the function: the match of the pattern, the position of the pattern match in the string, and the original string

function htmlEscape (text) {
    return text.replace(/[<>"&]/g.function (match, pos, originalText) {
        switch (match) {
            case '<': return '< '
            case '>': return '> '
            case '"': return '" '
            case '&': return '& '
        }
    })
}
alert(htmlEscape('<p class="greeting">Hello world</p>')) // < p class=" greeting" > Hello world< /p>
Copy the code
  1. split(): Is used to cut the string into multiple segments according to the specified delimiter and place the result in an array. It takes two arguments: one is a re or a string, which specifies the delimiter; The second is a number that limits the length of the result array
var text = 'cat,bat,sat,fat'
text.split(', ') // ['cat', 'bat', 'sat', 'fat']
text.split(', '.2) // ['cat', 'bat']
text.split(/ [, ^ \] + /) // [',', ',', ',', ',', ',']
Copy the code
  1. localeCompare(): Compares alphabetical order of body and argument strings, returns a result that is language-specific
  2. fromCharCode(): static method that converts character numbers to characters, essentially doing the opposite of the instance method charCodeAt()
String.fromCharCode(104.101.108.108.111) // hello
Copy the code

The author information

Other Articles by author

  • [Vue] Vue1.0+Webpack1+Gulp project upgrade construction solution stomp road
  • 【 Interview 】 Social recruitment intermediate front-end written interview questions summary – answers and development
  • What is cross-domain, why do browsers prohibit cross-domain, and the divergent learning it causes