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

常见的原生函数:

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

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

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

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

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

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

1.2 封装对象

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

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

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

var a = "abc";
var b = new String( a );
var c = Object( a );
typeof a; // "string"
typeof b; // "object"
typeof c; // "object"
b instanceof String; // true
c instanceof String; // true
Object.prototype.toString.call( b ); // "[object String]" Object.prototype.toString.call( c ); // "[object String]"

拆封

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

var a = new String( "abc" );
var b = new Number( 42 );
var c = new Boolean( true );
a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true

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

var a = new String( "abc" );
var b = a + ""; // b的值为"abc"
typeof a; // "object"
typeof b; // "string"

Symbol(..)

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

1.3 原生对象的原型

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

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

1.4 值类型转换

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

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

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

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

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

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

JSON 字符串化

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

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

数组中则会返回 null

不安全的 JSON 值:

  • undefined
  • function
  • symbol
  • 包含循环引用的对象
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(..) 会出错。

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 到当前时间):

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

显式解析数字字符串

var a = "42";
var b = "42px";
Number( a ); // 42
parseInt( a ); // 42
Number( b ); // NaN
parseInt( b ); // 42

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

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

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

ToBoolean

  1. 假值(falsy value)

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

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

var a = "0";
var g;
!!a; // true
!!g; // false
// 不常用
Boolean( a ); // true
Boolean( g ); // false

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

var a = 42;
var b = String( a );
var c = "3.14";
var d = Number( c );
b; // "42"
d; // 3.14

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

var a = 42;
var b = a.toString();
var c = "3.14";
var d = +c;
b; // "42"
d; // 3.14

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

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

var a = 42;
var b = a + "";
b; // "42"

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

var a = "3.14";
var b = a - 0;
b; // 3.14

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

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

  function onlyOne() {
var sum = 0;
for (var i=0; i < arguments.length; i++) {
// 跳过假值,和处理0一样,但是避免了NaN
if (arguments[i]) {
sum += arguments[i];
}
}
return sum == 1;
}
var a = true;
var b = false;
onlyOne( b, a );// true
onlyOne( b, a, b, b, b );// true

|| 和 &&

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

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

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 的结果。

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

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

Number.prototype.valueOf = function() { return 3;};
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. VFP CursorAdapter 起步三(作者:Doug Hennig 译者:fbilo)

    可重用数据类 VFP 的程序员们想要一个可重用的数据类已经很久了.尽管在过去的版本中也有许多解决这个问题的办法,不过总是有点美中不足.现在在 VFP 8里,我们有了真正的可重用数据类.这个月,Doug ...

  2. 开源堡垒机jumpserver的安装

    开源跳板机jumpserver安装 简介 Jumpserver 是全球首款完全开源的堡垒机, 使用GNU GPL v2.0 开源协议, 是符合4A 的专业运维审计系统 Jumpserver 使用Pyt ...

  3. Vscode开发Python环境安装

    VSCode 开发 Python 使用python,主要是做一些工具和爬虫的操作,语法简单,功能复杂,入手很快. 我们通过在 VSCode 中搜索 Python 插件,发现,开发 python 的话, ...

  4. 高并发之——不得不说的线程池与ThreadPoolExecutor类浅析

    一.抛砖引玉 既然Java中支持以多线程的方式来执行相应的任务,但为什么在JDK1.5中又提供了线程池技术呢?这个问题大家自行脑补,多动脑,肯定没坏处,哈哈哈... 说起Java中的线程池技术,在很多 ...

  5. NPM install -save 和 -save-dev 区别

    最近在写Node程序的时候,突然对 npm install 的-save和-save-dev 这两个参数的使用比较混乱.其实博主在这之前对这两个参数的理解也是模糊的,各种查资料和实践后对它们之间的异同 ...

  6. 让div充满整个body

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Vue 路由&组件懒加载(按需加载)

    当打包构建应用时,Javascript 包会变得非常大,影响页面加载速度.使用Vue路由懒加载和组件懒加载可以提升页面加载速度,减少白屏时间,提升用户体验. 用法有如下三种:(路由懒加载与组件懒加载用 ...

  8. 跨域打开页面:Uncaught DOMException: Blocked a frame with origin

    Uncaught DOMException: Blocked a frame with origin 使用postMessage()方法可以解决跨域传值的问题 Api: https://develop ...

  9. Python面试(网编+数据库)

    第一部分 必答题 简述 OSI 7层模型及其作用?(2分) 应用层:与用户直接交互,软件.网站等 表示层:使用软件.网站可以查看的数据,图片等 会话层:保持登录状态,电脑中为cookie 传输层:选择 ...

  10. 04.JS逻辑结构

    前言:  学习一门编程语言的基本步骤(01)了解背景知识(02)搭建开发环境(03)语法规范(04)常量和变量(05)数据类型(06)数据类型转换(07)运算符(08)逻辑结构8.逻辑结构——logi ...