[Effective JavaScript note] 28th: Do not trust the ToString method of a Function object

Source: Internet
Author: User
Tags closure object object hosting

The JS function has an extraordinary feature, the ability to reproduce its source code as a string.

(function (x) {   return x+1}). toString ();//"function (x) {   return x+1}"

Reflection Gets the function source code is very powerful, using the function object's ToString method has serious limitations.
Limitations of the ToString method
The ECMAScript standard returns the result of the ToString method of the Function object (that is, the string) without any requirement. This means that different JS engines will produce different strings, and even the resulting string is not related to the function.

If the function is implemented using pure JS, then the JS engine will attempt to provide a true representation of the source code of the function.

An example of failure
(function (x) {     return x+1}). Bind (+). toString ();//"function () {[native code]}"
Reason for failure:

Functions provided by the built-in libraries of the hosting environment are used.

    • Because of the many hosting environments, the BIND function is implemented by other programming languages (usually C + +). The host environment provides a compiled function, in which the function does not have JS source code for display.

    • Because the standard allows the browser engine to change the output of the ToString method, it is easy to write the program in a JS system correctly run, in other JS system does not run correctly. The program is sensitive to the specifics of the function's source code string, even if a slight change in the JS implementation can break the program.

    • The source code generated by the ToString method does not show the values stored in the closure that are related to the internal variable reference

(function (x) {   return function (y) {      return x+y;}   }) (). toString ();//"function (y) {      return x+y;   }"

Note: Although the function is actually a closure with a binding of x 42, the resulting string still contains a variable that references x.

In a sense, the limitations of the ToString method of JS make it not particularly useful and trustworthy to extract the function source code. You should usually avoid using it. The use of the extracted function source code is quite complex and should be made with a carefully crafted JS interpreter and processing library. It is most prudent to consider the JS function as an abstraction that should not be violated.

    • When calling the ToString method of the function, it does not require the JS engine to obtain the source code of the function accurately.

    • Since the results of the ToString method may vary in different engines, never trust the details of the source code of the function

    • The results of the ToString method do not expose local variables stored in closures

    • Typically, you should avoid using the ToString method of a Function object

Appendix I: ToString Method

The result of calling the ToString method for different data types.
The ToString method is a method in object prototype objects, so objects that inherit from this class inherit this method and can overwrite the ToString method.
5 Simple data types in the JS standard library: Undefined,null,boolean,number and string. There is also a complex data type the essence of Object,object is a set of unordered name-value pairs.

Simple data types
Number (Undefined). ToString ();//"Error" (Null). ToString ();//error (true). ToString ();//"True" (1). ToString ();/"1" (' 111 '). toString ();//"111"

You can see why several other basic types can run in addition to the undefined and null types.
This is mentioned in our previous article, [effective JavaScript note] 4th: Primitive types better than closed objects, when a simple data type calls the ToString method, the original type is first converted to a wrapper object.
At this point the corresponding wrapper object is

    • Numbers are number objects

    • Boolean value is a Boolean object

    • String Object

These objects are also inherited from object objects, and the respective ToString methods are overridden.
However, both the undefined type and the null type have only one value undefined,null, and there is no corresponding encapsulated object.
Although the value of typeof null is "Object", it is useless.

Reference type
Object ({a:10,b:20}). ToString ();//"[Object Object]"//date object (New Date). ToString ();//"Tue June 15:37:15 gmt+ 0800 (China Standard Time) "//regexp object (/^sss$/g). toString ();//"/^sss$/g "//function object function aa () {return" BB "}aa.tostring ();/" function aa () {return "BB"} "//window object Window.tostring ();//" [Object Window] "//math object math.tostring ();//" [Object Math] "

To see the ToString method above, Object,window,math is using the Object prototype method. Other objects use the ToString method of their own override.

typeof operator

The following results are obtained when using the TypeOf operator for all of the above types

typeof 1;//"number" typeof ' 1 ';//"string" typeof true;//"boolean" typeof undefined;//"undefined" typeof (function A () {}) ;//"function" typeof null;//"Object" typeof {};//"Object" typeof (new Date);//"Object" typeof [];//"Object" typeof window ;//"Object" typeof math;//"Object" typeof (/sdfsf/g);//"Object"

As you can see, it is almost impossible to judge a type using the TypeOf operator alone.
One might say that for an object string to be returned, the constructor can be used to judge the type as the Instanceof method.

({}) instanceof Object;//true (new Date) instanceof date;//true ([]) instanceof array;//true (/sdfsf/g) instanceof RegExp ;//true

Then the null type as long as

var a=null;a===null;//true;

It seems to be possible to implement the following type judgment code.

function GetType (obj) {    if (typeof obj!== ' object ') {        return typeof obj;    else{           if (obj===null) {            return ' null ';        }        if (Obj===window) {            return ' window ';         }        if (Obj===math) {            return ' Math '        }              if ((obj) instanceof date) {            return ' date ';        }        if ((obj) instanceof array) {            return ' array ';        }        if ((obj) instanceof RegExp) {            return ' RegExp ';        }         if ((obj) instanceof object) {            return ' object ';}}    }

Whether the above code can run test, and no problem

GetType (1);//"Number" getType (true);//"Boolean" GetType (' 1 ');//"string" getType (undefined);//"Undefined" getType ( function () {});//"function" GetType (/sf/);//"regexp" getType (null);//"Null" getType (window);//"window" GetType ({}) ;//"Object" GetType ([]);//"Array"

But one of the things to be aware of here is that the test for object type in this code must be put on the last side.
As shown below, all objects are inherited from object, so instanceof detects whether all objects are returned as true for an instance of type Object

([]) instanceof array;//true ([]) instanceof Object;//true

See the above code is not too complex trouble, there is a simpler way to judge the type? The answer, of course, is to see how the ToString method is used.

ToString App

As mentioned above, objects that inherit from object have the ToString method, but each object implements its own tostring method, resulting in the inability to type judgment with the ToString method. You can use the call or Apply method that you talked about earlier to invoke the Object.prototype.toString method.

function GetType (obj) {   var tostring=object.prototype.tostring;   return Tostring.call (obj);   

Test each type to get the following results

GetType (1);//"[Object number]" GetType (true);//"[Object Boolean]" GetType (' 1 ');//"[Object String]" GetType (undefined );//"[Object Undefined]" GetType (function () {});//"[Object Function]" GetType (/sf/);//"[Object RegExp]" GetType (null) ;//"[Object Null]" getType (window);//"[Object Window]" GetType ({});//"[Object Object]" GetType ([]);/"[Object Array]" GetType (math);//"[Object Math]"

All types can be distinguished, isn't it simple? This is not the right thing to do, wrong, there is a special value does not handle Nan.

GetType (NaN);//"[Object number]"

Nan is not a number of type #, it is a value that is not a number, and this value is also handled here. You can focus on the previous article
The contents of Nan. The processing code is as follows

function Isreallynan (x) {   return x!==x;}

The full version

function GetType (obj) {   if (obj!==obj) return "NaN";   var tostring=object.prototype.tostring;   return Tostring.call (obj);   
GetType (Nan);//"Nan"

Here we need to understand the knowledge of JS interpreter.
The relevant links are:
A detailed explanation of the JavaScript design pattern in the interpreter mode
JavaScript design mode-interpreter mode (interpreter)
Chrome V8

[Effective JavaScript note] 28th: Do not trust the ToString method of a Function object

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.