preface

Q&A

  • 1. Q: Why is it so long? Is it necessary? Are you out of your mind? A: I think that’s the question most people ask when they see this headline. Because as a man, I like to be long, and I don’t like to be divided into several parts. The family will be together, in order. All right, seriously, the entire preface is essentially an answer to that question. You can choose to read the preface first and then decide whether you want to read it with the book. Here first a simple grasp :1, much content: first of all this reading notes originally a lot of content, is a comprehensive explanation of the book.2, for the new person: for the kind of red book to read a cursory, on THE JS interface call only floating novice.3, left to the reader their own refinement: read this kind of social science books are generally read first thick, then read thin. This note belongs to the initial ‘read thick’ stage. After the reader thoroughly read, then further refining. I’ll talk more about how to read later.

  • 2. Q: What exactly does that include? A: The structure of the list of notes is exactly the same as that of the book. A more general interpretation of the content of each section (for newcomers), and a deeper explanation of the examples, some supplemented with flowcharts and corresponding MDN connections; To summarize the content, more clear section context; Added a lot of practical work considerations, added more clear and understandable examples and annotations, and on the basis of the original text for expansion and summary; The book of errors and said that the later will be introduced, but not the introduction of the fill pit, translation or easy to cause misunderstanding of the name; Added personal reading experience and ridicule.

  • 3. Q: You have enough books already, but you need to read your long notes? A: First of all, you should know that reading a technical book is not a novel! Reading it doesn’t mean you understand it. It’s about taking the knowledge in the book and making it your own. This note is to help the novice to understand the knowledge more easily and to read more smoothly. After reading a section, you can also find out what knowledge you don’t understand or miss, or understand wrong through comparison. And some of the caveat, the misleading, the jokes about the ideas in the book, and so on, are already in the notes.

  • Q: Is the book as good as others say? Answer: this is an answer that rises first and then depresses. First of all this is a very good book without a doubt! It systematically and comprehensively interprets JavaScript, with both advantages and disadvantages. Once you’ve read this book through, almost every question you have about JavaScript will be answered (my question about whether scopes are “objects”? But it also has a certain threshold, if you are not familiar with JS, common interfaces are not familiar, many nouns are not quite understand the surface meaning. This book isn’t for you. You’ll probably spend more time googling it than reading it. Unlike many other books, this book is often undefined and requires you to read and re-read his words on your own. The vein structure of each section is not so clear, sometimes need to comb their own; Do not know whether the translation of the pot, a lot of things to explain a little mystery, originally very simple, but said a pile of not commonly used terms (may not be called so domestic), see you a face meng! Sometimes three or four different nouns refer to the same concept without any explanation; The whole book has a strong subjective feeling in it. The first half, the JS prop up very high, said it engine optimization of all kinds of good! But in the second half of the critique about “emulating classes and inheritance in JavaScript”, they are very misleading! Even more scornful! All it takes is swearing, as if JavaScript is a heretic and should be burned on a cross! But his point of view comes from the perspective of other languages. I think more readers are probably only exposed to JavaScript this language, for them, in fact, there is no such “confusion”!

Reading Advice:

  • 1 do not hold any utilitarian and impetuous heart to read! This kind of theory, concept based books, in fact, people are not so willing to read. One is that it’s hard to read, abstract. The second is practical work, almost not used in the current impetuous front circle this is thankless. What’s the greatest use of this book? Yes, it is used by many people for interviews! ? This in itself is fine, you read the series of three books, all involving JS interview will be easy to handle. But when you are utilitarian, you are more perfunctory. Mechanically copy the concepts in the book and paste in your own superficial understanding. OK, it’s enough to deal with those interviewers who are also following suit. Generally you answered, they will not continue to ask, ask deep themselves are not clear, also not good to deny you. If you’re confident enough,’ bullshit ‘can be fooled. If you can’t answer, the thick-skinned will tell you to go back and find out for yourself. The interviewer who really knows, in fact, will explain to you, and they are not too busy to miss this point. In fact, they are also happy to show that they are knowledgeable. This utilitarian way of reading, even if you finish it (which most people do), won’t do anything for your skills. Because by the end of the book, you know only half of it. This is even worse, and may even confuse your previous correct understanding of JavaScript.

  • 2. It’s better to read a book seriously than to have a hundred articles on it (which you don’t even know half of).

I always think that to understand a knowledge system, books are the best choice, it is definitely better than you find a pile of articles better! Now look around the front end, a bunch of prototype chains, closures,this… These contents. inside the content is much the same, a lot of understanding is relatively shallow, the consideration is also relatively one-sided. But impetuous people is like this article, feel their own collection, read a thorough understanding (! ?). In fact, many of these articles are borrowed from this book.

First of all, you have to realize that knowledge is structured, not completely independent. For example, to fully understand the prototype chain, closure,this. you must first understand the scope and function. Knowledge is all interconnected and interrelated. If you want to thoroughly understand, or choose the choice of reading, from the shallow to the deep, fully clear all the knowledge points connected. Remember that “half-informed” is always worse than “ignorant”! (Of course, don’t pretend to understand, but also reasonable people are different).

  • 3. How to Read: Read thick, then thin! First of all, read the book thick: to understand all the knowledge points in each section, without omission. Write down all the points mentioned and highlight the important points (in the e-book). Then in his own local MD notes, in a certain logical order, try to use his own words to elaborate and summarize these knowledge points. If you have read several times also do not understand the place, you can query THE MDN, combined with their own practical work experience, or first circle, continue to read, with the in-depth understanding of the back, the front do not understand the place naturally clear. This reading note is to show you how to read the book thick. Then read the book: this part requires the reader to make a summary of the whole situation on the basis of a thorough understanding. First, the summary of thinking guiding schema is carried out according to the chapter. Then summarize the rules from chapter to chapter, and remember the special cases. For example, both scopes and prototype chains have a similar “proximity principle “, because of which” shielding “occurs. These are the need to stand in the overall perspective to summarize. Although there are others on the Internet who have summed it up, we should not get into the habit of relying on others and copying everything ourselves (if you want to be a ‘copy-and-paste’ programmer all the time).

Part one: Scope and closure

Chapter 1 What is scope

1.1 Compilation Principles

Traditional compilation consists of three steps

  • 1. Tokenizing/Lexing: This process breaks up strings of characters into blocks of code that make sense (to the programming language), called tokens. For example, consider the program var a = 2; . This program is usually broken down into lexical units :var, a, =, 2,; . Whether whitespace is treated as a lexical unit depends on whether whitespace has meaning in the language.
  • Parsing: The process of converting a stream of lexical units (an array) into a tree of progressively nested elements that represent the syntax structure of a program. This Tree is called an Abstract Syntax Tree (AST). var a = 2; There might be a top-level node called VariableDeclaration, followed by a child node called Identifier(whose value is a), and a child node called AssignmentExpression. The AssignmentExpression node has a child node called NumericLiteral(which has a value of 2).
  • 3. Code generation: The process of converting the AST into executable code is called code generation. This process depends on the language, the target platform, and so on. Without getting into specifics, the simple idea is that there is some way to set var a = 2; The AST is converted to a set of machine instructions that create a variable called a (including allocating memory, etc.) and store a value in a.

Note: Just remember the first step here: segmentation/lexical analysis. Step 2: parse/parse to get the abstract syntax tree (AST). Step 3: code generation to convert the abstract syntax tree into machine instructions.

Differences between JavaScript and traditional compilation:

  • 1. JavaScript engines don’t have as much time to optimize as other language compilers do.
  • JavaScript is different from traditional compiled languages in that it is not compiled prior to building. In most cases, it is compiled microseconds (or less) before the code is executed.
  • 3. The JavaScript engine uses all kinds of methods (such as JIT, which can delay compilation or even implement recompilation) to ensure optimal performance.
  • 4. JavaScript compilation results are not portable in distributed systems.

1.2 Understanding scopes

1.2.1 Actors List (Participants whose code compiles to execution)

First introduce the program to be involved in var a = 2; In order to understand what you’re about to hear.

  • The engine is responsible for the compilation and execution of the entire JavaScript program from beginning to end.
  • One of the compiler engine’s best friends, doing the dirty work of parsing and code generation (see the previous section).
  • Another good friend of the scope engine is responsible for collecting and maintaining a set of queries made up of all declared identifiers (variables) and enforcing a very strict set of rules that determine the currently executing code’s access to these identifiers.

1.2.2 Dialog (Code compilation and Execution process)

1.2.3 LHS query and RHS query in scope

As you can see from the figure above, the engine queries the scope variables after getting code from the compiler.

Now change the example to var a = b; At this point, the engine queries the scope for both variables A and B. There are two types of queries :LHS and RHS, where L stands for left. R stands for right, that is, LHS query for variable A. Perform an RHS query on variable b.

Just by looking at it. LHS is a scoped query for the variables to the left of =. RHS is a scoped query for the variable to the right of =. First of all, LHS and RHS query variables. That’s why I started the example from var a=2; Var a = b; instead The difference between the two is that they don’t end up looking up the same thing. LHS is the declaration (not the value) of the variable to be queried so that it can be assigned a value later. RHS is the final value of the variable to be queried. Also,LHS and RHS mean “to the left or right of the assignment operation” does not necessarily mean “to the left or right of the = assignment operator.” There are several other forms of assignment, so it is best conceptually understood as “who is the target of the assignment (LHS)” and “who is the source of the assignment (RHS)”. Or think of it this way: if the code needs to get the ‘source value’ of the variable, it will do an RHS query.

1.2.4 Dialogue between engine and scope

The simpler part is to personify the collaborative process of engine and scope. The bottom line is that the engine looks for scopes when making LHS and RHS queries.

function foo(a) { 
  console.log( a ); / / 2
}
foo( 2 );
Copy the code

Let’s think of the processing of the above code as a conversation that might look something like this.

Engine: I said scope, I need to make an RHS reference for Foo. Have you seen it? Scope: Don’t say I have. The compiler kid just declared it. It’s a function. Here you go. Engine: dude too enough meaning! All right, let me do foo. Engine: Scope, one more thing. I need to make an LHS reference for A, have you seen this? Scope: We’ve seen this before. The compiler recently named it as a formal argument to foo. Here you go. Engine: Thank you so much. You’re always so good. Now I’m going to assign 2 to a. Engine: Sorry to bother you again, buddy. I’m going to do an RHS reference for console, have you seen it? Scope: we two who and who, and that’s what I do. I have that, too. Console is a built-in object. Give it to you. Engine: My-my-dah. I have to see if there’s a log(..) in there . Great, there it is. It’s a function. Engine: Dude, can you help me find the RHS reference to A again? I remember it, but I want to make sure. Scope: Don’t worry, this variable has not changed, take away, thank you. Engine: Great. Let me pass the value of a, which is 2, into log(..) .

1.3 Scope nesting

Nesting of scopes occurs when a block or function is nested within another block or function. This creates a chain of scopes. Therefore, if a variable cannot be found in the current scope, the engine will continue to search in the outer nested scope until it is found or reaches the outermost (i.e., global) scope.

When the engine needs to query the scope. The engine looks for variables from the current execution scope, and if it can’t find them, it goes up a level. When the outermost global scope is reached, the search stops whether it is found or not.

1.4 abnormal

Example:

function foo(a) { 
  console.log( a + b ); 
  b = a;
}
foo( 2 );
Copy the code
  • If the RHS query cannot find the required variable in all nested scopes, the engine will throw itReferenceErrorThe exception. Take the example aboveconsole.log(a+b)Because RHS cannot find the value of b at this point. Therefore, a ReferenceError is thrown.
  • If the RHS query finds a variable, but you try to perform an unreasonable operation on the value of the variable, such as trying to make a function call on a value of a non-function type, or referring to a property in a value of null or undefined type, then the engine throws a different type of exception. TypeError.
  • When the engine executes an LHS query,If the target variable cannot be found at the top level (global scope), a variable with that name is created in the global scopeAnd return it to the engine, provided the program runsIn non-strict mode. For example, in the example aboveb=a;.
  • When an LHS query fails in strict mode, a global variable is not created and returned, and the engine throws a ReferenceError exception similar to that used when an RHS query fails.

1.5 Summary of LHS and RHS

  • Both LHS and RHS queries are engine queries for scopes
  • Both LHS and RHS queries are variable only queries
  • Both LHS and RHS are queried along the scope chain, all the way to the top global scope. If not, in non-strict mode,LHS creates a variable with the same name globally. RHS throws a ReferenceError exception.
  • If the purpose of the lookup is to assign a value to a variable, then the LHS query is used; If the goal is to get the value of the variable, the RHS query is used.
  • LHS is simply a container for finding variables to facilitate assignment
  • =Operators or operations passed as arguments when a function is called result in an assignment to the associated scope. In this case, LHS queries are performed
  • RHS queries need to find the value of the variable.

Chapter 2 lexical scope

Scopes work in two modes:

  • 1. Lexical scope. Is by far the most common pattern adopted by most programming languages. Of course JavaScript is also used in lexical scope.
  • 2. Dynamic scope. Less used, such as Bash scripts, some patterns in Perl, and so on.

2.1 Lexical stage

Lexical phase: The first working phase of most standard language compilers is called lexicalization (also called lexicalization). The lexicalization process checks the characters in the source code and, in the case of stateful parsing, assigns semantics to the words.

Lexical scope: Lexical scope is the scope defined at the lexical stage, also known as static scope. In JavaScript, scope is generated in the first lexical phase of the compiler, and is determined by you as you write code.

Lexical scope position: Lexical scope position scope is entirely determined by the position declared by the function during code writing.

To understand lexical scope and nesting: Here’s an example:

function foo(a) { 
  var b = a * 2;
  
  function bar(c) { 
    console.log( a, b, c );
  }

  bar( b * 3 ); 
}
foo( 2 ); / / 2, 4, 12

Copy the code

There are three hierarchically nested scopes in this example. To help you understand them, you can divide them into three “bubble scopes” that are contained progressively.

  • 1: contains the entire global scope with only one identifier :foo.
  • 2: contains the scope created by foo with three identifiers :a, bar, and b.
  • 3: contains the scope created by bar with only one identifier :c.

Note: No function bubble can appear (partly) in two outer scopes at once, just as no function can appear (partly) in two parent functions at once.

The engine’s search for scopes: this part, described in the previous section, is a step up from the current scope to the top global scope. Here’s a little bit more detail. The scope lookup stops when the first matching identifier is found. Identifiers of the same name can be defined in multiple nested scopes. This is called the “shading effect” (the inner identifier “overshadows” the outer identifier). Despite the shadotting effect, the scope lookup always starts at the innermost scope where it is run and works outward or upward until the first matching identifier is encountered.

Note:

  • Global variables are automatically properties of global objects, such as the Window object in a browser, and can therefore be accessed indirectly, not directly by the lexical name of the global object, but by a citation to the global object’s properties. Such as:window.aThis technique allows access to global variables that are obscured by variables of the same name. But non-global variables, if obscured, are not accessible in any way.
  • Lexical scope lookups only look for level 1 identifiers, such as A, B, and C. If foo.bar.baz is referenced in the code, the lexical scope lookup will only attempt to find the foo identifier. Once this variable is found, the object property access rules will take over access to the bar and baz properties, respectively.

2.2 Deception lexical

Cheat lexical: The engine “modifies” (or cheats) lexical scope at run time. In other words, the lexical scope is dynamically modified while the engine is running (which was already determined during compilation of the lexical scope).

Two mechanisms for deception :(the following two mechanisms are understood, not recommended for actual development)

2.2.1 eval

In the JavaScript eval (..) A function can take a string as an argument and treat its contents as if they existed at that point in the program when it was written. Place eval in the lexical scope, and the code carried by eval is dynamically added to the lexical scope.

Use the following example to further understand:

function foo(str, a) { 
  eval( str ); / / cheating!
  console.log( a, b );
}
var b = 2;
foo( "var b = 3;".1 ); / / 1 and 3

Copy the code

eval(..) Var b = 3; This code is treated as if it were there. Since that code declares a new variable, b, it makes a difference to the existing foo(..) The lexical scope of is modified. When the console. The log (..) When executed, it will be executed in foo(..) You can find both A and B on the inside of, but you can never find B on the outside. So “1, 3” is printed instead of the normal “1, 2”.

Note:

  • eval(..) Often used to execute dynamically created code. Variables and functions can be passed in by concatenating them together as characters dynamically according to program logic.
  • In strict mode,eval(…) The scope cannot be modified.
  • And eval (…). Similar, setTimeout (..) And setInterval (..) The first argument to can be a string, and the contents of the string can be interpreted as dynamically generated function code.
  • new Function(..) The function behaves similarly, with the last argument taking a code string and converting it to a dynamically generated function (the first argument is the parameter of the newly generated function). The syntax for this constructor is better than eval(..). Slightly safer, but should be avoided if possible.
var sum = new Function("a"."b"."return a + b;");
console.log(sum(1.1111));  / / 1112

Copy the code

2.2.2 with(Not recommended)

Example:

function foo(obj) { 
  with (obj) {
    a = 2; }}var o1 = {
  a: 3
};

var o2 = { 
  b: 3
};
foo( o1 );
console.log( o1.a ); / / 2

foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2 -- a is leaking into the global scope!
Copy the code

At first you might think that the a attribute of O1 is overshadowed by the lexical reference to the a in with and becomes 2. O2 does not have an a attribute. In this case, with cannot be lexically referenced, so o2.a will become undefined. But why does console.log(a) end up being 2? When foo(o2) is executed,with makes an LHS query for a=2, but it is not found in the o2 scope,foo() scope, or global scope, so a global variable a is created and then assigned 2.

In general,with treats an object with no or more attributes as a completely separate lexical scope, so the object’s attributes are also treated as lexical identifiers defined in that scope.

Note: Use eval(..) The reason for the “with” and “with” are affected (restricted) by the strict mode. With is completely forbidden, and using eval(..) indirectly or unsafely while retaining the core functionality Also banned.

2.2.3 performance

The JavaScript engine performs several performance optimizations during compilation. Some of these optimizations rely on being able to do static analysis based on the morphology of the code and determine in advance where all variables and functions are defined so that identifiers can be found quickly during execution. But the eval (..) And with deceive other lexical scopes defined at write time by modifying or creating new scopes at run time. Doing so makes it impossible for the engine to know what changes eval and WITH make to the lexical scope. Only part of the processing and optimization! So if you use eval(..) a lot in your code Or with, it must be very slow! .

2.3 summary

  • Lexical scope is determined when you write code. The lexical scope is generated during the first phase of compilation, the lexical analysis phase. At this point, the lexical scope basically knows where all identifiers are and how they are declared, so it can predict how they will be looked up during execution.
  • eval(..) And with. The former can evaluate a string of “code” containing one or more declarations, and thereby modify an existing lexical scope (at run time). The latter essentially creates a new lexical scope (again at run time) by treating references to an object as scoped and its properties as scoped identifiers.
  • In general, don’t use eval(…) in your actual code And with, because it’s dangerous and can cause performance problems!

Chapter 3 Function scope and block scope

3.1 Scope in a function

  • JavaScript has a function-based scope, and typically a function scope is created for every function declared.
  • Function scope means that all variables belonging to a function can be used and reused throughout the scope of the function (and in fact within nested scopes). The benefit of this is that JavaScript variables can change their value types as needed.

3.2 Hide the internal implementation

because

  • The child function scope can directly access identifiers in the parent function scope.
  • The parent function scope cannot directly access identifiers in the child function scope.

So wrapping code around function declarations is essentially “hiding” the code.

Why “hide” the code? Because of the minimum authorization or minimum exposure principle. The principle is that in software design, the essential content should be exposed to a minimum and the rest “hidden,” such as the API design for a module or object. Hidden benefits:

  • Privatize the code to reduce external interference with the internal code and maintain its stability.
  • Conflict avoidance:You can avoid naming conflicts between identifiers with the same name, where two identifiers may have the same name but have different uses, which can inadvertently cause naming conflicts. Conflicts can cause the value of a variable to be accidentally overwritten. So what are the common ways to avoid conflict?
      1. Global namespaces: A typical example of variable conflicts exists in the global scope. When multiple third-party libraries are loaded into a program, they can easily cause conflicts if they do not properly hide internal private functions or variables. These libraries typically declare a variable with a sufficiently unique name in the global scope, usually an object. This object is used as the library’s namespace, and all functions that need to be exposed to the outside world become properties of this object (namespace), rather than exposing their identifiers to top-level lexical scope.
    • 2. Module management: Another way to avoid conflicts, close to the modern module mechanism, is to use one of many module managers. In fact, we often use amd, CommonJS,import module mechanism.

3.3 Function scope

Function declaration and function expression:

function foo() {... }Copy the code

We know that the variables and functions in function foo are hidden and do not pollute the global scope. However, the variable name foo still exists in the global scope, causing contamination. So what can we do to avoid contaminating function names? That is, as a function expression, not as a standard function declaration. In this way, the function name only exists in its own function scope, not its parent scope, so there is no pollution. Here’s an example of a function declaration:

var a = 2;
(function foo(){ 
  var a = 3;
  console.log( a ); / / 3}) ();console.log( a ); / / 2
Copy the code

When we wrap a function with () and execute it immediately. The wrapper function declaration starts with (function) instead of the function keyword. Thus foo is treated as a function expression, not as a function declaration (that is, foo does not exist in the parent scope). Going back to the example above,foo is not accessible from the global scope; foo exists only in its own function scope.

In JS, we need to know three ways to declare functions:

  • Function Expression:Defines a function as part of an expression statement (usually variable assignment, but also in self-calling form). Functions defined by function expressions can be named or anonymous. Because it can have no function name, it is often used as an anonymous function. If so, the function name exists only in its own function scope. And function expressions cannot start with “function”. Function expressions can be stored in variables or object properties. (A function declaration can be converted to a function expression by prefixing it with an operator. For example,!.+.-.(a). Here’s an example:! function(){console.log(1)}()The result is 1, and no error will be reported.
  • Function Declaration: A Function Declaration is a separate structure that declares a named Function and must begin with Function. And the function declaration will perform function promotion. This allows it to be called anywhere in its scope, meaning that later in the code it can assign a value to a variable or object property by its name.
  • Function() constructor: The Function constructor is used to create a Function. This usage is not recommended and can cause problems
/ / Function () constructor
var f =new Function(a)// Function expression
var f = function() {
      console.log(1);  
}

// Declare the function
function f (){
     console.log(2);
}

console.log(f())
// What is printed here
Copy the code

How to distinguish between a function declaration and a function expression: See where the function keyword appears in the declaration (not just on a line of code, but throughout the declaration). If function is the first word in the declaration, then it is a function declaration, otherwise it is a function expression. For example, in the example above, start with (instead of function.

Supplement: The above paragraph is the explanation of the original book, I think this explanation is not complete, here is my own explanation.

  • Representation difference: As it says, any declaration that begins with function and contains a function name is a function declaration.
  • Internal differences: I made it clear when I added the two definitions above, so let me summarize the comparison.
    • Function promotion: A function declaration that promotes the entire function. A function expression, on the other hand, is not promoted; it is assigned while the engine is running and is not called until the expression has been assigned.
    • A function expression may not have a function name. If it does, its function name will only exist in its own scope.var f = function fun(){console.log(fun)}It doesn’t exist anywhere else. This also avoids global contamination and facilitates recursion.

3.3.1 Anonymity and name

Function expressions can be anonymous, and function declarations cannot omit the function name. A function with a name is a named function; a function without a name is an anonymous function.

Disadvantages of anonymous functions:

    1. Anonymous functions do not show meaningful function names in the stack trace, making debugging difficult.
    1. Without a function name, you can only use expired arguments.callee references when the function needs to refer to itself, such as in recursion. Another example of a function needing to refer to itself is when an event listener needs to unbind itself after an event is fired.
    1. Anonymous functions omit function names that are important for code readability/understandability. A descriptive name allows the code to speak for itself.

So specifying a function name for a function expression can effectively solve this problem. It is a best practice to always name function expressions.

PS: My opinion is if the function expression is assigned to a variable or property name or is called once. You don’t have to add the name of the function. Because the name in the code is very difficult, it can cause misunderstanding.

3.3.2 Execute function expressions immediately

For example (function foo(){.. }) (). The first () turns the function into an expression, and the second () executes the function. This is the immediate Invoked Function Expression, also known as IIFE, and stands for Immediately Invoked Function Expression.

IIFE can be named or anonymous. The benefits are the same as mentioned above. IIFE can also be of the form (function(){.. }()). The two forms are functionally identical.

3.4 Block scope

Function scope is the most common unit of scope in JavaScript. Sometimes we just assign var variables to if or for {… } and is not used anywhere else. But it still pollutes the outer layer’s function scope. At this point you want to have a scope that separates the outer function scopes and declares variables only in that scope. Block scope (usually {… } inside the package) can help us do this.

Block scopes have been in JavaScript since the release of ES3, and the with and catch clauses are two small examples of block scopes.

3.4.1 track with

We discussed the with keyword in Chapter 2. Not only is this a difficult construct to understand, but it is also an example of block scope (a form of block scope). Scopes created from objects with with are valid only in the with declaration and not in the outer scope.

3.4.2 try/catch

The catch clause of try/catch creates a block scope in which declared variables are valid only within the catch.

try {
  undefined(a);// Perform an illegal operation to force an exception
}
catch (err) {
  console.log( err ); // It works!
}
console.log( err ); // ReferenceError: err not found
Copy the code

Err exists only inside the catch clause, and an error is thrown when an attempt is made to reference it from elsewhere. So what if we want to use catch to create a block scope that does not just receive err?

try{throw 2; }catch(a){ 
  console.log( a ); / / 2
}
console.log( a ); // ReferenceError
Copy the code

This creates a block scope where a=2 exists only in the catch clause. Before ES6 we could use this approach to use block scope.

Rule 3.4.3 let

ES6 introduces a new let keyword that provides an alternative to var for variable declaration. The let keyword binds variables to whatever scope they are in (usually {.. } inside).

The behavior of using a let to append variables to an existing block scope is implicit. For example, in the if {… } declare a variable with a let. So what is explicitly creating a block scope? {} is created separately as a block scope for a let. Instead of borrowing the {} provided by if or for. For example {let a=2; Console.log (a)} Note: declarations made with lets are not promoted in block scope. Benefits of block scope:

  • 1. Garbage collection
function process(data){
        // Do something interesting here
     }
     var someReallyBigData=function(){
         //dosomeing
     }
     process(someReallyBigData);

     var btn=document.getElementById("my_button");
     btn.addEventListener("click".function click(evt){
        alert("button click");
		// If we continue calling someReallyBigData from here, we will form a closure that will not be garbage collected.
     },false);
Copy the code

The click callback to the click function does not require the someReallyBigData variable. In theory this means that when process(..) After execution, data structures that take up a lot of space in memory can be garbage collected. However, because the click function forms a closure that covers the entire scope, the JavaScript engine will most likely still hold the structure (depending on the implementation). But using block scope explicitly makes it clear to the engine that there is no need to keep someReallyBigData:

function process(data){
       // Do something interesting here
    }
    // The contents defined in this block can be destroyed!
    {
      let someReallyBigData = { .. }; 
      process( someReallyBigData );
    }
    var btn=document.getElementById("my_button");
    btn.addEventListener("click".function click(evt){
       alert("button click");
    },false);
Copy the code
    1. Let cycle
for (let i=0; i<10; i++) { 
	  console.log( i );
     }
console.log( i ); // ReferenceError
Copy the code

The let at the head of the for loop not only binds I to the block of the for loop, it actually rebinds it to each iteration of the loop, ensuring that it is re-assigned with the value at the end of the last iteration of the loop. This prevents I from contaminating the outer function scope.

3.4.4 const

In addition to lets, ES6 introduces const, which can also be used to create block-scoped variables whose values are fixed (constants). Any subsequent attempt to modify the value will result in an error.

var foo = true;
if (foo) {
  var a = 2;
  const b = 3; // Block scope constant contained in if
  a = 3; / / normal!
  b = 4; / / error!
}
console.log( a ); / / 3
console.log( b ); // ReferenceError!
Copy the code

3.5 summary

Functions are the most common units of scope in JavaScript. Essentially, variables or functions declared inside a function are “hidden” in the scope they are in, effectively separating them from the outer scope.

But functions are not the only units of scope. Block scope means that variables and functions can belong not only to their scope but also to a block of code (usually {.. } inside) is the block scope. ES6 provides lets and const to help create block scopes.

Chapter 4 Promotion

4.1 Which came first, the chicken or the egg (assignment)?

Consider the first piece of code

a = 2;
var a; 
console.log( a );
Copy the code

The output is 2 instead of undefined

Consider the second piece of code

console.log( a ); 
var a = 2;
Copy the code

The output is undefined instead of ReferenceError, which is something you should be thinking about after all this code. Do you declare (egg) first or assign (chicken) first?

4.2 The compiler strikes again

The content of the compiler, recall that the engine first compiles JavaScript code before interpreting it. Part of the compilation phase is finding all the declarations and associating them with the appropriate scope. The engine then asks the scope and assigns the declaration.

So what does the compiler do after all the declarations are found at compile time? The answer is to improve the example in the first section above, when you see var a = 2; You might think of it as a statement. But JavaScript actually treats this as two declarations :var a; And a = 2; . The first definition declaration is made at compile time. The second assignment declaration is left to wait for the execution phase. When the first declaration is made at compile time, the compiler will make an exception to var a; Declaration to promote (var a; A = 2; a = 2; It stays where it is. So the code is going to be

var a; 
a = 2;
console.log( a );
Copy the code

As you can see, declarations are promoted by the compiler during compilation. The egg comes first (declaration) and then the chicken (assignment). Which statements will be promoted?

  • Variable declaration: as in the above examplevar a;.excluding the lattera = 2;namelyDoes not contain an assignment declaration.
  • Function declaration: Note that this is a function declaration, not a function expression! (See section 3.3 above for details). When a function is declared to be promoted, the whole function is promoted, not just the name of the function.

4.3 Function Priority

Both function declarations and variable declarations are promoted. But one notable detail (which can occur in code with multiple “duplicate” declarations) is that functions are promoted first, variables second. Consider this code:

foo(); / / 1
var foo;
function foo() { 
  console.log( 1 );
}
foo = function() { 
  console.log( 2 );
};
Copy the code

Will print 1 instead of 2! This code snippet is interpreted by the engine to look like this:

function foo() { 
  console.log( 1 );
}
foo(); / / 1
foo = function() { 
  console.log( 2 );
};
Copy the code

Note that var foo even appears in function foo()… But it is a duplicate declaration (and therefore ignored) because function declarations are promoted before ordinary variables. Note: js ignores previously declared declarations (variable or function declarations with the same name will not be repeated). But a new assignment to the variable overrides the previous one. In short: function declarations take precedence over variable declarations, and will be prioritized before it.

4.4 summary

  • forvar a = 2The JavaScript engine treats var a and a = 2 as two separate declarations, the first being a compile task and the second being an execution task.
  • Wherever a declaration appears in scope, it is processed first before the code itself is executed. Visualize this process as all declarations (variables and functions) being “moved” to the top of their scope, a process known as promotion.
  • The declaration itself is promoted, but assignment operations, including function expression assignments, are not promoted.
  • Note: when the common var declaration is mixed with the function declaration, and the declaration is the same (var name is the same as the function name, which causes js to ignore the repeated declaration)! Be careful not to repeat statements!

Chapter 5: Scoped closures

5.1 enlightenment

  • Closures are everywhere in JavaScript; you just need to be able to recognize and embrace them.
  • Closures are a natural consequence of writing code based on lexical scope; you don’t even need to consciously create closures in order to exploit them.

5.2 Substantive Issues && 5.3 Now I get it

Because these two sections understand thoroughly in fact found that the book does not talk about anything, here to merge, and expand my own understanding and summary. What is a closure? Closures occur when a function can remember and access the lexical scope in which it is located, even if the function is executed outside the current lexical scope. A closure is a combination of a function and the lexical context in which it is declared. My explanation (detailed version): Two points must be included:

  • (1) there are functions, which by their very nature have access to the lexical scope in which they are located. And can store external lexical scope variables and functions into its own function scope.
  • 2. There is a lexical context for the function. In JavaScript, any function is in a lexical environment. Both global scope and function scope.

An MDN interpretative closure is a combination of a function and the lexical context in which it is declared. You can extend this to a minimalist version: functions in JavaScript form closures. Tips: Notice the separate use of lexical scope and lexical context above? (1) the function has not yet been executed, so the lexical scope is used in the static scope. (2) The function is executed, and the lexical scope becomes the lexical environment (both static and dynamic). So MDN is actually a little bit more accurate,

What we call closures (narrow, strict) in everyday use: to facilitate the observation and use of the closure scope. Our actual use would expose the function scope of the closure outside the current lexical scope. This book emphasizes the need for closures to be executed outside of their lexical scope. The authors consider this to be a true closure (i.e., ‘using a closure ‘, and using any callback function is also a closure). So the narrow version is: a closure is a combination of a function and the lexical context in which it is declared, and exposes the function scope of the closure outside the current lexical scope.

Closures expose the scope of a function in three ways: The following section, which is not in the book, is a summary of its actual use, and one of the three forms that fits into everyday use is what we call closures (narrow version)

  • 1. Exposure through arguments to external functions.
function foo() { 
  var a = 2;
  function bar() { 
   baz(a) // Exposed by the arguments of the external function
  }
  bar(); 
};
function baz(val) { 
   console.log( val ); / / 2
}
foo();
Copy the code
  • 2. Exposure via external-scoped variables
var val;
function foo() { 
  var a = 2;
  function bar() { 
   val=a // Exposed by external-scoped variables
  }
  bar(); 
};
foo();
console.log(val)  / / 2
Copy the code
  • 3. Expose the entire function directly by return
function foo() { 
   var a = 2;
   function bar() { 
    console.log(a)
   }
   return bar // Expose the entire function directly via return
};
var val=foo();
val()  / / 2
Copy the code

On closures: First of all, it is important to note that using closures does not always leak memory. It is only the wrong closure that can leak memory. Why do closures leak memory? The reason is mentioned above, because it generally exposes its scope to external use. If not used properly, the memory may be occupied all the time and cannot be reclaimed by the JS garbage collection mechanism. It creates a memory leak. Note: Even if there is nothing in the closure, it still implicitly refers to the variables used in its scope. Because of this hidden feature, closures often cause memory leaks that are not easily detected. Here are some common situations where closures can leak memory:

  • 1. The timer is not cleared in time. Because the timer will only be recycled if it stops first. So the solution is simply to clear the timer in time and assign null to the variable that causes the memory.
  • 2. Circular reference to each other. This is a common mistake, and not easy to spot. Here’s an example:
function foo() { 
  var a = {}; 
  function bar() { 
    console.log(a); 
  }; 
  a.fn = bar; 
  return bar; 
};
Copy the code

Here we create an object of a that is referenced by the internal function bar. Then, A creates a property, fn, that points to bar, and returns innerFn(). This creates a circular reference between bar and A. If you do not use console.log(a) in bar, there will be no reference and no memory leak. NONONO,bar as a closure, all variables in foo are implicitly referenced by bar even if there is nothing inside it. This is something I forgot to mention before, and something that is not mentioned in the book. You know what? I’ll just add it to the front. So even if there is nothing in bar that still causes circular references, the real solution is not to use a.fn = bar.

  • 3. Reference closures to global variables. Because global variables are only recycled when the page is closed.
  • 4. Improper DOM references in closures. This was common in the old Internet Explorer, but modern browsers have grown up and learned to handle this situation on their own. I won’t go into it here. Those who want to know can ask gu Niang and Baidu by themselves.

In a nutshell, the solution is to make closures properly referenced and properly recycled. If not, manually assign the variable to NULL after use, forcing garbage collection.

5.4 Loops and closures

Here’s an example:

for (var i=1; i<=5; i++) { 
  setTimeout( function timer() {
    console.log( i );
  }, i*1000 );
}
Copy the code

The desired result is to output the numbers 1 to 5, one per second. But the actual result is that this code will run at a rate of one per second and output six five times. (As for the explanation in the book, I think it’s a bit complicated and misses the point. Here’s my explanation.) Why is that? Timer () is, of course, a closure. It is an externally accessible variable I. During the for loop,timer() will be repeated 5 times, which means it will console. These five I’s are actually the same I. It comes from the outer scope, the I declared in for. The variable I can only correspond to a unique value in the lexical scope, i.e. the variable and its value are one-to-one. It’s not going to change. So what is this value? This value is the final value! The final value of I is going to be 6 which is the value of I after the for loop. When the engine executes console.log(I), it asks for the scope of I and what the value of I is. At this point, the RHS query of the scope results in the final value of 6.

Why do we think we output 1 to 5 separately? Because in a for loop, we mistakenly assume that the output I of the function is dynamically varying according to the loop each time. That’s the sum of 1 to 5. But in fact the I it calls is the same fixed value, which is the final value 6. You may wonder, does it make sense for me to loop? I was actually set at 6 from the beginning. No change! Wrong! I changed. It did go from 1 to 6. It’s just that the value of I in the outer scope can only be the final value at the end of the loop, and the function timer() does not hold the value of each change in I. It just accesses the value of I in the outer scope which is the final value 6.OK we know what went wrong, that we didn’t store the value of I in a separate scope each time. Now, let’s see what the result of this improved example is.

for (var i=1; i<=5; i++) { 
  (function() {
    setTimeout( function timer() { 
	  console.log( i );
    }, i*1000); }) (); }Copy the code

Its final value is still 5 6. Why? In the example above, it wraps the timer with an anonymous function and executes it immediately. During the for loop, five separate function scopes are created (created by the anonymous function because it is a closure). But the I in these five separate function scopes are all references to the outer scope. That is, they all access the final value of I 6. This is not what we want, we want five separate scopes, each holding a value of “at” I.

Solution: Let’s rewrite it this way.

for (var i=1; i<=5; i++) { 
  (function () {
    var j =i;
    setTimeout( function timer() { 
	  console.log( j );
    }, j*1000); }) (); }// This time the final result is to output the numbers 1 to 5, one per second, each time.
Copy the code

So the anonymous function stores the value of “I” every time through j, so that the value of “I” is stored in a separate scope through j. Note that the value of I stored is the ‘when’ value, not the final value at the end of the loop. By the end of this loop, you have actually created five separate scopes, each holding a value of ‘at’ I (via j). When the engine executes console.log(j) and asks for its corresponding independent scope, the value it gets is the ‘when’ value, no longer 6. We can further abbreviate it as follows:

for (var i=1; i<=5; i++) { 
  (function(j) {
    setTimeout( function timer() { 
	  console.log( j );
    }, j*1000 );
  })(i);
}
// The result is to output numbers 1 to 5, one per second.
Copy the code

Solve with block scope: In ES6, not only can we use functions to create a separate scope, we can also use let declarations to create a separate block scope (within {}). So we can also rewrite it like this:

for (let i=1; i<=5; i++) { 
  setTimeout( function timer() {
    console.log( i );
  }, i*1000 );
}
// The result is to output numbers 1 to 5, one per second.
Copy the code

To rewrite this,let declares I on each loop. And create a separate block scope with the loop’s built-in {}. And the let declaration I holds the value of ‘when’ I in the current block scope. So when the engine executes console.log(I), it asks for the value of I at the corresponding block scope, and the result is the value stored ‘at that time’.

Extension: The block scope can actually be called a ‘pseudo’ closure (pseudo because the closure specifies that it can only be a function). Because it has almost all the features of closures. It can also create a separate scope, and the external scope cannot access variables in the block scope. But a block scope can access an external scope. Here’s an example:

function foo() { 
  var a = 2;
  {  // Display the block scope with {}
    let b = a;
	console.log('In block scope',b) / / 2
  }
  console.log('Outside the block zone',b) //b is not defined
}
foo()
Copy the code

If you want to save a variable to a block scope, you must use a let declaration. If you want to save a variable to a block scope, you must use a let declaration.

Summary of general when to consider using closures: this part is also a summary of my work, if there are any additions or wrong place, welcome to comment correction.

  • 1. You need to create a separate scope and hide some variables or functions from external use. Or you might want to save some variables or functions from the outer scope into this independent scope.
  • 2. You only want to expose a part of your scoped variables or functions for external use.

5.5 module

Let’s start with the following example:

function CoolModule() {
  var something = "cool";
  var another = [1.2.3];
  function doSomething() { 
    console.log( something );
  }
  function doAnother() {
    console.log( another.join( ! "" ")); }return {
      doSomething: doSomething,
	  doAnother: doAnother
  }; 
}
var foo = CoolModule(); 

foo.doSomething(); // cool
foo.doAnother(); / / 1! 2! 3
Copy the code

First of all, let’s analyze the above code as follows: private data variable :something, another Internal function :doSomething, doAnother. The object it returns, the module, is called the public API(at least in the books).CoolModule() is the module constructor or module function. Note:

  • The modules here are not exactly the same as what we call modular development!
  • A module doesn’t have to be a standard object. It can also be a function. Functions are essentially objects, and functions can have properties.
  • There’s a line in the bookCoolModule() is just a function that must be called to create an instance of the module. Neither the inner scope nor the closure can be created if the outer function is not executed.I think this sentence needs to be extended. A single call to a function creates a scope of the function (not created without a call), including the variables and functions within it.

Module pattern: The module pattern must meet the following two conditions :(Here with the above example, the definition in the book is easy to understand)

  • 1. There must be an external enclosing function (i.e. CoolModule), which must be called at least once (each call creates a new module instance –> module instance is the object returned by the function return).
  • 2. The enclosing function (i.e. CoolModule) must return at least one inner function (i.e. DoSomething, doAnother) so that the inner function can form a closure in a private scope and can access or modify the private state (i.e. Something, another).

Module: The object ostensibly returned by the module function (CoolModule in this example) is a module. But a module must also contain the inner functions of the module function (that is, the closure function). It can only be called a module if it contains. Once again, the modules here are different from the modules in modularity, and are not modules in NodeJS.

Module function: A module function is also the module constructor, CoolModule() in this example. It has two common uses.

  • Modify the output module by accepting parameters.
  • By adding the module to add related internal functions, to achieve the output module data.(in the bookName the object to be returned as a public APII thinknamedI think it’s wrong. I think it’sModify theIt is better to add, delete, change and check)

5.5.1 Modern Module mechanisms

Most modules rely on loaders/managers that essentially encapsulate the module definition into a friendly API. Here’s a simple module manager implementation example (line by line with the examples in the book):

// First instantiate our module manager and name it myModules
var MyModules=(function Manager() {
    
    // Save all defined modules as our module pool
    var modules={};

    /** * Define a new module in an AMD-like fashion, taking 3 arguments *name: the name of the module *deps: an array representing the other modules on which it depends *impl: the implementation of the module function **/ 
    function define(name,deps,impl) {
        
        // Iterate over each item in the array of dependent modules, fetching the corresponding module from the pool and assigning a value to it.
		// After the loop,deps changes from an array of module names to an array of corresponding modules.
        for (var i=0; i<deps.length; i++) { deps[i]=modules[deps[i]]; }// Store the new module in the module pool and inject the modules it depends on via apply (i.e., the traversed DEps, in effect, using deps as the impL input)
        modules[name]=impl.apply(impl,deps);
    }
    // Remove the corresponding module from the module pool
    function get (name) {
        return modules[name];
    }
    // Expose the two apis that define and get the module
    return {
        define: define,
        get: get
    }
})()
Copy the code

Note: Later in the book, it is said that the wrapper function (which can be passed any dependency) is introduced for the definition of a module. The wrapper function here is Manger(), which is also the module function mentioned in the previous section. First, let’s explain what a wrapper function is. For example, function A has function b. When we want to call function B, we need to call function A first. So function A is called A wrapper function for function B. That is, when we want to call a module, we need to call its wrapper function, which is called Manger() in this case. It follows that and stores the return value, the module’s API, in a list of modules managed by name. Note that the return value here is the return value of the IMPL.

Next, look at defining and using modules through the manager

MyModules.define('bar'[],function () {
    function hello (who) {
        return "Let me introduce: " + who;
    }
	// Return the public API that provides a hello interface
    return {
        hello:hello
    };
});

MyModules.define('foo'['bar'].function (bar) {
    var hungry = "hippo";
	
    functin awesome () {
	// Where bar is the public API returned by the return module bar
        console.log( bar.hello( hungry ).toUpperCase() );
    }
	// Return the public API to provide an awesome interface
    return {
        awesome:awesome
    }
})

var bar=MyModules.get('bar');// Get module 'bar' from manager
var foo=MyModules.get('foo');// Get the module 'foo' from the manager

console.log(
// Call the hello interface of the module bar
         bar.hello( "hippo"));// Let me introduce: hippo 

// Call the awesome interface of module foo
foo.awesome(); // LET ME INTRODUCE: HIPPO

Copy the code

The main point of this section is to see how the module is now handled in a canonical way. There are two main parts to it. One is to define the module properly by name and dependency and store it. The other is a call to a stored module by name. You can also add a method to remove modules.

5.5.2 Future module mechanisms

Ok, modules in this section are often referred to as modular development. And it mainly mentions the import commonly used in ES6. There is nothing to say.

5.6 summary

Joke: the same function concept has three different names in section 5.5. Later called module constructor! Later called module function! And finally the wrapper function! Every time it changes, you have to think about what it means! Really is powerless to ridicule!!!!

Closures: Closures occur when a function can remember and access the lexical scope in which it is located, and the function is executed outside the current lexical scope.

Modules have two main features:

  • (1) A wrapper function is called to create an internal scope (instantiation of the module constructor, not to joke about frequent name changes);
  • (2) The return value of the wrapper function (that is, the module) must include at least one reference to the inner function, which creates a closure that covers the entire inner scope of the wrapper function.

The second part

Chapter one is about this

1.1 Why use this

Because this provides a more elegant way to implicitly “pass” a reference to an object (that is, a context object), the API can be designed to be more concise and easy to reuse.

1.2 the misunderstanding

Both of the following common interpretations of this are wrong (just look at them and don’t read too much into them to give the wrong impression).

1.2.1 Pointing to itself

It’s easy to think of this as referring to the function itself.

Named function, you can refer to itself internally using the function name to recurse, to add attributes, and so on. (This point was mentioned in Chapter 3, and since it’s mentioned here, I’ll say it again.) Such as:

function foo() {
  foo.count = 4; // Foo points to itself
}
Copy the code

Anonymous functions that want to call themselves need to use arguments.callee but this property is disabled in ES5 strict mode and is not recommended. For details, see the MDN description.

1.2.2 Its scope

Remember: this does not refer to the lexical scope of the function under any circumstances. You cannot use this to refer to something inside the lexical scope. All you have to do for this part is memorize this paragraph.

The ultimate question: Are scopes in JavaScript objects at all? What really matters to me about this section is the phrase “In JavaScript, scopes are really like objects, and any visible identifiers are properties of them. But the scope “object” is not accessible through JavaScript code, it exists inside the JavaScript engine. It reminds me of a question I had when I first learned JS: is scope an object in JavaScript? Although “everything in JS is an object”, the scope does not feel like an object. More like a scope, the scope is enclosed by the {} of a function, restricting access to variables within it. But my intuition is that it should have some connection to the object. Until I read this passage in the book, which confirmed my feelings even more. In JavaScript, a scope is a special object, and all visible identifiers in the scope are its properties. The scope object is not accessible to us through JavaScript code, it only exists inside the JavaScript engine. So scope as an “object” is often ignored.

1.3 What is this

This is bound at runtime, not at write time, and its context (object) depends on the conditions at the time of the function call. The binding to this has nothing to do with where the function is declared, and only depends on how the function is called.

When a function is called, an activity record (sometimes called the execution context) is created. This record contains information about where the function was called (the call stack), how the function was called, and the parameters passed in. This is one of the recorded properties that will be used during the execution of the function. (PS: so this is not equivalent to the execution context)

1.4 summary

  • The first step in learning this is to understand that this refers neither to the function itself nor to the lexical scope of the function
  • This is actually a binding that happens when the function is called, and what it points to depends entirely on where the function is called (something you have to remember about this).

Chapter two: Comprehensive analysis of this

2.1 Call Location

As we know from the previous section, the binding to this depends on where the function is called. What is the call location. The call location is where the function is called in the code (rather than where it is declared).

To find the call location, the most important thing is to analyze the call stack (that is, all the functions called to get to the current execution location). The call location we care about is in the previous call to the currently executing function. PS: Call stack is actually a somewhat complicated concept to explain. Here I will not explain more, here recommended an article, good explanation.

The examples in this section are well explained, so I won’t copy the code here. Analyzing the call stack is really just about finding out at run time where the function we care about is called and by whom. But it’s not going to be as clear when you’re not actually writing code, so let’s just remember that the point to this is the context object that we’re calling this function from. That means that wherever we call this function,this points to it. And the call stack can also be viewed through the browser’s developer tools, simply by adding a debugger to the line of confused code. When the browser is in debug mode, we can view the call stack in the call list. We usually only use this method when we are looking for bugs.

2.2 Binding Rules

Once you have found the call location, you need to determine which of the following four binding rules your code belongs to. This can then be bound. Note: This binds to the context object, not the function itself or the lexical scope of the function

2.2.1 The default binding

What is a stand-alone function call: a call to a function reference that is used directly by the function without any decorations. Let’s make it simple. Let’s just call it func() and there’s nothing in front of it. Instead of calling through an object property such as obj.func(), there is no new keyword new Function(); There is no apply,bind, or call to force this to change. Default binding: when used as a stand-alone function call (regardless of where the function is called, whether globally or within another function), this defaults to window; Note: If strict mode is used, the default binding cannot be used for global objects, so this is bound to undefined.

2.2.2 Implicit binding

Implicit binding: A function is owned or contained by an object. That is, functions are referred to as properties of objects. For example, obj.func(). This is bound to the object. Implicit loss: Implicit loss, either through function aliases or by taking a function as an input. Just find where it’s really called, and the function has no prefixes or explicit bindings (as we’ll see in the next section)(in non-strict mode). This is bound to the window by default. Note: in practice, most of the errors with this are caused by a lack of understanding of implicit missing. Remember that the function is called without any decoration or explicit binding (i.e., call, apply, bind). This refers to window

2.2.3 Explicitly bound

When analyzing implicit binding, we must bind this indirectly (implicitly) to an object by including a property within the object that points to a function and indirectly refers to the function through that property. Explicit binding is needed if we want to force a function call on an object instead of including a function reference inside the object. Explicit binding: A binding object that can specify this directly is called an explicit binding. The call, apply, and bind methods are all explicit bindings. Note: If you pass a primitive value (String, Boolean, or numeric) as the binding object for this, the primitive value is converted to its object form (i.e., new String(..)). , new Boolean (..) Or the new Number (..) ). This is often referred to as “boxing”.

Hard binding: Use the call, apply, and bind methods to force explicit binding of this. This is called hard binding. A typical application scenario for hard binding is to create a wrapper function that passes in all parameters and returns any values it receives. We often use apply in wrapped functions, partly because it allows you to bind this manually, but more importantly because you can use the second parameter of apply to easily inject all the arguments passed in. For example, the previously mentioned modules[name]= imp.apply (IMPl,deps). Because we don’t know how many parameters are passed in, we can easily inject them all with a single DEps. Another common one is foo.apply(null,argue). When we set the first argument of apply to NULL, this is bound to window by default. Make sure that this is not used in function foo when using this. Otherwise, global contamination is likely. If it’s a function from a third-party library, don’t use it, because you don’t know if someone else’s function uses this(more on this in the next section). Another common one is foo.call(this). The “this” in foo will point to the context of the current call.

“Context” for API calls: Many functions in third-party libraries, as well as many new built-in functions in the JavaScript language and host environments, provide an optional argument, often called a “context,” that functions in conjunction with bind(..). Again, make sure your callback uses the specified this.

2.2.4 new binding

The mechanism for new in JavaScript is actually quite different from the class-oriented language. In JavaScript, constructors are just functions that are called when the new operator is used. They don’t belong to a class, and they don’t instantiate a class. In fact, they are not even a special type of function, they are just normal functions called by the new operator. There is no such thing as a “constructor,” only a “construct call” to a function.

Using new to call a function, or when a constructor call occurs, automatically does the following.

  • Create (or construct) an entirely new object.
  • 2. The new object will be connected by [[prototype]].
  • 3. This new object is bound to the function call this.
  • 4. If the function returns no other object, then the function call in the new expression automatically returns the new object.

Example:

function foo(a) { 
  this.a = a;
}
var bar = new foo(2); 
console.log( bar.a ); / / 2
Copy the code

Use new to call foo(..) We construct a new object and bind it to foo(..). On this in the call. If you call a function with the new keyword in front of it, you create a new prototype object connected to the function. This refers to the new object.

2.3 priority

Conclusion: New binding = Display binding > Implicit Binding > Default binding But then again, in practice, you don’t have this kind of complex interleaved binding. So just remember the following decision.

Judge this: Now we can determine which rule the function is applying at a particular call location based on the priority. It can be judged in the following order:

  • 1. Is the function called in new (new binding)? If so, this binds to the newly created object. var bar = new foo()
  • 2. Are functions called by call, apply(explicit binding), or hard binding? If so, this is bound to the specified object. var bar = foo.call(obj2)
  • 3. Is the function called in a context object (implicit binding)? If so, this binds to that upper context object. var bar = obj1.foo()
  • 4, if neither, use the default binding. If in strict mode, it is bound to undefined, otherwise it is bound to a global object. Var bar = foo() For normal function calls, this should help you understand how this binds.

2.4 Binding Exceptions

2.4.1 Ignored this

If you pass null or undefined to call, apply, or bind as a binding object to this, these values will be ignored. The default binding rule is applied, and this will be bound to the window. Usage Scenario: A very common practice is to use apply(..) To “expand” an array (which can also be used for easy argument injection) and pass it as an argument to a function. Similarly, bind(..) The parameters can be curified. it is convenient to curified. it is much simpler than writing it yourself.

Note:

  • In ES6, you can use… Operator instead of apply(..) To “expand” the array, foo(… [1,2] is the same as foo(1,2) to avoid unnecessary this binding. Unfortunately, there is no curry syntax in ES6, so bind(..) is still needed. .
  • When using null or undefined, make sure that this is not used in the function. Otherwise, global variables can be corrupted! Especially using third-party libraries!

If this is used inside the function, using null would be globally destructive. So we can create a “DEmilitarized zone” object — which is an empty undelegated object (delegates are covered in Chapters 5 and 6). Make this bound to the “DMZ “. So that it doesn’t damage the whole picture. How to create a DMZ. This is to create an empty Object via object.create (null). This method is similar to {}, but does not create the object. prototype delegate, so it is “emptier” than {}.

If a function has this in it, it must have a variable or function that needs to be called, and bind it directly to an empty object. What’s the point of not getting anything? So the function passes null without this. If this is present, bind it to the object that really needs it, not to an empty object. These are my own opinions, if there is something wrong, welcome to comment correction.

2.4.2 Indirect Reference

function foo() { 
  console.log( this.a );
}
var a = 2;
var o = { a: 3.foo: foo }; 
var p = { a: 4 };
o.foo(); / / 3
(p.foo = o.foo)(); // 2 is foo(). This is bound to window by default
Copy the code

The indirect reference in the example is the result of a poor understanding of the function. In fact, (p.foo = o.foo)() is (foo)(), which is a global call to foo(), so this is bound to the window by default. Note: For the default binding, what determines the object bound to this is not whether the call location is in strict mode, but whether the body of the function is in strict mode. If the function body is in strict mode, this is bound to undefined, otherwise this is bound to the global object. (That should have been said in section 2.2.1!)

2.4.3 soft binding

Hard binding greatly reduces the flexibility of the function, making it impossible to modify this using either implicit or explicit binding. This is where soft binding is needed. Tips: The soft binding method given here is good. However, it is recommended to use it in your own code and comment it out. Lest others use the wrong judgment of this.

2.5 this lexical

ES6 introduces a special type of function that doesn’t work with the above four rules: arrow functions. Instead of using the four standard rules for this, the arrow function determines this based on the outer (functional or global) scope. (Traditional this has nothing to do with function scope, but only with the context object where the call is made. This point was emphasized at the beginning of this chapter.)

Important:

  • Arrow functions are most commonly used in callback functions, such as event handlers or timers.
  • Arrow functions can be used like bind(..) Make sure that the this function is bound to the specified object
  • The arrow function replaces the traditional this mechanism with a more common lexical scope.

Note: In this case:

function module() {
  return this.x;
}
var foo = {
  x: 99.bar:module.bind(this) // This in bind is window.
  
}
var x="window"

console.log(foo.bar())//window

Copy the code

Before ES6 we were already using a pattern that was almost identical to the arrow function:

function foo() {
var self = this; // lexical capture of this 
  setTimeout( function(){
             console.log( self.a );
         }, 100 );
  }
var obj = { 
    a: 2
};
foo.call( obj ); / / 2
Copy the code

Although both self = this and the arrow function seem to replace bind(..) , but essentially, they want to replace the this mechanism. (That’s true. I usually use “me” instead of “self.” Because it’s two words short =.=)

Coding specifications for this suggest:

    1. Use only lexical scope and completely abandon error-this style code;
    1. Use the completely this style, using bind(..) when necessary , try to avoid self = this and arrow functions.

In my actual work, I use a mixture of the two, and use the lexical scope style in most cases. Because sometimes it’s really hard to be totally unified. So the way I’m used to it now, when I write any function, the first thing I start with is var me =this; So at first glance, you know: Oh, this function is lexical scoped. Especially when callbacks are involved in functions. This avoids the problem of finding that this is bound somewhere else, and that this is not uniform within a function.

2.6 summary

If you want to determine the this binding of a running function, you need to find where the function was called directly. Once found, the following four rules can be applied in order to determine the binding object for this.

    1. Called by new? Bind to the newly created object.
    1. Is it called by call or apply(or bind)? Bind to the specified object.
    1. Called by the context object? Bind to that context object.
    1. Default: bound to undefined in strict mode, otherwise bound to global object.

Be aware that some calls may inadvertently use the default binding rules. If you want to “safely” ignore this binding, you can use a DMZ Object such as ø = object.create (null) to protect the global Object.

Instead of using the four standard binding rules, the arrow function in ES6 determines this based on the current lexical scope. Specifically, the arrow function inherits the this binding of the outer function call (whatever this is bound to). This is actually the same as the self = this mechanism in the previous ES6 code.

Note: The most important thing to note is that when you use jquery or Vue, this is dynamically bound. Most jQuery methods set this to the selected DOM element. When using vue.js, methods and calculation functions typically set this to an instance of the Vue component. All the lifecycle hooks in the Vue document automatically bind the this context to the instance, so you can access data and perform operations on properties and methods. This means that you cannot use arrow functions to define a lifecycle method (such as created: () => this.fetchTodos()). This is because the arrow function is bound to the parent context, so unlike what you would expect from a Vue instance, the behavior of this.fetchTodos is undefined. This includes using third-party Ajax, such as Axios. The solution is as simple as either using traditional function or using let _this=this to take over. In fact, when you use vue, your default idea is that this refers to an instance of vue. So, except for the hook function and axios which are a little bit affected, that’s fine.

Context (object) and function scope are different from each other.

  • Context: Can be thought of as an object in which all variables are stored. Context is created when a function is called and executed by the engine. If you don’t call it, then there’s no context.
  • Scope: In addition to global scope, only functions and ES6’s new let,const, can create scope. Creating a function creates a scope. Once a function is created, it has its own scope, whether you call it or not. Scope controls access to variables in the called function.
  • Both: Scope is function based, while context is object based. Scope involves variable access in the function being called, and is different for different call scenarios. Context is always related to the this keyword, which controls the reference to this. A scope may contain multiple contexts. There may never be a context (the function is not called); It is possible that the context is now destroyed after the function is called (garbage collection); It is possible for one or more (closures) to exist simultaneously.

Chapter III Objects

3.1 grammar

Objects can be defined in two forms: declarative (literal) forms (often referred to as object literals) and constructors.

  • Declaration forms (object literals):
var myObj = { 
  key: value
  // ... 
};
Copy the code
  • Structural form:
var myObj = new Object(a); myObj.key = value;Copy the code

The constructed form and the literal form generate the same objects. The only difference is that in a literal declaration you can add multiple key/value pairs, but in a constructed form you must add attributes one by one. PS: Most of the time we use object literals to create objects.

3.2 type

There are six main types in JavaScript (the term is “language types “)

  • string
  • number
  • boolean
  • null
  • undefined
  • object

Simple data types: String, Boolean, number, null, and undefined are simple primitive types, not objects. Null is sometimes used as an object type, but this is a bug in the language itself. Typeof NULL returns the string “object” when executed against NULL. In fact, NULL itself is a primitive type. PS: The reason is that different objects are represented as binary at the bottom level. In JavaScript, if the first three bits of binary are all zeros, it will be judged as object. The binary representation of null is all zeros, and naturally the first three bits are also zeros, so typeof returns “object”.

Objects: Objects are not created by hand, but there are many built-in objects in JavaScript, which are also subtypes of objects. Built-in objects:

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

In JavaScript, these built-in objects are really just built-in functions. These built-in functions can be used as constructors (function calls generated by new — see Chapter 2). A few notes:

  • A function is a subtype of an object (technically a “callable object”). Functions in JavaScript are “first class citizens” because they are essentially just like normal objects (only callable), so they can be manipulated just like any other object (for example, as arguments to another function).
  • When a String,Number, or Boolean is created from a literal, the engine automatically converts the literal to a String,Number, or Boolean, so they have access to the built-in properties and methods of the corresponding object.
  • Null and undefined have no corresponding constructors; they are only literal. Date, by contrast, has a construct and no literal form.
  • Object, Array, Function, and RegExp(regular expressions) are objects whether they are literal or constructed. It is not a literal (this is certain, because whatever form is created is an object type and cannot be any other type, and there is actually no such thing as a literal). But using constructors provides some additional options (built-in).
  • Error objects are rarely created explicitly in code and are usually created automatically when an exception is thrown. You can also use new Error(..) This construction form, but generally you don’t need it.

3.3 the content

Object properties: Values (of any type) stored in a specific named location. Property name: The name of the property stored inside the object container. The property value does not exist in the object. Instead, the property name (like a pointer, which is technically a reference) points to where the value is actually stored (like a door number). There are two forms of attribute names:

    1. use.The operator, which is also the most common form we use. It is often referred to as “property access “..The operator will require that the attribute name conform to the naming convention of the identifier.
    1. use[".."Syntax. This is often called “key access.”[".."The syntax can accept any UTF-8/Unicode string as the property name. and[".."Syntax uses strings to access properties, and if your property name is a variable, you can use the example in the bookmyObject[idx]This is where “key access” is most often used. butIf idx is an attribute name, write it againmyObject["idx"]The value is a string.

Note: The book says that in objects, attribute names are always strings. If you use a value other than string(literal) as the property name, it will first be converted to a string. Even numbers are no exception, although they are used in array subscripts, they are converted to strings in object property names. This was true before ES6, but now we have symbol. Symbol can also be used as an object property name, and symbol cannot be converted to a string!

Supplement: Here I modified the examples in the book to get this example:

var myObject = { 
  a:2.idx:111
};
var idx="a";
console.log( myObject[idx] ); / / 2
console.log( myObject["idx"]);/ / 111
console.log( myObject[this.idx] );  // 2 In this case, this refers to window.[] this also follows the rules in the previous chapter
// Is the result the same as you thought?
Copy the code

3.3.1 Computable attribute name

ES6 adds computable attribute names, which can be used in literal form by wrapping an expression with [] :

var prefix = "foo";

var myObject = {
   [prefix + "bar"] :"hello", 
   [prefix + "baz"] :"world"
};
myObject["foobar"]; // hello
myObject["foobaz"]; // world
Copy the code

3.3.2 rainfall distribution on 10-12Properties and Methods

  • We often refer to functions referenced within an object as “methods” (which they are).
  • The function doesn’t actually belong to the object; it’s just a reference to the function. Object property access returns a function that is no different from any other function (except for the possible implicit binding of this to the object).
  • Even if you declare a function expression in the literal form of an object, the function does not “belong” to the object — they are just multiple references to the same function object.

3.3.3 An array of

  • An array of support[]Form access stored values, where[]By default, the value in is a numeric subscript (an integer starting from 0, also known as the index). For example,myArray[0]
  • Arrays are also objects, so even though each index is an integer, you can still add properties to arrays. For example,myArray.baz = "baz"Note: Adding a new attribute does not change the length value of the array, although it is accessible.
  • Array can be passedmyArray[1]=11; myArray["2"]=22;This form modifies the contents of the array, adding.
  • Arrays can pass through as well as objects, thoughKey/value pairsBut JS has optimized the behavior and use of arrays. So it’s recommended to use the defaultSubscript/value pairForm to use.

We doCopy the object

  • Replication is divided into shallow copy and deep copy. A shallow copy replicates the basic data types in an object (creating a new region in memory), and in the case of an object, continues to reference them. Instead of recreating an “identical” object. A deep copy is a deep copy of all contents (including objects).
  • In general, you can copy objects with JSON.var newObj = JSON.parse( JSON.stringify( someObj ) );But it is important to note that this approachThis does not work for objects containing function or Date!
  • ES6 defines object.assign (..) Method to implement shallow replication. The specific usage will not be repeated here.

3.3.5 Attribute descriptor

Starting with ES5, all properties have property descriptors.

  • See property descriptors: can use Object. GetOwnPropertyDescriptor (myObject, “a”); Method to view the property descriptor for property A in myObject.
  • Configure property descriptors: You can use object.defineProperty (..) Method for property descriptors is like configuration. Here’s an example:
var myObject = {};
Object.defineProperty( myObject, "a", {
        value: 2.writable: true.configurable: true.enumerable: true}); myObject.a;/ / 2
// This method can configure four property descriptors
Copy the code

Note: The book’s reference to attribute descriptors as “data descriptors” is inaccurate. There are two main types of attribute descriptors that exist in objects: data descriptors and access descriptors. A data descriptor is an attribute that has a value that may or may not be writable. Access descriptors are properties described by getter and setter functions. The descriptor must be one of these two forms; It can’t be both. (getters and setters are two descriptors that we will talk about later.) the relationship between them is as follows :(see MDN for details)

configurable enumerable value writable get set
Data descriptor Yes Yes Yes Yes No No
Access descriptor Yes Yes No No Yes Yes

A descriptor is considered a data descriptor if it does not have value,writable, GET, or set. If a descriptor has both (value or writable) and (get or set) keywords, an exception will be generated.

Value is the corresponding value of the attribute. The default is undefined. The three remaining attribute descriptor keys are described below:

  • 1. Writable determines whether the value of the attribute can be modified. When set to false, and then modify the value of the attribute, it will silently fail. In strict mode, TypeError is reported.
  • 2. A freely Configurable property descriptor is freely Configurable. If true, you can use defineProperty(..) Method to modify the property descriptor. Note: Modifying a non-configurable property descriptor is an error whether or not you are in strict mode. Changing the information to false is a one-way operation and cannot be undone. One exception, however, is that you can change the writable state from true to false, even if the property is freely :false. However, you cannot change the state from false to true. In addition to being unable to be modified, a different :false information also disables the removal of this property.
  • Enumerable determines whether the property appears in the object’s property enumeration. For example the for… In circulation. If Enumerable is set to false, this property does not appear in the enumeration, although it is still accessible. In contrast, setting it to true causes it to appear in the enumeration.

3.3.6 invariance

In addition to the object.defineProperty (..) mentioned above ES5 can also implement immutable properties or objects in a variety of ways. Note: All of these methods are only shallowly immutable. If the target object references other objects (arrays, objects, functions, etc.), the contents of those other objects are not affected and are still mutable. It’s like a shallow copy.

Note: Deep immutability is rarely required in JavaScript programs. Some special cases may require this, but given the general design patterns, if you find that you need to seal or freeze all your objects, you might want to take a step back and rethink the design of your program to make it more responsive to changing object values.

Methods:

  • Object constants (immutable) can be created using a different :false and writable:false property that is freely configurable, freely defined, and freely deleted.
  • Use Object. Prevent Extensions(myObject). You can prevent an Object from adding new properties and keep existing properties. In non-strict mode, the creation of property B fails silently. In strict mode, a TypeError is raised.
  • 3. Seal (not configurable, but modifiable) using object.seal (..) Will create a “sealed” Object, this method will actually call object.preventExtensions (..) on an existing Object. And marks all existing properties as different :false. Not only can you not add new attributes after sealing, you can’t reconfigure or remove any existing attributes (although you can change the values of the attributes).
  • 4. Freeze (not configurable, not modifiable) Creates a frozen Object, which actually calls object.seal (..) on an existing Object. And mark all “data access” properties as writable:false so that their values cannot be modified. This method is the highest level of immutability you can apply to an object, and it disallows changes to the object itself and any of its immediate properties (though as we said earlier, other objects referenced by the object are not affected).

Note: You can “deep freeze” an Object (even the referenced Object) by first calling object.freeze (..) on the Object. , then iterates through all the objects it references and calls object.freeze (..) on those objects. . But be careful! The object you refer to may be referenced elsewhere.

Note: Deep immutability is rarely required in JavaScript programs. Some special cases may require this, but given the general design patterns, if you find that you need to seal or freeze all your objects, you might want to take a step back and rethink the design of your program to make it more responsive to changing object values.

3.3.7 [[Get]]

var myObject = { 
   a: 2
};
myObject.a; / / 2
Copy the code

How does myObject.a get the value 2? Myobject. a uses the built-in [[Get]] operation on the object by default (a bit like a function call :[Get]). First, it looks in the object to see if there is a property with the same name, and if it does, it returns the value of that property. If no attribute with the same name is found, another very important behavior is performed as defined by the [[Get]] algorithm. This is essentially traversing the chain of possible [[Prototype]] properties. If none of the attributes with the same name are found, the [[Get]] operation returns the value undefined.

Note: If you refer to a variable that does not currently exist in the lexical scope, undefined is not returned like object properties. Instead, a ReferenceError exception is thrown.

3.3.8 [[Put]]

Since there is a [[Get]] operation to Get the value of a property, there must be a corresponding [[Put]] operation to set or create the property.

[[Put]] is triggered in two cases :1. The object already has this property. 2. Object does not have this property.

If this property already exists in the object, the [[Put]] algorithm generally checks for the following:

    1. Are properties access descriptors (see next section)? Call the setter if it is and there is a setter.
    1. Is writable false in the data descriptor of the property? If so, it fails silently in non-strict mode and raises TypeError in strict mode.
    1. If neither, set the value to the value of the property.

If this property does not exist in the object, the [[Put]] operation becomes more complex. This is covered in detail in Chapter 5 when we discuss [[Prototype]].

3.3.9 Getter and Setter

The default [[Put]] and [[Get]] operations on objects control setting and getting property values, respectively. Currently we can’t override the entire object with the operations [[Get]] and [[Put]], but in ES5 we can override some default operations using getters and setters, which can only apply to individual properties, not to the entire object.

Note: Access descriptors later in the book are access descriptors. For attribute descriptors, access descriptors, and data descriptors, see MDN interpretation.

Getter: The getter is a hidden function that is called when the property value is retrieved. The default [[Get]] action on the individual property is also overridden. When you set the getter, you must not set value or writable at the same time, otherwise an exception will be generated. And when you set getters or setters,JavaScript ignores their value and writable features.

Syntax: {get prop() {… }} or {get [expression]() {… }}. Prop: specifies the name of the property to be set. Expression: specifies the name of the computed property that can be used from ECMAScript 2015. Usage:

var myObject = {
  a: 1111.Myobject. a is 2. This is because the getter is set so the value property is ignored.
  // Method 1: define a getter when the new object is initialized
  get a() {
    return 2}};Object.defineProperty( 
  myObject, // The target object
  "b"./ / the property name
  {
    Define a getter on an existing object using defineProperty
    get: function(){ return this.a * 2 },
    // Make sure that b appears in the object's property list
    enumerable: true}); myObject.a =3;  // Ignore the writable feature because the getter is set. So this assignment didn't work
myObject.a; / / 2
myObject.b; / / 4

delete myObject.a;// Can be deleted using the delete operator
Copy the code

Setter: The setter is a hidden function that is called when the property value is retrieved. It also overrides the default [[Put]] operation (that is, the assignment operation) for that single property. When you set the setter, you cannot set value or writable at the same time, otherwise an exception will be generated. And when you set getters or setters,JavaScript ignores their value and writable features.

Grammar: {set prop (val) {…}} or {set [expression] (val) {…}}. Where prop: the name of the property to be set val: an alias for the variable used to hold the value attempted to assign to prop. Expression: The name of a computed property can be used from ECMAScript 2015. Usage:

var myObject = {
  // Note: Getters and setters usually come in pairs (defining only one will often result in unexpected behavior):
  // Method 1: define a setter when the new object is initialized
  set a(val) {
    this._a_ = val * 2
  },
  get a() {
    return this._a_ 
  }
};

Object.defineProperty( 
  myObject, // The target object
  "b"./ / the property name
  {
    set: function(val){ this._b_ = val * 3 },
    Define setters on an existing object using defineProperty
    get: function(){ return this._b_ },
    // Make sure that b appears in the object's property list
    enumerable: true}); myObject.a =2;  
myObject.b = 3;  
console.log(myObject.a); / / 4
console.log(myObject.b);/ / 9

console.log(myObject._a_);/ / 4
console.log(myObject._b_);/ / 9

delete myObject.a;// Can be deleted using the delete operator
Copy the code

3.3.10 existence

Attribute existence: To determine whether an object has a property (specifically, to check whether the property name exists), you need to:

    1. inThe in operator checks whether the property is in the object and its [[Prototype]] Prototype chain (see Chapter 5).
    1. hasOwnProperty(..) hasOwnProperty(..) Only check if the property is in the myObject object, not the [[Prototype]] chain.

Note:

  • 1. If some objects are not connected to object.prototype (created by object.create (null) — see chapter 5). In this case, something like MyobecjT.hasOwnProperty (..) It will fail. Then you can use a tougher approach to judgment: Object. The prototype. The hasOwnProperty. Call (myObject, “a”), it use basic hasOwnProperty (..) Method and explicitly bind it (see Chapter 2) to myObject.
  • 2. For arrays, do not use the in operator because it checks for attribute names. In arrays, attribute names are indexes and are not the main concern. We are more concerned with the value stored in an array, so we should check whether a value exists in an array using the indexOf method.

Property enumerability: If an property exists and its Enumerable property descriptor is true. Then it is enumerable. And can be used for… In loop. Not only does an attribute need to exist, but it needs enumerable to be true in order for it to be enumerable. For… In is not suitable for traversing an array, which is done using a traditional for loop.

To determine the enumerability of attributes, use the following methods:

    1. propertyIsEnumerable(..) It checks if the given property name exists directly in the object (not on the prototype chain) and satisfies Enumerable: True.
    1. Object.keys(..) Returns an array containing all the enumerable properties.
    1. Object.getOwnPropertyNames(..) Returns an array containing all properties, whether or not they are enumerable.

3.4 traversal

I think it’s a good idea to clarify for… In and for.. There is no need to dig too deeply into the use of @@iterator and Symbol. Note that the second line of done on page 123 is a Boolean value indicating whether there are other values that can be traversed. There is an error. Instead, done should be a Boolean indicating whether or not the traversal is complete. Otherwise, when you read the description behind it, you will feel the contradiction. Here I also use for… In and for.. Of is the main explanation, but also more close to our actual use.

for.. in

  • for.. The in loop can be used to traverse a list of enumerable properties of an object (including the [[Prototype]] chain).
  • In fact the for… In traverses not the value of the property, but the name of the property (i.e., key). If you want to get the value of the property, you need to manually use obj[key] to get it.
  • In general, it is recommended to use for.. In. You can also use arrays using for.. When traversing groups, it is recommended to use for.. of.

for.. of

  • ES6 added a for.. Of loop syntax (you can also iterate over objects if they define iterators)
  • for.. Of and the for… The big difference with in is that it loops over the property value, not the property name. But it only loops through the values in the array, not the keys in the object.(I’ll get to that in a later example.)
  • for.. The of loop first requests an iterator object from the object being accessed, and then iterates over all the returned values by calling the iterator object’s next() method. Arrays have built-in @@iterators, (objects don’t, so you can’t use for.. Of, unless we define one) therefore for.. Of can be applied directly to arrays.

Example comparison

let arr = ['shotCat'.111, {a:'1'.b:'2'}]
arr.say="IG niu pi!"
/ / use the for... In circulation
for(let index in arr){
    console.log(arr[index]);//shotCat 111 {a:'1',b:'2'} IG niu pi!
}
/ / use the for... Of circulation
for(var value of arr){
    console.log(value);//shotCat 111 {a:'1',b:'2'}
}
/ / note for.. Of does not traverse to get 'IG niu PI! The reason I mentioned earlier is that it only loops through the values stored in the array and doesn't touch the key in the object. Will be in)
Copy the code

How to make objects use for.. of ? You can choose to use the book yourself to define a Symbol. Iterator property via object.defineProperty (). I won’t go over it here. It’s also the closest to native usage. But I’m going to show you a slightly simpler way to do it. Use object.keys () as described in the previous section. Here’s an example:

var shotCat={
    name:'shotCat'.age:'forever18'.info: {sex:'true man'.city:'wuhan'.girlFriend:'New wall knot clothes! '}}for(var key of Object.keys(shotCat)){
    // Use the object.keys () method to get an array of Object keys
    console.log(key+":"+shotCat[key]);
}
Copy the code

3.5 summary

The summary of the book is quite complete, here I carry under

  • Objects in JavaScript have literal forms (e.g., var a = {.. }) and constructs (for example, var a = new Array(..)). ). The literal form is more common, but sometimes the constructed form can provide more options.
  • Objects are one of six (or seven, depending on your point of view) base types. Objects have subtypes, including function. Different subtypes have different behaviors. For example, the internal tag [object Array] indicates that this is an Array of subtypes of the object.
  • An object is a collection of key/value pairs. The value of the property can be obtained using.propName or [“propName”] syntax. When accessing a property, the engine actually calls the internal default [[Get]] operation ([[Put]] when setting the property value). The [[Get]] operation checks to see if the object itself contains the property, and if it doesn’t, it looks for the [[Prototype]] chain (see Chapter 5).
  • Properties of properties can be controlled using property descriptors, such as writable and freely. Alternatively, you can use object.preventExtensions (..) And the Object. Seal (..) And the Object. Freeze (..) To set the immutability level of an object (and its properties).
  • Properties do not necessarily contain values — they may be “access descriptors” with getters/setters. In addition, attributes can be enumerable or non-enumerable, which determines whether they appear in for.. In the loop.
  • You can use ES6’s for.. Of syntax to traverse values in data structures (arrays, objects, and so on), for.. Of looks for a built-in or custom @@iterator and calls its next() method to iterate over the data values.

Chapter 4 Mixed Object “Classes”

Note: As indicated in the book, more than half of the chapter is devoted to object-oriented and class concepts. Can read the person cloud in the fog, give a person oh, probably is this kind of feeling. Later on, I’ll try to find a place in JavaScript for these abstract concepts, but I won’t get too carried away with them.

4.1 class theory

Description:

  • Class is a form of organizational structure that describes a code.
  • In JS, the common class is the constructor. It can also be the class keyword provided by ES6. Inheritance is a function; Instantiation is an object, most commonly through the new constructor.

Note: The Javascript language does not support “classes”, which are also simulated “classes”. Even the “classes” introduced by ES6 are essentially the syntactic sugar of JavaScript’s existing prototype-based inheritance.

4.1.1 The class design pattern

In a word: a class is actually a design pattern!

  • Classes are not a required programming foundation, but an optional code abstraction.
  • Some languages (like Java) don’t give you a choice. Classes aren’t optional — everything is a class.
  • Other languages (such as C/C++ or PHP) offer both procedural and class-oriented syntax, and developers can choose one style or use a mix of the two.

4.1.2 Classes in JavaScript

JavaScript has only a few syntax elements that are approximations to classes (such as new and instanceof), but later in ES6 some elements were added, such as the class keyword, which is essentially the syntax sugar of JavaScript’s existing prototype-based inheritance. It’s not really a class.

4.2 The mechanism of class

I had a lot of trouble understanding the description in this part of the book, mainly because the reference to stack and heap clashed with my understanding of stack and heap in memory. Here briefly say my understanding, such as wrong, grateful for correction.

The stack class is actually a data structure. It stores data and provides some common methods (similar to the classes mentioned above). But the stack class is really just an abstract representation, and if you want to manipulate it, you need to instantiate it.

2To build

This section explains the relationship between classes and instances. In JavaScript,” classes “are primarily constructors, and” instances “are objects.

A class is like a blueprint. To get an object that we can actually interact with, we have to instantiate something in terms of the class. This thing (object) is usually called an instance, and if necessary, we can call methods directly on the instance and access all its public data properties.

In a nutshell: Classes are instantiated to get instance objects.

4.2.2 The constructor

  • Class instances are constructed by a special class method, usually with the same name as the class, called a constructor.
  • An instance is instantiated by the constructor: the new constructor.
  • Most constructors need to be called with new so that the language engine knows that you want to construct a new instance of the class.
  • The constructor returns an object, which is called an instance. This object can call methods of the class.

4.3 Class inheritance

In a class-oriented language, you can define a class and then define a class that inherits from the former. The latter is usually referred to as a “subclass” and the former as a “superclass”. A subclass can inherit the behavior of its parent class and can modify the inherited behavior according to its own needs (it usually does not change the behavior of its parent class). Note: The superclasses and subclasses we’re talking about are not instances. In JavaScript, classes are usually constructors.

4.3.1 polymorphism

Maybe you read the “explanation” and still don’t know much about polymorphism. Let me explain again: what is polymorphism? The same operation, applied to different objects, produces different results. After issuing the same instruction, different objects will have different reactions to the instruction, so called polymorphism. Inherited means super. And note that these examples in the book are all pseudocode! It’s not really done that way in JavaScript. Added: here is the MDN link about super.

  • Polymorphism:
    • Relativity: Relativity is a reference to a parent class by a child class (such as using super), and a reference to a parent class does not affect the behavior of the parent class (it does not redefine the behavior of the parent class itself), such as the example child class reference to drive() in the book.
    • Repeatable definition:A subclass inherits a method from its parent class and can redefine that method, as in the book where a subclass modifies the output in drive().The appropriate definition is automatically selected when the method is calledWhen a subclass instantiates drive(), it does not directly execute drive(). It’s drive() on a subclass. Simply put, the instance comes from that class, and it uses the methods of that class.

Description:

  • In JavaScript, “classes” belong to constructors (similar to foo.prototype… Type reference). Since the relationship between the superclass and the subclass in JavaScript only exists in the.prototype object corresponding to their constructors, there is no direct relationship between their constructors and it is not easy to implement a relative reference between the two (this can be “solved” in ES6 classes with super, See Appendix A).
  • Polymorphism does not mean that a child class is associated with its parent class; it only gets a copy of its parent class. Class inheritance is really just copying.
  • These concepts have been used countless times before, but now you need to understand, “Oh, that’s your name!”

4.3.2 Multiple inheritance

Multiple inheritance: A child class can inherit methods from multiple parent classes. Problems with multiple inheritance: Multiple inheritance can cause problems with conflicting legal names of multiple parent classes, so which method does the subclass refer to? Multiple inheritance and JavaScript: JavaScript itself does not provide multiple inheritance. However, it can achieve the effect of multiple inheritance in other ways.

4.4 Mixed with

There are only objects in JavaScript; there are no “classes” that can be instantiated. An object is not copied to other objects, they are associated (see Chapter 5)(actually references, so its polymorphism is “relative”). Since classes in other languages behave in a copycat way, JavaScript developers have come up with a way to simulate the copycat behavior of classes. This method is called mixin.

4.4.1 Explicit with

For the record, all classes in the book are in the form of objects. The sourceObj, targetObj examples can be misleading. There are no real classes in JavaScript, and classes are just syntactic sugar (including classes in ES6). In JavaScript, a “class” is usually a constructor, and you can’t do that, you can only do that on its instance object. Don’t get carried away by the examples in the book, don’t get confused, after all, we end up using JavaScript(and not other object-oriented languages). The class in it is often not an object!

Explicit blending: The book does not give an explicit definition of explicit blending, but read the whole chapter. You basically know what is explicitly mixed in. Explicit blending is either explicitly copying the parent object properties one by one, or selectively copying (i.e. existence checking in the example) onto the child objects, through methods like mixin().

Explicitly mixing in common methods: As in the example in the book, you first take child objects and specialize them (define your own properties or methods). The parent object is then selectively copied to the child object through the mixin() method (i.e., existence checking, filtering existing properties of the child object to avoid conflicts).

Explicit blending Caution: When explicitly blending, one thing to keep in mind is that you want to avoid properties of the parent object that conflict with properties of the child object that are specialized. This is why we do the existence check in the example and, later on, the risk of rewriting with mixed copies.

Explicit polymorphism: the method in the parent object is explicitly bound to the child object. The example in the book is Vehicle.drive.call(this). Explicit polymorphism is also for JS to simulate the implementation of multiple inheritance! Note: Before ES6, there was no mechanism for relative polymorphism. So we use call for explicit binding to achieve explicit dynamics. Note that methods of implementing polymorphism in JavaScript are also known as “pseudo-polymorphism “. So don’t get confused by the idea of pseudo-polymorphism that pops up later (which is often done throughout the book)

The downside of explicit polymorphism (pseudo-polymorphism) : Because of masking in JavaScript (the actual context of the function reference), it is necessary to use explicit pseudo-polymorphism to create a function association at the time of reference. All of this adds to the complexity and maintenance of the code (with too many this bindings, it really makes the code hard to read).

2. Mixed copying (explicit blending another less common method) The previous method of explicit blending is to have child objects and specialize, followed by selective copying of parent object properties. This less common approach works the other way around, using the example in the book to copy the properties of the parent object with an empty object, which then copies the properties of the specialized object, and finally produces the child object. This approach is obviously more cumbersome than the first, and when you copy the specialization object, you may overwrite the previously named property (that is, the property of the copied parent object). Therefore, this method is risky and inefficient.

Defects of explicit mixing:

    1. True copying is not possible: if the copied object contains a reference to a function, then the child object gets the same reference to the same function as the parent. If a child object modifies a function, both the parent and other children are affected. It’s clear that it’s not safe. The reason is that functions in JavaScript can’t really be copied; you can only copy references to shared function objects.
    1. Function names and property names have the same name: this can happen if multiple objects are mixed in. There is still no good way to handle functions and properties with the same name (question: who gets priority in this case? Funny face).

A variant of the explicit mixin pattern is called “parasitic inheritance,” which is both explicit and implicit. First a copy of the definition of the parent class (object) is made, then the definition of the child class (object) is mixed in (reserving special references to the parent if necessary), and then an instance is built from this composite object. Note: Parasitic inheritance is very similar to mixed replication, the biggest difference is that parasitic inheritance is through the instantiation constructor (” class “in JS) to achieve replication.

4.4.2 Implicit in

Implicit blending: The biggest difference between display blending and implicit blending is that there is no obvious copying process for the properties of the parent class. It is implemented by using explicit bindings in constructor or method calls such as something.cool.call (this). The essence is to blend in by changing the direction of this.

4.5 summary

The whole point of this chapter is really to get you to understand what a class is. Except that the mixin in the last section has a little bit to do with JavaScript. The rest of the summary has nothing to do with JavaScript. It is important to understand the idea and design pattern of classes. Key points:

  • 1. Class means copy!
    1. When a traditional class is instantiated, its behavior is copied to the instance. When a class is inherited, the behavior is also copied to the subclass.
    1. Polymorphisms (functions with the same name but different functions at different levels of the inheritance chain) may seem to refer to a parent class from a subclass, but they are essentially the result of copying.
    1. JavaScript does not automatically create copies of objects (as classes do). (You have to manually copy it yourself, and not completely!)
    1. Blending patterns (explicit or implicit) can be used to simulate the copying behavior of a class, but often results in ugly and fragile syntax, such as explicit pseudo-polymorphism (otherobj.methodname.call (this,… ), which makes the code harder to understand and harder to maintain.
    1. Explicit blending doesn’t actually fully simulate the copying behavior of a class, because objects (and functions! You can only copy the reference. You cannot copy the referenced object or the function itself. To ignore this point can lead to many problems.
    1. Emulating classes in JavaScript is not worth the cost. Although it can solve the current problem, it may cause more problems. (But we use them a lot.)

Chapter v Prototype

Note: This chapter assumes that you are already familiar with prototypes and prototype chains. Not familiar or do not know can, through this article familiar with the next.

5.1 [[Prototype]]

Objects in JavaScript have a special [[Prototype]] built-in property that is a reference to another object (typically a reference to the Prototype property of its constructor). Almost all objects are created with the [[Prototype]] property given a non-null value. Note: The [[Prototype]] link for an object can be empty, as we’ll see soon, though rarely. I looked back and forth three times and couldn’t find the [[Prototype]] link for the object it says can be empty. The situation! The [[Prototype]] link to an object can be null. This is the Object obtained by object.create (null). Its [[Prototype]] is empty. All of it is empty. Why is that? Because NULL is the top of the prototype chain. It has no [[Prototype]]. Compare console.log(object.create ({})) to console.log(object.create (null))

[[Prototype]] I expected the author to say that it can be a public property that holds an instance object, and then go a little further like a class. But this time it just says what it says.

What it does: it stores properties that are not in the object itself. When we access a property of an object, the default internal operation on the object, [[Get]], first checks to see if the object itself has the property and uses it if it does. If not,[[Get]] continues to access the object’s [[Prototype]] chain.([[Prototype]] is the Prototype property of its constructor. Also an object.) If found, the property value is returned. If not, move on to the next [[Prototype]] chain. Until you find the entire [[Prototype]] chain. If not,[[Get]] returns undefined.

Supplement:

  • Use for.. Any property that can be accessed through the prototype chain (and is Enumerable :true) is enumerated when in traverses the object. (Actually I said this in Chapter 3)
  • Using the in operator also looks up the entire stereotype chain of the object (whether the property is enumerable or not)

5.1.1 Object.prototype

All normal [[Prototype]] chains eventually point to the built-in object.prototype. (object. prototype [[prototype]] eventually points to null. Null is the final endpoint). This object.prototype Object contains many common JavaScript functionality, such as toString(), valueOf(), hasOwnProperty(..). And isPrototypeOf (..) .

5.1.2 Property Settings and masking

Note: When you’re done with this section, don’t be confused about simple object attribute assignments like myObject.foo = “bar”. This assignment has absolutely no effect on the prototype chain! There are almost no unsuccessful assignments. If someone tried to change the attribute descriptor of an object in a team project, they would have been dragged out and killed!! This part of it can be seen as supplementary knowledge, just to know that there are weird Settings. The more important thing to focus on in this section is the return value of myObject.foo. Note: The verb masking in the book actually refers to creating a property of the same name on an object (which is already on the prototype chain). Be careful not to get confused. A =myObject. A +1. There is nothing special to be careful about.

5.2 “class”

  • JavaScript only has objects, not classes!
  • JavaScript does not need to abstract objects through classes. Instead, you create your own objects and define their behavior.

5.2.1 Class function

Mock: The parody class has been called a strange and shameless abuse! ? Otherwise, how to implement the advanced usage of JS? How can there be a hundred flowers blooming at the front end (wheels all over the ground)? This is also the way to take the way ah! After all, JS was only a minority at that time, and it was not expected to have much power. After all, it’s just what one person borrowed in seven days.

“Class” functions: Functions that JavaScript uses to mimic classes are called class functions, which are often referred to as constructors.

The key to “class” functions that emulate classes is that all functions by default have a public and non-enumerable (see Chapter 3) property called Prototype, which points to another object. When we use the new function (constructor) to obtain an instance object, we give it an internal [[Prototype]] property. The [[Prototype]] property inside the instance object refers to the same object as the constructor’s Prototype property. How does this JS feature simulate classes? First of all, the nature of classes is to copy! With this in mind, we need to implement pseudo replication. We can put the class property in the function’s Prototype property. This allows instance objects of the function to access these properties via [Prototype]. We often refer to this as archetypal inheritance (the author will joke wildly about it later, and I’ll explain why). In this way, pseudo “copy” is realized. It can achieve the effect similar to the class.

Note: All functions have a prototype property by default, though. But there are special times. It’s not the default. This is when hard binding is done via bind(). Return the binding function, which has no Prototype property!

To illustrate a real class versus a JS mock class:

The joke is on the word “inheritance” because in class-oriented languages,” inheritance “means copying. In JavaScript, prototype inheritance doesn’t mean that at all. Instead of copying, it’s done with a chain of prototypes. So crazy joke about its misleading.

What is differential inheritance? I have never heard of this term, first read the author’s so-called explanation, what is it? What’s he trying to say? After reading it many times, I finally understood it. If you don’t understand what the author is trying to say, pass this section. There’s no need to understand. It’ll confuse you even more. All right, let me explain what differential inheritance is. Differential inheritance is an uncommon alias for prototype inheritance. We know that objects can inherit some properties through the prototype chain, but we can still set different properties to objects. This is called differential inheritance.

5.2.2 “Constructor”

A constructor is a constructor because it is called by new, and if it is not called by new, it is an ordinary function. In effect,new hijacks any normal function and calls it as a constructed object, and constructs an object to return anyway.

5.2.3 requirestechnology

I’ll skip over the two “class-oriented” techniques, but it’s easy to understand the use of this in chapters 1 and 2 of this section.

Prototype. constructor: To properly understand constructor. I added prototype to the title to emphasize that an object that accesses constructor will, by default, access the constructor property on its prototype object.

Note:

function Foo() { / *.. * / }
Foo.prototype = { / *.. * / }; // Sometimes we need to create a new prototype object, so there will be no default constructor property pointing to the constructor
// Need to "fix" the missing.constructor property on foo.prototype
/ / about defineProperty (..) , see chapter 3
Object.defineProperty( Foo.prototype, "constructor" , {
  enumerable: false.// Cannot be enumerated
  writable: true.configurable: true.value: Foo // let.constructor point to Foo});// The above method is more rigorous and troublesome. And using object.defineProperty () is risky.
// So this is how we actually modify it
Foo.prototype.constructor=Foo; // Assign it directly to Foo. The only thing to notice is that constructor is enumerable. The instance object's for.. in.. To traverse.
Copy the code

5.3 (Prototype) inheritance

Prototype-object inheritance: for example, bar. prototype to foo. prototype delegate, the correct JavaScript “prototype-style” :

function Foo(name) {
  this.name = name;
}
Foo.prototype.myName = function() { 
  return this.name;
};
function Bar(name,label) { 
  Foo.call( this, name ); 
  this.label = label;
}

// We create a new bar. prototype object, and its [[prototype]] is associated with foo.prototype
Bar.prototype = Object.create( Foo.prototype );
/ / attention! Object. The create () returns a new Object, so there is no Bar. The prototype. The constructor
// You may need to manually fix this property if you need it
Bar.prototype.myLabel = function() { 
  return this.label;
};

var a = new Bar( "a"."obj a" );

a.myName(); // "a"
a.myLabel(); // "obj a"
Copy the code

Wrong usage:

  • 1, Bar.prototype = Foo.prototype;Instead of creating a new object associated with bar. prototype, it simply makes bar. prototype refer directly to the foo. prototype object. So when you do something like bar.prototype. myLabel =… The Foo. Prototype object itself is directly modified when the Foo.
  • 2, Bar.prototype = new Foo();It uses Foo(..) If Foo does something else, especially related to this, it will affect the descendants of Bar().

Conclusion: To create a proper associated Object, we need to use object.create (..) Instead of using Foo(..) which has side effects . The only downside to doing this is that you need to create a new object and discard the old one (primarily by manually setting Constructor), instead of modifying the existing default objects directly.

Check the class relationship

  • The instanceof operator: Verify that the whole [[prototype]] chain of the normal object on the left has a prototype pointing to the function on the right, for example:a instanceof Foo.
  • isPrototypeOf(..) methods: verify that object B is present in the whole [[Prototype]] chain of object A. Such as:b.isPrototypeOf( a );

Note: If you use the built-in.bind(..) Function to generate a hardbound function (see Chapter 2) does not have a.prototype property. If instanceof is hardbound, then its bind’s prototype becomes the hardbound function’s prototype.

About __proto__: We know that the function can access the prototype object directly through the prototype property. How can objects be accessed? We know it through the [[prototype]] chain. How to access it? The standard method in ES5 is to get the Object prototype through the object.getPrototypeof () method. Object.getPrototypeOf( a ) === Foo.prototype; // True, another method: a method that was not standard before ES6, but is supported by most browsers, to access internal [[prototype]] objects. For example :a.__proto__ === foo.prototype; // true. You can even pass.__proto__. __proto__ doesn’t actually exist in the object you’re using. And it looks like a property, but it’s really more like a getter/setter(see Chapter 3).

5.4 Object Association

[[Prototype]] is an internal link that exists in an object that references other objects.

If the desired property or method reference is not found on the object, the engine will continue to search on the object associated with [[Prototype]]. Similarly, if no reference is found in the latter, it will continue to look for its [[Prototype]], and so on. This chain of objects is called a prototype chain.

5.4.1 Create a link

Q :” We’ve seen why JavaScript’s [[Prototype]] mechanism is not the same as a class, and how it establishes associations between objects.” A: The mechanism of a class is copy. In JavaScript, the mechanism of a prototype chain is reference.

Q :” What does the [[Prototype]] mechanism mean? Why do JavaScript developers go to so much trouble to create these associations in their code?” A: The point is to emulate classes. JavaScript does not need to copy (which I think is not an advantage) and implements “instance” inheritance from “class” through a prototype chain. This allows instance objects to reuse certain properties (that is, properties in the prototype objects).

Object.create(..) This is a method we’ve used many times before.” object.create (..) Creates a new object (bar) and associates it with the object we specified (foo), This allows us to harness the power of the [[Prototype]] mechanism (delegate) and avoid unnecessary hassles (such as the.prototype and.constructor references generated by calling new). In effect, this method returns a new object whose Prototype ([[Prototype]]) is bound to the parameter object foo that we entered. And since it’s not a constructor form, you don’t need to set a separate prototype for the function. Although the Object. The create (..) Good, but we actually use the constructor form more often. Note: the Object. The create (..) The second parameter of “specifies the names of the properties to be added to the new object and the property descriptors for those properties (see Chapter 3).

“Object.create(null) creates an Object with an empty (or null)[[Prototype]] link that cannot delegate. Since this object does not have a prototype chain, the instanceof operator (as explained earlier) cannot make the determination and will always return false. These special empty [[Prototype]] objects are often referred to as “dictionaries” and are completely undisturbed by the Prototype chain, making them ideal for storing data.

“The polyfill code for object.create ().” I’m not going to read into this part, because it’s 8102,es6 is already popular, and it’s almost impossible for you to use any of the pre-ES5 syntax. So this is just something you need to understand.

5.4.2 The association is standby

[[Prototype]] : The idea is “an alternate option for dealing with ‘missing’ properties or methods.” (alternate design pattern). “But that’s not very common in JavaScript. So if you’re using this model, maybe it’s time to step back and rethink whether it’s appropriate.” The author’s point of view is :” The delegate design pattern, i.e., the internal delegate in the example (i.e., the object is referred to again with a shell in order to hide the delegate). This makes our API design cleaner.” By cleaner, we mean that when we need to refer to a property method of a prototypical object, we set a special property (doCool in our example) inside the object and delegate internally (in effect, hide it). So the properties of our object are “complete”.

In practice, we often use the prototype object as the place to store the object’s public property methods. The internal delegate (hidden delegate) is only used for important operations!

5.5 summary

Summed up very well very comprehensive, here I still directly excerpts, not lazy oh!

  • To access a property that does not exist in the object, the [[Get]] operation (see Chapter 3) looks for the object associated with the [[Prototype]] inside the object. This association essentially defines a “stereotype chain” (sort of like a nested scope chain) that is traversed when a property is found.
  • All normal objects have a built-in object. prototype that points to the top of the prototype chain (such as the global scope) and stops if the specified property is not found in the prototype chain. ToString (), valueOf(), and other generic functions exist on object.prototype objects, so all objects in the language can use them.
  • The most common way to associate two objects is to make a function call using the new keyword. In the four steps of the call (Chapter 2), a new object is created that associates other objects.
  • Calling a function with new associates the.prototype property of the new object with the “other object” (the object to which the constructor’s prototype refers). Function calls with new are often referred to as “constructor calls,” although they are not really the same as class constructors in traditional class-oriented languages.
  • One of the core differences in the JavaScript mechanism is that there is no duplication, objects are linked to each other by an internal [[Prototype]] chain.
  • “Delegate” is a more appropriate term because the relationship between objects is not a copy but a delegate. (Meaning that the original inheritance should be changed to the original delegate?)

Chapter VI Entrustment of conduct

[[Prototype]] refers to an internal link in an object that references another object. In other words, the essence of this mechanism in JavaScript is the association between objects. It’s also called a commission in Chapter 6. PS: In front of the prototype when I used to refer to the prototype object parent (similar to “parent “), using the child object refers to the object (similar to” child “). This chapter will also use this title, so it will not be explained below.(In fact, I prefer to use parent and child names.)

6.1 Delegation-oriented design

The [[Prototype]] mechanism is delegate oriented design, which is different from class oriented design. Class theory and delegate theory are described below.

6.1.1 Class theory

Class theory design method: first define a generic parent (base) class, in the parent class defined all tasks have (common) behavior. We then define the subclasses, which inherit from the parent class and add some special behavior to handle the task, and can override (and polymorphic) the behavior of the parent class with methods when inheriting.

Many behaviors in class theory can be “abstracted” to a parent class and then specialized (overridden) by a child class. Ps: This part is enough to understand, focus on understanding the following JavaScript used delegate.

6.1.2 Entrust theory

Class theory design approach: First you define a “parent” object (equivalent to the parent class in the previous section) that contains specific behavior that all tasks can use (delegate). Then, for each task you can define an object (” child “object) to store the corresponding data and behavior. You can associate specific task objects with parent objects, allowing them to delegate when needed. In fact, we usually use parent objects to define common methods, and child objects to delegate. Then the property methods of the child object’s own personality are written in the child object itself, avoiding conflicts with the property names of the parent object.)

But we don’t need to keep these behaviors together, ** by copying classes ** we can put them in separate objects, allowing XYZ object to delegate to Task if necessary. There is an error.” copy by class “should be changed to” copy by “[[Prototype]] mechanism “. This should be a mistake by the author. In JavaScript, the [[Prototype]] mechanism associates objects to other objects. No matter how hard you try to convince yourself, there is no abstraction like “classes” in JavaScript. (The main reason is that JavaScript doesn’t have a full copy mechanism.)

Suggestions for the use of delegation theory: PS: There are 3 in the book, but there are only 2, the third is just a description of the first, here I merge.

    1. In general, in a [[Prototype]] delegate it is best to keep state on the delegate (child object) rather than the delegate target (parent object). The way to do this is through “implicit binding of this “. State is saved by this definition in a function on the delegate target (parent object). When the delegator (child object) references the function method, this is automatically bound to the delegator.
    1. In delegates, we try to avoid using the same name at different levels of the [[Prototype]] chain, otherwise we need to use clunky and fragile syntax to disambiguation references (see Chapter 4).
  • 3. In the DESIGN of API interface, delegate is best implemented internally, not directly exposed. This is done more for security and interface stability reasons. It is recommended that the child object hide all methods that refer to the parent object in a function and give it a semantic property name.

Note the use of delegation theory:

    1. Disallow two objects to delegate to each other: an error is reported when you reverse delegate a second object to a previous object.
    1. debugging: That’s all you need to know. Understand that delegate resolution is not always the same for different browsers and toolsIn fact, this behavior was identified as a Chrome bug at the time of writing this book, and it will probably have been fixed by the time you read this book.I just want to say WTF! Ok, I know chrome has probably had this “bug” before.

Also 6.1.3Comparative thinking model

This section compares “prototype inheritance via constructors (mock classes)” with “association via objects (delegate form, object.create (…)”. ) implement prototype inheritance.”

Conclusion: Through object association, delegate form, more concise, more clear and easy to understand.

PS: I originally drew a prototype of the example here. But the discovery is really complex, and is similar to the concise diagram in the book, so I won’t show it here, to avoid making the reader’s eyes bigger. It is recommended that readers draw their own prototypes on scratch paper.

6.2 Classes and Objects

In this section, we are still talking about “prototype inheritance through constructors (mock classes)” and “association through objects (delegate form, object.create (…)). ) implement prototype inheritance.” However, this time is mainly to explain the actual use of the front-end scenario.

6.2.1 Control “class”

Instead of using the examples from the book, I’m going to take a closer look at this “class” style of code. The best features: 1 is to simulate the class through the constructor, and 2 is to correlate the two functions through explicit pseudo-polymorphism (hard-bound functions). Note:

  • Whether it’s a class or an object. Both forms typically require two types of data to be defined. The first is the “initial stored data” used by the instance object. The second is the definition of generic behavior, including the addition, deletion, modification and query of instance object data.
  • The explicit pseudo-polymorphism mentioned below (see Chapter 4) really refers to hard binding using the call() method.
  • Notice that I didn’t specify the ES6 class simulation class. In fact, classes are still implemented through the [[Prototype]] mechanism, which is just plain syntax sugar.

Although the book calls explicit pseudo-polymorphisms “ugly,” it also uses a modal verb, “bah!” Let’s call it call.Call it call.Call it call.Call it call.Call

6.2.2 Delegate control object

Best features: To simulate parents through Object carrier, and through Object,create(…) To associate two objects. And reference it in the form of a delegate. There is another difference from the class form mentioned in the previous section: after the object foo is built, it needs to be initialized manually by calling the setUp method. So the building of an object is separate from the initialization. The constructor form, on the other hand, builds and initializes the object at the same time as the new constructor (more on this later).

To use class constructors in the book, you need (it’s not mandatory, but highly recommended) to do both construction and initialization in the same step. However, in many cases it is more flexible to separate the two steps (as with object association code). When we use the new constructor, we actually build the object and initialize the object data in one step (through the call in the constructor). With this form of delegate, we are using object.create (…) ; Build objects and foo.setup (…) ; That is, we did it in two steps. This separation is actually more flexible and more consistent with the separation of concerns principle in programming.

6.3 More concise design

This section also compares the two to highlight the various advantages of the delegated design pattern. I won’t go through the examples in the book here. It’s pretty simple, if you really understand classes and delegates. If you’re feeling complicated, you can put the relationship between functions and objects on paper, but I’ll just summarize the benefits of the delegate design pattern that I’ve mentioned here, but at the core, it’s more concise.

Simplicity is reflected in:

  • The important thing about delegating is that you only need two entities (two objects related to each other), whereas the previous “class” pattern requires three (parent “class “, child” class “, instance object).
  • 2. There is no need for a base class (parent class) to “share” the behavior between two entities. There is no need to instantiate classes and no need to synthesize. In fact, this second result is a description of the first result.
  • Additional emphasis: when using a constructor to simulate a class, the subclass usually overrides the behavior of the parent class (with the same property name); In delegate mode, it doesn’t, it takes a new property name and references the behavior on the parent object.

6.4 Better syntax

This section mainly introduces two concise writing methods provided by ES6 and their pitfalls.

Grammar:

  • In ES6 we can use terse method declarations in the literal form of any object, for example:
var Foo = {
 bar() { / *.. * / },// Literal declaration
};

Copy the code
  • In ES6 we can use object.setPrototypeof (..) [[Prototype]] = [[Prototype]];
// Use better object literal syntax and concise methods
var AuthController = {
         errors: [],
         checkAuth() {
           // ... 
         },
         server(url,data) {
             // ...
         }
         // ... 
};
// Now associate AuthController with LoginController
Object.setPrototypeOf( AuthController, LoginController );
Copy the code

Disadvantages:

  • Object literal syntax: this is essentially an anonymous function expression. Anonymous functions lead to three disadvantages :1. Debugging stacks are harder to trace; 2. 2. Self-reference (recursion, event (unbinding), etc.) is harder; 3. The code is (slightly) harder to understand. (ACTUALLY, I think these three disadvantages are not that big a deal.) However, this concise syntax is special in that it gives the corresponding function object an internal name property, which in theory can be used in the trace stack. So really the only downside is number 2. The final piece of advice: If you need to self-reference, it’s best to use traditional named function expressions to define the corresponding functions, rather than concise methods.
  • Object. setPrototypeOf(..) This is not mentioned in the book, I think it is necessary to add. First of all, the Object. SetPrototypeOf (..) Performance issues may arise, and if performance is a concern, you should use object.create () instead. Object. setPrototypeOf(..) Main differences with object.create () : object.setprototypeof (..) The [[prototype]] of an existing Object is directly modified, while object.create () returns a new Object. So you’ll need to manually set the missing constructor property (if you need it). While using setPrototypeOf (..) No.

6.5 introspection

Joke: Throughout the book, the author’s criticisms of “emulation classes and inheritance” in JavaScript are highly misleading! Even more scornful! JavaScript is like a heretic and should be burned on a cross! But that’s the kind of point he makes when he’s looking at it from the perspective of other languages. I think more readers are probably only exposed to JavaScript. So he actually does not have these doubts!! Instead, you give them a whole bunch of “correct” meanings in other languages, and sometimes the opposite happens! Make the reader more confused, if it is not thoroughly understood, but will doubt that they have written the right code! So the reader should be able to understand the author’s intention, and have their own opinions and viewpoints!

What is introspection? First, this section needs to understand what is introspection, or introspection. The explanation in the book is that introspection is examining the type of instance. The main purpose of class instance introspection is to determine the structure and function of the object by the way it is created. Let me explain more generally here: when we build an instance object, sometimes we don’t clear its properties and methods. Especially third-party libraries. Sometimes reckless use can lead to many errors (for example, the method called does not exist, or an error is reported, etc.). This is when we need to go through introspection. It is a series of operations to determine whether the instance is the one we want and whether the method of the instance is the one we want.

Introspection methods:

  • 1. Instanceof grammar
function Foo() { 
  // ...
}
Foo.prototype.something = function(){
  // ... 
}
var a1 = new Foo();
// Suppose we do not know the above procedure, but only know to get instance object A1
// We want to know if a1 is built for the function Foo I want
if (a1 instanceof Foo) { 
  a1.something();
}
Copy the code

In this case, we have an instance object a1, but we don’t know if a1 is constructed by the function Foo that we want, so we can use instanceof. Bug: But if we want to determine whether function A is A “subclass” of function B, it will be A little more complicated. We need to do this a.prototype instanceof B. And you can’t directly determine whether two objects are related.

  • 2. By “duck type “: Why the name? Read the author’s explanation, or can not accept. Don’t really understand the brain circuits of foreigners. When you say “duck type” to someone back home, you probably don’t understand. It’s quite simple. The so-called “duck type” is the one we use in practice:
// If there is something in a1, then we can call it
if ( a1.something) { 
  a1.something();
}
Copy the code

In fact, this method is very common, and eliminates the risk of calling without knowing the existence of the case. Faults: The four-word word for the faults in the book is “over-generalize.” The example in the book about Promise is over-generalize. This is why we use the “something” method later, when if determines the existence of a1.something. Do not use anotherthing directly, which is unconfirmed.

  • 3. When using object association:Can be relatively simple to useObject.getPrototypeOf(..)For exampleObject.getPrototypeOf(a)===AWhere A,A are objects. If true, the prototype chain of A contains object A.

6.6 summary

  • In addition to class and inheritance design patterns, behavioral delegation is actually a more powerful and scalable pattern (this opinion is the author’s only!).
  • Behavior delegation considers objects to be brothers, to each other, rather than parents and children. JavaScript’s [[Prototype]] mechanism is essentially a behavior delegate mechanism. (I think it’s still a father-son relationship. My commentary is father and son.)
  • When you design code using only objects, you not only make the syntax cleaner, but you also make the code structure cleaner.
  • Object association (objects are previously associated with each other) is a coding style that advocates creating and associating objects directly, without abstracting them into classes. Object association can be implemented quite naturally with behavior delegation based on [[Prototype]]

Subsequent updates

The next section is about < JavaScript you don’t know >. Still in writing, in hand there is a webpack completely refers to the north of the article, currently write half 2W words, are also for beginners, truly comprehensive from shallow to deep. Recently, a new project team was parachuted in, and it only took one month to develop and test, and new people were brought in, so the update was very slow. But I’ll try to get them out by the end of the year. If you like, you can follow me, the article is still in the nuggets.

At present, I am working in a state-owned software company (semi-pension type). I will consider leaving at the end of the year or early next year. Hope to enter a better Internet enterprise. If you have a good opportunity to send me email :[email protected]. Thank you very much! At present, I will give priority to Wuhan (my house, friends and things are all here, it is too inconvenient to go to other places).

Baidu web disk download

In order to facilitate everyone to download to the local view, the MD file has been uploaded to baidu web disk. Link: pan.baidu.com/s/1ylKgPCw3… Jags (I believe you will come back to like after entering! =. =)