1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > char强制类型转换为int_为强制类型转换正名

char强制类型转换为int_为强制类型转换正名

时间:2019-04-05 00:10:47

相关推荐

char强制类型转换为int_为强制类型转换正名

为强制类型转换正名​

引子

强制类型转换是JavaScript开发人员最头疼的问题之一, 它常被诟病为语言设计上的一个缺陷, 太危险, 应该束之高阁.

作为开发人员, 往往会遇到或写过涉及到类型转换的代码, 只是我们从来没有意识到.

猜猜看:

作为基本类型值, 为什么我们可以使用相关的属性或方法? eg:'hello'.charAt(0)(参见(#内置类型和内建函数的关系))a && (b || c)这波操作我们知道, 那么if (a && (b || c)), 这里又做了哪些操作? (参见(#条件判断))if (a == 1 && a== 2) { dosomething },dosomething竟然执行了, 什么鬼? (参见(#ToPrimitive))[] == ![]=> true ?;false == []=> true ?;"0" == false=> true ?(参见(#抽象相等))if (~indexOf('a')), 这波操作熟悉不? (参见(#隐式强制类型转换))在String,Number,Boolean类型之间比较时, 进行的强制类型转换又遵循了哪些规则? (参见(#抽象操作))

掌握了JavaScript强制类型转换的抽象操作, 以上的问题都是小儿科.

滑至文章底部可直接看栗子 .掘金Markdown部分语法不支持, 可点此阅读原文.

(以下内容是以《你不知道的JavaScript》强制类型转换部分为大纲, 并参照ref="https://www.ecma-/ecma-262/5.1/">ecma 规范撰写.)

类型

内置类型

JavaScript 有七种内置类型.空值: null,未定义: undefined,布尔值: boolean,数字: number,字符串: string,对象: object,符号: symbol. 除对象:object, 为复杂数据类型, 其它均为基本数据类型.

内建函数

常用的内建函数:String(),Number(),Boolean(),Array(),Object(),Function(),RegExp(),Date(),Error(),Symbol().

内置类型和内建函数的关系

为了便于操作基本类型值, JavaScript提供了封装对象(内建函数), 它们具有各自的基本类型相应的特殊行为. 当读取一个基本类型值的时候, JavaScript引擎会自动对该值进行封装(创建一个相应类型的对象包装它)从而能够调用一些方法和属性操作数据. 这就解释了问题 1.

类型检测

typeof=> 基本类型的检测均有同名的与之对应.null除外, null是假值, 也是唯一一个typeof检测会返回'object'的基本数据类型值.

typeof null // "object"let a = null;(!a && typeof a === 'object') // true

复杂数据类型typeof检测返回'object', function(函数)除外. 函数因内部属性[[Call]]使其可被调用, 其实属于可调用对象.

typeof function(){} // "function"

Object.prototype.toString=> 通过typeof检测返回'object'的对象中还可以细分为好多种, 从内建函数就可以知道.它们都包含一个内部属性[[Class]], 一般通过Object.prototype.toString(...)来查看.

const str = new String('hello');const num = new Number(123);const arr = new Array(1, 2, 3);console.log(Object.prototype.toString.call(str))console.log(Object.prototype.toString.call(num))console.log(Object.prototype.toString.call(arr))// [object String]// [object Number]// [object Array]

抽象操作

在数据类型转换时, 处理不同的数据转换都有对应的抽象操作(仅供内部使用的操作), 在这里用到的包括ToPrimitive,ToString,ToNumber,ToBoolean(详见ecma规范). 这些抽象操作定义了一些转换规则, 不论是显式强制类型转换, 还是隐式强制类型转换, 无一例外都遵循了这些规则. 这里就解释了问题 5问题 6.

ToPrimitive

该抽象操作是将传入的参数转换为非对象的数据. 当传入的参数为 Object 时, 它会调用内部方法[[DefaultValue]]遵循一定规则返回非复杂数据类型, 规则详见(ecma规范: DefaultValue). 故ToString,ToNumber,ToBoolean在处理Object时, 会先经过ToPrimitive处理返回基本类型值.

[[DefaultValue]](hint)语法:

[[DefaultValue]]的规则会依赖于传入的参数hint,ToString传入的hint值为String,ToNumber传入的hint值为Number.

[[DefaultValue]](String)=> 若toString可调用, 且toString(Obj)为基本类型值, 则返回该基本类型值. 否则, 若valueOf可调用, 且valueOf(Obj)为基本类型值, 则返回该基本类型值. 若以上处理还未得到基本类型值, 则抛出TypeError.[[DefaultValue]](Number)=> 该规则正好和上规则调用toString,valueOf的顺序相反. 若valueOf可调用, 且valueOf(Obj)为基本类型值, 则返回该基本类型值. 否则, 若toString可调用, 且toString(Obj)为基本类型值, 则返回该基本类型值. 若以上处理还未得到基本类型值, 则抛出TypeError.[[DefaultValue]]()=> 未传参时, 按照hint值为Number处理.Date对象除外, 按照hint值为String处理.

现在我们就用以上的知识点来解释问题 3是什么鬼.

let i = 1;Number.prototype.valueOf = () => {return i++};let a = new Number("0"); // 字符串强制转换为数字类型是不执行Toprimitive抽象操作的.console.log('a_1:', a);if(a == 1 && a == 2) {console.log('a==1 & a==2', 'i:', i);}// a==1 & a==2 i: 3

我们改写了内建函数Number原型上的valueOf方法, 并使得一个字符串转换成Number对象, 第一次Object类型和Number类型做比较时,Object类型将进行ToPrimitive处理(参见(#抽象相等)), 内部调用了valueOf, 返回2. 第二次同样的处理方式, 返回3.

ToString

该抽象操作负责处理非字符串到字符串的转换.

ToNumber

该抽象操作负责处理非数字到数字的转换.

常见的字符串转换数字:

字符串是空的 => 转换为0.字符串只包含数字 => 转换为十进制数值.字符串包含有效的浮点格式 => 转换为对应的浮点数值.字符串中包含有效的十六进制格式 => 转换为相同大小的十进制整数值.字符串中包含除以上格式之外的符号 => 转换为 NaN.

ToBoolean

该抽象操作负责处理非布尔值到布尔值转换.

真值&假值

假值(强制类型转换false的值) =>undefined,null,false,+0,-0,NaN,"". 真值(强制类型转换true的值) => 除了假值, 都是真值.

特殊的存在

假值对象 => documen.all 等. eg:Boolean(window.all)// false

隐式强制类型转换

+/-/!/~

+/- 一元运算符=> 运算符会将操作数进行ToNumber处理.!=> 会将操作数进行ToBoolean处理.~=> (~x)相当于 -(x + 1) eg: ~(-1) ==> 0; ~(0) ==> 1; 在if (...)中作类型转换时, 只有-1时, 才为假值.+加号运算符=> 若操作数有String类型, 则都进行ToString处理, 字符串拼接. 否则进行ToNumber处理, 数字加法.

条件判断

if (...),for(;;;),while(...),do...while(...)中的条件判断表达式.? :中的条件判断表达式.||&&中的中的条件判断表达式.

以上遵循ToBoolean规则.

||和&&

返回值是两个操作数的中的一个(且仅一个). 首先对第一个操作数条件判断, 若为非布尔值则进行ToBoolean强制类型转换.再条件判断.||=> 条件判断为true, 则返回第一个操作数; 否则, 返回第二个操作数. 相当于 a ? a : b;&&=> 条件判断为true, 则返回第二个操作数; 否则, 返回第一个操作数, 相当于 a ? b : a;

结合条件判断, 解释下问题 2

let a = true;let b = undefined;let c = 'hello';if (a && (b || c)) {dosomething()}a && (b || c) 返回 'hello', if语句中经Toboolean处理强制类型转换为true.

抽象相等

这里的知识点是用来解释问题 4的, 也是考验人品的地方. 这下我们要靠实力拼运气.

同类型的比较.+0 == -0 // true null == null // true undefined == undefined // true NaN == NaN // false, 唯一一个非自反的值null和undefined的比较.null == undefined // true undefined == null // trueNumber类型和String类型的比较. =>String类型要强制类型转换为Number类型, 即ToNumber(String).(参见(#ToNumber))Boolean类型和其它类型的比较. =>Boolean类型要强制类型转换为Number类型, 即ToNumber(Boolean).(参见(#ToNumber))Object类型和String类型或Number类型. =>Object类型要强制转换为基本类型值, 即ToPrimitive(Object).(参见(#ToPrimitive))其它情况,false.

回头看看问题 4中的等式.[] == ![],false == [],"0" == false.[] == ![]=>!操作符会对操作数进行ToBoolean处理,[]是真值,!true则为false. 再遵循第4点,Boolean类型经过ToNumber转换为Number类型, 则为数值0. 再遵循第5点, 对[]进行ToPrimitive操作, 先后调用valueOf(),toString()直到返回基本类型, 直到返回"".(先[].valueOf() => [], 非基本类型值; 再[].toString() => "", 基本类型值, 返回该基本类型值.). 再遵循第3点, 对""进行ToNumber处理, 则为数值0. 到此,0 == 0, 再遵循第1点(其实没写全 , 详见(详见ecma规范: The Abstract Equality Comparison Algorithm)), return true, 完美! .false == []=> 同理[] == ![]."0" == false=> 同理[] == ![].

[] == ![] // truefalse == [] // true"0" == false // true

over!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。