详细理解javascript中的强制类型转换

  将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况;隐式的情况称为强制类型转换,JavaScript 中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值。

  如何理解: 类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时?

  1、如果是静态语言,比如c等,所有的类型转换应该都是在编译阶段处理的吧?

  2、如果是动态语言,如js等, 编译阶段会处理类型转换吗?

一、ToString

  它负责处理非字符串到字符串的强制类型转换

  数字的字符串化遵循通用规则,

  数组的默认toString()方法经过了重新定义

  Json字符串化

  说明:JSON.stringify()在将JSON对象序列化为字符串时也用到了ToString,但是, JSON字符串化并非严格意义上的强制类型转换:

1、对多数简单值来说,JSON字符串化和toString()效果基本相同,只不过序列化的结果总是字符串,所有安全的JSON值都可以使用JSON.stringify()字符串化, 安全的JSON值是指能够呈现为有效JSON格式的值

JSON.stringify(42); // "42"
JSON.stringify("42"); // ""42""
JSON.stringify(null); // "null"
JSON.stringify(true); // "true"

2、不安全的JSON值

undefined,function,symbol和包含循环引用的对象都不符合JSON结构标准, JSON.stringify()在对象中遇到undefined,function,symbol时会自动将其忽略, 在数组中则会返回null

   JSON.stringify(undefined); undefined
    JSON.stringify(function(){}); // undefined
    JSON.stringify([1, undefined, function(){}, 4]); // "[1, null, null, 4]"
    JSON.stringify({a: 2, b: function(){}}); // "{"a": 2}""

  对包含循环引用的对象执行JSON.stringify()会报错

3、如果对象中定义了toJSON方法, 那么在调用JSON.stringify之前会默认的隐式调用该方法,然后对该方法的返回值使用stringify,如果要对含有非法JSON值的对象做字符串化,可以使用toJSON来返回一个安全的JSON值,然后在stringify中对其JSON字符串化

var o = { };
var a = {
    b: 42,
    c: o,
    d: function(){}
};
// 在a中创建一个循环引用
o.e = a;

// 循环引用在这里会产生错误
// JSON.stringify( a );

// 自定义的JSON序列化
a.toJSON = function() {
    // 序列化仅包含b
    return { b: this.b };
};
JSON.stringify( a ); // "{"b":42}"

4、JSON.stringify()中还可以传入第二个参数,这个参数是一个数组或者是函数,用来说明在序列化过程中哪些属性应该被处理:

(1)如果是一个数组,那么应该是一个包含对象中需要处理的属性的字符串数组

(2)如果是一个函数,那么它会对对象本身处理一次,然后对对象中的各个属性分别处理一次

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

  这个函数中需要传递两个参数:键k和值v, 参数k在第一次调用时为undefined

二、ToNumber

  将非数字值当作数字来使用

  其中 true转换为1, false转换为0, undefined转换为NaN,null转换为0,对字符串的处理遵循数字常量的相关规则, 处理失败时返回NaN

1、对象如何转换为数字:先转换为对应的基本类型,然后再强制转换为数字

  先检查是否有valueOf方法,如果有并且返回的是基本类型的值, 则使用该值进行强制类型转换,

  如果没有valueOf()或者其返回的不是基本值,就使用toString()方法的返回值来进行强制类型转换

  如果都没有valueOf()和toString(),则会产生TypeError错误

  所以, 使用Object.create(null)创建的对象是不能进行强制类型转换的,例如:

var obj = Object.create(null);
obj == 1; //Uncaught TypeError: Cannot convert object to primitive value
obj.valueOf = function(){return 1;}
obj == 1; // true

三、ToBoolean

  在这一部分意识到了之前的一个误区:先说明一下:

  我们知道

‘‘==false// true
‘‘==true; //false

  但是如果字符串非空呢?

  之前的错误想法是‘aa‘==true返回为true,但后来经过测试发现我错了,aa==true和aa==false返回的都是false

  后来想了一下原因: aa在进行比较时会先转换为数字,’aa’转为数字是NaN, 所以返回的结果是false

  言归正传:

  JS中, 1和true,0和false可以相互转换,但它们并不是一回事:

可以被强制类型转换为false的值:

undefined

null

false

+0, -0, NaN

""

假值对象

  浏览器在某些情况下在常规的js语法之外创建的一些外来的值,这些值就是假值对象,在将它们强制类型转换为布尔值时结果就是false,eg::

document.all == false// false

document.all == true// false

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

  目前应该只有IE里面下面代码会打印1, 在别的浏览器里面会打印2

if(document.all){
    console.log(1);
}else{
    console.log(2);
}

真值

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

var a = ‘false‘
var b = ‘0‘
var c = ‘""‘

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

四、显示强制类型转换

1、字符串和数字之间的显式强制转换

  字符串和数字之间的强制转换是通过String和Number函数进行,除此之外也有别的方式:

var a = 42;
var b = a.toString();

var c = ‘3.14‘;
var d = +c; // ‘+‘或者‘-‘运算符单独使用都可以将操作数转换为数值,区别在于‘-‘还会改变操作数的符号

b; //"42";
c; // 3.14

var c = ‘3.14‘;
var d = 5+ +c; // 由于‘++‘会被当作自加运算符处理, 所以应该在中间加一个空格‘+ +‘
d; // 8.14

  +c将其转换成了数值

2、日期转换为数字

  +运算符还有一个作用是可以将日期对象转化为数字,返回的是Unix时间戳,以微秒为单位

  js中作为构造函数的函数如果没有参数,可以不带括号的,eg:var a = +new Date,此外还可以使用:

var timestamp = (new Date).getTime()
或者:
var timestamp = Date.now() //(推荐的用来获取当前时间戳的方式)

3、显示解析数字字符串

  解析字符串中的数字和将字符串强制类型转换为数字是有区别的

    var a = ‘42‘;
    var b = ‘42px‘;

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

    Number(b); // NaN
    parseInt(b); // 42

  解析,顾名思义,用的是parseInt或者parseFloat,

  强制转换, 使用的则是Number函数

  解析需要传入的参数是字符串,如果是非字符串的话,则会先转换成字符串,解析中还可以传入第二个参数,代表转换的进制,有一个看似无厘头,实则很正确的一个例子:

parseInt(1/0, 19); // 18

  分析如下:

  1/0 返回的是 Infinity,首先,转换成字符串 “Infinity”,也就是说,实际执行的是:

  parseInt("Infinity", 19), ‘I’在19进制中代表的是18, 而’n’是没有意义的,所以解析完I以后,就返回了,所以结果是18,其他一些神奇的例子:

parseInt(0.000008);  // 0
parseInt(0.0000008); // 8
parseInt(false, 16); // ‘f‘*16 + ‘a‘= 15 * 16 + 10 = 250
parseInt(parseInt, 16); // ‘f‘=15
parseInt(‘0x10‘); // 16
parseInt(‘103‘, 2); //2
parseInt(78, 8); // 8

4、显式转换为布尔值

(1)使用Boolean函数或者!!进行ToBoolean的强制类型转换

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

(2)显示ToBoolean的一个作用是可以在JSON序列化过程中将不安全的值返回对应的布尔值

var a = [1,
        function(){},
        2,
        function(){}];
JSON.stringify(a); // "[1, null, 2, null]"

JSON.stringify(a, function(k, v){
    if(typeof v == ‘function‘){
        return !!v;
    }else{
        return v;
    }
})  // "[1, true, 2, true]"

(3)杜绝使用var b = a ? true : false(当a涉及了隐式转换为布尔值的时候)

五、隐式强制类型转换

1、字符串和数字之间的强制转换

  如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作。如果其中一个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作

var a = {
    valueOf: function(){
        return 42;
    },
    toString: function(){
        return 4;
    }
}
a + ‘‘; // ‘42‘ 调用`valueof方法` 先转换成原始值,在将原始值转换成字符串
String(a); // ‘4‘ //String()会调用toString方法将其转换成字符串

  数字到字符串的隐式转换也是类似的

var a = [1], b = [2];
a - b; // -1

2、布尔值和数字之间的 隐式强制转换

function onlyOne() {
    var sum = 0;
    for(var i = 0; i < arguments.length; i++){
        if(arguments[i]){
            sum += Number(!!arguments[i]);
        }
    }
    return sum == 1;
}

var a = true;
var b = false;

onlyOne(a, b); //true
onlyOne(a, a, b); //false

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

if, for, while

? :三目运算符

* 逻辑运算符||和’&&’操作符左边的操作数

3、||和&&

  js中,这两个操作符返回的不一定是布尔值,它们实际上是选择两个操作数中的一个,然后将其返回。可以用它来进行代码压缩:

if(a){
    foo()
}
//等价于
a&&foo()

六、宽松相等 和 严格相等

  之前在区分==和===时,一直觉得说“==运算符比较值是否相等,===运算符会检查值和类型是否相等”很对, 但是这次看书上说,

  ==允许在相等比较中进行强制类型转换,而===不允许

  觉得这种说法更加准确

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

  无论字符串是在操作符的左边还是右边,所采取的操作都是将字符串转换成数字进行比较

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

  会将其他类型转换成数值,然后进行比较,

var x = true, y = ‘42‘;
x == y; // false

3、null 和 undefined之间的相等比较

  null和undefined之间的==比较也涉及隐式强制类型转换

var a = null;
var b;

a == b; //true
a == null; //true
b == null; // true

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

4、对象和非对象之间的相等比较

  对象和标量基本类型进行相等比较是,对象会先转换成原始值,然后进行比较

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

