Recently, I was in charge of front-end technology. When I asked others questions, I found that THE basic JS knowledge was not solid enough, so I took time to sort it out:
1. JS data type
Js data types are divided into basic data types (7 types) and reference data types object. Base data type: null, undefined, number, string, Boolean, symbol (ES6), BigINT (can represent integers greater than 2^ 53-1) Reference data type: Object. Object includes array, function, Date, and RegExp
2. The way to judge the data type:
(1) Typeof: used to determine the basic data type except null, typeof NULL returns object. All reference data types except function return Object
var a = 1;
var b = 'lala';
var c = true;
var d = undefined;
var e = Symbol(a);var f = 1n;
var g = null;
var h = {};
var i = [];
var j = function() {};
console.log(typeof a); // number
console.log(typeof b); // string
console.log(typeof c); // boolean
console.log(typeof d); // undefined
console.log(typeof e); // symbol
console.log(typeof f); // bigint
console.log(typeof g); // object
console.log(typeof h); // object
console.log(typeof i); // object
console.log(typeof j); // function
Copy the code
(2) Instanceof is used to determine the reference data type, class instance. Instanceof will be searched up the prototype chain and return true or false
var h = {};
var i = [];
var j = function() {};
var k = new Date(a);var l = /[0-9]/g;
console.log(h instanceof Object); // trye
console.log(i instanceof Array); // true
console.log(j instanceof Function); // true
console.log(k instanceof Date); // true
console.log(l instanceof RegExp); // true
Copy the code
(3) Object.prototype.toString.call()
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call('lala');// "[object String]"
Object.prototype.toString.call(1);// "[object Number]"
Object.prototype.toString.call(true);// "[object Boolean]"
Object.prototype.toString.call(new Date());// "[object Date]"
Object.prototype.toString.call(/ [0-9]);// "[object RegExp]"
Copy the code
Handwritten instanceof
function myInstanceof(left, right) {
// Use typeof to determine the underlying data type. If so, return false
if(typeofleft ! = ='object' || left === null) return false;
// getProtypeOf is an Object API that can get the parameters of the prototype Object
let proto = Object.getPrototypeOf(left);
while(true) { // Loop down until you find the same prototype object
if(proto === null) return false;
if(proto === right.prototype) return true;// Find the same prototype object, return true
proto = Object.getPrototypeof(proto); }}// verify that myInstanceof is OK
console.log(myInstanceof(new Number(123), Number)); // true
console.log(myInstanceof(123.Number));
Copy the code
3. Deep and light copy
It starts with where the data is stored in memory, the base data type is stored in the stack, the reference data type is stored in the heap, and the stack stores the address of the access object.
For the underlying data type, it’s a copy of the value; But for reference data types, there are deep copies and shallow copies.
Shallow copy first creates an object and copies exactly the property values of the original object. If the property values are base data types, the copy is the value of the base data type; if the property values are reference data types, the copy is the memory address.
Deep copy copies all attributes and dynamically allocated memory to which the attributes point. Deep copy occurs when an object is copied along with the object it references. In other words, the heap memory is recreated. After copying, the data is stored in the new address, while the pointer points to the new address, and the original data is completely isolated.
Common shallow copy implementations are object.assign(target,… Sources) extension operator… Concat Copy array slice Copy array arr. Slice (begin, end);
// Object.assign()
let target = { a: 1 }
let source = { b: 2.c: { d: 3}};Object.assign(target, source);
console.log(target); // { a: 1, b: 2, c: { d: 3 } };
target.b = 5;
target.c.d = 4;
console.log(source); // { b: 2, c: { d: 4 } };
console.log(target); // { a: 1, b: 5, c: { d: 4 } };
// ...
let obj = { a:1.b: { c:1}}letobj2 = { ... obj }let arr = [1.2.3];
let newArr = [...arr];
// concat()
let arr = [1.2.3];
let newArr = arr.concat();
// slice()
let arr = [1.2, {val: 4}];
let newArr = arr.slice();
Copy the code
But there are a few caveats to using the object.assign method:
- It does not copy the object’s inherited properties;
- It does not copy the object’s non-enumerable properties;
- Properties of type Symbol can be copied.
let obj1 = { a: {b:1 }, sym:Symbol(1)};
Object.defineProperty(obj1, 'innumerable', {value:'Non-enumerable properties'.enumerable:false
});
let obj2 = {};
Object.assign(obj2,obj1)
obj1.a.b = 2;
console.log('obj1',obj1); // {a: {b: 2}, sym: Symbol(1), innumerable: 'unenumerable attribute '}
console.log('obj2',obj2); // { a: { b: 2 }, sym: Symbol(1)}
Copy the code
Deep copy implementation: json.stringify
However, there are a few things worth noting about using json.stringify to implement deep copy, which can be summarized as follows:
- If the value of the copied object is function, undefined, or symbol, the key/value pair will disappear in the string serialized by json. stringify.
- Copying the Date reference type becomes a string;
- Unable to copy non-enumerable properties;
- Unable to copy object’s prototype chain
- Copying a RegExp reference type becomes an empty object.
- Object containing NaN, Infinity, and -infinity, the result of JSON serialization is null;
- Looping applications that cannot copy objects, i.e. objects are looped (obj[key] = obj).
function Obj() {
this.func = function () { alert(1)};this.obj = {a:1};
this.arr = [1.2.3];
this.und = undefined;
this.reg = / 123 /;
this.date = new Date(0);
this.NaN = NaN;
this.infinity = Infinity;
this.sym = Symbol(1);
}
let obj1 = new Obj();
Object.defineProperty(obj1,'innumerable', {enumerable:false.value:'innumerable'
});
console.log('obj1',obj1);
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log('obj2',obj2);
Copy the code
Handwritten shallow copy
const shallowClone = (target) = > {
if (typeof target === 'object'&& target ! = =null) {
let cloneTarget = Array.isArray(target) ? [] : {};
for(let prop in target) {
if (target.hasOwnProperty(prop)) { // Iterate over the object's own enumerable properties (regardless of inherited properties and prototype objects)
cloneTarget[prop] = target[prop]
}
}
return cloneTarget
} else {
return target
}
}
Copy the code
Handwritten deep copy
const deepClone = (target) = > {
if (typeof target === 'object'&& target ! = =null) {
let cloneTarget = Array.isArray(target) ? [] : {};
for(let prop in target) {
if (target.hasOwnProperty(prop)) { // Iterate over the object's own enumerable properties (regardless of inherited properties and prototype objects)
cloneTarget[prop] = deepClone(target[prop]); // Recursion causes date and re to become {}}}return cloneTarget
} else {
return target
}
}
Copy the code
Improved deep copy
Consider dates, re’s, and circular references
const deepClone = (target, map = new WeakMap(a)) = > {
if (target === null) return null
if (typeoftarget ! = ='object' || target.constructor === Date || target.constructor === RegExp) return target
if (map.has(target)) return target // Resolve circular references
const deepTarget = Array.isArray(target) ? [] : {};
map.set(target, true)
for (let prop in target) {
if (target.hasOwnProperty(prop)) { // Iterate over the object's own enumerable properties (regardless of inherited properties and prototype objects)
deepTarget[prop] = deepClone(target[prop], map)
}
}
return deepTarget
}
Copy the code
4. The prototype
A prototype object
Consider three questions first:
- What is a prototype object?
- When is the prototype object generated?
- How are the prototype objects accessed?
What is a prototype object?
A prototype object is essentially an object. All functions have a prototype attribute that points to the prototype object of the function.
- Constructor: Points to the constructor
- Attributes and methods inherited from Object
When is the prototype object generated?
A prototype object is created when a function is created. Each declaration of a function does the following:
- The browser creates an object in memory
- Add a constructor attribute to the
- The constructor attribute points to the function
- Assigns the newly created object to the function’s Prototype property
How are the prototype objects accessed?
Prototype Creates an instance of the function using the __proto__ attribute. Each instance contains a pointer to the prototype object of the constructor.
5. The prototype chain
Prototype chain is to access an attribute or method on the instance, first look in the instance, find that is returned; If not, use the __proto__ attribute to find the constructor’s prototype object. If not, the constructor continues to search for the __proto__ property of prototype until object. prototype is found.
A diagram to understand the prototype, the prototype chain
conclusion
All functions are instances of Function, and Function is an instance of Function Function inherits from Object, and everything else inherits from Object
6. Inheritance
There are two functions, function A and function B, which implement function A inheriting the properties and methods of function B:
Prototype chain inheritance
A.prototype = new B()
A.prototype.constructor = A
var a1 = new A()
var a2 = new A()
Copy the code
Disadvantages:
- The reference type properties of the superclass B function prototype object are shared by instances A1, A2
- Instances that create subclass A cannot pass arguments to parent class B
- Subclasses cannot inherit from more than one function, where A can only inherit from B
Constructor inheritance
function A(e) {
B.call(this, e)
}
Copy the code
Disadvantages: Subclasses do not have access to properties and methods on the parent class stereotype
Combination of inheritance
function B (name, age) {
this.name = name
this.age = age
}
B.prototype.setName = function (name) {
this.name = name
}
function A (name, age, price) {
B.call(this, name, age)
this.price = price
}
A.prototype = new B ()
A.prototype.constructor = A
A.prototype.setPrice = function (price) {
this.price = price
}
var sub = new A()
Copy the code
Disadvantages: calls B() twice: once b.call (); Once is new B()
Parasitic combinatorial inheritance
Create method, which takes two parameters: an Object to be used as a prototype for the new Object and, optionally, an Object to define additional properties for the new Object.
function B (name, age) {
this.name = name
this.age = age
}
B.prototype.setName = function (name) {
this.name = name
}
t
function A (name, age, price) {
B.call(this, name, age)
this.price = price
}
// The first way
// Create an Object {} and assign _proto_ of the Object to object.create
// A.prototype.__proto__ = B.prototype
A.prototype = Object.create(B.prototype, {consturctor: A})
// The second way
//var F = function () {
//F.prototype = B.prototype; // Core code
//A.prototype = new F();
//A.prototype.contructor = A
A.prototype.setPrice = function (price) {
this.price = price
}
var sub = new A()
Copy the code
ES6 Class Class
class B {
static mood = 'good' // Static attributes
constructor () {
this.money = 1000000
}
buybuybuy () {
this.money -= 100
console.log('money'.this.money)
}
}
class A extends B {
super()}var a1 = new A()
a1.buybuybuy()
Copy the code
7. Execution Context
Once the browser gets the source code, it does several things:
- Word segmentation/lexical analysis () : Split the code to generate tokens;
- Parsing/parsing () : convert tokens into AST abstract syntax trees according to the syntax;
- Executable code: Parsers generate bytecode and interpret execution line by line, parsers monitor hot code, and compilers compile hot code into machine code.
What is an execution context?
Execution context, also known as execution context. Execution contexts fall into three types:
- Global execution context: When the program starts, the global execution context is created and pushed onto the execution stack.
- Function execution context: Creates the function execution context when the function is called and pushes the function onto the execution stack.
- Eval Execution context: The specific execution context of the eval function.
The execution context has two phases: the creation phase and the execution phase.
Create a stage
The execution context mainly consists of two parts: lexical environment and variable environment.
LexicalEnvironment
Classification of lexical environments: global, function, module
Lexical environment:
-
Environment Record: Stores and initializes variables
Declarative Environment Record: Holds elements defined directly by identifiers, such as Object Environment Records declared by const lets: syntactic environments used primarily for with.Copy the code
-
Outer environment: Creates scope chains that access references to the parent scope
-
ThisBinding: Determines the direction of this in the current environment
variableEnvironment
It’s also a lexical environment. The main difference is that variables declared through var and function declarations are stored in the variable environment.
In short, the execution context creation phase does three things:
- Initialize variables, functions, and parameters
- Create scope chains
- Binding this
executionContext = {
variableObject: {
arguments: {},name: undefined.getData: undefined
}, // Initialize variables, functions, parameters
scopeChain: {}, // Create scope chain
this: {} / / bind this
}
Copy the code
Execution phase
The implementation phase mainly does two things:
- Assign variables, function references, and assignments
- Execute the code
Variable promotion vs temporary dead zone Variables and function declarations declared by var have been initialized and assigned to undefined during the execution context creation phase. Variables defined by var can also be accessed with undefined before the code is executed to the var assignment line. This phenomenon is called variable promotion
In contrast, variables declared by const and let are initialized to flag bits in lexical environments. An error is reported when a variable is read before the let and const assignment line is executed. This feature is called a temporary dead band.
Execution context stack
The browser’s JS interpreter is single-threaded, meaning that the browser can only do one thing at a time. There is only one global execution context in the code, and an infinite number of function execution contexts, making up the execution context stack. The execution context of a function that is removed from the stack after the function completes execution.
8. Scope
The primary purpose of a scope is to isolate variables and functions and control their life cycles. It is mainly divided into three types:
- Global scope
- Function scope
- Block-level scope
A scope is defined when the execution context is created, not when the code executes, and is therefore also known as a lexical scope.
Lexical scope vs dynamic scope
The difference between lexical scope and dynamic scope is that lexical scope is defined at the execution context creation stage, whereas dynamic scope is created at the code execution stage. To better understand the lexical scope that JS uses, take a look at an example:
var name = 'xuna'
function getName() {
console.log(name)
}
function getName1() {
var name = 'na.xu'
return getName()
}
getName1() // xuna
Copy the code
The scope chain
When a function is nested within another function and a variable cannot be found in the lexical and variable Environment Record of the current execution context, the parent scope is accessed through the Outer Environment. If it is not found, the parent scope is searched layer by layer. Until the variable is found or the global scope is reached, such a chain is called a scope chain.
9. Closure
Closures typically occur when functions are nested and the inner function accesses the variables of the outer function. A closure is a function that has access to a variable in the scope of another function. When a function is removed from the stack after execution, the current execution context does not have direct access to the lexical scope of the function being removed from the stack, while another function retains a reference to the lexical scope of the function. This reference is called a closure.
Application of closures
Encapsulating private variables
function Person() {
var money = 10000
return buy() {
money -= 1}}var person = new Person()
person.buy() // Money is a private variable of person and can only be changed by buy ()
Copy the code
Cache data
function getDataList() {
let data = null
return {
getData() {
if(data) return Promise.resolve(data)
return fetch().then(res= > data = res.json())
}
}
}
const list = getDataList()
list.getData()
Copy the code