Javascript中的类型和语法(五) -- 强制类型转换

1)字符串,数字的转换

var a = 42;
var b = a.toString();//实际上先将a转化为封转对象再调用toString方法
var c = "3.14";
var d = +c;//+c 是 + 运算符的一元(unary)形式(即只有一个操作数)。
//+ 运算符显式地将 c 转换为数字,而非数字加法运算
b; // "42"
d; // 3.14

//我们常用下面的方法来获得当前的时间戳,三种方式结果一致
var timestamp = +new Date();
//另一种方式
var timestampOld = new Date().getTime();
//用 ES5 中新加入的静态方法 Date.now():
var timestampNew = Date.now();

2)位运算符

字位运算符只适用于 32 位整数,运算符会强制操作数使用 32 位格式。这是通过抽象操作 ToInt32 来实现的(ES5 规范 9.5 节)。
按位或|:转换二进制,有1则1,都0则0.
按位与&:转换二进制,有0则0,都1则1.
按位非~:js之中,简言之,就是(原码+1)的相反数
按位异或^:转换二进制,不同则1,相同则0.

// 按位或| , 按位与& , 按位非~ , 按位异或^
0 | -0; // 0
0 | NaN; // 0
0 | Infinity; // 0
0 | -Infinity; // 0

var a = "Hello World";
~a.indexOf( "lo" ); // -4 <-- 真值!
if (~a.indexOf( "lo" )) { // true
 // 找到匹配!
}
~a.indexOf( "ol" ); // 0 <-- 假值!
!~a.indexOf( "ol" ); // true
if (!~a.indexOf( "ol" )) { // true
 // 没有找到匹配!
}
//如果 indexOf(..) 返回 -1,~ 将其转换为假值 0,其他情况一律转换为真值。

字位截除:
~~x 能将值截除为一个 32 位整数,x | 0 也可以,而且看起来还更简洁。
出于对运算符优先级的考虑,我们可能更倾向于使用 ~~x:

Math.floor( -49.6 ); // -50
~~-49.6; // -49

3)解析数字字符串:

  • 解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回 NaN。
  • 解析字符串中的浮点数可以使用 parseFloat(…) 函数。
  • 从 ES5 开始 parseInt(…) 默认转换为十进制数,除非另外指定。如果你的代码需要在 ES5
    之前的环境运行,请记得将第二个参数设置为 10。
//parseInt遇到无法解析为数字的字符会自动终止解析
var a = "42";
var b = "42px";
Number( a ); // 42
parseInt( a ); // 42
Number( b ); // NaN
parseInt( b ); // 42

//疑难点
parseInt(1/0, 19);//18 实际上是 parseInt("Infinity", 19)。第一个字符是 "I",以 19 为基数
//时值为 18。第二个字符 "n" 不是一个有效的数字字符,解析到此为止,和 "42px" 中的 "p"一样。

parseInt( 0.000008 ); // 0 ("0" 来自于 "0.000008")
parseInt( 0.0000008 ); // 8 ("8" 来自于 "8e-7")
parseInt( false, 16 ); // 250 ("fa" 来自于 "false")
parseInt( parseInt, 16 ); // 15 ("f" 来自于 "function..")
parseInt( "0x10" ); // 16
parseInt( "103", 2 ); // 2

4)运算强制转换

4.1)数字<==>字符串:

var a = [1,2];
var b = [3,4];
a + b; // "1,23,4"
/**如果其中一个操作数是对象(包括数组),则首先对其调用ToPrimitive 抽象操作(规范 9.1 节),该抽象操作再调用 [[DefaultValue](规范 8.12.8节),以数字作为上下文。
因为数组的valueOf() 操作无法得到简单基本类型值,于是它转而调用 toString()。因此上例中的两
个数组变成了 "1,2" 和 "3,4"。+ 将它们拼接后返回 "1,23,4"。**/

a + “”(隐式)和前面的 String(a)(显式)之间有一个细微的差别需要注意。根据ToPrimitive 抽象操作规则,a + “” 会对 a 调用 valueOf() 方法,然后通过 ToString 抽象操作将返回值转换为字符串。而 String(a) 则是直接调用 ToString()。

