preface

As the title says, this is the third time I’ve opened the Little Red Book, JavaScript Advanced Programming 3rd Edition. I have to say, although the book has a few years and many of the knowledge points are not suitable for modern front-end development, but for front-end beginners who want to master the basics of JavaScript, Or if you’re like me and you want to retrieve those bits and pieces of knowledge that have been lost in the corner of your memory, this book is still perfect, and deserves to be called the “JavaScript Bible.”

This article is my reading notes. There is another reason why I choose to read this book again. I have made paper notes before, but THIS time I want to use it as an electronic version to sort out my previous knowledge

The length of this article is long, and it is intended to be my electronic study notes. I will try my best to remove the dross and take the essence. At the same time, I will add some important knowledge points not recorded in the book

The previous one was here

Base and reference types

JavaScript does not allow direct access to locations in memory, which means you cannot directly manipulate an object’s memory space

JS can only operate on the value of a reference type in the heap memory via Pointers (variables stored in the stack), i.e. objects. Assigning a variable to another variable that holds a reference type value to a variable actually creates a new pointer to an object in the same heap memory

let obj = {}
let o = obj

obj.a = "123"

console.log(obj) // {a:"123"}
console.log(o)// {a:"123"}
Copy the code

As mentioned at the end of the previous chapter, this results in changing the value of a reference type through one of these variables being reflected in all the variables pointing to it

If the value is of a primitive type, a new copy is created directly, meaning that the two do not interact

Once you understand this, you can see why JavaScript has the concept of deep copy and shallow copy, both of which are function and reference types, right

  • A shallow copy creates a new object and assigns the root attribute and value of the original object to the new object, but an attribute whose value is still a reference type points to the same object
  • Deep copy copies each layer of properties recursively so that the copied object and the original object do not interact

The extension operator and json.stringify are common copy functions, the former for shallow copies and the latter for deep copies

let arr = [1.2.3]
let shallowArr = [...arr]
let deepArr = JSON.parse(JSON.stringify(arr))
Copy the code

For more on copying, see my other blog object deep copy and shallow copy

Detection of type

The typeof operator can easily determine whether a variable is a primitive type, but will always return ‘object’ or ‘function’ for reference types.

To further know which reference type the variable is, use the instanceof operator

let arr = []

console.log(typeof arr) // 'object'
console.log(arr instanceof Array) // true indicates that it is an array reference type
Copy the code

Using instanceof for primitives returns false

let str = 'abc'
console.log(str instanceof String) // false
Copy the code

Note that String is not a String, it is a wrapper type of a String, which is also a reference type. Since it is a primitive type, even the wrapper type of a String will return false

But Instanceof has its drawbacks

let arr = []
console.log(arr instanceof Object) // true 
Copy the code

The reason for this is that array types are inherited from object types, so instanceof can’t tell if a variable is instantiated by a subclass or a parent class. So there is a third solution Object. The prototype. ToString, it can solve the problem of instanceof cannot judge parents and children

let arr = []
console.log(Object.prototype.toString.call(arr)) // "[object Array]"
Copy the code

Ideal is always good, reality is always bony, although Object. The prototype. ToString solved the problem of the concrete is that a reference type, but another problem is introduced

It cannot determine whether it is a basic type or a basic wrapper type

let str = 'abc'
let objStr = new String("abc")
console.log(Object.prototype.toString.call(str)) // "[object String]"
console.log(Object.prototype.toString.call(objStr)) // "[object String]"
Copy the code

As you can see, both the primitive type and the primitive wrapper type always return “[Object String]”, which still makes it impossible to distinguish the specific type, but we can combine it with typeof

const isType = variable= > {
    let type = typeof variable
    if(type === 'object' || type === 'function') {return Object.prototype.toString.call(variable).match( /\[object (\w+)]/) [1]}else{
        return type
    }
}

console.log(isType('123')) // 'string'
console.log(isType(new String('123'))) // 'String'
Copy the code

Execution environment and scope

Execution environment is sometimes referred to as the context, each function has its own execution environment, executive function, will perform the function of push into stack globally unique environment, create a scoped variable object chain at the same time, the scope is a set of rules JS engine how to find the rules of the variables, it is a nested structure of layer, formed a chain

Since there is no variable A in innerFunc, the JS engine will go down the scope chain to find the variable A stored in func. (If there is still no variable a in func, the JS engine will look at the global scope and return undefined.)

When the function is executed, copy the current function’s scope chain and put the active object of the current function at the front of the scope chain

You can see that innerFunc is not executed when you print the innerFunc. At this point, the innerFunc already has a scope chain of two scopes. At runtime, the innerFunc function is placed at the top of the scope chain

JavaScript currently has three environments

  • The global environment
  • Function of the environment
  • The eval environment

There are three scopes

  • Global scope
  • Function scope
  • Block-level scope (ES6+)

Block-level scope

Block-level scope is simply code in curly braces. Prior to ES6, JavaScript did not have block-level scope, so variables could only be declared using var, which had the effect of promoting variables (promoting the behavior of declaring variables to the current environment creation).

if(false) {
    var color = "blue"
}

console.log('color' in window) // true
Copy the code

A color attribute is created on the window even if the if statement is not entered, because the color variable is created when the global environment is created, and then the code is executed

After ES6, curly braces wrapped in let/const can be block-level scoped, and variables declared with let/const do not get variable promotion

if(false) {
    let color = "blue"
    var otherColor = "red"
}

console.log('color' in window) // false
console.log('otherColor' in window) // true
Copy the code

To be continued

The resources

JavaScript Advanced Programming 3rd Edition

JavaScript You Don’t Know