0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

JS强制类型转换

修罗 2021-12-23 1503 0条评论 JS

首页 / 正文

参考自:《你不知道的JS》中卷

  • 将值从一种类型转换为另一种类型通常称为类型转换(type casting),这是显式的情况,隐式的情况称为强制类型转换(coercion)。类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时。
  • JavaScript中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值,不会返回对象和函数。我们能够从代码中看出哪些地方是显式强制类型转换,而隐式强制类型转换则不那么明显,通常是某些操作产生的副作用。

一. 字符串、数字和布尔值之间类型转换的基本规则

1.1 ToString

非字符串转换为字符串。

  • 基本类型转字符串null转换为"null"undefined转换为"undefined"true转换为"true"null和undefined没有toString方法,可以通过String方法转为字符串,如:String(null)
  • 普通对象:除非自定义,否则调用原型的toString方法返回内部属性[[Class]]的值,如"[object Object]"
// 数组重写了toString
var a = [1,2,3];
console.log(a.toString()); // "1,2,3"

1.1.1 JSON.stringify

JSON.stringify(..)将JSON对象序列化为字符串。

JSON.stringify(42); // "42"
JSON.stringify("42"); // ""42""
JSON.stringify(null); // "null"
JSON.stringify(true); // "true"
  • 所有安全的JSON值都可以使用JSON.stringify(..)字符串化。安全的JSON值是指能够呈现为有效JSON格式的值。不安全的JSON值包括undefinedfunctionsymbol和包含循环引用的对象
  • 如果对象中定义了toJSON()方法,JSON字符串化时会首先调用该方法,然后用它的返回值来进行序列化。
    toJSON()返回的应该是一个能够被字符串化的安全的JSON值,可以是任何类型,然后再由JSON.stringify(..)对其进行字符串化。
var a = {
    val:[1,2,3],
    toJSON:function(){
        return this.val.slice(1)
    }
}
JSON.stringify(a) // "[2,3]"

JSON.stringify第二个参数可以为数组或对象,用来指定对象序列化过程哪些属性应该被处理,哪些应该被排除,和toJson很像

var a = {
    b : 42,
    c : "42",
    d : [1,2,3]
};
console.log(JSON.stringify(a,["b","c"])); // "{"b":42,"c","42"}"
console.log(JSON.stringify(a, function(k, v){ if(k != "c") return v })); // "{"b":42,"d",[1,2,3]}"

1.2 ToNumber

非数字值转换为数字值。

  • 基本类型转数字值:其中true转换为1,false转换为0。undefined转换为NaNnull转换为0。
  • 对象类型转数字值:会通过ToPrimitive转换为基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
    ToPrimitive:为了将值转换为相应的基本类型值,抽象操作ToPrimitive会首先检查该值是否有valueOf()方法,如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用toString()的返回值来进行强制类型转换,都没返回基本类型就报错TypeError
var a = {
    valueOf: function(){
        return "42";
    }
};

var b = {
    toString: function(){
        return "42";
    }
};

var c = [4,2];
c.toString = function(){
    return this.join(""); // "42"
};

Number(a); // 42
Number(b); // 42
Number(c); // 42
Number(""); // 0
Number([]); // 0
Number(["abc"]); / /NaN

1.3 ToBoolean

1.3.1 假值

  • undefined
  • null
  • false
  • +0、-0和NaN
  • ""

假值的布尔强制类型转换结果为false

所有的对象都是真值。

var a = new Boolean(false);
var b = new Number(0);
var c = new String("");

var d = Boolean( a && b && c );
console.log(d); // true

1.3.2 真值

真值就是假值列表之外的值。

var a = "false";
var b = "0";
var c = "''"; // 除空字符串,所有字符串都是真值。

var d = Boolean( a && b && c );
console.log(d); // true

var a = []; 
var b = {};
var c = function(){};

var d = Boolean( a && b && c );
console.log(d); // true

二. 显式强制类型转换

2.1 字符串和数字之间的显式转换

