该类型只有一个值,就是undefined。当使用var或let声明了变量但没有赋值时,就相当于给变量赋值了undefined值。
对于未声明的变量,只能执行一个有用的操作,就是对它调用typeof。无论是声明还是未声明,typeof 返回的都是字符串"undefined"。
Null类型只有一个值,即特殊值null。null值表示一个空对象指针(所以typeof null会返回object)。
在定义将来要保存对象值的变量时,建议使用null来初始化,不要使用其他值。
console.log(null == undefined); // true
console.log(null === undefined); // false
Boolean有两个字面量:true 和 false。(布尔值 true 和 false是区分大小写的,True 和 False是有效的标识符)
其他类型转Boolean可以调用Boolean()
,下表总结了不同类型与布尔值之间的转换规则:
数据类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
Boolean | true | false |
String | 非空字符串 | “”(空字符串) |
Number | 非零数值(包括无穷值) | 0、NaN |
Object | 任意对象 | null |
Undefined | N/A(不存在) | undefined |
Boolean([]) // true
Boolean({}) // true
(if等流控制语句会制动执行其他类型值到布尔值的转换)
Number类型使用IEEE 754格式表示整数和浮点数(即双精度)。不同的数值类型有对应不同的数值字面格式:
0
开头,如 075;当字面量中包含的数字超过了应有范围,就会忽略前面的 0,后面的数字序列会被当做十进制处理,如 08 就是十进制的 8。(八进制字面量在严格模式下是无效的,会导致js引擎抛出语法错误。ECMAScript 2015或ES6中的八进制以 0o
开头;严格模式下 0
会被视为语法错误,如果要表达八进制值,应该使用 0o
)0x
开头。(使用八进制和十六进制格式创建的数值在所有数学操作中都会被视为十进制)
console.log(010 + 010) // 16
console.log(0x10 + 0x10) // 32
由于js保存数值的方式,在实际中可能存在正零(+0)或负零(-0),但它们在所有情况下都被认为是等同的。
(+0) === (-0) // true
1.0 + 1.0 // 2
不要去测试某个特定的浮点数
,如 0.1 + 0.2 == 0.3 // false
Number.MIN_VALUE
中,可以表示的最大值保存在 Number.MAX_VALUE
.Infinity
。任何无法表示的负数以-Infinity
(负无穷大,可用 Number.POSITIVE_INFINITY
取得),任何无法表示的正数以 Infinity
(正无穷大,可用 Number.NEGATIVE_INFINITY
取得)表示。isFinite()
函数(返回false为无穷值,返回true为非无穷值)。有一个特殊的值叫 NaN
,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。
console.log(0/0); // NaN
console.log(-0/+0); // NaN
console.log(5/0); // Infinity
console.log(5/-0); // -Infinity
NaN
的操作始终会返回 NaN
NaN
不等于 NaN
在内的任何值isNaN()
函数可以判断传入参数是否为数值 console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10是数值
console.log(isNaN("11")); // false,可以转换为数值11
console.log(isNaN("hsy")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值1
Number()
:规则如下类型 | 规则 |
---|---|
Boolean | true转换为1,false转换为0 |
Number | 直接返回 |
null | 返回0 |
undefined | 返回NaN |
String | 如果字符串包含数值,数值前面带加减号的情况,则会被转换为一个十进制的数值(会忽略前面的0); 如果字符串包含有效的浮点值格式,则会被转换为相应的浮点值(会忽略前面的0); 如果字符串包含有效的十六进制格式,则会被转换为该十六进制对应的十进制整数值; 如果是空字符串,则返回0; 其他情况返回NaN。 |
Object | 调用valueOf() 方法,并按照上述规则转换返回值。如果转换的结果是NaN,则调用toString() 方法,在按照转换字符串的规则转换 |
一元加操作符遵循与Number()相同的转换规律
1 + 1 // 2
1 + '1' // '11' 一元加操作符有隐形转换字符串类型的特性
'1' + '1' // '11'
parseInt()
:专注于字符串是否包含数值的模式。字符串前面的空格会被忽略,从第一个非空字符开始转换。
如果第一个字符不是数值字符、加号、减号,立即返回NaN(空字符串也会返回NaN)。
如果第一个字符是数值字符、加号、减号,则会继续依次检测每个字符,直至字符串末尾或碰到非数值字符串。如“123hsy”会被转换为123,“22.7”会被转换为22。
parseInt()
也能识别不同进制格式。但如果字符串以“0”开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为八进制整数。
不同数值格式很容易混淆,因此parseInt()
可以传入第二个参数,用于指定底数(进制数),当提供了进制参数时,进制格式前面的“0x”可以省略。如parseInt(‘AF’,16)返回175。建议始终传入第二个进制参数
Number('010') // 10
parseInt('010') // 10
parseInt('010', 8) // 8
parseInt('010hsy', 8) // 8
parseInt('010.1') // 10
parseInt('0x010') // 16
parseFloat()
:与parseInt()
类似。但其第一次解析到的小数点是有效的。它始终忽略字符串开的的零,十六进制数值始终会返回0。它只能解析十进制,但能识别所有浮点格式(包括科学计数法)。如果字符串表示整数(没有小数点或小数点后面只有零),则会返回整数(js总是想方设法地把数转化为整数)。
js中Numberr类型只能安全的表示-253+1 和 253-1 任何超出此范围的整数值都可能失去精度。BigInt 是一种内置对象,它提供了一种方法来表示超出 [-253+1, 253-1] 范围的整数。BigInt 可以用任意精度表示整数。
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数 BigInt()(但不包含 new 运算符)并传递一个整数值或字符串值。
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// ? 9007199254740991n
const hugeString = BigInt("9007199254740991");
// ? 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// ? 9007199254740991n
const hugeBin = BigInt(
"0b11111111111111111111111111111111111111111111111111111",
);
// ? 9007199254740991n
typeof 1n === "bigint"; // true
typeof BigInt("1") === "bigint"; // true
typeof Object(1n) === "object"; // true
const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER);
// ? 9007199254740991n
const maxPlusOne = previousMaxSafe + 1n;
// ? 9007199254740992n
const theFuture = previousMaxSafe + 2n;
// ? 9007199254740993n
const multi = previousMaxSafe * 2n;
// ? 18014398509481982n
const subtr = multi – 10n;
// ? 18014398509481972n
const mod = multi % 10n;
// ? 2n
const bigN = 2n ** 54n;
// ? 18014398509481984n
bigN * -1n
// ? –18014398509481984n
const expected = 4n / 2n;
// ? 2n
const rounded = 5n / 2n;
// ? 2n, not 2.5n
BigInt 和 Number 不是严格相等的,但是宽松相等的。
0n === 0;
// ? false
0n == 0;
// ? true
String字符串类型表示零个或多个16位Unicode字符序列。可用单引号、双引号或反引号标示。
字面量 | 含义 |
---|---|
\n | 换行 |
\t | 制表 |
\b | 退格 |
\r | 回车 |
\f | 换页 |
\xnn | 以十六进制编码nn表示的字符(其中n为十六进制数字) |
\unnnn | 以十六进制编码的Unicode字符(其中n为十六进制数字) |
字符串的长度可以通过length属性
获取,这个属性返回字符串中16位字符的个数。如果字符串中包含双字节字符,那么length属性返回的值可能不是准确的字符数。
ECMAScript中的字符串是不可变的,一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。
toString()
:可用于数值、布尔值、对象和字符串值。null 和 undefined 值没有该方法。在对数值调用时,可以接收一个底数传参,即以什么底数来输出数值的字符串表示。String()
:如果不确定一个值是不是null 或 undefined ,可以使用该方法。如果值有toString()方法,则调用该方法(不传参)并返回结果;如果值是null,返回“null”;如果值是undefined,返回“undefined”。用反引号`包裹,与使用单引号或双引号不用,模板字面量保留换行字符,可以跨行定义字符串。模板字面量会保持反引号内部的空格。
字符串插值通过${}
在模板字面量里使用一个js表达式。所有插入的值都会使用toString()
强制转换为字符串,任何js表达式都可以用于插入值。
模板字面量支持定义标签函数,而通过便签函数可以自定义插值行为。
let a = 6;
let b = 9;
function simpleTag(strings, ...expressions) {
console.log(strings);
for (const expression of expressions) {
console.log(expression);
}
return 'foobar';
}
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(taggedResult); // foobar
function zipTag(strings, ...expressions) {
return strings[0] +
expressions.map((e, i) => `${e}${strings[i + 1]}`)
.join('');
}
let taggedResult2 = zipTag`${ a } + ${ b } = ${ a + b }`;
console.log(taggedResult2); // 6 + 9 = 15
使用模板字面量可以直接获取原始的模板字面量内容,而不是被转换后的字符表示。为此可以使用磨人的String.raw
标签函数。
符号是原始值,且符号实例是唯一、不可变的 。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。符号就是用来创建唯一记号,进而作用非字符串形式的对象属性。
符号需要使用 Symbol()
函数初始化。
let sym = Symbol();
console.log(typeof sym); // symbol
调用 Symbol()
函数时,可以传入一个字符串作为对符号的描述,将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全没有关系。
let sym1 = Symbol('foo');
let sym2 = Symbol('foo');
console.log(sym1 == sym2); // false
符号没有字面量语法。
Symbol()
函数不能与new关键字一起作为构造函数使用。
let myBoolean = new Boolean();
console.log(typeof myBoolean); // "object"
let myString = new String();
console.log(typeof myString); // "object"
let myNumber = new Number();
console.log(typeof myNumber); // "object"
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,使用 Symbol.for()
在全局符号注册表中创建并重用符号。
let fooSymbol = Symbol.for('foo');
console.log(typeof fooSymbol); // symbol
Symbol.for()
对没分字符串键都执行幂等操作。第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应符号,就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用会同样检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。
let fooSymbol = Symbol.for('foo'); // 创建新符号
let otherFooSymbol = Symbol.for('foo'); // 重用已有符号
console.log(fooSymbol === otherFooSymbol); // true
即使使用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol()
定义的符号也并不相等。
let fooSymbol = Symbol.for('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol === otherFooSymbol); // false
全局注册表中的符号必须使用字符串来创建,因此作为参数传给Symbol.for()
的任何值都会被转换为字符串。注册表中使用的键同时也会被用作符号描述。
let mySymbol = Symbol.for();
console.log(mySymbol); // Symbol(undefined)
可以使用 Symbol.keyFor()
来查询全局注册表,这个方法接收符号,返回该全局符号对应的字符串键。如果查询的不是全局符号,则返回undefined。如果传给 Symbol.keyFor()
的不是符号,则该方法抛出TypeError。
let mySymbol1 = Symbol.for('foo');
console.log(Symbol.keyFor(mySymbol1)); // foo
let mySymbol2 = Symbol('foo');
console.log(Symbol.keyFor(mySymbol2)); // undefined
Symbol.keyFor(123); // TypeError: 123 is not a symbol
凡是可以使用字符串或数值作为属性的地方,都可以使用符号。这包括了对象字面量属性和Object.defineProperty()
/ Object.defineProperties()
定义的属性。对象字面量只能在计算属性语法中使用符号作为属性。
let s1 = Symbol('foo'),
s2 = Symbol('bar'),
s3 = Symbol('baz'),
s4 = Symbol('qux');
let o = {
[s1]: 'foo val'
};
console.log(o); // { [Symbol(foo)]: 'foo val' }
Object.defineProperty(o, s2, {value: 'bar val'});
console.log(o); // { [Symbol(foo)]: 'foo val', [Symbol(bar)]: 'bar val' }
Object.defineProperties(o, {
[s3]: {value: 'baz val'},
[s4]: {value: 'qux val'}
})
console.log(o); // { [Symbol(foo)]: 'foo val', [Symbol(bar)]: 'bar val', [Symbol(baz)]: 'baz val', [Symbol(qux)]: 'qux val' }
类似于 Object.getOwnPropertyNames()
返回对象实例的常规属性数组, Object.getOwnPropertySymbols()
返回实例的符号属性数组。这两个方法的返回值彼此互斥。 Object.getOwnPropertyDescriptors()
会返回同时包含常规和符号属性描述符的对象。 Reflect.ownKeys()
会返回两种类型的键。
let s1 = Symbol('foo'),
s2 = Symbol('bar');
let o = {
[s1]: 'foo val',
[s2]: 'bar val',
baz: 'baz val',
qux: 'qux val'
};
console.log(Object.getOwnPropertyNames(o)); // [ 'baz', 'qux' ]
console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(foo), Symbol(bar) ]
console.log(Object.getOwnPropertyDescriptors(o)); // { baz: {...}, qux: {...}, [Symbol(foo)]: {...}, [Symbol(bar)]: {...}}
console.log(Reflect.ownKeys(o)); // [ 'baz', 'qux', Symbol(foo), Symbol(bar) ]
在提到ECMAScript规范时,经常会引用符号在规范中的名称,前缀为 @@
。 比如@@iterator指的是Symbol.iterator。
js中万物皆对象
对象其实就是一组数据和功能的集合。每个Object对象实例都有以下属性和方法:
constructor
:用于创建当前对象的函数。hasOwnProperty(propertyName)
:用于判断当前对象实例(不是原型)上是否存在给行的属性。要检查的属性名必须是字符串。isPrototypeOf(object)
:用于判断当前对象是否为另一个对象的原型。propertyIsEnumerable(propertyName)
:用于判断给定的属性是否可以使用for-in语句枚举。要检查的属性名必须是字符串。toLocaleString()
:返回对象的字符串表示,改字符串反映对象所在的本地执行环境。toString()
:返回对象的字符串表示。valueOf()
:返回对象对应的字符串、数值、或布尔值表示。通常与toString()的返回值相同。返回值:
"undefined"
:表示值未定义;"boolean"
:表示值为布尔值;"string"
:表示值为字符串;"number"
:表示值为数值;"object"
:表示值为对象(而不是函数)或null;"function"
:表示值为函数;"symbol"
:表示值为符号调用 typeof null 返回的是"boject",这是因为特殊值null被认为是一个空对象的引用。