[Effective JavaScript 笔记]第28修:不要信赖函数对象的toString方法

js函数有一个别致之特点,即将其源代码重现为字符串的力量。

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

映获取函数源代码的效益异常强大,使用函数对象的toString方法有重的局限性。
toString方法的局限性
ECMAScript标准对函数对象的toString方法的归来结果(即该字符串)并不曾其它要求。这代表不同的js引擎将生不同的字符串,甚至发的字符串与该函数并无相干。

设函数是行使纯js实现的,那么js引擎会试图提供该函数的源代码的诚实表示。

一个败的例证

(function(x){
     return x+1
}).bind(16).toString();//"function () { [native code] }"

黄原因:

动用了由宿主环境之内置库提供的函数。

  • 由于广大宿主环境被,bind函数是出于其余编程语言实现之(通常是c++)。宿主环境提供的是一个编译后底函数,在这个环境下该函数没有js的源代码供显示。

  • 是因为专业允许浏览器引擎改变toString方法的出口,很爱使编写的主次一个js系统中科学运行,在任何js系统中也束手无策正确运行。程序对函数的源代码字符串的切切实实细节颇机智,即使js的落实有好几轻微的别还或破坏程序。

  • 是因为toString方法变的源代码并无显得闭包中保留的及内变量引用相关的值

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

专注:尽管函数实际上是一个绑定x为42之闭包,但结果字符串仍然蕴藏一个引用x的变量。

由某种意义上说,js的toString方法的这些局限而该故来领取函数源代码并无是特地实用和值得信赖。通常应避免使用它。对提取函数源代码相当复杂的应用相应采取精心制作的js解释器和处理库。将js函数看作是一个请勿该违的纸上谈兵是绝稳妥的。

提示

  • 当调用函数的toString方法时,并从未要求js引擎能够规范地收获到函数的源代码

  • 由当不同之引擎下调用toString方法的结果可能两样,所以不用要信赖函数源代码的详细细节

  • toString方法的推行结果连无见面暴露存储于闭包中之有的变量

  • 普普通通情况下,应该避免以函数对象的toString方法

附录一:toString方法

差数据类型调用toString方法的结果。
toString方法是Object原型对象被之一个主意,所以连续自是类似的对象都见面持续这主意,并得以对toString方法开展覆盖。
js标准库中之5种简易数据类型:Undefined,Null,Boolean,Number和String。还有雷同种植复杂的数据类型Object,Object的本来面目是同样组无序的名值对成。

简简单单数据类型

//数字
(Undefined).toString();//"error"
(Null).toString();//error
(true).toString();//"true"
(1).toString();//"1"
('111').toString();//"111"

得视其中除Undefined和Null类型外,为什么其他几独核心型可以运作吧。
立马在我们前面的章《[Effective JavaScript 笔记]
第4长条:原始类型优于封闭对象》中称到,当简单数据类型调用toString方法会首先把老类型转换成包装对象。
这会儿对应的包装对象为

  • 数字为Number对象

  • 布尔值为Boolean对象

  • 字符串为String对象

这些目标啊都是连续自Object对象的,并再次写了独家的toString方法。
可是Undefined类型和Null类型都只来一个值undefined,null,并不曾相应的包装对象。
虽说typeof null的值是”object”,但并没有因此。

援类型

//Object对象
({a:10,b:20}).toString();//"[object Object]"
//Date对象
(new Date).toString();//"Tue Jun 07 2016 15:37:15 GMT+0800 (中国标准时间)"
//RegExp对象
(/^sss$/g).toString();//"/^sss$/g"
//Function对象
function aa(){return "bb"}
aa.toString();//"function aa(){return "bb"}"
//window对象
window.toString();//"[object Window]"
//Math对象
Math.toString();//"[object Math]"

看看地方的toString方法,Object,window,Math是动Object原型方法。其它对象都应用了自家覆盖的toString方法。

typeof操作符

本着上述所有类型应用typeof操作符时会赢得以下的结果

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"

足见见,想采取就的typeof操作符来对品种进行判定几乎是匪容许的。

有人也许会见说对返回object字符串,可以下构造函数来判定项目即instanceOf方法。

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

下一场null类型只要

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

看似得兑现下面这样的档次判断代码了

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';
        }

    }
}

点代码是否可运作测试一下,并没问题

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"

可这边要注意的一个题材就,这个代码里之对object类型的检测一定要是放最后面。
一般来说所示,所有目标还是继承自Object,所以instanceof检测所有目标是不是为Object类型的实例返回都是true

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

顾上述代码是不是道最复杂麻烦了,有无产生雷同栽更简单的道来针对品种进行判定也?答案自然是有,下面来拘禁toString方法的运用。

toString应用

苟上面所说,继承自Object的对象还产生toString方法,但每个对象实现了各自的toString方法,导致力不从心用toString方法进行项目判断。这里可以运用之前讲到了之call或apply方法来调用Object.prototype.toString方法。

function getType(obj){
   var toString=Object.prototype.toString;
   return toString.call(obj);   
}

测试一下个型会得到如下结果

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]"

有品种且得区分出,是勿是十分简短呀!

尚产生个独特之价值需要注意NaN.

getType(NaN);//"[object Number]"

NaN是一个Number类型的特种值,它是达是一个请勿是一个数字的价,这里针对这价也要拓展处理。可以关心之前文章《[Effective
JavaScript笔记]第3长:当心隐式的要挟转换》里关于NaN的内容。处理代码如下

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

 

备忘:

此间要去询问一下,js解释器的学问。
连锁的链接有:
javascript设计模式之解释器模式详解
javascript设计模式 –
解释器模式(interpreter)
Chrome V8