// 将数字转换为字符串
var a = 42;
var b = String(a);

// 将字符串转换为数字
var c = "3.14";
var d = Number(c); 

console.log(b); // "42"
console.log(d); // 3.14

// +c显式地将c转换为数字
var e = 5+ +c; 
console.log(e); // 8.14

2.1.1 日期显式转换为数字

var d = new Date("Mon, 18 Aug 2014 08:53:06 CDT");
// +还能将日期类型转时间戳
console.log(+d); // 1408369986000

我们不建议对日期类型使用类型转换,应该使用Date.now()来获得当前的时间戳,使用new Date(..).getTime()来获得指定时间的时间戳。

2.1.2 奇特的~运算符

非运算符,二进制位按位取反

~1计算步骤:

  • 1转二进制 = 00000001
  • 按位取反 = 11111110
  • 发现符号位(即最高位)为1(表示负数),将除符号位之外的其他数字取反 = 10000001
  • 末位加1取其补码 = 10000010
  • 转换回十进制 = -2
// ~x大致等同于-(x+1)。
~42; // -(42+1) ==> -43

// ~和indexOf()一起可以将结果强制转换为真/假值。
// JavaScript中字符串的indexOf(..)方法在字符串中搜索指定的子字符串,如果找到就返回子字符串所在的位置(从0开始),否则返回-1。
var a = "Hello World";

~a.indexOf("lo"); // -4 <-- 真值!

if(~a.indexOf("lo")){ // true
    //找到匹配!
}

// ~-1的结果为0。
~a.indexOf("ol");// 0 <-- 假值!
!~a.indexOf("ol");// true

if(!~a.indexOf("ol")){ // true
    // 没有找到匹配!
}

字位截除

  • 一些开发人员使用~~来截除数字值的小数部分,~~只适用于32位数字。
  • 我们在使用~~~进行此类转换时需要确保其他人也能够看得懂。

2.2 显式解析数字字符串

var a = "42";
var b = "42px";
var c = "3.14"

Number(a); // 42
parseInt(a); // 42

// Number从整体来解析
Number(b); // NaN
// parseInt/parseFloat从左到右解析
parseInt(b); // 42

Number(c); // 3.14
parseInt(c); // 42
parseFloat(c) // 3.14

2.3 显式转换为布尔值

var a = "0";
var b = [];
var c = {};

var d = "";
var e = 0;
var f = null;
var g;

Boolean(a); // true
Boolean(b); // true
Boolean(c); // true

Boolean(d); // false
Boolean(e); // false
Boolean(f); // false
Boolean(g); // false

!!a; // true
!!b; // true
!!c; // true

!!d; // false
!!e; // false
!!f; // false
!!g; // false
  • if(..)..这样的布尔值上下文中,如果没有使用Boolean(..)!!,就会自动隐式地进行ToBoolean转换。
  • 建议使用Boolean(a)!!a来显式强制类型转换。

三. 隐式强制类型转换

3.1 字符串和数字之间的转换

a+b若其中一个类型为字符串,就是字符串拼接。

var a = "42";
var b = "0";

var c = 42
var d = ""

a + b; // "420"
c + d; // "42"

对象类型会通过ToPrimitive转成基本类型再处理

var e = [1,2];
var f = [3,4];

e + f; // "1,23,4"

-符号能将字符串转数字

var i = "3.14";
var j = i - 0;

j; // 3.14

3.2 布尔值到数字的转换

// 参数只有一个true才返回true
function onlyOne(){
    var sum = 0;
    for(var i = 0; i<arguments.length; i++){
        // 跳过假值
        if(arguments[i]){
            sum += arguments[i];
        }
    }
    return sum == 1;
}
var a = true;
var b = false;

console.log(onlyOne(b,a)); // true
console.log(onlyOne(b,a,b,b,b)); // true
console.log(onlyOne(b,b)); // false
console.log(onlyOne(b,a,b,b,b,a)); // false