var a = {
  valueOf: function() { return 42; },
  toString: function() { return 4; }
};
a + ""; // "42"
String( a ); // "4"

var a = [3];
var b = [1];
a - b; // 2 因为数组对象的valueOf不是简单类型,所以会接着调用toString再转换成数字进行运算

4.2)布尔值:
4.2.1)布尔值==>数字:
true->1, false->0
实现:所有输入参数仅有一个为true,则返回true的函数

function onlyOne() {
	var sum = 0;
	for (var i=0; i < arguments.length; i++) {
		// 跳过假值,和处理0一样,但是NaN还是会出现
		if (arguments[i]) {
			sum += arguments[i];
		}
		//也可以这么写 sum += Number( !!arguments[i] ); 结果不会出现NaN
	}
	return sum == 1;
}
var a = true;
var b = false;
onlyOne( b, a ); // true
onlyOne( b, a, b, b, b ); // true
onlyOne( b, b ); // false

4.2.2)哪些条件会隐式转换成布尔值:
(1) if (…) 语句中的条件判断表达式。
(2) for ( … ; … ; … ) 语句中的条件判断表达式(第二个)。
(3) while (…) 和 do…while(…) 循环中的条件判断表达式。
(4) ? : 中的条件判断表达式。
(5) 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。

5) || 与 &&:

将它们称为“逻辑运算符”,因为这不太准确。称它们为“选择器运算符”(selector operators)或者“操作数选择器运算符”(operand selector operators)更恰当些。
因为和其他语言不同,在 JavaScript 中它们返回的并不是布尔值。它们的返回值是两个操作数中的一个(且仅一个)。即选择两个操作数中的一个,然后返回它的值。

“||” 的结果会取第一个真值。"&&" 的结果会取第一个假值。
“||”("&&")出现真值(假值)则发生短路(终止运算,得出结果)

/**给a,b指定默认值的一种操作**/
function foo(a,b) {
 a = a || "hello";
 b = b || "world";
 console.log( a + " " + b );
}
foo(); // "hello world"
foo( "yeah", "yeah!" ); // "yeah yeah!"

/**||,&& 与 三目运算**/
a || b;
// 大致相当于:
a ? a : b;

a && b;
// 大致相当于:
a ? b : a;
//为什么是大致相当?如果 a 是一个复杂一些的表达式(比如有副作用的函数调用等),
//它有可能被执行两次(如果第一次结果为真)。

//这里 a && (b || c) 的结果实际上是 "foo" 而非 true,
//然后再由 if 将 foo 强制类型转换为布尔值,所以最后结果为 true。
var a = 42;
var b = null;
var c = "foo";
if (a && (b || c)) {
  console.log( "yep" );
}

6)符号的强制转换

//符号不能够被强制类型转换为数字(显式和隐式都会产生错误),
//但可以被强制类型转换为布尔值(显式和隐式结果都是 true),也可以转换为string。
var s1 = Symbol( "cool" );
String( s1 ); // "Symbol(cool)"
var s2 = Symbol( "not cool" );
s2 + ""; // TypeError

7)== (宽松相等) 与 ===(严格相等):

正确的解释是:“== 允许在相等比较中进行强制类型转换,而 === 不允许。”
7.1) == 与 === 的性能比较
实际上虽然"=="操作中强制类型转换确实要多花点时间,但仅仅是微秒级(百万分之一秒)的差别而已。 == 和 === 都会检查操作数的类型。区别在于操作数类型不同时它们的处理方
式不同。
7.2) 各个类型相等比较的细节
7.2.1)字符串和数字之间的相等比较:
(1) 如果 Type(x) 是数字,Type(y) 是字符串,则返回 x == ToNumber(y) 的结果。
(2) 如果 Type(x) 是字符串,Type(y) 是数字,则返回 ToNumber(x) == y 的结果。
7.2.2) 其他类型和布尔类型之间的相等比较:
(1) 如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果;
(2) 如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。
7.2.3)null 和 undefined 之间的相等比较:
(1) 如果 x 为 null,y 为 undefined,则结果为 true。
(2) 如果 x 为 undefined,y 为 null,则结果为 true。
7.2.4)对象和非对象之间的相等比较:
(1) 如果 Type(x) 是字符串或数字(布尔值会先被强制类型转换为数字),Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果;
(2) 如果 Type(x) 是对象,Type(y) 是字符串或数字(布尔值会先被强制类型转换为数字),则返回 ToPromitive(x) == y 的结果。
7.2.5)对象和对象之间的相等比较:
就是纯粹的引用比较了,地址相同则相同。

