1. Types in JavaScript -------- Although JavaScript is an object-based language, objects (Object) are not the first type in JavaScript. JS is a language with Function as the first type. This is not only because functions in JS have various characteristics of functions in high-level languages, but also because in JS, Object is also implemented by functions. ——On this point, you can see further explanation in the "Construction and Destruction" section of .
JS is weakly typed, and its built-in types are simple and clear: -------------------------- ------------------------------- undefined : Undefined number : Number boolean : Boolean value string: string function: function object: object
1). undefined type ================ ======== In IE5 and below, except for direct assignment and typeof(), any other operation on undefined will cause a exception. If you need to know whether a variable is undefined, you can only use the typeof() method:
But in IE5.5 and above, undefined is an implemented system reserved word. Therefore, undefined can be used for comparison and operation. A simpler way to check if a value is undefined could be:
So in order to make the core code (partially) compatible with IE5 and earlier versions, there is a line of code in the Romo core unit to "Declare" an undefined value: //---------------------------------- ----------------------- // code from Qomolangma, in JSEnhance.js //---------- ----------------------------------------------- var undefined = void null;
There is another point that needs to be explained in this line of code, which is the application of void statement. void means "execute the following statements and ignore the return value". Therefore any "single" statement that can be executed can appear after void. The result of execution is <script> <BR>var v; <BR>if (typeof(v) == 'undefined') { <BR>// ... <BR>} <BR></script>undefined. Of course, if you want, you can also "define undefined" with one of the codes below. //--------------------------------------------- ------------ // 1. A more complex method, using an anonymous empty function to return //------------ --------------------------------------------- var undefined = function(){}();
//--------------------------------- -------------------------- // 2. The code is simpler, but not easy to understand //----- -------------------------------------------------- -- var undefined = void 0;
void can also be used like a function, so void(0) is also legal. Sometimes, some complex statements may not be able to use the void keyword form , but must use the void function form. For example: //------------------------------------------------ ---------------- // Complex expressions in the form of void() must be used //---------------- ---------------------------------------- void(i=1) ; // Or the following statement: void(i=1, i );
2). number type ================== ======= JavaScript always deals with floating point numbers, so it does not have constants like MaxInt in Delphi. Instead, it has two constant value definitions like these : Number.MAX_VALUE : Returns the largest number that JScript can express. Approximately equal to 1.79E 308. Number.MIN_VALUE: Returns the number closest to 0 in JScript. Approximately equal to 2.22E-308.
Because there is no integer type, in some operations on CSS and DOM properties, if you expect the value to be an integer 2, you may get the string "2.0" - or similar Some situations in this regard. In this case, you may need to use to the parseInt() method of the global object (Gobal).
There are two properties in the global object (Gobal) related to number type operations: NaN: If the result of an arithmetic expression is not a number, a NaN value is returned. Infinity: A number greater than MAX_VALUE.
If a value is NaN, then it can be detected through the isNaN() method of the global object (Gobal). However, two NaN values are not equal to each other. As an example: //------------------------------------------------ --------------- //NaN operation and detection //-------------------- ------------------------------------- var v1 = 10 * 'a '; v2 = 10 * 'a'; document.writeln(isNaN(v1)); document.writeln(isNaN(v2)); document.writeln(v1 == v2 );
The Infinity of the global object (Gobal) represents a value greater than the largest number (Number.MAX_VALUE). In JS, has the same value as positive infinity in mathematical operations. ——In some practical techniques, it can also be used to do boundary detection of an array sequence.
Infinity is defined as POSITIVE_INFINITY in the Number object. In addition, negative infinity is also defined in Number: Number.POSITIVE_INFINITY: A value greater than the largest positive number (Number.MAX_VALUE). Positive infinity. Number.NEGATIVE_INFINITY : A value smaller than the smallest negative number (-Number.MAX_VALUE). Negative infinity.
Different from NaN, two Infinity (or -Infinity) are equal to each other. As an example: //------------------------------------------------ --------------- //Infinity operation and detection //-------------------- ------------------------------------- var v1 = Number.MAX_VALUE * 2; v2 = Number.MAX_VALUE * 3; document.writeln(v1); document.writeln(v2); document.writeln(v1 == v2);
Other methods related to the number type in Global are: isFinite(): If the value is NaN/positive infinity/negative infinity, return false, otherwise return true. parseFloat(): Take a floating point number from (the prefix part of the string). If unsuccessful, NaN is returned.
3). boolean type ======================== (omitted)
A fact that is often overlooked by developers is that JavaScript itself does not have an event system. Usually the onclick and other events we usually use in JavaScript are actually provided by IE's DOM model. From a more core perspective: IE publishes a set of event interfaces to the DOM through the interface attributes of COM.
There are two reasons why "whether an attribute is an event" cannot be well identified in JS: - The COM interface itself only has methods, properties and events, which are all passed through a set of get/ set method to publish. - In JavaScript, there is no independent "event" mechanism.
So we see that the identification method of event is to detect whether the attribute name starts with the string 'on' (it is Qomo's convention to start with 'On' ). Next, since events in the DOM object do not need to specify a handler function, in this case the event handle is null (Qomo adopts the same convention); in other cases, the user can Like obj2, define an event with a value of undefined. Therefore, the judgment condition of "event" is processed into a complex expression: ("The attribute starts with on/On" && ("The value is null/undefined" || "The type is function"))
In addition, judging from the running results of the above code. Using for..in on DOM objects cannot enumerate object methods.
One final point. In fact, in many language implementations, "events" are not "object-oriented" language features, but are provided by specific programming models. For example, the event-driven mechanism in Delphi is provided by the window message mechanism in the Win32 operating system, or is implemented by user code actively calling the event processing function in Component/Class.
"Events" are a mechanism/problem of "how to drive the programming model", not a problem of the language itself. However, the OOP concept based on PME (property/method/event) has been deeply rooted in the hearts of the people, so when the programming language or system shows these characteristics, no one cares about "event". Who achieved it?”
3). Use of this and with keywords ------ In the JavaScript object system, the this keyword is used in two places: - in construction In the converter function, refers to the newly created object instance - When the object's method is called, refers to the object instance that called the method
If a function is used as a normal function (rather than an object method) Called, then the this keyword in the function will point to the window object. Similarly, if the this keyword is not in any function, then it also points to the window object.
Because there is no clear distinction between functions and methods in JavaScript. So some of the code looks weird: //------------------------------------------ -------------------- // Several possible calling forms of the function //------------- ----------------------------------------------- function foo( ) { // This below refers to the object instance that calls this method if (this===window) { document.write('call a function.', ' ') ; } else { document.write('call a method, by object: ', this.name, ' '); } }
function MyObject(name) { // This below refers to the newly created instance of the new keyword this.name = name; this.foo = foo; }
var obj1 = new MyObject('obj1'); var obj2 = new MyObject('obj2');
// Test 1: Calling foo() as a function;
// Test 2: Call as object method obj1.foo(); obj2.foo();
// Test 3: Call function as "specified object" "Method call foo.call(obj1); foo.apply(obj2);
In the above code, obj1/obj2's call to foo() is a very common calling method . ——That is, is a method to specify a function as an object on the constructor.
The call() and apply() in test 3 are quite special.
In this test, foo() is still called as an ordinary function, but the JavaScript language feature allows an object instance to be passed in to specify foo() when calling()/apply() A reference to the this keyword that appears in the context of . ——It should be noted that foo() at this time is still a normal function call, not an object method call.
is somewhat similar to this "indicates the object instance that calls this method", and the with() syntax is also used to qualify "use the object instance by default in a piece of code".——If the with() syntax is not used, then then this code will be affected by the outer with() statement; if there is no outer with(), then then the " The default object instance used will be window.
However, it should be noted that this and with keywords do not affect each other. Such as the following code: //---------------------------------------- ------------------ // Test: this and with keywords do not affect each other //---------- ----------------------------------------------- function test() { with (obj2) { this.value = 8; } } var obj2 = new Object(); obj2.value = 10;
You cannot expect such code to set the obj2.value attribute to 8 after the call is completed. The result of these lines of code is: the window object has an additional value attribute, and the value is 8. The syntax
with(obj){...} can only limit the reading of the existing attributes of obj, but cannot actively declare it. Once the object in with() does not have specified attributes, or with() qualifies a data that is not an object, the result will be an exception.
4). Operations using the in keyword ------ In addition to using for..in to reflect the member information of the object, JavaScript also allows direct use of in Keyword to detect whether the object has an attribute with the specified name. The reason why the
in keyword is often mentioned is not its ability to detect whether the attribute exists, so in the early code of , many people like to use "if (!obj.propName) {}" This way to detect whether propName is a valid property. ——In many cases, checking validity is more practical than checking “whether the attribute exists”. Therefore, in this case, in is only an optional and official solution. An important application of the
in keyword is high-speed string retrieval. Especially when you only need to determine "whether the string exists". For example, if 100,000 strings are stored in an array, the retrieval efficiency will be extremely poor. //--------------------------------------------- ------------ // Use objects to retrieve //--------------------- ---------------------------------- function arrayToObject(arr) { for (var obj= new Object(), i=0, imax=arr.length; iobj[arr[i]]=null; } return obj; }
var arr = ['abc', 'def', 'ghi']; // more and more... obj = arrayToObject(arr);
function valueInArray(v) { for (var i=0, imax=arr.length; iif (arr[i]==v) return true; }
return false; }
function valueInObject(v) { return v in obj; }
This method of using the keyword in, There are also some limitations. For example, only strings can be found, while array elements can be any value. In addition, arrayToObject() also has some overhead, which makes it unsuitable for frequently changing lookup sets. Finally, (I think you may have noticed) using the object to search does not accurately locate the search data, while the array can point to the subscript of the result.
8. JavaScript object-oriented support ~~~~~~~~~~~~~~~~~~~ (continued)
2. JavaScript object-oriented Object support -------- (continued)
5). Operation using instanceof keyword ------ in JavaScript The instanceof keyword is provided to detect the type of instance. This has already been discussed before when discussing its "five-fold identity". But the problem with instanceof is that it always enumerates the entire prototype chain to detect types (the principles of prototypal inheritance are described in the "Construction and Destruction" section), such as: //------- -------------------------------------------------- //Problems in using instanceof //---------------------------------- ----------------------- function MyObject() { // ... }
function MyObject2 () { // ... } MyObject2.prototype = new MyObject();
We see that obj1 and obj2 are both instances of MyObject, but they are generated by different constructors . ——Note that this is correct in object-oriented theory: because obj2 is a subclass instance of MyObject, it has the same characteristics as obj1. In applications, this is one of the manifestations of obj2's polymorphism.
However, even so, we must face this problem: How to know whether obj2 and obj1 are instances of the same type? ——In other words, even the constructors are the same?
The instanceof keyword does not provide such a mechanism. One that provides the ability to implement this detection is the Object.constructor property. --But first remember, it's harder to use than you think.
Okay, let’s end the question first. The constructor attribute has already involved the issue of "construction and destruction". We will talk about this later. "Prototypal inheritance" and "construction and destruction" are the main issues, core issues, and "fatal issues" in JavaScript's OOP .
6). null and undefined ------ In JavaScript, null and undefined once confused me. The following text will help you understand it more clearly (or make you more confused): - null is the keyword; undefined is a property of the Global object. - null is an object (empty object, without any properties and methods); undefined is a value of undefined class type. Try the following code: document.writeln(typeof null); document.writeln(typeof undefined); - In the object model, all objects are instances of Object or its subclasses, but Exception for null objects: document.writeln(null instanceof Object); - null is "equal (==)" to undefined, but not "congruent (===)" to undefined: document.writeln(null == undefined); document.writeln(null == undefined); - Both null and undefined can be type converted to false during operation, but are not equal to false: document.writeln(!null, !undefined); document.writeln(null==false); document.writeln(undefined==false);
8. JavaScript object-oriented support ~~~~~~~~~~~~~~~~~~ (continued)
3. Construction, destruction and prototype issues ----- --- We already know that an object needs to be generated through a constructor function. Let’s remember a few points first: - The constructor is an ordinary function - The prototype is an object instance - The constructor has prototype properties, but the object instance does not - (If inheritance is implemented normally model, ) the constructor attribute of the object instance points to the constructor - derived from the third and fourth items: obj.constructor.prototype points to the prototype of the object
OK, let’s analyze an example to illustrate JavaScript "Inherited prototype" declaration, with and the construction process. //--------------------------------------------- ------------ //Examples of understanding prototypes, construction, and inheritance //-------------------- ------------------------------------- function MyObject() { this. v1 = 'abc'; }
function MyObject2() { this.v2 = 'def'; } MyObject2.prototype = new MyObject();
var obj1 = new MyObject(); var obj2 = new MyObject2();
1). Formal code of new() keyword ------ Let’s first look at the new keyword in the line of code “obj1 = new MyObject()”. The
new keyword is used to generate a new instance (I would like to add here that I am used to calling reserved words key words . In addition, the new keyword in JavaScript is also an operator) , the default attribute of this instance will (at least) hold a reference to the prototype attribute (prototype) of the constructor function (in the ECMA Javascript specification, this attribute name of the object is defined as __proto__) .
Every function, whether it is used as a constructor or not, will have a unique prototype object. For JavaScript "built-in object constructor", it points to an internal prototype. By default, JavaScript constructs an "empty initial object instance (not null)" and points the prototype reference to it. However, if you assign a new object to this prototype of the function, then the new object instance will hold a reference to it.
Next, the construction process will call MyObject() to complete the initialization. ——Note, this is just “initialization”.
In order to explain this process clearly, I use code to formally describe this process: //--------------------- ------------------------------------ //Formal code of new() keyword //--------------------------------------------- ------------ function new(aFunction) { // Basic object instance var _this = {};
// Prototype reference var _proto= aFunction.prototype;
/* if compat ECMA Script _this.__proto__ = _proto; */
// Add for accessing attributes in the prototype (internal) getter _this._js_GetAttributes= function(name) { if (_existAttribute.call(this, name)) return this[name] else if (_js_LookupProperty.call(_proto , name)) retrun OBJ_GET_ATTRIBUTES.call(_proto, name) else return undefined; }
// Add (internal) for accessing attributes in the prototype setter _this._js_GetAttributes = function(name, value) { if (_existAttribute.call(this, name)) this[name] = value else if (OBJ_GET_ATTRIBUTES.call(_proto, name) !== value) { this[name] = value // Create a new member of the current instance } }
// Call the constructor to complete initialization, (if any ,) Pass in args aFunction.call(_this);
// Return object return _this; }
So we see the following two points: - The constructor (aFunction) itself only "initializes" the passed this instance, and does not construct an object instance. - The construction process actually occurs inside the new() keyword/operator.
Furthermore, the constructor (aFunction) itself does not need to operate the prototype, nor does it need to return this.
2). Prototype chain maintained by user code ------ Next we will discuss the prototype chain and construction process in more depth. This is: - The prototype chain is created by user code, the new() keyword does not assist in maintaining the prototype chain
Taking Delphi code as an example, when we declare the inheritance relationship, we can use this Code: //------------------------------------------------ ---------------- // "Class" type declaration used in delphi //----------------- ---------------------------------------- type TAnimal = class (TObject); // Animal TMammal = class(TAnimal); // Mammal TCanine = class(TMammal); // Canine mammal TDog = class(TCanine); // Dog
At this time, Delphi's compiler will maintain an inheritance relationship list through compilation technology. We can query this linked list through code similar to the following: //-------------------------------- ---------------------------- //Key code for using inheritance relationship linked list in delphi //--- -------------------------------------------------- ---- function isAnimal(obj: TObject): boolean; begin Result := obj is TAnimal; end;
var dog := TDog ;
// ... dog := TDog.Create(); writeln(isAnimal(dog));
You can see that in the Delphi user code , there is no need to directly inherit the linked list of the inheritance relationship. This is because Delphi is a strongly typed language. When processing types declared with the class() keyword, Delphi's compiler has already constructed this inheritance chain for the user. ——Note that this process is a statement, not an execution of code.
In JavaScript, if you need to know whether an object is a subclass object of a certain base class, then you need to manually maintain a linked list (similar to the Delphi example). Of course, this linked list is not called a type inheritance tree, but a "prototype linked list (of objects)". ——In JS, there is no "class" type.
Refer to the previous JS and Delphi code, a similar example is as follows: //----------------------- ---------------------------------- //The key code of "prototype linked list" in JS //-------------------------------------------------- ---------- // 1. Constructor function Animal() {}; function Mammal() {}; function Canine() {}; function Dog() {};
// 2. Prototype linked list Mammal.prototype = new Animal(); Canine.prototype = new Mammal(); Dog. prototype = new Canine();
// 3. Example function function isAnimal(obj) { return obj instanceof Animal; }
var dog = new Dog(); document.writeln(isAnimal(dog));
As you can see, in the JS user code, the construction method of the "prototype linked list" is a line of code: "Constructor function of the current class".prototype = "Instance of the direct parent class"
This is different from languages like Delphi: the essence of maintaining the prototype chain is to execute the code, not the declaration.
So, what does “execution rather than declaration” mean?
JavaScript will have a compilation process. This process mainly deals with "grammar error detection", "grammar declaration" and "conditional compilation instructions".The "grammar declaration" here mainly deals with function declarations. ——This is also one of the reasons why I say "functions are of the first type, but objects are not."
Example below: //--------------------------------------------- --------------------- //The relationship between function declaration and execution statement (firefox compatible) //-------- -------------------------------------------------- // 1. Output 1234 testFoo(1234);
The result of executing this sample code in the JS environment is: ------ ------------------------------- 1234 undefined Exception: 'obj2' undefined [object Object] hi, obj ------------------------------------------------ The problem is that testFoo() is executed before it is declared; while the object variable defined in the form of "direct declaration" cannot be referenced before it is declared. ——In the example, the second and third inputs are incorrect.
Functions can be referenced before declaration, while other types of values must be used after declaration. This shows that "declaration" and "execution-time reference" are two processes in JavaScript.
In addition, we can also find that when using "var" to declare, the compiler will first confirm that the variable exists, but the value of the variable will be "undefined". ——So "testFoo(obj1)" will not throw an exception. However, normal output will not occur until the assignment statement about obj1 is executed. Please compare the differences between the output of the second and third lines and the fourth and fifth lines.
Since JavaScript maintains the prototype chain through "execution" rather than "declaration", this means that "the prototype chain is maintained by user code rather than by the compiler.
Based on this inference, let’s look at the following example: //-------------------------------- -------------------------- // Example: Wrong prototype chain //--------- --------------------------------------------- // 1. Constructor function Animal() {}; // Animal function Mammal() {}; // Mammal function Canine() {}; // Canine mammals Animal
// 2. Construct prototype chain var instance = new Mammal(); Mammal.prototype = new Animal(); Canine.prototype = instance;
// 3. Test output var obj = new Canine(); document.writeln(obj instanceof Animal);
This output allows us to see a wrong prototype chain The result is that "canine mammals are not an animal".
The root cause is that the following lines of code in "2. Construct the prototype chain" are interpreted and executed, rather than "declared" and understood at compile time like var and function. The way to solve the problem is to modify those three lines of code to make its "execution process" logical: //--------------------- ------------------------------------ //Correction code for the above example (part) //--------------------------------------------- ----------- // 2. Construct the prototype chain Mammal.prototype = new Animal(); var instance = new Mammal(); Canine.prototype = instance;
3). How prototype instances are used in the construction process ------ Still taking Delphi as an example. During the construction process, Delphi will first create an "empty object" with a specified instance size, then assign values to the properties one by one, call methods in the construction process, trigger events, etc.
The construction process implicit in the new() keyword in JavaScript is not completely consistent with the construction process of Delphi. But the behavior that happens in the constructor function is similar to the above: //-------------------------- ----------------------------- //Construction process in JS (formal code) //- -------------------------------------------------- ------ function MyObject2() { this.prop = 3; this.method = a_method_function;
if (you_want) { this.method( ); this.fire_OnCreate(); } } MyObject2.prototype = new MyObject(); // The declaration of MyObject() is omitted
var obj = new MyObject2 ();
If a single class is used as the reference object, JavaScript can have the same rich behavior as Delphi during this construction process. However, since the construction process in Delphi is "dynamic", in fact Delphi will also call the construction process of the parent class (MyObject) and trigger the OnCreate() event of the parent class.
JavaScript has no such feature. The construction process of the parent class only occurs on the line of code that assigns a value to the prototype (prototype attribute). Afterwards, no matter how many new MyObject2() occur, the MyObject() constructor will not be used. ——This also means: - During the construction process, the prototype object is generated once; the new object only holds a reference to this prototype instance (and uses the "copy-on-write" mechanism to access its properties) , and no longer calls the prototype's constructor.
Because the constructor of the parent class is no longer called, some features in Delphi cannot be implemented in JavaScript. This mainly affects some events and behaviors in the construction phase. ——Some "during object construction process" code cannot be written into the constructor of the parent class. Because no matter how many times the subclass is constructed, the construction process of the object this time will not activate the code in the parent class constructor at all.
The access of attributes in JavaScript is dynamic, because the object's access to parent class attributes depends on the prototype linked list, but the construction process of is static and does not access the constructor of the parent class; in Delphi In some compiled languages, access to attributes (those that do not use readers and writers) is static, while the object's construction process dynamically calls the constructor of the parent class. So once again, please read clearly this line in the formal code of the new() keyword: //--------------------- ---------------------------------- //Formal code of new() keyword //--------------------------------------------- ----------- function new(aFunction) { // Prototype reference var _proto= aFunction.prototype;
// ... }
In this process, what JavaScript does is "get a prototype_Ref", while what other languages such as Delphi do is "Inherited Create()".
8. JavaScript object-oriented support ~~~~~~~~~~~~~~~~~~~ (continued)
4). Required Another attribute maintained by the user: constructor ------ Recalling the previous content, we mentioned: - (If the inheritance model is implemented normally,) the constructor attribute of the object instance points to the constructor Object - obj.constructor.prototype points to the prototype of the object - Through the Object.constructor property, you can detect whether obj2 and obj1 are instances of the same type
and the prototype chain must be determined through user code Just like maintaining the prototype attribute, the constructor attribute constructor of the instance also needs to be maintained by user code.
function objectTypes(obj) { if (typeof obj !== 'object') return typeof obj; if (obj == = null) return 'null';
for (var i in _object_types) { if (obj.constructor===_object_types[i]) return i; } return ' unknow'; }
// Test data and related code function MyObject() { } function MyObject2() { } MyObject2.prototype = new MyObject();
window.execScript('' 'Function CreateVBArray()' ' Dim a(2, 2)' ' CreateVBArray = a' 'End Function', 'VBScript');
document.writeln('
dom');
// Test code var ax = new ActiveXObject( "Microsoft.XMLHTTP"); var dom = document.getElementById('dom'); var vba = new VBArray(CreateVBArray()); var obj = new MyObject(); var obj2 = new MyObject2();
In this example, we find that the constructor attribute is not completely implemented. For DOM objects and ActiveX objects , this attribute is not returned correctly.
To be precise, DOM (including Image) objects and ActiveX objects are not part of the standard JavaScript object system. So they may also have their own constructor attributes and have different interpretations from JavaScript. . Therefore, It is reasonable to not maintain their constructor attributes in JavaScript.
Some other single objects (not constructors) do not have constructor attributes, such as "Math" and "Debug", "Global" and "RegExp objects".他们是JavaScript内部构造的,不应该公开构造的细节。
下面我们先从JavaScript中对象的“失效”问题说起。简单的说: - 一个对象在其生存的上下文环境之外,即会失效。 - A global object will become invalid if it is not used (referenced).
For example: //------------------------------------- -------------------- //When does a JavaScript object expire //--------------- ------------------------------------------ function testObject() { var _obj1 = new Object(); }
function testObject2() { var _obj2 = new Object(); return _obj2; }
// Example 1 testObject();
// Example 2 testObject2()
// Example 3 var obj3 = testObject2(); obj3 = null;
// Example 4 var obj4 = testObject2(); var arr = [obj4]; obj3 = null; arr = [ ];
In these four examples: - "Example 1" constructs _obj1 in the function testObject(), but when the function exits, it has already left the function context, so _obj1 is invalid; - In "Example 2", an object _obj2 is also constructed in testObject2() and passed out, so this object has an "outside function" context ( and lifetime), however, since the return value of function is not "held" by other variables, _obj2 also becomes invalid immediately; - In "Example 3", _obj2 constructed by testObject2() is externally The variable obj3 is held, At this time, until the line of code "obj3=null" takes effect, _obj2 will become invalid because the reference relationship disappears. - For the same reason as Example 3, _obj2 in "Example 4" will not become invalid until the "arr=[]" line of code .
However, the "invalidation" of an object does not wait for it to be "released". Within the JavaScript runtime environment, there is no way to tell the user exactly when the object will be released. This relies on JavaScript ’s memory recycling mechanism. ——This strategy is similar to the recycling mechanism in .NET.
In the previous Excel operation sample code, the owner of the object, that is, the process "EXCEL.EXE" can only occur after the "release of the ActiveX Object instance". The file lock and the permission credentials for operating the system are related to the process. So if the object is just "invalidated" rather than "released", then there will be problems for other processes handling the file and referencing the operating system's permission credentials.
- Some people say this is a BUG in JavaScript or COM mechanism. Actually no, this is caused by a complex relationship between OS, IE and JavaScript, rather than an independent problem.
Microsoft has disclosed a strategy to solve this problem: actively calling the memory recycling process.
A CollectGarbage() process (usually referred to as the GC process) is provided in (Microsoft) JScript. The GC process is used to clean up the "invalid object exceptions" in the current IE, that is, calling The object's destruction process.
In the above example, the code to call the GC process is: //--------------------------- ----------------------------- // When dealing with ActiveX Object, the standard calling method of the GC process // -------------------------------------------------- ------- function writeXLS() { //(omitted...)
The first line of code calls the excel.Quit() method to stop the excel process and exit. At this time, since the JavaScript environment holds an excel object instance, excel The process does not actually terminate.
The second line of code makes excel null to clear the object reference, thereby "invalidating" the object.However, since the object is still in the function context, if the GC process is called directly, the object will still not be cleaned up.
The third line of code uses setTimeout() to call the CollectGarbage function, and the time interval is set to '1', only so that the GC process occurs after the writeXLS() function is executed. In this way, the excel object meets the two conditions of "can be cleaned by GC": no reference and leaving the context.
The use of GC process is very effective in JS environment using ActiveX Object. Some potential ActiveX Objects include XML, VML, OWC (Office Web Component), flash, and even VBArray in JS. From this point of view, the ajax architecture uses XMLHTTP and must also meet the feature of "no page switching". Therefore, actively calling the GC process at the appropriate time will result in a better UI experience. .
In fact, even if the GC process is used, the excel problem mentioned above will still not be completely solved. Because IE also caches permission credentials. The only way to update the page's permission credentials is to "switch to a new page". So in fact, in the SPS project mentioned earlier, the method I used was not GC, but the following. A section of code: //----------------------------------------- ------------------ // Page switching code used when processing ActiveX Object //------------ --------------------------------------------- function writeXLS () { //(omitted...)
excel.Quit(); excel = null;
// The following code is used to solve IE call Excel A BUG, the method provided in MSDN: // setTimeout(CollectGarbage, 1); // Since the trusted status of the web page cannot be cleared (or synchronized), methods such as SaveAs() will be executed in // Invalid the next time it is called. location.reload(); }
Last and last, a supplementary note about GC: when the IE form is minimized, IE will The CollectGarbage() function will be actively called once. This allows the memory usage to be significantly improved after the IE window is minimized.
8. JavaScript object-oriented support ~~~~~~~~~~~~~~~~~~ (continued)
4. Examples and Instance reference -------- In the .NET Framework, CTS (Common Type System) agrees that "everything is an object" and is divided into two types: "value type" and "reference type". In the process of converting "value type" objects into "reference type" data, a "boxing" and "unboxing" process is required.
The same problem exists in JavaScript. The typeof keyword we saw returns the following six data types: "number", "string", "boolean", "object", "function" and "undefined".
We also found that there are four object constructors in the JavaScript object system: String, Number, Function, and Boolean. So, our question is: if there is a number A, will the result of typeof(A) be 'number', or will it be an object whose constructor points to function Number()?
//------------------------------------------------ --------------- // Test code about JavaScript types //------------------ --------------------------------------- function getTypeInfo(V) { return (typeof V == 'object' ? 'Object, construct by ' V.constructor : 'Value, type of ' typeof V); }
var A1 = 100; var A2 = new Number(100);
document.writeln('A1 is ', getTypeInfo(A1), ' '); document.writeln('A2 is ', getTypeInfo(A2), ' '); document.writeln([A1.constructor === A2.constructor, A2.constructor === Number]);
Test code execution The result is as follows: ---------- A1 is Value, type of number A2 is Object, construct by function Number() { [native code] } true ,true ----------
We noticed that the constructors of A1 and A2 both point to Number. This means that identifying objects by their constructor properties is (sometimes) more efficient than typeof. Because "value type data" A1 has exactly the same characteristics as A2 when viewed as an object.
- Except for issues related to instance references.
Referring to the JScript manual, we conduct the same inspection on other basic types and constructors, and we can find: - undefined, number, boolean and string in the basic types are "value type" variables - Array, function and object in basic types are "reference type" variables - Use the new() method to construct objects, which are "reference type" variables
The following code explains the "value type" The difference between "reference type": //---------------------------------- ----------------------- // About value/reference issues in JavaScript type system //-------- -------------------------------------------------- var str1 = 'abcdefgh', str2 = 'abcdefgh'; var obj1 = new String('abcdefgh'), obj2 = new String('abcdefgh');
Test code execution The results are as follows: ----------- true, true false, false -----------
us It can be seen that whether it is the equivalent operation (==) or the congruent operation (===), the understanding of "object" and "value" is different.
To further understand this phenomenon, we know: - When the operation result is a value type, or the variable is a value type, equality (or congruence) comparison can get the expected result - (Even if they contain the same data,) Different object instances are not equal (or congruent) - Different references to the same object are equal (==) and congruent (== =)'s
But for the String type, there is a little addition: According to the description of JScript, when comparing two strings, as long as one of them is a value type, they will be compared by value. This means that in the above example, the code "str1==obj1" will result in true. The congruence (===) operation needs to detect the consistency of variable types, so the result of "str1===obj1" returns false.
Function parameters in JavaScript are always passed in value parameters, and reference types (instances) are passed in as pointer values. Therefore, the function can rewrite the entry variable at will without worrying about the external variable being modified. However, you need to pay attention to the reference type variables passed in, because its method calls and property reads and writes may affect the instance itself. ——However, data can also be passed out through reference type parameters.
Finally, the value type comparison will detect the data in the object instance byte by byte, which is low in efficiency but high in accuracy; while the reference type only detects the instance pointer and data type, so it is in high efficiency and low in accuracy. . If you need to check whether two reference types actually contain the same data, you may need to try converting it to a "string value" before comparing.
6. Function context -------- As long as you have written code, you should know that variables are divided into "global variables" and "local variables". Most JavaScript programmers also know the following concepts: //----------------------------- ---------------------------- //Global variables and local variables in JavaScript //---- -------------------------------------------------- --- var v1 = 'Global variable-1'; v2 = 'Global variable-2';
function foo() { v3 = 'Global variable-3' ;
var v4 = 'Only those defined within a function using var are local variables'; }
According to the usual understanding of language, different code calls Functions will have an independent set of local variables. So the following code is easy to understand: //--------------------------------- -------------------------- // JavaScript local variables //--------------------- --------------------------------------------- function MyObject () { var o = new Object;
this.getValue = function() { return o; } }
var obj1 = new MyObject(); var obj2 = new MyObject(); document.writeln(obj1.getValue() == obj2.getValue());
The result shows false, indicating different (example The local variables "obj1/obj2" returned by the method) call are not the same.
The local and global characteristics of variables are similar to the "private" and "public" in OOP's encapsulation. Therefore, most of the information always explains the "encapsulation permission level" issue in JavaScript object-oriented systems in the following way: //------------------ --------------------------------------- //OOP encapsulation in JavaScript //--------------------------------------------- ---------- function MyObject() { // 1. Private members and methods var private_prop = 0; var private_method_1 = function() { // ... return 1 } function private_method_2() { // ... return 1 }
Here, "private" indicates that it can only be accessed within the (constructor) function, and "privileged" specifically refers to a "public" access to the "private domain". )"method. "Public" indicates that it can be called and accessed outside the (constructor) function.
In addition to the above encapsulation permissions, some documents also introduce two other related concepts: - Prototype properties: Classname.prototype.propertyName = someValue - (Class) static properties: Classname.propertyName = someValue
However, from an object-oriented perspective, these concepts are difficult to justify: why and how does JavaScript divide these encapsulation permissions and concepts?
——Because we must pay attention to the problems caused by the following example: //---------------------- ---------------------------------- //Local variables in JavaScript //- -------------------------------------------------- ------ function MyFoo() { var i;
MyFoo.setValue = function (v) { i = v; } MyFoo .getValue = function () { return i; } } MyFoo();
var obj1 = new Object(); var obj2 = new Object ();
In this test code, obj1/obj2 are both Object() instances. We use function.call() to call setValue/getValue, so that this is replaced with the obj1/obj2 instance during the call to MyFoo().
However, we found that after "Test 2" was completed, obj2, obj1 and the local variables held by function MyFoo() all returned "obj2". ——This indicates that the three functions use the same local variable.
It can be seen that when JavaScript handles local variables, "ordinary functions" and "constructors" are treated separately. This processing strategy is explained in some JavaScript-related materials as the "private domain in object-oriented" problem. In fact, I prefer to tell you the truth from the source code level: this is a matter of the context of the object. ——On the surface, the problem of "context environment" has been transferred to the problem of object encapsulation.
(Before reading the following text,) let’s make a conceptual explanation: - In ordinary functions, the context is held by the window object - In "Constructor and Object Method", the context is held by the object instance
In the JavaScript implementation code, every time an object is created, the interpreter will create a context chain for the object, which is used to store the object before entering the "construction "Constructor and Object Method" is a backup of the internal data of function(). JavaScript ensures that this object will always hold the context and a related this object when it enters the "constructor and object methods" in the future. Since an object may have multiple methods, and each method may have multiple levels of nested functions, this actually forms a tree-like linked list structure of the context. Outside of constructors and object methods, JavaScript does not provide any way to access the context (of the constructor and object methods).
In short: - The context is relevant when an object instance calls "constructor and object methods", not (normal) functions - The context records when an object is "constructed" Private data inside "functions and object methods" - The context environment adopts a chain structure to record the context in multi-level nested functions
Since the context environment is only related to the constructor and its internal nesting Function-related, re-read the previous code: //------------------------------------------------ --------------------- //Local variables in JavaScript //------------- --------------------------------------------- function MyFoo() { var i;
MyFoo.setValue = function (v) { i = v; } MyFoo.getValue = function () { return i; } } MyFoo();
var obj1 = new Object(); MyFoo.setValue.call(obj1, 'obj1');
We found that setValue() can indeed access the "local variable i" located inside the MyFoo() function, but since the owner of the setValue() method is the MyFoo object (remember that functions are also objects), the MyFoo object owns MyFoo( ) function's only "context".
Although the next MyFoo.setValue.call() call passes in a new this object for setValue(), it is still the MyFoo object that actually has the "context environment". Therefore, we see that no matter how many obj1/obj2 are created, the same private variable i is ultimately operated.
The "context environment" holder of global functions/variables is window, so the following code explains "why global variables can be accessed by any object and function": //---- -------------------------------------------------- --- //Context of global function //--------------------------------- -------------------------- /* function Window() { */ var global_i = 0; var global_j = 1;
function foo_0() { }
function foo_1() { } /* }
window = new Window(); */
So we can see that foo_0() and foo_1() can access global_i and global_j at the same time. The next corollary is that the context determines the "global" and "private" variables.Instead of discussing contextual issues through the private and global variables.
A further corollary is: global variables and functions in JavaScript are essentially private variables and methods of the window object. This context block is located at the top of the context list of all object instances (inside the window object), so it can be accessed.
Using the theory of "contextual environment", you can successfully explain in this section the issues related to the "global/local" scope of variables and the encapsulation permissions of object methods. In fact, in the C source code that implements JavaScript, this "context" is called "JSContext" and is passed in as the first parameter of the function/method. - If you are interested, you can confirm the theory described in this section from the source code.
In addition, Section 4.7 of the book "The Definitive Guide to JavaScript" also talks about this issue, but it is called "the scope of variables". However, the important thing is that this book turns the problem on its head. ——The author tries to use "global and local scope" to explain the "contextual environment" problem that produces this phenomenon. Therefore, this section seems messy and difficult to explain.
However, in section 4.6.3, the author also mentioned the issue of execution context, which is consistent with the "context environment" we are talking about here. However, what is even more troublesome is that the author leads readers to the wrong method, trying to use the context of functions to explain problems in DOM and ScriptEngine.
But what this book says about the query method of "context environment linked list" is correct and reasonable. It's just that calling this "scope" is a bit wrong or inappropriate.
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn