<JavaScript深入>类型、值和变量

JavaScript 深入系列 #1

<JavaScript深入>类型、值和变量
Photo by Greg Rakozy / Unsplash

类型

主要类型

  • number
  • string
  • boolean
  • undefined
  • null
  • object

简单基本类型 (string、boolean、number、null[1] 和 undefined)


  1. null 二进制为0,typeof null 为 'object' 是因为 typeof 判断前三位都是 000,则认为 null 的类型为 object ↩︎

内置对象

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

类型、值和变量

  • 计算机程序的运行需要对值(value)进行操作。

  • 数据类型分为两类:原始类型 和 对象类型。

    • 原始类型:数字、字符串、布尔值
    • 特殊的原始值:null 和 undefined
    • 对象:对象(object)是属性(property)的集合,每个属性都由“名/值对”构成。eg:特殊的对象--全局对象
  • 普通的对象是“命名值”的无序集合。数组,表示带编号的值的有序集合。

  • 函数,是具有与它相关联的可执行代码的对象,通过调用函数来运行可执行代码,并返回运算结果。

  • 如果函数用来初始化(new)一个新建的对象叫做构造函数。每一个构造函数定义了一类对象—由构造函数初始化的对象组成的集合。类可以看做是对象类型的子类型。

  • JavaScript 核心定义了 — 数组(Array)、函数(Function)、日期(Date)、正则(RegExp)和错误(Error)。

  • JavaScript 解释器有自己的内存管理机制,可以自动对内存进行垃圾回收。当不再有任何引用指向一个对象,解释器就会知道这个对象没用了,然后自动回收它所占用的内存资源。

  • JavaScript 是一种面向对象的语言。不严格的将,这意味着我们不用全局的定义函数去操作不同类型的值。

  • JavaScript 的类型可以分为 原始类型 和 对象类型,也可分为 可以拥有方法的类型 和 不能拥有方法的类型,同样可分为 可变类型 和 不可变类型。

    • 对象和数组属于 可变类型:可以更改对象属性值和数组元素的值。
    • 数字、布尔值、null、undefined 属于不可变类型。
    • 字符串可以看成由字符组成的数组,在 JavaScript 中,字符串是不可变的,可以访问字符串任意位置的文本,但 JavaScript 并未提供修改已知字符串的文本内容的方法。
  • JavaScript 可以自由地进行数据类型转换。

  • JavaScript 变量是无类型的,变量可以被复制任何类型的值,同样一个变量可以重新赋予不同类型的值。

  • JavaScript 采用词法作用域。不在任何函数内声明的变量成为全局变量。在函数内声明的变量具有函数作用域,并且只在函数内可见。

数字

  • JavaScript 不区分整数值和浮点数值。所有数字均用浮点数值表示。采用 IEEE 754标准定义的64位浮点格式表示数字。

  • JavaScript 中实际的操作是基于32位整数

  • JavaScript 中,一个数字直接出现,称之为 数字直接量。

  • 在 ES6 的严格模式下,八进制直接量是明令禁止的。

  • 浮点型直接量,可以含有小数点,他们采用的是传统的实数写法。一个实数由整数部分、小数点、小数部分组成。

  • 可以使用指数记数法表示浮点型直接量,即在实数后跟字符 E/e,后面再跟正负号,其后再加一个整型的指数。这种记数方法表示的数值,是由前面的实数乘以10的指数次幂 [digits][.digits][(E|e)[(+|-)]digits]

  • JS 中的算术运算在溢出(overflow)、下溢(underflow)或被零整除时不会报错。当超过 JavaScript 所能表示的数字上限。结果为一个特殊的无穷大值 — infinity,在 JS 中以 Infinity 表示,当负数的值超过了能表示的负数范围,结果为负无穷大,在 JS 中以 -Infinity 表示。

  • 下溢 是当运算结果无限接近于零并比 JS 能表示的最小值还小的时候发生的一种情形。这种情况下,会返回0。当一个负数发生下溢时,JS 返回一个特殊的值“负零”。这个值几乎和正常的0完全一样。

  • 0/0 是没有意义的,结果为 NaN。无穷大除以无穷大、给任意负数做开方运算、算术运算符与不是数字或无法转换为数字的操作数一起使用时都将返回 NaN。

  • JS 中的非数字值有一点特殊:它和任何值都不相等,包括自身 NaN == NaN //false

  • JS 中的数字具有足够的精度,并可以极其接近于0.1。但事实是,数字不能精确表述的确带来了一些问题
    0.3 - 0.2 == 0.2 - 0.1 //false 0.3 - 0.2 == 0.1 //false 0.2 - 0.1 == 0.1 //true
    在任何使用二进制浮点数的编程语言中都会有这个问题(基于 IEEE754 数值的浮点计算的通病)。

  • 字符串 是一组由16位值组成的不可变的有序序列,每个字符通常来自于 Unicode 字符集。字符串的长度是其所含16位值的个数,索引从0开始。

  • 在 ES3 中,字符串直接量必须写在一行中,在 ES5中,字符串直接量可以拆分成数行,每行必须以反斜线()结束。

 // ES3
 "two\nlines";
 // ES5
 "two\
 nlines"