a == b;  //true

  如果其中的对象是原始值的封装对象,那么在比较时,对象转换成原始值的过程其实也就是对象的解封装过程,但有些情况需要注意:

  (1)undefined和null是没有封装对象的

  (2) NaN虽然有封装对象,但是解封回来的原始值也是NaN,而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(e);
e == f; //false

  最后,是一些比较的易错点

‘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`

  抽象关系比较:

  (1)如果比较双方都是字符串,那么进行字符串间的比较

  (2)否则,会将比较双方都转换成原始值,如果转换结果中有非字符串,那么就都转换成数字进行比较

  然后里面有一些比较诡异的事情:

var a = {b: 42};
var b = {b: 43};
a < b; //false
a == b; //false
a > b; //false

a <= b; //true
a >= b; //true 

  分析如下:

  首先比较双方都不是字符串,先将其都转换成原始值:

  a == ‘[object Object]’, b == ‘[object Object]’;

  那么不应该是 a == b吗?

  因为根据规范,js中的比较是这么处理的:

  a <= b; 被处理为 !(b<a), 因为 b < a的结果是false, 所以a<=b返回true

时间: 2024-10-06 21:42:20

详细理解javascript中的强制类型转换的相关文章

深入理解JavaScript中创建对象模式的演变(原型)

创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Object构造函数和对象字面量方法 工厂模式 自定义构造函数模式 原型模式 组合使用自定义构造函数模式和原型模式 动态原型模式.寄生构造函数模式.稳妥构造函数模式 第一部分:Object构造函数和对象字面量方法 我之前在博文<javascript中对象字面量的理解>中讲到过这两种方法,如何大家不熟悉,可以点进去看一看回顾一下.它们的优点是用来创建单个的对象非常方

深入理解javascript 中的 delete(转)

在这篇文章中作者从<JavaScript面向对象编程指南>一书中关于 delete 的错误讲起,详细讲述了关于 delete 操作的实现, 局限以及在不同浏览器和插件(这里指 firebug)中的表现. 下面翻译其中的主要部分. ...书中声称 “函数就像一个普通的变量那样——可以拷贝到不同变量,甚至被删除” 并附上了下面的代码片段作为说明: >>> var sum = function(a, b) {return a+b;}; >>> var add =

理解JavaScript中函数的使用

函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解. JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的.通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递.在继续讲述之前,先看一下函数的使用语法: function func1(…){…} var func2=function(…){…}; var func3=function func4(…){…}; var func5=new Function(

【拾遗】理解Javascript中的Arguments

前言 最近在看JavaScript相关的知识点,看到了老外的一本Javascript For Web Developers,遇到了一个知识盲点,觉得老外写的很明白很透彻,记录下来加深印象,下面是我摘出来的一些片段,片段下有对应的解释,希望也能帮助其他人扫除这个盲点.如有翻译的不得体的地方还请在评论区指出,不胜感激. 理解Javascript中的Arguments Function arguments in ECMAScript don’t behave in the same way as fu

理解 JavaScript 中的 this

前言 理解this是我们要深入理解 JavaScript 中必不可少的一个步骤,同时只有理解了 this,你才能更加清晰地写出与自己预期一致的 JavaScript 代码. 本文是这系列的第三篇,往期文章: 理解 JavaScript 中的作用域 理解 JavaScript 中的闭包 什么是 this 消除误解 在解释什么是this之前,需要先纠正大部分人对this的误解,常见的误解有: 指向函数自身. 指向它所在的作用域. 关于为何会误解的原因这里不多讲,这里只给出结论,有兴趣可以自行查询资料

深入理解JavaScript中的属性和特性

深入理解JavaScript中的属性和特性? JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaScript中理解对象的本质.理解对象与类的关系.对象与引用类型的关系 对象属性如何进行分类 属性中特性的理解 第一部分:理解JavaScript中理解对象的本质.理解对象与类的关系.对象与引用类型的关系 对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值.对象或者函数.即

【干货理解】理解javascript中实现MVC的原理

理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法.模型有对数据直接访问的权利.模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作. 视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面.当然也包括一些事件的注册或者aja

JavaScript大杂烩6 - 理解JavaScript中的this

在JavaScript开发中,this是很常用的一个关键字,但同时也是一个很容易引入bug的一个关键字,在这里我们就专门总结一下页面中可能出现的this关键字(包括几种在其他页面文件中出现的this). JavaScript中的this关键字通常只使用在函数中,它指向当前函数的调用者,这是this关键字的本质,所有的使用方式都是围绕这个展开的,让我们来看一下在各种性质的函数中this的用法.1. 在对象的函数中使用this var person = { name: 'Frank', say: f

全面理解Javascript中Promise

全面理解Javascript中Promise 最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为 比较好的文章,供大家更好地学习js中非常有趣的Promise Promise概念 2015 年 6 月,ECMAScript 6 的正式版 终于发布了. ECMAScript 是 JavaScript 语言的国际标准,javascript 是 ECMAScript 的实现.ES6 的目标,是使得 JavaScript 语言可以用来编写大