preface
The second part of the book, “This and Object Stereotypes,” is a great segue to the first part of the book, “Scopes and closures,” and further introduces two very important parts of the JavaScript language, the this keyword and stereotypes. These two sections will be important for your future studies, as they are the foundation of programming with JavaScript. You can only create large and complex applications like Google Maps in JavaScript if you know how to create, relate, and extend objects.
directory
Chapter 1 is about this
Chapter 2 this comprehensive analysis
Chapter 3 Objects
Chapter 4. Mixed Object “Classes”
Chapter 5 Prototype
Chapter 6 Commission of acts
Chapter 1 is about this
If you do not use this, you need to explicitly pass a context object to the function. However, this provides a more elegant way to implicitly “pass” an object reference, so the API can be designed to be cleaner and easier to reuse. As your usage patterns get more complex, passing context objects explicitly will clutter up your code. Using this will not. When we introduce objects and prototypes, you’ll see how important it is for functions to automatically reference the appropriate context object.
Wrong idea about this
2. This refers to the scope of the function. It’s a bit complicated, because in some cases it’s true, but in others it’s not.
We said earlier that this is bound at run time, not at write time, and its context depends on various conditions when the function is called. The binding of this has nothing to do with the position of the function declaration, except how the function is called.
When a function is called, an active 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, the parameters passed in, and so on. This is one of the properties of the record that is used during the execution of the function.
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. You may have been misled by such explanations, but they are all wrong. 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.
Chapter 2 this comprehensive analysis
In general, finding the calling location is looking for “where the function is called,” but this is not as easy to do because some programming patterns can hide the actual calling 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.
function baz() {
// The current stack is :baz
// Therefore, the current call location is global scope
console.log( "baz" );
bar(); // <-- bar call location
}
function bar() {
// The current stack is baz -> bar
// Therefore, the current call location is in baz
console.log( "bar" );
foo(); // <-- foo's call location
}
function foo() {
// The current call stack is baz -> bar -> foo // therefore, the current call location is in bar
console.log( "foo" );
}
baz(); // <-- baz call location
Copy the code
You can think of the call stack as a chain of function calls, as we wrote in the comments of the previous code section. But this approach is cumbersome and error-prone. Another way to look at the call stack is to use your browser’s debugging tool. Most modern desktop browsers have developer tools built in, including a JavaScript debugger. For this example, you can either set a breakpoint on the first line of foo() in the tool, or insert a debugger directly before the first line; Statements. When you run your code, the debugger pauses at that location and displays a list of function calls to the current location, which is your call stack. So, if you want to analyze the binding of this, use the developer tool to get the call stack, and then find the second element in the stack, which is the actual call location.
1. The default binding starts with the most common type of function call: the standalone function call. You can think of this rule as the default rule when no other rule can be applied.
function foo() {
//"use strict";
console.log( this.a );/ / 2
}
var a = 2;
foo();
Copy the code
So how do we know that the default binding is applied here? You can see how a function is called by analyzing the call location. In the code, functions are called directly using a function reference without any embellishment, so only the default binding can be used and no other rules can be applied.
If strict mode is used, the default binding is not available for global objects, so this is bound to undefined. The default binding can only be bound to global objects if foo() is not running in strict mode; Strictly independent of where foo() is called.
Another rule to consider is whether the calling location has a context object, or is owned or contained by an object, although this can be misleading.
When a function reference has a context object, the implicit binding rule binds this in the function call to that context object.
Only the top or last level in the object attribute reference chain affects the call location. For example:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42.foo: foo
};
var obj1 = {
a: 2.obj2: obj2
};
obj1.obj2.foo(); // 42 points to obj2, the last call
Copy the code
Implicit Loss One of the most common problems with this binding is that a function that is implicitly bound will lose its bound object, that is, it will apply the default binding, binding this to a global object or undefined, depending on whether it is in strict mode.
function foo() {
console.log( this.this.a );//
}
var obj = {
a: 2.foo: foo
};
var bar = obj.foo; // Function alias!
var a = "oops, global"; // a is an attribute of the global object
bar(); // bar call, whose this points to Windows
Copy the code
Although bar is a reference to obj.foo, it actually refers to the foo function itself, so bar() at this point is really a function call without any decorations, so the default binding is applied.
function foo() {
console.log( this.this.a );
}
function doFoo(fn) {
console.log( this)
// fn actually refers to foo
fn(); // <-- call location!
}
var obj = {
a: 2.foo: foo
};
var a = "oops, global"; // a is an attribute of the global object
doFoo( obj.foo ); // "oops, global"
Copy the code
Argument passing is essentially an implicit assignment, so when we pass in a function we get an implicit assignment, so the result is the same as in the previous example.
If you pass a function into the language’s built-in function instead of your own declared function, the result is the same, no difference.
3. Display the binding
Call (..) is available for most functions provided by JavaScript and for all functions you create yourself. And the apply (..) Methods.
How do these two methods work? Their first argument is an object, which they bind to this, which they then specify when calling the function. Because you can specify the object to bind this directly, we call it explicit binding.
If you pass a primitive value (String, Boolean, or number) as a binding object to this, the primitive value will be converted to its object form (i.e., new String(..)). , new Boolean (..) Or the new Number (..) ). This is often referred to as “boxing”.
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); / / 2
setTimeout( bar, 100 ); / / 2
// A hard-bound bar cannot change its this
bar.call( window ); / / 2
Copy the code
Call forces foo’s this to bind to obj. No matter how the function is called later, it will always manually call foo on obj. This binding is explicitly forced and is therefore called hard binding.
Because hard binding is a very common pattern, there is a built-in function.prototype.bind method available in ES5, which can be used as follows:
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a:2
};
var bar = foo.bind( obj );
var b = bar( 3 ); / / 2, 3
console.log( b ); / / 5
Copy the code
bind(..) Returns a new hard-coded function that sets the argument to the context of this and calls the original function. 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 referred to as a “context,” which serves the same purpose as bind(..) Also, make sure your callback uses the specified this.
For example:
function foo(el) {
console.log( el, this.id );
}
var obj = {
id: "awesome"
};
/ / calls foo (..) Bind this to obj
[1.2.3].forEach( foo, obj );
// 1 awesome 2 awesome 3 awesome
Copy the code
These functions are actually called by call(..). Or apply (..) Explicit binding is implemented so that you can have less code.
4. The new binding
When a function is called with new, or when a constructor call occurs, the following operations are performed automatically.
- Create (or construct) a brand new object. \
- The new object will be connected by the implementation [[prototype]]. \
- This new object is bound to the function call’s this. \
- If the function returns no other object, the function call in the new expression automatically returns the new object.
New is the last way to influence the behavior of the this binding when a function is called, and we call it the new binding.
priority
In terms of priority, the new binding is higher than the explicit binding than the implicit binding is higher than the default binding.
To determine this
Now we can determine which rule is applied to a function at a particular call location based on priority. It can be judged in the following order:
- Is the function called in new (new binding)? If so, this binds to the newly created object.
var bar = new foo()
Copy the code
- Is the function called by call, apply(explicit binding), or hard binding? If so, this binds to the specified object.
var bar = foo.call(obj2)
Copy the code
- Is the function called in a context object (implicitly bound)? If so, this binds to the upper context object.
var bar = obj1.foo()
Copy the code
- If neither, use the default binding. If in strict mode, it is bound to undefined, otherwise it is bound to global objects.
var bar = foo()
Copy the code
That’s it. With this in mind for normal function calls, you can understand how this bindings work. But… There should always be exceptions to the rule.
Bind the exception
If you pass null or undefined as a binding object for this to call, apply, or bind, these values will be ignored when called, and the default binding rules apply
When do you pass null? A very common approach is to use apply(..) To “expand” an array and pass it as a parameter to a function.
Similarly, bind(..) Parameters can be currified (pre-set some parameters), which is sometimes very useful:
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
}
// Expand the array into arguments
foo.apply( null[2.3]);// a:2, b:3
// Our DMZ empty object
// var ø = object.create (null); // Expand the array into parameters, which is safer
// foo.apply(ø, [2, 3]); // a:2, b:3
/ / use the bind (..) Carry on the Cremation \
var bar = foo.bind( null.2 );
bar( 3 ); // a:2, b:3
Copy the code
2. Indirect references
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
Copy the code
The return value of the assignment expression p.foo = o.foo is a reference to the target function, so the call location is foo() rather than p.foo() or o.foo(). As we said earlier, the default binding is applied here.
3. Soft binding
if (!Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this;
// Capture all curried parameters
var curried = [].slice.call( arguments.1 );
var bound = function() {
return fn.apply(
(!this || this= = = (window || global))? obj :this
curried.concat.apply( curried, arguments)); }; bound.prototype =Object.create( fn.prototype );
return bound;
};
}
Copy the code
This lexical
Instead of using the four standard rules for this, arrow functions determine this based on the outer (function or global) scope. Although self = this and the arrow function both seem to replace bind(..) But essentially, they want to replace the This mechanism.
If you often write this style code, but mostly use self = this or the arrow function to negate this, you might want to:
- Use only lexical scopes and discard the error this style of code entirely; \
- Use this style entirely, using bind(..) when necessary. , try to avoid using self = this and arrow functions.
Of course, programs that include both code styles will work fine, but mixing them in the same function or program often makes code harder to maintain, and possibly harder to write.
Chapter 3 Objects
grammar
Objects can be defined in two forms: declarative (literal) and constructive. The literal syntax for the object looks something like this:
var myObj = {
key: value
// ...
};
Copy the code
The structure looks something like this:
var myObj = new Object(a); myObj.key = value;Copy the code
Constructional forms and literal forms generate the same objects. The only difference is that in a literal declaration you can add multiple key/value pairs, but in a construct you must add attributes one by one.
type
Objects are the foundation of JavaScript. There are six main types in JavaScript (the term is “language types”):
• string • number • boolean • null • undefined • object
Note that the simple primitive types (String, Boolean, number, NULL, and undefined) are not themselves objects. Null is sometimes referred to as an object type, but this is really just a bug in the language that typeof NULL returns the string “object”. In fact, NULL itself is a primitive type.
Built-in objects
• String • Number • Boolean
• Object • Function • Array
• Date • RegExp • Error
But in JavaScript, they’re really just built-in functions. These built-in functions can be used as constructors (function calls generated by new — see Chapter 2) to construct a new object corresponding to a subtype.
content
The content of an object is made up of values (of any type) stored in a specific named location, called properties. To access the value at position A in myObject, we need to use. Operator or the [] operator. The. A syntax is often called “attribute access” and the [“a”] syntax is often called “key access”. They actually access the same bit and return the same value 2, so the terms are interchangeable.
Prior to ES5, the JavaScript language itself didn’t provide a way to directly detect attributes, such as whether they were read-only. But starting with ES5, all attributes have attribute descriptors.
In ES5, you can override default actions using getter and setter sections, but only for individual properties, not for entire objects. A getter is a hidden function that is called when a property value is obtained. Setter is also a hidden function that is called when the property value is set.
for.. The IN loop can be used to iterate through an object’s enumerable property list (including the [[Prototype]] chain). Some auxiliary iterators for arrays have been added in ES5, including forEach(..) And every (..) And some (..) . Each auxiliary iterator can take a callback function and apply it to each element of the array. The only difference is how they handle the value returned by the callback function. forEach(..) All values in the array are iterated and the return value of the callback function is ignored. every(..) Will run until the callback returns false(or “false”), some(..) Runs until the callback returns true(or a “true” value).
every(..) And some (..) The special return value in the for loop is similar to the break statement in the normal for loop, which terminates the traversal prematurely.
Use for.. An in traversal object can’t get property values directly, because it actually iterates over all the enumerable properties in the object, so you need to get property values manually.
So how do you iterate over values directly instead of array subscripts (or object attributes)? Fortunately, ES6 adds a for for traversing groups of numbers. Of loop syntax (iterators can also be traversed if the object itself has an iterator defined):
var myArray = [ 1.2.3 ];
for (var v of myArray) {
console.log( v );
}
// 1/2/3
Copy the code
for.. The of loop first requests an iterator object from the object being accessed, and then iterates over all returned values by calling the iterator object’s next() method.
Arrays have a built-in @@iterator, so for.. Of can be applied directly to arrays. Let’s use the built-in @@iterator to manually iterate through the array and see how it works:
var myArray = [ 1.2.3 ];
var it = myArray[Symbol.iterator]();
it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { done:true }
Copy the code
Chapter 4. Mixed Object “Classes”
Class-oriented design patterns: Instantiation, inheritance, and (relative) polymorphism.
JavaScript has only A few syntactic elements that approximate classes (such as new and instanceof), although some elements were added later in ES6, such as the class keyword (see attachment A). Does that mean there are actually classes in JavaScript? In short: No.
Despite the class-like syntax, JavaScript’s mechanics seem to keep you from using class design patterns. Under the guise of a class approximation, JavaScript’s mechanics are completely different from classes. Syntactic sugar and (widely used) JavaScript “class” libraries try to mask this reality, but sooner or later you’ll have to face it: classes in other languages are not the same as “classes” in JavaScript.
A class instance is constructed from a special class method, usually with the same name as the class, called a constructor. The task of this method is to initialize all the information (state) required by the instance.
Class constructors belong to classes and usually have the same name as classes. In addition, constructors mostly need to be called with new so that the language engine knows that you want to build a new class instance.
Once a subclass is defined, it is a separate and distinct class from its parent class. Subclasses contain the original copy of the parent’s behavior, but they can override all inherited behavior and even define new behavior. It is important to note that the parent and child classes we are talking about are not instances.
Some class-oriented languages allow you to inherit from multiple “parent classes.” Multiple inheritance means that all definitions of parent classes are copied into subclasses. JavaScript, by contrast, is much simpler: it doesn’t provide multiple inheritance per se. Many people think this is a good thing because it is too expensive to use multiple inheritance. However, that’s not going to stop developers from trying all sorts of ways to implement multiple inheritance, as we’ll see in a minute.
JavaScript’s object mechanism does not automatically perform the copying behavior when inherited or instantiated. Simply put, there are only objects in JavaScript, and there are no “classes” that can be instantiated. An object is not copied to other objects, they are associated (see Chapter 5).
Since all classes in other languages behave like clones, JavaScript developers have come up with a way to mimic the behavior of copying classes: mixin. Next we’ll see two types of mixing: explicit and implicit.
Since JavaScript does not automatically implement the superclass to subclass copying behavior, we need to implement the copying function manually. This feature is called extend(..) in many libraries and frameworks. , but for ease of understanding we call it mixin(..) .
It’s important to note that we’re not dealing with classes anymore, because there are no classes in JavaScript, and the so-called “superclasses” and “subclasses” are objects that we can copy and paste, respectively.
JavaScript(prior to ES6; See Appendix A) there is no mechanism for relative polymorphism. Using explicit pseudo-polymorphisms in JavaScript (due to masking) creates a function association in all places where (pseudo-) polymorphic references are needed, which can greatly increase maintenance costs. In addition, because explicit pseudopolymorphism can simulate multiple inheritances, it further increases code complexity and maintenance difficulty.
Using pseudo-polymorphisms often leads to more complex, harder to read, and harder to maintain code, so you should avoid using explicit pseudo-polymorphisms as much as possible, as they often outweigh the benefits.
A variant of the explicit mixin pattern is known as “parasitic inheritance”, which is both explicit and implicit and is primarily promoted by Douglas Crockford.
Implicit mixing is similar to the explicit pseudo-polymorphism mentioned earlier, and therefore suffers from the same problems.
Chapter 5 Prototype
Objects in JavaScript have a special [[Prototype]] built-in property that is essentially a reference to another object. Almost all objects are created with a non-null value for the [[Prototype]] property.
If the object does not find the property in its current property and the [[Prototype]] chain is not empty, the search continues down the Prototype chain. This process continues until the matching attribute name is found or the entire [[Prototype]] chain is found. In the latter case, the return value of the [[Get]] operation is undefined. The links formed by this process are called prototype chains.
All normal [[Prototype]] chains end up pointing to the built-in Object.prototype.
Masking occurs if the attribute name foo appears in both myObject and above the [[Prototype]] chain of myObject. The foo property contained in myObject blocks all foo properties at the top of the prototype chain, because myObject.foo always selects the foo property at the bottom of the prototype chain.
Shielding is more complicated than we thought.
- If a common data access attribute named foo exists at the top of the [[Prototype]] chain (see Chapter 3) and none is marked as read-only (writable:false), a new attribute named foo is added directly to myObject, which is a masked attribute.
- If foo exists at the top of the [[Prototype]] chain, but it is marked as read-only (writable:false), you cannot modify existing properties or create masked properties on myObject. If run in strict mode, the code throws an error. Otherwise, the assignment statement is ignored. In short, masking does not occur.
- If foo exists at the top of the [[Prototype]] chain and it is a setter(see Chapter 3), that setter must be called. Foo is not added to (or shielded from)myObject, nor is the setter foo redefined.
Most developers assume that if you assign a value to an attribute ([[Put]]) that already exists at the top of the [[Prototype]] chain, it will always trigger masking, but as you can see, only one of the three cases (the first) does.
There are situations where masking can be implicit, so be careful. Consider the following code:
var anotherObject = {
a:2
};
var myObject = Object.create( anotherObject );
anotherObject.a; / / 2
myObject.a; / / 2
anotherObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "a" ); // false
myObject.a++; // implicit masking!
anotherObject.a; / / 2
myObject.a; / / 3
myObject.hasOwnProperty( "a" ); // true
Copy the code
Although myObject.a++ looks like it should find and add the anotherObject.a property (via delegate), remember that the ++ operation is equivalent to myObject.a = myObject.a+ 1. So the ++ operation first looks for attribute A through [[Prototype]] and gets the current attribute value 2 from anotherObject.a, then increses that value by 1, and then assigns 3 to the new masking attribute A in myObject with [[Put]].
Be careful when modifying delegate properties. If you want to increase the value of anotherObject.a, the only way is anotherObject.a++.
JavaScript only has objects. Unlike class-oriented languages, JavaScript does not have classes as an abstract schema or blueprint for objects.
In JavaScript, we don’t copy one object (” class “) to another object (” instance “), we just associate them. This mechanism is often referred to as stereotype inheritance. It is often seen as class inheritance from the dynamic language version. The name is intended primarily to correspond to the sense of “inheritance” in the class-oriented world, but violates (writes contravene, reads as overrides) the corresponding semantics in dynamic scripts.
The word “inheritance” creates very strong expectations (see chapter 4). Simply adding the word “stereotype” in front of it doesn’t distinguish JavaScript from class inheritance, which is almost the exact opposite of what it does, and has led to huge misunderstandings over the past 20 years.
So I think the confusing combinatorial term “prototypical inheritance” (and the use of other class-oriented terms like “class,” “constructor,” “instance,” “polymorphism,” etc.) seriously undermines our understanding of how JavaScript actually works.
Inheritance means copying operations, and JavaScript(by default) does not copy object properties. Instead, JavaScript creates an association between two objects so that one can delegate access to the properties and functions of the other. Delegate (see Chapter 6) is a term that more accurately describes the association mechanism for objects in JavaScript.
function Foo() {
// ...
}
Foo.prototype.constructor === Foo; // true
var a = new Foo();
a.constructor === Foo; // true
Copy the code
As is customary in the JavaScript world, “class” names are capitalized, so writing the name Foo instead of Foo seems to indicate that it is a “class.” Obvious, right? ! This convention is so powerful that many JavaScript developers will blame you if you call a lower-case method with new or a function that starts with an uppercase letter without new. It’s amazing how hard we try to assert (pseudo) “class-oriented” rights in JavaScript, even though uppercase doesn’t make any sense to JavaScript engines.
The previous code makes it easy to think that Foo is a constructor because we call it with new and see that it “constructs” an object.
In fact, Foo is no different from any other function in your program. The function itself is not a constructor; however, when you prefix a normal function call with the new keyword, you make the function call a “constructor call.” In effect, new hijacks all ordinary functions and calls them as constructors.
Foo is just a normal function, but when called with new, it constructs an object and assigns it to A, which looks like a side effect of new (an object will be constructed anyway). This call is a constructor call, but Foo itself is not a constructor.
In other words, the most accurate definition of “constructor” in JavaScript is all function calls with new. A function is not a constructor, but a function call becomes a “constructor call” if and only if new is used.
To create a suitable associated Object, we must use Object.create(..). Instead of using the side effect Foo(..) . The only drawback is that you need to create a new object and then discard the old one. You can’t directly modify the existing default object.
It would be nice if there were a standard and reliable way to modify an object’s [[Prototype]] association. Prior to ES6, we could only do this by setting the.proto property, but this wasn’t standard and wasn’t compatible with all browsers. ES6 added auxiliary function Object.setProtoTypeof (..) Associations can be modified using standard and reliable methods.
Let's compare two ways to associate bar. prototype with foo. prototype:// Before ES6 you need to discard the default bar.prototype
Bar.ptototype = Object.create( Foo.prototype );
// ES6 starts with the ability to modify existing bar.prototype directly
Object.setPrototypeOf( Bar.prototype, Foo.prototype );
Copy the code
If you omit object.create (..) The method carries a slight performance penalty (discarded objects require garbage collection), and it is actually shorter and more readable than ES6 and subsequent methods. But anyway, these are two completely different grammars.
The [[Prototype]] mechanism is an internal link in an object that references other objects.
In general, this link is used: if the desired attribute 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, the [[Prototype]] is searched, and so on. This series of links is called a “prototype chain.”
Chapter 6 Commission of acts
The [[Prototype]] mechanism means that an internal link in an object refers to another object.
If the desired attribute or method reference is not found on the first object, the engine continues to search on the object associated with [[Prototype]]. Similarly, if no reference is found in the latter, the [[Prototype]] is searched, and so on. This series of links is called a “prototype chain.”
The essence of this mechanism in JavaScript is associative relationships between objects.
In JavaScript, the [[Prototype]] mechanism associates objects to other objects. No matter how hard you try to convince yourself that there is no abstraction like a “class” in JavaScript. It’s a bit like swimming upstream: you can do it, but if you choose to fight the truth, it’s obviously more difficult to get there.
There are also some differences in the object association style code.Copy the code
- In the above code, the ID and label data members are stored directly on XYZ (not Task). In general, in [[Prototype]] delegates it is best to keep state with the delegate (XYZ, ABC) rather than the delegate target
(Task).
- In the class design pattern, we intentionally have outputTask methods in both the parent (Task) and subclass (XYZ) to take advantage of overwriting (polymorphism). In delegate behavior, the opposite is true: we try to avoid using the same names at different levels of the [[Prototype]] chain, which would otherwise require awkward and fragile syntax to disambiguate references (see Chapter 4).
This design pattern calls for minimizing the use of generic method names that can be easily overridden in favor of more descriptive method names, especially the type of behavior of the corresponding object. Doing so actually creates code that is easier to understand and maintain because method names (not just where they are defined, but throughout the code) are clearer (from documentation).
- this.setID(ID); Methods in XYZ first look for whether XYZ itself has setID(..). [[Prototype]] {setID(..);}} {setID(..);}} Methods. In addition, since the call location triggers the implicit binding rule for this (see Chapter 2), although setID(..) Method in Task, runtime this will still be bound to XYZ, which is exactly what we want. We’ll also see this.outputid () in later code, which works the same way.
In other words, we can use common methods in Task when interacting with XYZ because XYZ delegates Task. Delegation behavior means that some object (XYZ) will delegate the request to another when it can’t find a property or method reference
Object (Task). This is an extremely powerful design pattern, as opposed to the concepts of superclass, subclass, inheritance, polymorphism, etc. In your head
Objects are not organized vertically in superclass to subclass relationships, but side by side through delegate associations in any direction.
There are three major drawbacks to anonymous function expressions, which we’ll briefly discuss and then compare with the concise method definition. Anonymous functions have no name identifier, which results in:
- The debug stack is harder to track;
- Self-referrals (recursion, event (unbinding), and so on) are harder;
- The code is (slightly) harder to understand.
Using ES6’s concise approach can make object association styles more personal (and still be more concise and better than typical prototype-style code). The concise approach has one very small but important drawback. The concise approach does not have the first and third drawbacks.
You can choose whether to use class and inheritance design patterns in software architecture. Most developers take it for granted that classes are the only (appropriate) way to organize code, but in this chapter we’ve seen another, rarer but more powerful design pattern: behavioral delegation.
Behavioral delegation considers objects to be siblings, delegating to each other, rather than superclass and subclass. JavaScript’s [[Prototype]] mechanism is essentially a behavior delegation mechanism. That said, we can either try to implement the class mechanism in JavaScript (see Chapters 4 and 5) or embrace the more natural [[Prototype]] delegate mechanism.
When you design code using only objects, you not only make the syntax cleaner, but you also make the code structure clearer. Object association (before objects are related to each other) is a coding style that advocates the creation and association of objects directly
They are abstracted into classes. Object associations can be implemented quite naturally with behavior delegates based on [[Prototype]].
Scope chains are based on the call stack rather than scope nesting in the code. To be clear, JavaScript doesn’t actually have dynamic scope. It has lexical scope and is straightforward. But this mechanism is somewhat like dynamic scope. The main difference: lexical scope is determined at code or definition time, while dynamic scope is determined at run time. (this is! Lexical scopes focus on where functions are declared, while dynamic scopes focus on where functions are called from.
Finally, take a brief look at the performance issues posed by try/catch and try to answer the question “Why not just use IIFE
Create scope.
First of all, it’s true that try/catch performance is terrible, but technically there’s no good reason why try/catch should be this slow, or ever slow. Since TC39 supports try/catch in ES6 converters, the Traceur team has asked Chrome to improve its try/catch performance, and they clearly have every incentive to do so.
Second, IIFE and try/catch are not completely equivalent, because wrapping any part of a piece of code around a function changes the meaning of the code. This, return, break, and contine all change. IIFE is not a one-size-fits-all solution; it is only suitable for manual operation in certain situations.