null 和 undefined

  • typeof null //object 可以将 null 认为是一个特殊的对象值,含义是‘非对象’。通常认为 null 是它自由类型的唯一一个成语,它可以表示数字、字符串和对象是‘无值’的。
  • undefined 用未定义值表示更深成次的“空值”。表明变量没有初始化,如果属性或者值返回 undefined 则说明这个属性或元素不存在。
  • 如果函数没有返回任何值、引用没有提供实参的函数形参的值,都会得到 undefined。undefined 是预定义的全局变量(它和 null 不一样,不是关键字),它的值就是‘未定义’。
  • null 和 undefined 是不同的,但它们都表示“值的空缺”,两者往往可以互换。 null == undefined //true null === undefined //false 。都不包含任何属性和方法,实际上,使用 . 或者 [] 来存取这两个值的成员或方法都会产生一个类型错误。
  • undefined 是表示系统级的、出乎意料的或类似错误的值的空缺,而 null 是表示程序级的,正常的或者在意料之内的值的空缺。如果你想讲他们赋值给变量或者属性,或者将他们作为参数传入函数,最佳选择是使用 null。

全局对象

  • 当 JS 解析器启动时(或者任何 Web 浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性:

    • 全局属性,比如 undefined、Infinity 和 NaN
    • 全局函数,比如 isNaN()
    • 构造函数,比如 Date()
    • 全局对象,比如 Math 和 JSON

    全局对象的初始属性并不是保留字,但他们应当做保留字来对待。

包装对象

  • JS 对象是一种复合值,它是属性或已命名值的集合。当属性值是一个函数的时候,称其为方法。
var s = "test";
s.len = 4;
var t = s.len; //undefined
  • -> 当运行这段代码时,t的值是 undefined。

-> 第二行代码创建了一个临时字符串对象,并给其 len 属性赋值为4,随即销毁了这个对象。

-> 第三行通过原始的(没有被修改过)字符串创建一个新字符串对象,尝试读取其 len 属性,这个属性不存在,所以返回 undefined。

说明了在读取字符串、数字和布尔值的属性(或方法)的时候,表现的和对象一样。如果试图给其属性赋值,则会忽略这个操作,修改只发生在临时对象上,在创建赋值后被立马销毁,没有继续保存下来。

  • 存取字符串、数字和布尔值的属性时创建的临时对象称做包装对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值独享以及布尔值和布尔对象。需要注意的是,可通过 String(),Number()或 Boolean()构造函数来显式创建包装对象
var s = 'test', n = 1, b = true;
var S = new String(s);
var N = new Number(n);
var B = new Boolean(b);
s == S //true
s === S //false
typeof s // String
typeof S //object

JS 会在必要时将包装对象转换成原始值。因此 S,N,B 通常—但不总是—表现的和值 s,n,b一样。

不可变的原始值和可变的对象引用

  • JS 的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。

    • 原始值是不可更改的,任何方法都无法改变(或“突变”)一个原始值。毕竟, var a = 1;console.log(a) //2这种赋值1反而输出2的事本身就说不通(改变数字的值本身就说不通)。然而对字符串来说就没那么明显了,因为字符串看起来是由字符组成的数组,如果想通过制定索引修改字符串中的字符,实际上,JS 是禁止这样做的。
var s = "hello";
s.toUpperCase();
s  //hello
  • 原始值的比较是指的比较:只有值相等的时候它们才会相等。同样,对字符串来说并不明显,如果比较两个单独的字符串,当它们的长度相等且每个索引的字符都相等时,JS 才会认为它们相等。

    对象和原始值不同,首先,它们是可变的,它们的值是可修改的:
var o = { x : 1};
o.x = 2;	  // 修改 x 值为 2
o.y = 3;  // 添加一个新属性 y ,并复制 3

var a = [1,2,3];
a[0] = 0;   // 修改 a[0] 为 0
a[3] = 4;   // 增加一个元素在a[3]
a[6] = 6;   // 增加数组长度到 7,赋值a[6],中间未赋值的值为 undefined

对象的比较并非值的比较,即使两个对象包含同样的属性和同样的值,他们也是不相等的。各个索引元素完全相等的两个数组也是不相等的

var o = { x:1 },p = { x:1};
o == p //false
o === p //false
var a = [],b = [];
a == b //false
a === b //false
  • 通常将对象成为引用类型,以此来和 JavaScript 的基本类型区分开来。对象值都是引用,对象的比较均是引用的比较,当且但当它们引用同一个基对象时,他们才相等。
var a = [];
var b = a;
b[0] = 1;
a[0]  //1
a === b //true

var o = {};
var p = o;
p.x = 1;
o.x  //1
o === p  //true

将一个对象(或数组)赋值给一个变量,仅仅是赋值的引用值,对象本身没有被复制以此。如果想得好一个对象或数组的副本,则必须现式复制对象的每一个属性或数组的每个元素。

var a = ['a','b','c'];
var b = [];
for(var i = 0;i < a.length; i++){
  b[i] = a[i]
}

类型转换

转换为:
字符串 数字 布尔值 对象
undefined "undefined" NaN false throws TypeError
null "null" 0 false throws TypeError
true "true" 1 new Boolean(true)
fasle "fasle" 0 new Boolean(false)
"" 0 false New String("")
"1.2" 1.2 true New String("1.2")
"one" NaN true New String("one")
0 "0" false New Number(0)
-0 "0" false New Number(-0)
NaN "NaN" false New Number(NaN)
Infinity "Infinity" true New Number(Infinity)
-Infinity "-Infinity" true New Number(-Infinity)
1(无穷大) "1" true
{}任意对象 true
[]任意数组 "" 0 true
[9]1个数字元素 "9" 9 true
['a']其他数组 使用 join() NaN true
function(){}任意函数 NaN true
  • 一个值转换为另外一个值并不意味着两个值相等。JS 运算符和语句期望使用多样化的数据类型,并可以相互转换,但 == 运算符从不试图将其操作数转换为布尔值。

  • 除了 null 和 undefined 之外的任何值都具有 toString() 方法,这个方法的执行结果通常和 String()方法返回结果一致。

    • 如果试图将 null 或 undefined 转换为对象,则会抛出一个类型错误
    • Object()函数在这种情况下不会抛出异常,它简单返回一个新创建的空对象。
  • Number 类定义的 toString()方法可以接受表示转换基数的可选参数,如果不制定此参数,默认基于十进制

var n = 17;
var Binary_string = n.toString(2);  // 10001
var Octal_string = '0'+ n.toString(8); // 021
var Hex_string = '0x' + n.toString(16); //0x11
  • toFixed()根据小数点后的制定位数将数字转换为字符串
var n = 12345.789;
n.toFixed(0) // 123456
n.toFixed(2) // 123456.79
n.toFixed(5) // 123456,78900
  • toExponential()使用指数记数法将数字转换为指数形式的字符串,其中小数点前只有一位,小数点后的位数则由参数制定(有效数字位数比制定的位数要多一位)
n.toExponential(1) // 1.2e+5
n.toExponential(3) // 1.235e+5
  • toPrecision()根据制定的有效数字位数将数字转换成字符串。如果有效数字的位数小于数字整数部分的位数,则转换成指数形式。
n.toPrecision(4) // 1.235e+5
n.toPrecision(7) // 123456.8
n.toPrecision(10) // 123456,7890