Home > Article > Web Front-end > A brief discussion on the language features of JavaScript
Look at the code snippet first:
var f = function foo(){
Return typeof foo; // foo is valid in the internal scope
};
// foo is invisible when used externally
typeof foo; // "undefined"
f(); // "function"
What I want to say here is that foo in a function expression can only be referenced inside the function and cannot be referenced outside.
json
Many JavaScript developers mistakenly call JavaScript object literals (JSON Objects). JSON is designed to describe a data exchange format. It also has its own syntax, which is a subset of JavaScript.
{ "prop": "val" } Such a declaration may be a JavaScript object literal or a JSON string, depending on the context in which it is used. If it is used in a string context (quoted in single or double quotes, or read from a text file), it is a JSON string. If it is used in an object literal context, it is an object literal.
// This is a JSON string
var foo = '{ "prop": "val" }';
// This is an object literal
var bar = { "prop": "val" };
Also One thing to know is that JSON.parse is used to deserialize JSON strings into objects, and JSON.stringify is used to serialize objects into JSON strings. Older versions of browsers do not support this object, but you can achieve the same functionality through json2.js.
Prototype
function Animal (){
// ...
}
function cat (){
// ...
}
cat.prototype = new Animal();//This method will inherit the constructor inside the function.
cat.prototype = Animal.prototype;//This method will not inherit the constructor.
//Another important detail to note is that you must maintain your own prototype chain. Newbies will always forget this!
cat.prototype.constructor = cat;
If we completely change the prototype property of the function (by allocating a new object), the reference to the original constructor is lost, because the object we create does not include the constructor property:
function A() {}
A.prototype = {
x: 10
};
var a = new A();
alert(a.x); // 10
alert(a.constructor === A) ; // false!
Let's take a look at the explanation of constructor on MDN: prototype: Returns a reference to the Object function that created the instance's prototype. Therefore, the prototype reference to the function needs to be restored manually:
function A() {}
A.prototype = {
constructor: A,
x: 10
};
var a = new A();
alert(a.x); // 10
alert(a.constructor === A); // true
However, submitting the prototype attribute will not affect the prototype of the already created object (it will only be affected when the prototype attribute of the constructor changes), which means that only newly created objects have new prototypes , the created object still refers to the original old prototype (this prototype can no longer be modified).
function A() {}
A.prototype.x = 10;
var a = new A();
alert(a.x); // 10
A.prototype = {
constructor: A,
x: 20
y: 30
};
// Object a is the value obtained from the prototype of crude oil through the implicit [[Prototype]] reference
alert(a.x); // 10
alert(a.y) // undefined
var b = new A();
// But the new object is the value obtained from the new prototype
alert(b.x); // 20
alert(b.y) // 30
Therefore, "dynamically modifying the prototype will affect all "Objects will all have new prototypes" is wrong. The new prototype only takes effect on newly created objects after the prototype is modified. The main rule here is: the prototype of an object is created when the object is created, and cannot be modified to a new object thereafter. If it still refers to the same object, it can be referenced through an explicit prototype in the constructor. After the object is created , only the attributes of the prototype can be added or modified.
Variable object In the function execution context, VO (variable object) cannot be directly accessed. At this time, the activation object (activation object) plays the role of VO. The active object is created when entering the function context and is initialized through the arguments attribute of the function. The value of the arguments attribute is the Arguments object:
function foo(x, y, z) {
// The number of declared function parameters arguments (x, y, z)
alert(foo.length); // 3
// The number of parameters actually passed in (only x, y)
alert(arguments.length); // 2
// The callee of the parameter is the function itself
alert(arguments.callee === foo); // true
}
When entering the execution context (before code execution), VO already contains the following attributes: 1. All formal parameters of the function (if we are in the function execution context);
All function declarations (FunctionDeclaration, FD );
All variable declarations (var, VariableDeclaration);
Another classic example:
alert(x); // function
var x = 10;
alert(x); // 10
x = 20 ;
function x() {};
alert(x); // 20
According to the specification, function declarations are filled in when entering the context; there is also a variable declaration "x" when entering the context, then as we said above, the variable declaration follows the function declaration and formal parameter declaration in sequence Later, and during this context entry phase, variable declarations do not interfere with function declarations or formal parameter declarations of the same name that already exist in the VO. Compared with simple attributes, variables have an attribute: {DontDelete}. The meaning of this attribute is that the variable attribute cannot be directly deleted using the delete operator.
a = 10;
alert(window.a); // 10
alert(delete a); // true
alert(window.a); // undefined
var b = 20;
alert(window. b); // 20
alert(delete b); // false
alert(window.b); // still 20. b is variable,not property!
var a = 10; // Variable in global context
(function () {
var b = 20; // Local variable in function context
})();
alert(a ); // 10
alert(b); // The global variable "b" is not declared.
this In a function context, this is provided by the caller and is determined by the way the function is called. If the left side of the calling bracket () is a value of reference type, this will be set to the base object of the reference type value. In other cases (any other properties different from the reference type), this value will be null. However, there is no actual situation where the value of this is null, because when the value of this is null, its value will be implicitly converted to a global object.
(function () {
alert(this); // null => global
})();
In this example, we have a function object but not an object of reference type (it is not identifier, not a property accessor), accordingly, the this value is ultimately set to the global object.
var foo = {
bar: function () {
alert(this);
}
};
foo.bar(); // Reference, OK => foo
(foo.bar)(); // Reference, OK => foo
(foo.bar = foo.bar)(); // global
(false || foo.bar)(); // global
(foo.bar, foo.bar) (); // global
The problem is that in the following three calls, after applying certain operations, the value on the left side of the calling bracket is no longer a reference type.
The first example is obvious - an obvious reference type. The result is that this is the base object, which is foo.
In the second example, the group operator does not apply. Think of the methods mentioned above that get the actual value of an object from a reference type, such as GetValue. Correspondingly, in the return of the group operation - we still get a reference type. This is why the this value is set to the base object again, which is foo.
In the third example, unlike the group operator, the assignment operator calls the GetValue method. The returned result is a function object (but not a reference type), which means this is set to null and the result is a global object.
The same goes for the fourth and fifth ones - the comma operator and the logical operator (OR) call the GetValue method, and accordingly, we lose the reference and get the function. And set it to global again.
As we know, local variables, internal functions, formal parameters are stored in the activation object of a given function.
function foo() {
function bar() {
alert(this); // global
}
bar(); // the same as AO.bar()
}
The active object is always returned as this, The value is null - (that is, AO.bar() in pseudocode is equivalent to null.bar()). Here we return to the example described above again, with this set to the global object.
Scope chain
The scope attribute of a function created through the function constructor is always the only global object.
One important exception involves functions created via function constructors.
var x = 10;
function foo() {
var y = 20;
function barFD() { // Function declaration
alert(x);
alert(y);
}
var barFn = Function(' alert(x); alert(y);');
barFD(); // 10, 20
barFn(); // 10, "y" is not defined
}
foo();
Also:
var x = 10, y = 10;
with ({x: 20}) {
var x = 30, y = 30;
//x = 30 here covers x = 20;
alert(x ); // 30
alert(y); // 30
}
alert(x); // 10
alert(y); // 30
What happens when entering the context? The identifiers "x" and "y" have been added to the variable object. In addition, make the following modifications during the code running phase:
x = 10, y = 10;
The object {x:20} is added to the front of the scope;
Inside the with, the var declaration is encountered and of course nothing is created since all variables have been resolved when entering the context Add;
In the second step, only the variable "x" is modified, actually the "x" in the object is now parsed and added to the front of the scope chain, "x" was 20, becomes 30;
Same There is also a modification of the variable object "y". After being parsed, its value correspondingly changes from 10 to 30;
In addition, after the with declaration is completed, its specific object is removed from the scope chain (the changed variable "x "--30 is also removed from that object), that is, the structure of the scope chain is restored to the state before with was strengthened.
In the last two alerts, the "x" of the current variable object remains the same, and the value of "y" is now equal to 30, which has changed during the with statement run.
Function
Question about parentheses
Let’s look at this question: ‘Why do we have to surround a function with parentheses when it is called immediately after it is created? ’, the answer is: the restriction of expression sentences is like this.
According to the standard, an expression statement cannot start with a brace { because it is difficult to distinguish it from a code block. Similarly, it cannot start with a function keyword because it is difficult to distinguish it from a function declaration. That is, so if we define a function that executes immediately after its creation, we call it as follows:
function () {
...
}();
// even with the name
function foo() {
...
}();
We used function declarations. For the above two definitions, the interpreter will report an error when interpreting, but there may be many reasons. If it is defined in global code (that is, program level), the interpreter will treat it as a function declaration because it starts with the function keyword. In the first example, we will get a SyntaxError because the function declaration has no name. (We mentioned earlier that function declarations must have names). In the second example, we have a function declaration named foo that is created normally, but we still get a syntax error - a grouping operator error without any expression. It is indeed a grouping operator after the function declaration, not the parentheses used in a function call. So if we declare the following code:
// "foo" is a function declaration, created when entering the context
alert(foo); // function
function foo(x) {
alert(x);
} (1); // This is just a grouping operator, not a function call!
foo(10); // This is a real function call, the result is 10
The easiest way to create an expression is to use grouping operator brackets, and what is put inside is always an expression, so the interpreter is interpreting , there will be no ambiguity. During the code execution phase, this function will be created, executed immediately, and then automatically destroyed (if there is no reference)
(function foo(x) {
alert(x);
})(1); // This is a call, not a grouping operator
The above code is what we call enclosing an expression in parentheses, and then calling it through (1). Note that for the following function that is executed immediately, the surrounding parentheses are not necessary, because the function is already at the position of the expression, and the parser knows that it is dealing with the FE that should be created during the function execution phase, so that it is called immediately after the function is created. function.
var foo = {
bar: function (x) {
return x % 2 != 0 ? 'yes' : 'no';
}(1)
};
alert(foo.bar); / / 'yes'
As we can see, foo.bar is a string and not a function. The function here is only used to initialize this property based on the conditional parameters - it is created and called immediately.
So, the complete answer to the question "About parentheses" is as follows:
When the function is not in the position of the expression, the grouping operator parentheses are necessary - that is, manually converting the function into FE.
If the parser knows that it is dealing with FE, there is no need to use parentheses.
Free variables:
function testFn() {
var localVar = 10;//For the innerFn function, localVar is a free variable.
function innerFn(innerParam) {
alert(innerParam + localVar);
}
return innerFn;
}
Static scope of closure:
var z = 10;
function foo() {
alert( z);
}
foo(); // 10 – when using static and dynamic scopes
(function () {
var z = 20;
foo(); // 10 – when using static scopes, 20 – Using dynamic scope
})();
// It’s the same when using foo as a parameter
(function (funArg) {
var z = 30;
funArg(); // 10 – static scope, 30 – Dynamic scope
})(foo);
Theory: Because of the scope chain, all functions are closures (regardless of the function type: anonymous functions, FE, NFE, and FD are all closures). From a practical point of view: The following functions are considered closures: * It still exists even if the context in which it was created has been destroyed (for example, the inner function returns from the parent function)
* Free variables are referenced in the code
Finally:
ECMAScript is an object-oriented language that supports prototype-based delegated inheritance.