JavaScript 类型转换深度学习

wpzd6657 8年前
   <p><img src="https://simg.open-open.com/show/9a2c396a91bd0ece6ff952ac386c2ce9.jpg"></p>    <p>JavaScript 是一门弱类型语言,刚接触的时候感觉方便快捷(不需要声明变量类型了耶!),接触久了会发现它带来的麻烦有的时候不在预期之内</p>    <p>呵呵一笑,哪有这么夸张,可能有人看过这样一段代码</p>    <pre>  <code class="language-javascript">[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()  </code></pre>    <p>这个占了好大的篇幅哈 3167 个字符,粘贴到浏览器的 Console 控制台,直接弹出了 orange,随叫随到有不有</p>    <p>JSFuck 的变态程度达到了极致,因为它的理念是 Write any JavaScript with 6 Characters: []()!+</p>    <p>或许又有人说:这个只是搞怪的吧,实际谁这么写代码啊</p>    <p>说的没错,当一段代码变得晦涩难懂的时候,甚至到上文的混乱字符(天书),却能实现任意功能这就变得不可预期,也就是说 JS 代码的安全性没有保障</p>    <p>当然本文不会研究这些无意义的字符原理是怎么实现的因为人家的 Github 文档已经描述的特别全面了,感兴趣的可以研究下。</p>    <p>我们聊一聊每天能看到用到的方法底层是怎么解析的,熟知转换分成两种一种是隐式转换,另一种是强制的类型转换</p>    <h2><strong>隐式转换</strong></h2>    <p>当遇到以下几种情况,JavaScript会自动转换数据类型:</p>    <ul>     <li> <p>不同类型的数据进行互相运算</p> </li>     <li> <p>对非布尔值类型的数据求布尔值</p> </li>     <li> <p>对非数值类型的数据使用一元运算符(即 “+” 和 “-“)</p> </li>    </ul>    <h3><strong>隐式转换为 Boolean</strong></h3>    <p>大多数在做 if 判断时会用到,这里只需记住六个转换为 false,其它全部为 true</p>    <ul>     <li> <p>null</p> </li>     <li> <p>undefined</p> </li>     <li> <p>NaN</p> </li>     <li> <p>‘’</p> </li>     <li> <p>-0</p> </li>     <li> <p>+0</p> </li>    </ul>    <h3><strong>隐式转换为 String</strong></h3>    <p>字符串的自动转换,主要发生在加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。</p>    <pre>  <code class="language-javascript">'1' + 2  // '12'  '1' + true  // "1true"  '1' + false  // "1false"  '1' + {}  // "1[object Object]"  '1' + []  // "1"  '1' + function (){}  // "1function (){}"  '1' + undefined  // "1undefined"  '1' + null  // "1null"  </code></pre>    <h3><strong>隐式转换为 Number</strong></h3>    <p>除了加法运算符有可能把运算子转为字符串,其他运算符都会把两侧的运算子自动转成数值</p>    <pre>  <code class="language-javascript">'5' - '2'  // 3  '5' * '2'  // 10  true - 1  // 0  false - 1  // -1  '1' - 1  // 0  '5' * []  // 0  false / '5'  // 0  'abc' - 1  // NaN  +'abc'  // NaN  -'abc'  // NaN  +true  // 1  -false  // 0  </code></pre>    <p>隐式转换的基础表现都在这了,强调的是这些转换的背后都伴随着强制转换,使用 Boolean、Number 和 String,下面重点讲一下强制转换的原理</p>    <h2><strong>强制转换</strong></h2>    <p>看到上面例子也许你已经有些许疑问了,比如上面的这个 '1' + {} 怎么就输出 1[object Object] 了呢</p>    <p>如上面强调的,你会猜测首先执行 String({}) 得到 "[object Object]" ,然后再字符串拼接,是的我们总能得到转换背后的实现原理,其实真实原理要比这个复杂,见下文</p>    <h3><strong>强制转换为 Boolean</strong></h3>    <p>这里略过因为与隐式转换相同,切记 []、{} 都转换成 true</p>    <h3><strong>强制转换为 String</strong></h3>    <p>基本类型的转换结果与隐式转换相同,这里说一下对象的转换,加深上面引用例子的解析</p>    <p>对象转换字符串分成三步</p>    <ol>     <li> <p>先调用toString方法,如果toString方法返回的是原始类型的值,则对该值使用String方法,不再进行以下步骤</p> </li>     <li> <p>如果toString方法返回的是复合类型的值,再调用valueOf方法,如果valueOf方法返回的是原始类型的值,则对该值使用String方法,不再进行以下步骤</p> </li>     <li> <p>如果valueOf方法返回的是复合类型的值,则报错</p> </li>    </ol>    <p>再分解这个例子</p>    <pre>  <code class="language-javascript">String({})  // "[object Object]"  </code></pre>    <p>上面代码相当于下面这样</p>    <pre>  <code class="language-javascript">String({}.toString())  // "[object Object]"  </code></pre>    <p>如果 toString 方法和 valueOf 方法,返回的都不是原始类型的值,则 String 方法报错</p>    <pre>  <code class="language-javascript">var obj = {    valueOf: function () {      console.log("valueOf");      return {};    },    toString: function () {      console.log("toString");      return {};    }  };    String(obj)  // TypeError: Cannot convert object to primitive value  </code></pre>    <p>我们不难看出可以对 toString 方法和 valueOf 方法进行改写,测试其先后运行的顺序也简单的多</p>    <pre>  <code class="language-javascript">String({toString:function(){return 3;}})  // "3"    String({valueOf:function (){return 2;}})  // "[object Object]"    String({valueOf:function (){return 2;},toString:function(){return 3;}})  // "3"  </code></pre>    <p>结果表示toString方法先于valueOf方法执行</p>    <h3><strong>强制转换为 Number</strong></h3>    <p>基本类型转换如下</p>    <pre>  <code class="language-javascript">Number("123") // 123    Number("123abc") // NaN    Number("") // 0    Number(false) // 0    Number(undefined) // NaN    Number(null) // 0  </code></pre>    <p>对象转换一样要复杂些,与 String 唯一不同的就是 valueOf 方法在前, toString 方法在后,其它不赘述见上文例子。</p>    <p>isNaN() 并不陌生, isNaN({}) //true 的内在转换过程是相同的</p>    <h2><strong>总结</strong></h2>    <p>其它的转换原则还有很多,看到这我们还是不能解释文章开始的代码转换的过程,掌握这些更多是保证正常书写代码规避错误的发生,十分好奇的可以研究下比较特殊的转化原则,还有好多好多。</p>    <p> </p>    <p>来自:http://orangexc.xyz/2016/10/31/JavaScript-type-conversion-depth-learning/</p>    <p> </p>