/**字符串和数字比较**/
var a = 42;
var b = "42";
a === b; // false
a == b; // true
/**会对b进行ToNumber处理,最后进行两个数字的比较**/

/**其他类型和布尔值**/
var a = "42";
var b = true;
a == b; // false b会进行ToNumber变为1,"42"再转换成 42 和之前的 1 进行比较
/**Type(x) 是布尔值,所以 ToNumber(x) 将 true 强制类型转换为 1,变成 1 == "42",
二者的类型仍然不同,"42" 根据规则被强制类型转换为 42,最后变成 1 == 42,结果为 false。**/

/**null 和 undefined**/
var a = null;
var b;
a == b; // true
a == null; // true
b == null; // true

a == false; // false b == false;也为false
a == ""; // false b == "";也为false
a == 0; // false b == 0;也为false
if (a == null) {//仅在a为null或者undefined的时候成立
 // ..
}
/*******************/

/**对象 和 非对象**/
//[ 42 ] 首先调用 ToPromitive 抽象操作,返回 "42",变成 "42" == 42,
//然后又变成 42 == 42,最后二者相等。
var a = 42;
var b = [ 42 ];
a == b; // true

var a = "abc";
var b = Object( a ); // 和new String( a )一样
a === b; // false
a == b; // true

//因为没有对应的封装对象,所以 null 和 undefined 不能够被封装(boxed),
//Object(null)和 Object() 均返回一个常规对象。
var a = null;
var b = Object( a ); // 和Object()一样
a == b; // false Object()和null不等
var c = undefined; 
var d = Object( c ); // 和Object()一样
c == d; // false Object()和undefined不等

var e = NaN; 
var f = Object( e ); // 和new Number( e )一样
e == f; // false 因为NaN和自身不相等

7.3) 一些特殊例子
“”、"\n"(或者 " " 等其他空格组合)等空字符串被 ToNumber 强制类型转换
为 0。

Number.prototype.valueOf = function() {
  return 3;
};
new Number( 2 ) == 3; // true, Number转换会经过ToPrimitive操作因此最后是3 == 3

var i = 2;
Number.prototype.valueOf = function() {
	return i++;
};
var a = new Number( 42 );
if (a == 2 && a == 3) {
	console.log( "Oops, this happened." ); //a == 2的比较中会进行ToPrimitive操作
}

false == ""; // true false会进行ToNumber转换为0,Number("")===0,所以最后为0==0
"" == []; // true []进行ToPrimitive转为[].toString为空串 因此相等
0 == []; //true []进行ToPrimitive转为[].toString为空串,再ToNumber与0比较
		//因为Number([].toString()) === 0 ,所以最后为true

[] == ![] // true 根据 ToBoolean 规则,它会进行布尔值的显式强制类型转换。
//所以 [] == ![] 变成了 [] == false。
// false == []

var a = [ 4, 2 ];
var b = [ 0, 4, 3 ];
a < b; // false
//a 转换为 "4, 2",b 转换为 "0, 4, 3",同样是按字母顺序进行比较
var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a <= b; // true
a >= b; // true
//a < b 和 a == b 结果为 false,为什么 a <= b 和 a >= b 的结果会是 true 呢?
//因为根据规范 a <= b 被处理为 b < a,然后将结果反转。因为 b < a 的结果是 false,所以 a <= b 的结果是 true。
//这可能与我们设想的大相径庭,即 <= 应该是“小于或者等于”。实际上 JavaScript 中 <= 是
//“不大于”的意思(即 !(a > b),处理为 !(b < a))。同理 a >= b 处理为 b <= a。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值