3.3 隐式强制类型转换为布尔值

下面的情况会发生布尔值隐式强制类型转换:

  1. if(..)语句中的条件判断表达式。
  2. for(..;..;..)语句中的条件判断表达式。
  3. while(..)do..while(..)循环中的条件判断表达式。
  4. ? :中的条件判断表达式。
  5. 逻辑运算符||(逻辑或)与&&(逻辑与)的操作数。

3.4 || 和&&

和其他语言不同,在JavaScript中||&&的返回值是两个操作数中的一个。C和php中返回true或false。

var a = 42;
var b = "abc";
var c = null;

a || b; // 42
a && b; // "abc"

c || b; // "abc"
c && b; // null

// a || b 相当于 a ? a : b
// a && b 相当于 a ? b : a
  • 对于||来说,如果条件判断结果为true就返回第一个操作数的值,如果为false就返回第二个操作数的值。
  • 对于&&来说,如果条件判断结果为true就返回第二个操作数的值,如果为false就返回第一个操作数的值。

四. 宽松相等和严格相等

  • 常见的误区是宽松相等==检查值是否相等,严格相等===检查值和类型是否相等。准确的解释是:
  • ==允许在相等比较中进行强制类型转换,而===不允许。

4.1 抽象相等

4.1.1 字符串和数字之间的相等比较

字符串会转成数字再比较

var a = 42;
var b = "42"

a == b; // true

4.1.2 其他类型和布尔类型之间的相等比较

boolean会转成数字再比较

var a = "42";

// 不要这样用,条件判断不成立
// true首先转为1,a转为42
if(a == true){ }

4.1.3 null和undefined之间的相等比较

null和undefined两者互相宽松比较才会返回true,任何变量和null或undefined比较都会返回false。

var a = null;
var b;

// null == undefined
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

4.1.4 对象和非对象之间的相等比较

如下代码,a == b结果为true,因为b通过ToPromitive进行强制类型转换(也称为"拆封"),并返回标量基本类型值"abc",与a相等。

var a = "abc";
var b = Object(a);

a === b;// false
a == b; // true 

如下代码,因为nullundefined没有对应的封装对象,所以不能够被封装。
NaN能够被封装为数字封装对象,但拆封之后NaN == NaN返回false,因为NaN不等于NaN

var a = null;
var b = Object(a);
a == b; // false

var c = undefined;
var d = Object(c);
c == d; // false

var e = NaN;
var f = Object(a);
e == f; // false

4.1.5 对象和对象之间的相等比较

不管是宽松相等比较还是严格比较都比较两者的地址值

4.2 容易头疼的比较

'0' == false; // true
false == 0; // true
false == ''; // true
false == []; // true
'' == 0; // true
'' == []; // true
[] == ![]; // true, 首先进行![]的隐式转换,先将`[]`转换成布尔值,为`true`,然后取反是`false`,而`[] == false`是true

'' == [null]; // true, 注意`[null]`字符串话后是空字符串`''`
0 == '\n'; // true; 原因在于: ''或'\n'等空字符串在`ToNumber`的过程中被转换成`0`

4.2.1 安全运用隐式强制类型转换

  • 如果两边的值中有true或者false,千万不要使用==
  • 如果两边的值中有[]""或者0,尽量不要使用==

这时最好用===来避免不经意的强制类型转换。这两个原则可以让我们避开几乎所有强制类型转换的坑。

五. 抽象关系比较

var a = [42];
var b = ["43"];

a < b; // true
b < a; // false

var c = ["42"];
var d = ["043"];
c < d; // false

var e = [4,2];
var f = [0,4,3];

e < f; // false
// e转换为"4,2",f转换为"0,4,3",按字母顺序进行比较

var a = {b:42};
var b = {b:43};

a < b; // false
a == b; // false
a > b; // false

a <= b; // true
a >= b; // true
// 根据规范a <= b被处理为b < a,然后将结果反转,因为b < a的结果是false,所以a <= b的结果是true。

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis