你不知道的 JavaScript 3.类型和语法

类型

ECMAScript 语言中所有的值都有一个对应的语言类型。ECMAScript 语言类型包括 UndefinedNullBooleanStringNumberObject

对语言引擎和开发人员来说,类型是值的内部特征,它定义了值的行为,以使其区别于其他值。

内置类型

JavaScript 有七种内置类型:

  • null
  • undefined
  • boolean
  • number
  • string
  • object
  • symbol -- added in ES6!

可以用 typeof 操作符来查看值的类型,它返回的是类型的字符串值

// null 是一个对象???
typeof null === "object"; // true

function(函数)是 object 的一个“子类型”,因此函数是对象。

函数不仅是对象,还可以拥有属性。

数组也是对象,它也是 object 的一个“子类型”。

值和类型

JavaScript 变量没有类型,但它们持有的值有类型。变量可以随时持有任何类型的值。类型定义了值的行为特征。

在对变量执行 typeof 操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型,因为 JavaScript 中的变量没有类型。

undefinedundeclared

变量在未持有值的时候为 undefined。此时 typeof 返回 "undefined"。

已在作用域中声明但还没有赋值的变量,是 undefined 的。
还没有在作用域中声明过的变量,是 undeclared 的。

typeof Undeclared

示例:在程序中使用全局变量 DEBUG 作为“调试模式”的开关。
问题是如何在程序中检查全局变量 DEBUG 才不会出现 ReferenceError 错误。这时 typeof 的安全防范机制就成了我们的好帮手

// 这样会抛出错误
if (DEBUG) {
  console.log("Debugging is starting");
}

// 这样是安全的
if (typeof DEBUG !== "undefined") {
  console.log("Debugging is starting");
}

数组

  • 在 JavaScript 中,数组可以容纳任何类型的值
  • 对数组声明后即可使用,不需要预先设定数组大小。
  • 因为数组也是对象,也可以包含字符串键值和属性,但不推荐这么做。
  • 如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。

类数组

将类数组(一组通过数字索引的值)转换为真正的数组:

  • slice()
  • Array.from()

字符串

JavaScript 中字符串是不可变的,而数组是可变的。

字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。

数字

JavaScript 只有一种数值类型:number(数字),包括“整数”和带小数的十进制数。

JavaScript 中的“整数”就是没有小数的十进制数。

比较两个数字是否相等(在指定的误差范围内):Number.EPSILON

整数的安全范围:(Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER

如果需要对大数值进行数学运算,目前需要借助相关工具库。

整数检测

检测一个值是否是整数:Number.isInterger()
检测一个值是否是安全的整数:Number.isSafeInterger()

特殊数值

不是值的值

  • undefined:未定义,从未赋值。
  • null:空值。
  • void 运算符返回 undefined

特殊的数字

  • NaN:不是一个数字、无效数值。—— “执行数学运算没有成功,这是失败后返回的结果。”
  • 判断一个值是否是 NaNNumber.isNaN()
  • 无穷数:Infinity
  • 判断两个值是否绝对相等:Object.is()

值和引用

JavaScript 对值和引用的赋值/传递完全根据值的类型来决定。

简单值(即标量基本类型值,scalar primitive)总是通过值复制的方式来赋值/传递,包括 null、undefined、字符串、数字、布尔和 ES6 中的 symbol。

复合值(compound value)——对象(包括数组和封装对象)和函数,则总是通过引用复制的方式来赋值/传递。

原生函数

原生函数就是内建函数:

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol() -- ES6 新增!

一般不推荐直接使用封装对象

内部属性 [[class]]

所有 typeof 返回值为 "object" 的对象(如数组)都包含一个内部属性 [[Class]]
这个属性无法直接访问,一般通过 Object.prototype.toString(..) 来查看。

// 数组的内部 [[class]] 属性是 Array
Object.prototype.toString.call( [1,2,3] ); // "[object Array]"

// 正则表达式的内部 [[class]] 属性是 RegExp
Object.prototype.toString.call( /regex-literal/i ); // "[object RegExp]"

// 虽然 Null() 和 Undefined() 这样的原生构造函数并不存在,但是内部 [[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]"

封装对象包装

JavaScript 会自动为基本类型值包装(box 或者 wrap)一个封装对象。

var a = 'abc' // 基本类型

//  String 对象 才有 length 属性和 toUpperCase() 方法
a.length // 3
a.toUpperCase() // "ABC"

一般情况下,我们不需要直接使用封装对象。最好的办法是让 JavaScript 引擎自己决定什么时候应该使用封装对象。换句话说,就是应该优先考虑使用 "abc" 和 42 这样的基本类型值,而非 new String("abc")new Number(42)

拆封

获得封装对象中的基本类型值:valueOf()

原生函数作为构造函数

应该尽量避免使用构造函数,除非十分必要,因为它们经常会产生意想不到的结果。

  • Array()
  • Object()
  • Function()
  • RegExp()

强烈建议使用常量形式(如 /^a*b+/g)来定义正则表达式,这样不仅语法简单,执行效率也更高,因为 JavaScript 引擎在代码执行前会对它们进行预编译和缓存。

Date()Error()

创建日期:

  • new Date()
  • Date.now()

强制类型转换

JavaScript 中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值,不会返回对象和函数。

值类型转换

类型转换:将值从一种类型转换为另一种类型。

  • 显示类型转换;
  • 强制类型转换;

我们能够从代码中看出哪些地方是显式强制类型转换,而隐式强制类型转换则不那么明显,通常是某些操作产生的副作用。

var a = 42;

var b = a + ""; // 隐式强制类型转换

var c = String( a ); // 显式强制类型转换

抽象值操作

ToString():非字符串转换为字符串

...

JSON 字符串化

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

  1. 字符串、数字、布尔值和 nullJSON.stringify(..) 规则与 ToString() 基本相同。
  2. 如果传递给 JSON.stringify(..) 的对象中定义了 toJSON() 方法,那么该方法会在字符串化前调用,以便将对象转换为安全的 JSON 值。

JSON.stringify(..) 在对象中遇到 undefinedfunctionsymbol 时会自动将其忽略,在数组中则会返回 null(以保证单元位置不变)。

JSON.stringify( 42 );   // "42"
JSON.stringify( "42" ); // ""42"" (a string with a quoted string value in it)
JSON.stringify( null ); // "null"
JSON.stringify( true ); // "true"

// 不安全的 JSON 值
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(..) 传递一个可选参数 replacer,它可以是数组或者函数,用来指定对象序列化过程中哪些属性应该被处理,哪些应该被排除,和 toJSON() 很像。

JSON.stringify(..) 还有一个可选参数 space,用来指定输出的缩进格式。space 为正整数时是指定每一级缩进的字符数,它还可以是字符串,此时最前面的十个字符被用于每一级的缩进

ToNumber():非数值转换为数值

ToBoolean():转换为布尔值

以下这些是假值,假值的布尔强制类型转换结果为 false

  • undefined
  • null
  • false
  • +0, -0, and NaN
  • ""

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

显式强制类型转换

显式强制类型转换:显而易见的类型转换

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

  • String():将值转换为字符串基本类型
  • Number():将值转换为数字基本类型
  • toString()
  • 一元运算 + 被普遍认为是显式强制类型转换

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

显式解析数字字符串

解析字符串中的数字将字符串强制类型转换为数字的返回结果都是数字。但解析和转换两者之间还是有明显的差别。

解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回 NaN

  • 解析字符串中的浮点数:parseFloat()
  • parseInt()

显示转换为布尔值

  • Boolean(),不常用。
  • 显式强制类型转换为布尔值最常用的方法:!!

隐式强制类型转换

隐式强制类型转换指的是那些隐蔽的强制类型转换,副作用也不是很明显。
隐式强制类型转换的作用是减少冗余,让代码更简洁。

宽松相等和严格相等

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

JavaScript 中的相等比较:JS Copmarison Table

抽象关系比较

语法

JavaScript 语法定义了词法规则(syntax rule,如运算符和关键词等)是如何构成可运行的程序代码的。

语句和表达式

JavaScript 的语法中:语句相当于句子,表达式相当于短语,运算符则相当于标点符号和连接词。

运算符优先级

自动分号

错误

函数参数

try...finally

switch

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容