JavaScript$Data-Value-DataTypes

100 阅读1分钟

JavaScript$Data-Value-DataTypes

0.1 Data Types

MND_DataTypes

程序是现实的映射。

现实生活中的“存在”,都可以用数据 data 来模拟。不同类型的“存在”需要不同的数据类型表示。在 JavaScript 中,数据类型分为两大类:

注意,是数据类型,不是变量类型。
- JavaScript 是动态类型语言 dynamic language:变量的类型是可以改变的
- JavaScript 是弱类型语言 weakly typed language:在需要 a 类型的地方使用 b 类型,值会自动转换成 a 类型(symbol 和 bigint 不会隐式转换,如果类型错误将会报错)
  • primitive types (immutable):
    • number: 1, 0xff, 0b100_001, .33, 6.02e23, 1.4E-32, NaN, Infinity
    • string: 'name', 'c'
    • boolean: true, false
    • undefined: undefined
    • null: null
    • symbol: Symbol(), Symbol('Tom') !== Symbol('Tom'), Symbol.for('Jerry) === Symbol.for('Jerry')
    • bigint: 12n, oxf2n, BigInt(10), BigInt('15')
  • object types (mutable): properties(, methods)
    • ordinary JavaScript Object (an unordered collection of named values): {}, {name: 'Tom'}
    • Array (an ordered collection of numbered values): [], [1, 'Tom']
    • Set (a set of values): new Set([1, 2, 2])
    • Map (a mapping from keys to values): new Map([[1,2], [2,4]])
    • Regexp: /[1-5]/, new RegExp('[1-5]')
    • Date: new Date()
    • Error: new Error('not allowed')
    • ...

0.2 typeof

typeof 用来检测数据类型。需要注意的是:

  • typeof 返回的是小写字符串
  • typeof null === 'object'
  • typeof undefinedVariable === 'undefined'
  • 对象类型的都返回 'object', 除了函数: typeof function(){} === 'function'

1. Primitive Types

MND_Primitive

除了 nullundefined,其他的基本数据类型都有包装类 wrapper types。当在这些数据上调用方法时,其实是调用包装类的方法。这也意味着在 nullundefined 上调用方法将报错 TypeError。(解决方式是 optional chaining operator ?.

1.1 Numbers

一些语言中,数字类型分为整型和浮点型;在 JavaScript 中,只有数字类型,整型在内部也是以浮点型的形式存储的。The Number type is a double-precision 64-bit binary format IEEE 754 value.

  • 浮点型数据范围:210742^{-1074} (Number.MIN_VALUE) - 210242^{1024} (Number.MAX_VALUE),以及负数对应的范围。超过范围时可能会变成 +Infinity / -Infinity / +0 / -0Number.MAX_VALUE + 1 并不是 InfinityNumber.MAX_VALUE * 2 才是)
  • 整型数据范围: -(25312^{53}−1) (Number.MIN_SAFE_INTEGER) - 25312^{53}−1 (Number.MAX_SAFE_INTEGER)。超过这个范围就是去了精度。使用 Number.isSafeInteger() 来判断。
    • certain operations in JavaScript (such as array indexing and the bitwise operators) are performed with 32-bit integers.

因为浮点型的精度问题,ES2020 引入了 BigInt 来解决整型数据的精度问题。

numeric literal && constants

  1. Integer Literals
    1. 12 // decimal
    2. 0xff // hexadecimal literal (hexadecimal digits)
    3. 0o377 // octal (not recommended)
    4. 0b100_001 // binary, separators
    • error: _101, 101_, 0_01, 1._4
  2. Floating-Point Literals
    1. 3.14
    2. .333
    3. 6.02e23
    4. 1.4E-32
  3. 内置对象 / 全局 定义的常量
    1. Math.PI, Math.E
    2. Infinity // -Infinity; 1/0, Number.MAX_VALUE * 2
    3. NaN // Number.NaN; 0/0, Infinity/Infinity, NaN !== NaN
    4. Number.POSITIVE_INFINITY // Number.NEGATIVE_INFINITY
    5. Number.MAX_VALUE // Number.MIN_VALUE
    6. Number.MAX_SAFE_INTEGER // Number.MIN_SAFE_INTEGER // -(2**53-1)
    7. Number.EPSILON

Arithmetic in JavaScript

arithmetic operators: +(addition), -(subtraction), *(multiplication), /(division), %(modulo), **(exponentiation).

Math.pow(2, 53) // 2**53; Math.sqrt(3) // 3**(1/2); 
Math.floor(.6), Math.round(.6), Math.ceil(.6), Math.turnc(3.9), Math.abs(-5)
Math.max(x, y, z), Math.min(x, y, z)
Math.random()
// other methods in Math
Number.parseInt(), Number.parseFloat()
Number.isNaN() // different from isNaN(), see below
Number.isFinite() // different form isFinite(), see below
Number.isInteger()
Number.isSafeInteger() // (-2**53, 2**53)
let a = {}
isNaN(a) // true
Number.isNaN(a) // false

let b = '15'
isFinite(b) // true
Number.isFinite(b) // false

Binary Floating-Point and Rounding Errors

let x = .3 - .2
let y = .2 - .1
x === y // => false
x === .1 // => false
y === .1 // => true

1.2 Strings (Text)

字符串 string 通过 16 位整型数来表示文本信息,一些字符需要超过 16 位来表示,这时 .length 表示的不是字符数,而是占用内存的 16 bits 的倍数。

The String type represents textual data and is encoded as a sequence of 16-bit unsigned integer values representing UTF-16 code units.

Each element in the string occupies a position in the string. The first element is at index 0, the next at index 1, and so on.

The length of a string is the number of UTF-16 code units in it, which may not correspond to the actual number of Unicode characters; see the String reference page for more details.

Most string-manipulation methods defined by JavaScript operate on 16-bit values, not characters. for/of loop or ... operator get the actual characters, not the 16-bit values.

1. String Literals

string delimiter: single / double quotes or backticks (', ", `)
break a string literal across multiple lines: backslash(\) at the end of the line.
\n: next line.
// contractions, possessives, apostrophe; single-quoted strings.

2. Escape Sequences in String Literals

We use escape sequences to express "special characters". escape sequence: the backslash character (\) combined with the character that follows it.

常用的有:\n, \t, \\, \', \xnn, \unnnn, \u{n}

MDN_CharacterEscape

EscapeSequences

3. Template Literals

模板字符串的基本功能是在字符串中插入变量,插入的变量自动转换成字符串。

let name = "Tom";
let greeting = `Hello ${ name }.`; // greeting == "Hello Tom."
tagged template literals

如果把函数的() 换成两个反引号,那么引号里的字符串、表达式的值(如果使用${})将会作为参数传到该函数中。

If a function name (or "tag") comes right before the opening backtick, then the text and the values of the expressions within the template literal are passed to the function.

String.raw():
`\n`.length // => 1
String.raw`\n`.length

4. Working with Strings

let msg = 'Hello, ' + 'world'
===, !==; <, <=, >, >=. ('a'.localeCompare('b'))
s.length // unicode, not chars
let s = "Hello, world"

// get portions
s.substring(1,4) // => "ell"
s.slice(1,4) // => "ell"
s.slice(-3) // => "rld"
s.split(", ") // delimiter string

// search index
s.indexOf("l") // => 2
s.indexOf("l", 3) // => 3: at or after 3
s.indexOf("zz") // => -1
s.lastIndexOf("l") // => 10

// test
s.startsWith("Hell") // => true
s.endsWith("!") // => false
s.includes("or") // => true

// update (return another string)
s.replace("llo", "ya") // => "Heya, world"
s.toLowerCase() // => "hello, world"
s.toUpperCase() // => "HELLO, WORLD"
s.normalize() // Unicode NFC normalization: ES6
s.normalize("NFD") // NFD normalization. Also "NFKC", "NFKD"

// getOne: Inspecting individual (16-bit) characters of a string
s.charAt(0) // => "H"
s.charAt(s.length-1) // => "d"
s.charCodeAt(0) // => 72: 16-bit number at the specified position
s.codePointAt(0) // => 72: works for codepoints > 16 bits

// update (padding)
"x".padStart(3) // => " x"
"x".padEnd(3) // => "x "
"x".padStart(3, "*") // => "**x"
"x".padEnd(3, "-") // => "x--"

// update (trim)
" test ".trim() // => "test"
" test ".trimStart() // => "test ". Also trimLeft
" test ".trimEnd() // => " test". Also trimRight

// Miscellaneous string methods
s.concat("!") // => "Hello, world!": just use + operator instead
"<>".repeat(5) // => "<><><><><>": concatenate n copies

// strings can also be treated like read-only arrays
let s = 'hello, world'
s[0] // => "h"
s[s.length - 1] // => "d"

1.3 Boolean Values

true / false (truthy / falsy, truth / falsehood)
false: undefined, null, 0, -0, 0n, NaN, ""
Boolean(), !!
toString()
&&, ||, !

1.4 null and undefined

null: indicate the absence of an object. // NULL, nil, None.
typeof null => 'object'
undefined: a deeper kind of absence, the value of variables that have not been initialized, the value of undefined object property or array element.
typeof undefined => 'undefined'
typeof undefinedVariable => 'undefined'

1.5 Symbols

Symbol 的作用是为了预防字符串作为属性的冲突。后来语言的内部机制也利用了 Symbol。(比如 Symbol.iterator 作为方法名来实现对象的迭代。)

Symbols 用两种形式:

  • 唯一值,每一次调用都不同:Symbol(), Symbol('unique')
  • 全局仓库,多次调用返回同一值:Symbol.for('shared')
let s = Symbol("sym_x"); // unique
s.toString() // => "Symbol(sym_x)"

// global Symbol registry
let s = Symbol.for("shared");
let t = Symbol.for("shared");
s === t // => true
s.toString() // => "Symbol(shared)"
Symbol.keyFor(t) // => "shared" // only for Symbol.for()

1.6 BigInt

123n
0b111111n
0o7777n
0x8000000000000000n // => 2n**63n: A 64-bit integer

BigInt(Number.MAX_SAFE_INTEGER) // => 9007199254740991n
let string = "1" + "0".repeat(100); // 1 followed by 100 zeros.
BigInt(string) // => 10n**100n: one googol

1000n + 2000n
3000n - 2000n
2000n * 3000n
3000n / 997n // => 3n
3000n % 997n // => 9n
(2n ** 131071n) - 1n // A Mersenne prime with 39457 decimal digits

1 < 2n // => true
2 > 1n // => true
0 == 0n // => true
0 === 0n // => false

BigInt.asintN(4, -1n) // -1n
BigInt.asUnitN(4, -1n) // 15n
// BigInt does not support JSON serialization
let data = {
  bigNumber: 1234n
};
JSON.stringify(data); // TypeError
const replacer = (k, v) => typeof v === 'bigint' ? v.toString() : v;
JSON.stringify(data, replacer);
// {"bigNumber": "1234" }
const reviver = (k, v) => k === "bigNumber" ? BigInt(v) : v;
JSON.parse(`{"bigNumber": "1234"}`, reviver);
// { bigNumber: 1234n }

2. Objects

基本数据类型可以看作是一个个学生,它们不能改变。对象数据类型则像是教室,它们由一些学生和 / 或其他教室组成,可以改变内部内容。

这些学生或者教室,在对象看来,是属性 properties。虽然我们常说属性和方法,但是方法其实也是一种属性。

属性的 keys 是 strings / symbols,数组 1 这种的 key,会被转换成 '1'

属性的值分为两种,每种属性由对应的 attributes 描述,可通过 Object.defineProperty() 来设置:

  • data property:数据 value。
    • value:对应的值。多数情况我们定义对象时设置的都是这个值。
    • writable:值是否可设置。
    • enumerablefor...in 是否可获取。
    • configurable:值 property 是否可以删除、改为 accessor property, attributes 是否可改
  • accessor property:行为 get / set。
    • get:函数,返回值。
    • set:函数,设置值。
    • enumerable
    • configurable

The Global Object

环境加载后会有一个全局对象 global object。这个全局对象定义了一些数据:

- Global contants like undefined, Infinity, and NaN
- Global objects like Math and JSON
- Global functions like isNaN(), parseInt(), and eval()
- Constructor functions like Date(), RegExp(), String(), Object(), and Array()

不同环境的全局对象的名字不同,ES2020 统一为 globalThis

- Window: window
- Node: global
- Web worker: self

- Immutable Primitive Values & Mutable Object References

  • Primitives are immutable: there is no way to change (or "mutate") a primitive value. (compared by value)
  • Objects are mutable. (compared by value => if you regard that value as an address / a pointer) (object values are references.)
// copy
let a = ["a","b","c"]; // An array we want to copy
let b = []; // A distinct array we'll copy into
for(let i = 0; i < a.length; i++) { // For each index of a[]
	b[i] = a[i]; // Copy an element of a into b
}

let c = Array.from(b); // ES6

let obj2 = structureClone(obj)
function equalArrays(a, b) {
	if (a === b) return true; // Identical arrays are equal
	if (a.length !== b.length) return false; // Different-size arrays not equal
	for(let i = 0; i < a.length; i++) { // Loop through all elements
		if (a[i] !== b[i]) return false; // If any differ, arrays not equal
	}
	return true; // Otherwise they are equal
}

3. Type Conversions

类型转换:把 a 类型的数据转换成 b 类型。分为两种情况:

  • 隐式类型转换:在需要 b 类型的地方使用 a 类型的数据,数据自动转换为 b 类型供环境使用
  • 显式类型转换:强制将 a 类型转换成 b 类型

不是类型转换:使用 == 时,并没有将两边的数据进行隐式类型转换,而是由 JavaScript 标准定义的规则决定的。(运算符相关的内容将详解)

1. 隐式类型转换 Implicit Conversions

基本类型的隐式类型转换:

  • Number:undefined => NaN, null => 0, '' => 0; [] => 0, [9] => 9
  • Boolean: undefined, null, 0, -0, 0n, NaN, ""

2. 显式类型转换 Explicit Conversions

to Number

Number() // 可接受前后空格,不合规 => NaN (leading and trailing spaces)
parseInt(), parseFloat() // parseInt() accepts an optional radix/base argument
+x, x - 0
Intl.NumberFormat
parseInt("3 blind mice") // => 3
parseFloat(" 3.14 meters") // => 3.14
parseInt("-12.34") // => -12
parseInt("0xFF") // => 255
parseInt("0xff") // => 255
parseInt("-0XFF") // => -255
parseFloat(".1") // => 0.1
parseInt("0.1") // => 0
parseInt(".1") // => NaN
parseFloat("$72.47") // => NaN

parseInt("11", 2) // => 3: (1*2 + 1)
parseInt("ff", 16) // => 255: (15*16 + 15)
parseInt("zz", 36) // => 1295: (35*36 + 35)
parseInt("077", 8) // => 63: (7*8 + 7)
parseInt("077", 10) // => 77: (7*10 + 7)

to String

String()
toString() // 5.toString() wrong!, (5).toString()
x + ""
to String with Numbers
// toString with Numbers: (optional argument that specifies a radix, or base.)
let n = 17;
let binary = "0b" + n.toString(2); // binary == "0b10001"
let octal = "0o" + n.toString(8); // octal == "0o21"
let hex = "0x" + n.toString(16); // hex == "0x11"

// toString with Numbers for financial / scientific: (all 3 methods round the trailing digits or pad with zeros at appropriate)
let n = 123456.789;
n.toFixed(0) // => "123457"
n.toFixed(5) // => "123456.78900"
n.toExponential(1) // => "1.2e+5"
n.toExponential(3) // => "1.235e+5"
n.toPrecision(4) // => "1.235e+5"
n.toPrecision(7) // => "123456.8"
n.toPrecision(10) // => "123456.7890"

to Boolean

Boolean()
!!x