1.1 对象内部属性 [[Class]]

常见的原生函数:

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol()——ES6
  1. var a = new String( "abc" );
  2. typeof a; // 是"object",不是"String"
  3. a instanceof String; // true
  4. Object.prototype.toString.call( a ); // "[object String]"

typeof 在这里返回的是对象类型的子类型。

chrome下console.log( a );new String("abc") 创建的是字符串 "abc" 的封装对象,而非基本类型值 "abc"。

Object.prototype.toString(..) 查看对象的内部 [[Class]] 属性,通常对象的内部 [[Class]] 属性和创建该对象的内建原生构造函数相对应,但 null 和 undefined除外

  1. Object.prototype.toString.call( null );// "[object Null]"
  2. Object.prototype.toString.call( undefined ); // "[object Undefined]"
  3. Object.prototype.toString.call( "abc" ); // "[object String]"
  4. Object.prototype.toString.call( 42 ); // "[object Number]"
  5. Object.prototype.toString.call( true ); // "[object Boolean]"

Null() 和 Undefined() 这样的原生构造函数并不存在,但是内部 [[Class]] 属性值仍 然是 "Null" 和 "Undefined";基本类型值被各自的封装对象自动包装。

1.2 封装对象

基本类型值没有 .length.toString() 这样的属性和方法,javaScript 会自动为基本类型值包装一个封装对象,浏览器为 .length 这样的常见情况做了性能优化,直接使用封装对象来“提前优化”代码反而会降低执行效率。

  1. var a = new Boolean( false );
  2. if (!a) {
  3. console.log( "Oops" ); // 执行不到这里,a始终为真值
  4. }

可以使用 Object(..) 函数,自行封装基本类型值

  1. var a = "abc";
  2. var b = new String( a );
  3. var c = Object( a );
  4. typeof a; // "string"
  5. typeof b; // "object"
  6. typeof c; // "object"
  7. b instanceof String; // true
  8. c instanceof String; // true
  9. Object.prototype.toString.call( b ); // "[object String]" Object.prototype.toString.call( c ); // "[object String]"

拆封

使用 valueOf() 函数,得到封装对象中的基本类型值

  1. var a = new String( "abc" );
  2. var b = new Number( 42 );
  3. var c = new Boolean( true );
  4. a.valueOf(); // "abc"
  5. b.valueOf(); // 42
  6. c.valueOf(); // true

隐式拆封(强制类型转换)

  1. var a = new String( "abc" );
  2. var b = a + ""; // b的值为"abc"
  3. typeof a; // "object"
  4. typeof b; // "string"

Symbol(..)

  • 符号是具有唯一性的特殊值(并 非绝对),用它来命名对象属性不容易导致重名。
  • 使用 Symbol(..) 原生构造函数来自定义符号不能带 new 关键字,否则会出错
  • 论是在代码还是开发控制台中都无法查看和访问它的值,只会 显示为诸如 Symbol(Symbol.create) 这样的值。
  • ES6 中有一些预定义符号,以 Symbol 的静态属性形式出现,如 Symbol.create、Symbol. iterator 等

1.3 原生对象的原型

  1. typeof Function.prototype;
  2. Function.prototype();// "function" // 空函数!
  3. RegExp.prototype.toString(); // "/(?:)/"——空正则表达式
  4. "abc".match( RegExp.prototype );// [""]
  5. Array.isArray( Array.prototype ); // true
  6. Array.prototype.push( 1, 2, 3 );// 3
  7. Array.prototype;// [1,2,3]

Function.prototype 是一个函数,RegExp.prototype 是一个正则表达式,而 Array. prototype 是一个数组

1.4 值类型转换

将值从一种类型转换为另一种类型通常称为类型转换(type casting),这是显式的情况;隐式的情况称为强制类型转换(coercion)。

类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时。

  1. var a = 42;
  2. var b = a + ""; // 隐式强制类型转换
  3. var c = String( a ); // 显式强制类型转换

1.4 .1 类型转换的基本规则

**ToString **处理非字符串到字符串的强制类型转换。

如果对象有自己的 toString() 方法,字符串化时就会调用该方法并使用其返回值。

JSON 字符串化

工具函数JSON.stringify(..)在将 JSON 对象序列化为字符串时也用到了 toString

JSON.stringify(..) 在对象中遇到 undefined、function 和 symbol 时会自动将其忽略,在

数组中则会返回 null

不安全的 JSON 值:

  • undefined
  • function
  • symbol
  • 包含循环引用的对象
  1. JSON.stringify( undefined ); // undefined
  2. JSON.stringify( function(){} );// undefined
  3. JSON.stringify( [1,undefined,function(){},4]//"[1,null,null,4]"
  4. );
  5. JSON.stringify({ a:2, b:function(){} })//"{"a":2}"
  6. //对包含循环引用的对象执行 JSON.stringify(..) 会出错。

JSON.stringify(..) 并不是强制类型转换,它涉及 ToString 强制类型转换,具体表现在以下两点:

(1) 字符串、数字、布尔值和 null 的 JSON.stringify(..) 规则与 ToString 基本相同。

(2) 如果传递给 JSON.stringify(..) 的对象中定义了 toJSON() 方法,那么该方法会在字符串化前调用,以便将对象转换为安全的 JSON 值。

ToNumber处理非数字到当作数字的强制类型转换

  • true 转换为 1,false 转换为 0。undefined 转换为 NaN,null 转换为 0
  • 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再强制转换为数字。
  • 在获取对象的基本类型时,检查该值是否有 valueOf() 方法。 如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString()的返回值(如果存在)来进行强制类型转换。,如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。
  • 使用 Object.create(null) 创建的对象 [[Prototype]] 属性为 null,并且没有 valueOf() 和 toString() 方法,因此无法进行强制类型转换。

日期显式转换为数字

一元运算符 + 的另一个常见用途是将日期(Date)对象强制类型转换为数字,返回结果为 Unix 时间戳,以微秒为单位(从 1970 年 1 月 1 日 00:00:00 UTC 到当前时间):

  1. var d = new Date( "Mon, 18 Aug 2014 08:53:06 CDT" );
  2. +d; // 1408369986000
  3. var timestamp = Date.now();
  4. var timestamp = new Date().getTime();

显式解析数字字符串

  1. var a = "42";
  2. var b = "42px";
  3. Number( a ); // 42
  4. parseInt( a ); // 42
  5. Number( b ); // NaN
  6. parseInt( b ); // 42

解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停 止。而转换不允许出现非数字字符,否则会失败并返回 NaN。 ES5 开始 parseInt(..) 默认转换为十进制数

  1. var a = {
  2. num: 21,
  3. toString: function() { return String( this.num * 2 ); }
  4. };
  5. parseInt( a ); // 42

parseInt(..) 先将参数强制类型转换为字符串再进行解析

ToBoolean

  1. 假值(falsy value)

    1. undefined
    2. null
    3. false
    4. +0、-0 NaN
    5. ""

Boolean(..) 和 !! 来进行显式转换

  1. var a = "0";
  2. var g;
  3. !!a; // true
  4. !!g; // false
  5. // 不常用
  6. Boolean( a ); // true
  7. Boolean( g ); // false

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

  1. var a = 42;
  2. var b = String( a );
  3. var c = "3.14";
  4. var d = Number( c );
  5. b; // "42"
  6. d; // 3.14

字符串和数字之间的转换是通过 String(..) 和 Number(..) 来实现的,不用 new 关键字,并不创建封装对象。

  1. var a = 42;
  2. var b = a.toString();
  3. var c = "3.14";
  4. var d = +c;
  5. b; // "42"
  6. d; // 3.14

a.toString() 中涉及隐式转换。因为 toString() 对 42 这样的基本类型值不适用,所以 JavaScript 引擎会自动为 42 创建一个封 装对象然后对该对象调用 toString()

字符串和数字之间的隐式强制类型转换

  1. var a = 42;
  2. var b = a + "";
  3. b; // "42"

a + "" 会对a调用valueOf()方法,然后通过ToString抽象 操作将返回值转换为字符串。而 String(a) 则是直接调用 ToString()。

  1. var a = "3.14";
  2. var b = a - 0;
  3. b; // 3.14

-是数字减法运算符,因此a - 0会将a强制类型转换为数字。也可以使用a * 1和a /1,这两个运算符也只适用于数字

布尔值到数字的隐式强制类型转换

  1. function onlyOne() {
  2. var sum = 0;
  3. for (var i=0; i < arguments.length; i++) {
  4. // 跳过假值,和处理0一样,但是避免了NaN
  5. if (arguments[i]) {
  6. sum += arguments[i];
  7. }
  8. }
  9. return sum == 1;
  10. }
  11. var a = true;
  12. var b = false;
  13. onlyOne( b, a );// true
  14. onlyOne( b, a, b, b, b );// true

|| 和 &&

&& 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值。

  1. var a = 42;
  2. var b = "abc";
  3. var c = null;
  4. a || b; // 42
  5. a && b;// "abc"
  6. c || b; // "abc"
  7. c && b;// null
  • || 和 && 首先会对第一个操作数(a 和 c)执行条件判断,如果其不是布尔值(如上例)就 先进行 ToBoolean 强制类型转换,然后再执行条件判断。
  • 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数(a 和 c)的值,如果为 false 就返回第二个操作数(b)的值。
  • && 则相反,如果条件判断结果为 true 就返回第二个操作数(b)的值,如果为 false 就返回第一个操作数(a 和 c)的值。
  1. var a = 42;
  2. var b = null;
  3. var c = "foo";
  4. if (a && (b || c)) {
  5. console.log( "yep" );
  6. }

a && (b || c)的结果实际上是"foo"而非true,然后再由if将foo强制类型转换为布尔值,所以最后结果为 true。

宽松相等和严格相等

  • “== 允许在相等比较中进行强制类型转换,而 === 不允许。”
  • 如果两个值的类型不同,就需要考虑有没有强制类型转换的必要,有就用 ==,没有就 强制类型转换用 ===,不用在乎性能。
  • null == undefined //true

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

(1) 如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果;

(2) 如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 ToPromitive(x) == y 的结果。

  1. var a = 42;
  2. var b = [ 42 ];
  3. a == b; // true

[ 42 ] 首先调用 ToPromitive 抽象操作,返回 "42",变成 "42" == 42,然后又变成 42 == 42,最后二者相等。

  1. Number.prototype.valueOf = function() { return 3;};
  2. new Number( 2 ) == 3; // true

Number(2) 涉及 ToPrimitive 强制类型 转换,因此会调用 valueOf()。

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

  • 如果两边的值中有 true 或者 false,千万不要使用 ==。
  • 如果两边的值中有 []、"" 或者 0,尽量不要使用 ==。
  • typeof x == "function"是100%安全的
  • 要避免a < b中发生隐式强制类型转换,确保a和b为相同的类型

总结

对于强制类型转换,取其精华,去其糟粕

【你不知道的javaScript 中卷 笔记2】javaScript中的类型转换的更多相关文章

  1. 【你不知道的javaScript 中卷 笔记1】javaScript中的类型与值

    一.类型与值 1.0 javaScript 七种内置类型: 空值(null) 未定义(undefined) 布尔值( boolean) 数字(number) 字符串(string) 对象(object ...

  2. 《高性能JavaScript》学习笔记——日更中

    ------------------2016-7-20更------------------ 最近在看<高性能JavaScript>一书,里面当中,有讲很多提高js性能的书,正在看的过程中 ...

  3. 《你不知道的JavaScript(中卷)》读书笔记

    中卷有点无聊,不过也是有干货的,但是好像要背一下的样子.不过作者大大都夸我是“优秀的开发人员”了,肯定要看完呀~~ 开发人员觉得它们太晦涩,很难掌握和运用,弊(导致bug)大于利(提高代码可读性).这 ...

  4. 从头开始学JavaScript 笔记(一)——基础中的基础

    原文:从头开始学JavaScript 笔记(一)--基础中的基础 概要:javascript的组成. 各个组成部分的作用 . 一.javascript的组成   javascript   ECMASc ...

  5. 《编写可维护的javascript》读书笔记(中)——编程实践

    上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...

  6. 学习笔记:JavaScript传参方式———ECMAScript中所有函数的参数都是按值传递

    我们把命名参数(arguments)视为局部变量,在向参数传递基本类型值时,如同基本类型变量的复制一样,传递一个副本,参数在函数内部的改变不会影响外部的基本类型值.如: function add10( ...

  7. 编程笔记:JavaScript 中的类型检查

    在Badoo的时候我们写了大量的JS脚本,光是在我们的移动web客户端上面就有大概60000行,可想而知,维护这么多JS可是相当具有挑战性的.在写如上规模js脚本客户端应用的时候我们必须对一件事保持警 ...

  8. 《你不知道的javascript》读书笔记2

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...

  9. 【译】用jQuery 处理XML--浏览器中的XML与JavaScript

    用jQuery 处理XML--写在前面的话 用jQuery 处理XML-- DOM(文本对象模型)简介 用jQuery 处理XML--浏览器中的XML与JavaScript 用jQuery 处理XML ...

随机推荐

  1. ArtiPub:一款开源的一文多发平台

    文章来自我的博客:https://blog.ljyngup.com/archives/705.html/ 看到感觉挺有意思的,有空找个空闲的VPS搭建一下. 转自官方Github仓库 ArtiPub ...

  2. Oracle11G DG 搭建及管理

    一.准备工作 环境准备 主数据库Oracle Database安装 备服务器Oracle Database software 安装 二.正式配置 三.基本管理 -------------------- ...

  3. 《自拍教程14》Linux的常用命令

    Linux操作系统, 包括我们大家熟知的Android, Ubuntu, Centos, Red Hat, UOS等. 这些常用命令先大概了解下,当然能熟练掌握并运用到实际工作中那最好不过了. 后续技 ...

  4. opencv简单实用(cv2)

    一.介绍 安装:pip install opencv-python OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS ...

  5. linux下搭建DHCP服务

    一键搭建dhcpd服务脚本 [root@dhcp-server~]# cat auto_install_dhcpd.sh #!/bin/sh . /etc/init.d/functions #安装dh ...

  6. android手机拍照旋转的问题

    android开发中,遇到过手机拍照,明明是竖着拍的,显示的结果却是横这的,困扰了很久,找了很久找了一种解决方法: ExifInterface exifInterface = new ExifInte ...

  7. 用PHP&JS实现的ID&密码校验程序

    声明:本程序纯粹是本人在学习过程中突发奇想做的,并未考虑任何可行性,实用性,只是留下来供以后参考. 前端页面 sign.html <!DOCTYPE html> <html> ...

  8. C#设置自定义文件图标实现双击启动

    修改注册表,双击文件直接打开 string strProject = "Exec"; string p_FileTypeName =".cdb";//文件后缀 ...

  9. Electron – 基础学习(3): 项目打包成exe桌面应用 之electron-builder

    前次用 electron-packager 打包成功,这次改用 electron-builder 打包,然后根据项目中实际需要进行选择使用. 第一步:全局安装 electron-builder,便于系 ...

  10. PS切图工具

    缓存设置: 编辑-首选项-暂存盘 改完除了C盘之外的其他盘 单位设置: 编辑-首选项-单位与标尺 将单位修改成像素  PS预设: 工具   (窗口-工具) 标尺  (视图-标尺) 图层  (窗口-图层 ...