JavaScript Basic Notes
TC39
JavaScript = ECMAScript + DOM + BOM:
- ECMAScript: ECMA-262.
- DOM: DOM Core + DOM HTML (
document
). - BOM: Browser Object Model API (HTML5)
(
window
/navigator
/location
/screen
/performance
etc).
Primitive Values
Primitive data types:
- Undefined.
- Null.
- Boolean.
- Number.
- String.
- Symbol.
- BigInt.
Undefined
- 对象属性未定义时, 该属性值为
undefined
. - 未初始化变量的初值为
undefined
(表示等待被赋值).
Null
当引用为空或引用对象不存在时, 值为 null
.
null
值表示一个空对象指针.
Null
typeof null
-> object
.
Boolean
Zero Value Expression
零值表达式:
undefined
.null
.false
.NaN
.0
0n
.''
.
Boolean Conversion
x | Boolean(x) |
---|---|
undefined | false |
null | false |
boolean | x |
number | 0 → false , NaN → false |
Other numbers → true | |
bigint | 0n → false |
Other numbers → true | |
string | '' → false |
Other strings → true | |
symbol | true |
object | true |
Number
- Binary:
0b10
/0B10
. - Octal:
0o23
/0O23
. - Hex:
0xFF
. **
指数运算符.- BigInt.
const a = 2172141653
const b = 15346349309
const c1 = a * b
// => 33334444555566670000
const c2 = BigInt(a) * BigInt(b)
// => 33334444555566667777n
const inhabitantsOfLondon = 1_335_000
const distanceEarthSunInKm = 149_600_000
const fileSystemPermission = 0b111_111_000
const bytes = 0b1111_10101011_11110000_00001101
const words = 0xF3B_F00D
const massOfElectronInKg = 9.109_383_56e-31
const trillionInShortScale = 1e1_2
Number Conversion
x | Number(x) |
---|---|
undefined | NaN |
null | 0 |
boolean | false → 0 , true → 1 |
number | x |
bigint | -1n → -1 , 1n → 1 |
string | '' → 0 |
Other → parsed number, ignoring leading/trailing whitespace | |
symbol | Throws TypeError |
object | Configurable ([Symbol.toPrimitive]() /valueOf() ) |
assert.equal(Number(123.45), 123.45)
assert.equal(Number(''), 0)
assert.equal(Number('\n 123.45 \t'), 123.45)
assert.equal(Number('xyz'), Number.NaN)
assert.equal(Number(-123n), -123)
assert.equal(
Number({
valueOf() {
return 123
},
}),
123
)
Number Static Properties
Number.NaN
.Number.NEGATIVE_INFINITY
.Number.POSITIVE_INFINITY
.Number.MAX_SAFE_INTEGER
.Number.MIN_SAFE_INTEGER
.Number.EPSILON
.Number.isNaN()
.Number.isFinite()
.Number.isInteger()
.Number.isSafeInteger()
.Number.toExponential()
.Number.toFixed()
.Number.toPrecision()
.Number.parseInt(string, radix)
.Number.parseFloat(string)
.
;(1234).toExponential()
// '1.234e+3'
;(1234).toExponential(5)
// '1.23400e+3'
;(1234).toExponential(1)
// '1.2e+3'
;(0.003).toExponential()
// '3e-3'
;(0.00000012).toFixed(10)
// '0.0000001200'
;(0.00000012).toFixed()
// '0'
;(10 ** 21).toFixed()
// '1e+21'
;(1234).toPrecision(3)
// '1.23e+3'
;(1234).toPrecision(4)
// '1234'
;(1234).toPrecision(5)
// '1234.0'
;(1.234).toPrecision(3)
// '1.23'
Not A Number
const numberType = typeof Number.NaN // 'number'
Number.isFinite(Number.NaN)
// false
Number.isNaN(Number.NaN)
// true
Number.isNaN(123)
// false
Number.isNaN('abc')
// false
function isNumber(value) {
return typeof value === 'number' && Number.isFinite(value)
}
NaN
NaN === NaN
-> false
.
Infinity Number
Infinity represents all values greater than 1.7976931348623157e+308.
Infinity will be converted to null
with JSON.stringify()
.
const largeNumber = 1.7976931348623157e308
// eslint-disable-next-line ts/no-loss-of-precision
const largerNumber = 1.7976931348623157e309
console.log(largeNumber) // 1.7976931348623157e+308
console.log(largerNumber) // Infinity
console.log(46 / 0) // Infinity
console.log(Number.POSITIVE_INFINITY) // Infinity
console.log(Number.MAX_VALUE) // Infinity
// eslint-disable-next-line ts/no-loss-of-precision
console.log(-1.7976931348623157e309) // -Infinity
console.log(-46 / 0) // -Infinity
console.log(Number.NEGATIVE_INFINITY) // -Infinity
console.log(Number.MIN_VALUE) // -Infinity
console.log(Math.max()) // -Infinity
console.log(Math.min()) // Infinity
Number.isFinite(Number.POSITIVE_INFINITY)
// false
Number.isFinite(Number.NEGATIVE_INFINITY)
// false
Number.isFinite(Number.NaN)
// false
Number.isFinite(123)
// true
Safe Number
- Safe integers:
- Precision: 53 bits plus sign.
- Range: (
−2^53
,2^53
).
- Array indices:
- Precision: 32 bits, unsigned
- Range: [
0
,2^32−1
). - Typed Arrays have a larger range of 53 bits (safe and unsigned).
- Bitwise operators:
- Precision: 32 bits.
- Range of unsigned right shift (
>>>
): unsigned, [0
,2^32
). - Range of all other bitwise operators: signed, [
−2^31
,2^31
).
assert.equal(Number.MAX_SAFE_INTEGER, 2 ** 53 - 1)
assert.equal(Number.MIN_SAFE_INTEGER, -Number.MAX_SAFE_INTEGER)
assert.equal(Number.isSafeInteger(5), true)
assert.equal(Number.isSafeInteger('5'), false)
assert.equal(Number.isSafeInteger(5.1), false)
assert.equal(Number.isSafeInteger(Number.MAX_SAFE_INTEGER), true)
assert.equal(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1), false)
Number.isInteger(-17)
// true
Number.isInteger(33)
// true
Number.isInteger(33.1)
// false
Number.isInteger('33')
// false
Number.isInteger(Number.NaN)
// false
Number.isInteger(Number.POSITIVE_INFINITY)
// false
Float Number
- 计算浮点数时, 应先计算整数, 再利用移位/乘法/除法转化为浮点数.
- 浮点值的精确度最高可达 17 位小数.
const a = (1 + 2) / 10 // a = 0.1 + 0.2;
String
String Primitive Features
作为基本变量:
delete
无法删除某位字符.
String Reference Features
- 赋值与传参: 传递 string 字符串常量的引用.
- 所有 string 字面量都是不可变量, 当对 string 进行操作后, 将先会在堆区创建副本, 再通过副本进行修改, 并返回副本的索引.
for...in
: 返回下标数字.for...of
: 对字符串字符进行遍历.- 没有被任何变量引用的 string: 垃圾回收.
const goodString = 'I\'ve been a good string'
console.log(typeof goodString) // string
console.log(goodString instanceof String) // false
console.log(Object.prototype.toString.call(goodString)) // [object String]
// eslint-disable-next-line no-new-wrappers
const badString = new String('I\'ve been a naughty string')
console.log(typeof badString) // object
console.log(badString instanceof String) // true
console.log(Object.prototype.toString.call(badString)) // [object String]
const isPrimitiveString = value => typeof value === 'string'
console.log(isPrimitiveString(goodString)) // true
console.log(isPrimitiveString(badString)) // false
const isObjectWrappedString = value => value instanceof String
console.log(isObjectWrappedString(goodString)) // false
console.log(isObjectWrappedString(badString)) // true
const isString = value => typeof value === 'string' || value instanceof String
console.log(isString(goodString)) // true
console.log(isString(badString)) // true
function isStringAlternative(value) {
return Object.prototype.toString.call(badString) === '[object String]'
}
console.log(isStringAlternative(goodString)) // true
console.log(isStringAlternative(badString)) // true
String Conversion
x | String(x) |
---|---|
undefined | 'undefined' |
null | 'null' |
boolean | false → 'false' , true → 'true' |
number | 123 → '123' |
bigint | 123n → '123' |
string | x |
symbol | Symbol('abc') → 'Symbol(abc)' |
object | Configurable (toPrimitive /toStringTag /toString() ) |
String Unicode
// eslint-disable-next-line no-self-compare
const truthy = 'z' === 'z' // true
const truthy = '\x7A' === 'z' // true
const truthy = '\u007A' === 'z' // true
const truthy = '\u{7A}' === 'z' // true
String Char Code
string.charAt(index)
.string.charCodeAt(index)
.string.fromCharCode(charCode)
.string.codePointAt(index)
: 正确处理 4 字节存储字符.string.fromCodePoint(codePoint)
: 正确处理 4 字节存储字符.
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF
}
const truthy = String.fromCodePoint(0x78, 0x1F680, 0x79) === 'x\uD83D\uDE80y'
const after = before.charAt(0).toUpperCase() + before.slice(1)
String Slice and Merge
string.slice()
.string.substring()
.string.substr()
.string.split(separator)
: 选择割断符, 返回字符串数组.Array<string>.join(separator)
: 将字符串数组连接成字符串.
const stringValue = 'hello world'
console.log(stringValue.slice(3)) // "lo world"
console.log(stringValue.substring(3)) // "lo world"
console.log(stringValue.substr(3)) // "lo world"
console.log(stringValue.slice(3, 8)) // "lo wo"
console.log(stringValue.substring(3, 8)) // "lo wo"
console.log(stringValue.substr(3, 8)) // "lo world"
console.log(stringValue.slice(-3)) // "rld"
console.log(stringValue.substring(-3)) // "hello world"
console.log(stringValue.substr(-3)) // "rld"
console.log(stringValue.slice(3, -4)) // "lo w"
console.log(stringValue.substring(3, -4)) // "hel"
console.log(stringValue.substr(3, -4)) // "" (empty string)
String Query
string.includes(substr)
.string.startsWith(substr)
.string.endsWith(substr)
.- 使用第二个参数 n 时, endsWith 针对前 n 个字符, 其他两个方法针对从第 n 个位置直到字符串结束.
const s = 'Hello world!'
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
// Arrays difference
;[
[1, 2, 3, 4, 5],
[5, 2, 10],
].reduce((a, b) => a.filter(c => !b.includes(c)))
// [1, 3, 4]
// Arrays intersection
;[
[1, 2, 3],
[101, 2, 1, 10],
[2, 1],
].reduce((a, b) => a.filter(c => b.includes(c)))
// [1, 2]
string.match(RegExp): string[] | null
.string.matchAll(RegExp): string[] | null
.
interface RegExpMatchArray extends Array<string> {
index: number
input: string
groups: Record<string, string> | undefined
}
string.search(string | RegExp): number
.
'a2b'.search(/[0-9]/)
// 1
'a2b'.search('[0-9]')
// 1
String Replace
string.replace(string | RegExp, replaceValue | replacerFunction)
.string.replaceAll(string | RegExp, replaceValue | replacerFunction)
.
// eslint-disable-next-line prefer-regex-literals
const regexp = new RegExp('foo[a-z]*', 'g')
const str = 'table football, foosball'
const matches = str.matchAll(regexp)
for (const match of matches) {
console.log(
`Found ${match[0]} start=${match.index} end=${
match.index + match[0].length
}.`
)
}
// expected output: "Found football start=6 end=14."
// expected output: "Found foosball start=16 end=24."
// matches iterator is exhausted after the for..of iteration
// Call matchAll again to create a new iterator
Array.from(str.matchAll(regexp), m => m[0])
// Array [ "football", "foosball" ]
'aabbcc'.replaceAll('b', '.')
// => 'aa..cc'
'aabbcc'.replaceAll(/b/g, '.')
// => 'aa..cc'
String Pad
string.repeat(times)
.
'hello'.repeat(2) // "hellohello"
'na'.repeat(2.9) // "nana"
'na'.repeat(-0.9) // ""
'na'.repeat(-1) // RangeError
'na'.repeat(Number.NaN) // ""
'na'.repeat(Number.POSITIVE_INFINITY) // RangeError
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"
string.padStart(len, paddingStr)
.string.padEnd(len, paddingStr)
.
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
String Trim
string.trimLeft()
/string.trimStart()
: remove start whitespace.string.trimRight()
/string.trimEnd()
: remove end whitespace.
String Template Literals
str
表示模板字符串:
// 普通字符串
;`In JavaScript '\n' is a line-feed.``\`Yo\` World!``In JavaScript this is // 多行字符串
not legal.``${
x // 引用变量
} + ${y * 2} = ${x + y * 2}``${obj.x + obj.y}``foo ${
fn() // 调用函数
} bar`
Tagged Templates Literals
function boldify(parts, ...insertedParts) {
return parts
.map((s, i) => {
if (i === insertedParts.length)
return s
return `${s}<strong>${insertedParts[i]}</strong>`
})
.join('')
}
const name = 'Sabertaz'
console.log(boldify`Hi, my name is ${name}!`)
// => "Hi, my name is <strong>Sabertaz</strong>!"
function template(strings, ...keys) {
return function (...values) {
const dict = values[values.length - 1] || {}
const result = [strings[0]]
keys.forEach((key, i) => {
const value = Number.isInteger(key) ? values[key] : dict[key]
result.push(value, strings[i + 1])
})
return result.join('')
}
}
const t1Closure = template`${0}${1}${0}!`
t1Closure('Y', 'A') // "YAY!"
const t2Closure = template`${0} ${'foo'}!`
t2Closure('Hello', { foo: 'World' }) // "Hello World!"
编译模板 (小型模板引擎):
function compile(template) {
const evalExpr = /<%=(.+?)%>/g
const expr = /<%([\s\S]+?)%>/g
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`')
template = `echo(\`${template}\`);`
const script = `(function parse(data){
let output = "";
function echo(html){
output += html;
}
${template}
return output;
})`
return script
}
const template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`
const parse = compile(template)
div.innerHTML = parse({ supplies: ['broom', 'mop', 'cleaner'] })
// => <ul>
// => <li>broom</li>
// => <li>mop</li>
// => <li>cleaner</li>
// => </ul>
// 下面的hashTemplate函数
// 是一个自定义的模板处理函数
const libraryHtml = hashTemplate`
<ul>
#for book in ${myBooks}
<li><i>#{book.title}</i> by #{book.author}</li>
#end
</ul>
`
国际化处理:
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
// "欢迎访问xxx, 您是第xxxx位访问者!"
XSS protection:
const message = SaferHTML`<p>${sender} has sent you a message.</p>`
function SaferHTML(templateString, ...expressions) {
let s = templateString[0]
for (let i = 0; i < expressions.length; i++) {
const expression = String(expressions[i])
// Escape special characters in the substitution.
s += expression
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
// Don't escape special characters in the template.
s += templateString[i + 1]
}
return s
}
运行代码:
jsx`
<div>
<input
ref='input'
onChange='${this.handleChange}'
defaultValue='${this.state.value}' />
${this.state.value}
</div>
`
java`
class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello World!"); // Display the string.
}
}
`
HelloWorldApp.main()
Raw String
console.log(`\u00A9`) // ©
console.log(String.raw`\u00A9`) // \u00A9
console.log(`first line\nsecond line`)
// first line
// second line
console.log(String.raw`first line\nsecond line`)
// "first line\nsecond line"
function printRaw(strings) {
console.log('Actual characters:')
for (const string of strings)
console.log(string)
console.log('Escaped characters;')
for (const rawString of strings.raw)
console.log(rawString)
}
printRaw`\u00A9${'and'}\n`
// Actual characters:
// ©
// (换行符)
// Escaped characters:
// \u00A9
// \n
String Utils
function ucWords(string) {
return string.toLowerCase().replace(/\b[a-z]/g, l => l.toUpperCase())
}
function ucFirst(string) {
return string[0].toUpperCase() + string.substr(1)
}
function studlyCase(string) {
return string
.replace('-', ' ')
.replace('_', ' ')
.split(' ')
.map(str => str[0].toUpperCase() + str.substr(1).toLowerCase())
.join('')
}
function snakeCase(string, glue = '_') {
return string
.replace(/\W+/g, ' ')
.split(/ |\B(?=[A-Z])/)
.map(word => word.toLowerCase())
.join(glue)
}
function kebabCase(string) {
return snakeCase(string, '-')
}
function objectToQueryString(obj) {
return Object.keys(obj)
.reduce((carry, key) => {
if (obj[key] || obj[key] === 0)
return `${carry}${key}=${obj[key]}&`
return carry
}, '')
.replace(/&+$/, '')
}
Symbol
- A Symbol is a unique and immutable primitive value and may be used as the key of an Object property.
- Symbols don't auto-convert to strings and can't convert to numbers.
Symbol.for(key)
create global Symbol registry.
// eslint-disable-next-line symbol-description
const genericSymbol = Symbol()
// eslint-disable-next-line symbol-description
const otherGenericSymbol = Symbol()
console.log(genericSymbol === otherGenericSymbol) // false
const fooSymbol = Symbol('foo')
const otherFooSymbol = Symbol('foo')
console.log(fooSymbol === otherFooSymbol) // false
const fooGlobalSymbol = Symbol.for('foobar') // 创建新符号
const otherFooGlobalSymbol = Symbol.for('foobar') // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol) // true
Symbol Conversion
Convert To | Explicit Conversion | Coercion (Implicit Conversion) |
---|---|---|
boolean | Boolean(sym) → OK | !sym → OK |
number | Number(sym) → TypeError | sym * 2 → TypeError |
string | String(sym) → OK | '' + sym → TypeError |
sym.toString() → OK | ${sym} → TypeError | |
object | Object(sym) → OK | Object.keys(sym) → OK |
Built-in Symbol Methods
[Symbol.iterator]()
:for of
.[Symbol.asyncIterator]()
:for await of
.[Symbol.match/replace/search/split](target)
:string.match/replace/search/split(classWithSymbolFunction)
.[Symbol.hasInstance](instance)
:instance of
.[Symbol.species]()
: constructor for making derived objects.[Symbol.toPrimitive](hint)
: 强制类型转换.[Symbol.toStringTag]()
: string used byObject.prototype.toString()
.
iterator
:
const arr = ['a', 'b', 'c']
const iter = arr[Symbol.iterator]()
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
hasInstance
:
class Bar {}
class Baz extends Bar {
static [Symbol.hasInstance](instance) {
return false
}
}
const b = new Baz()
console.log(Bar[Symbol.hasInstance](b)) // true
console.log(b instanceof Bar) // true
console.log(Baz[Symbol.hasInstance](b)) // false
console.log(b instanceof Baz) // false
const ReferenceType = {
[Symbol.hasInstance](value) {
return (
value !== null
&& (typeof value === 'object' || typeof value === 'function')
)
},
}
const obj1 = {}
console.log(obj1 instanceof Object) // true
console.log(obj1 instanceof ReferenceType) // true
const obj2 = Object.create(null)
console.log(obj2 instanceof Object) // false
console.log(obj2 instanceof ReferenceType) // true
species
:
class MyClass {
static get [Symbol.species]() {
return this
}
constructor(value) {
this.value = value
}
clone() {
return new this.constructor[Symbol.species](this.value)
}
}
class MyDerivedClass1 extends MyClass {
// empty
}
class MyDerivedClass2 extends MyClass {
static get [Symbol.species]() {
return MyClass
}
}
const instance1 = new MyDerivedClass1('foo')
const instance2 = new MyDerivedClass2('bar')
const clone1 = instance1.clone()
const clone2 = instance2.clone()
console.log(clone1 instanceof MyClass) // true
console.log(clone1 instanceof MyDerivedClass1) // true
console.log(clone2 instanceof MyClass) // true
console.log(clone2 instanceof MyDerivedClass2) // false
toPrimitive
:
class Temperature {
constructor(degrees) {
this.degrees = degrees
}
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'string':
return `${this.degrees}\u00B0` // degrees symbol
case 'number':
return this.degrees
case 'default':
return `${this.degrees} degrees`
}
}
}
const freezing = new Temperature(32)
console.log(`${freezing}!`) // "32 degrees!"
console.log(freezing / 2) // 16
console.log(String(freezing)) // "32째"
toStringTag
:
class Person {
constructor(name) {
this.name = name
}
get [Symbol.toStringTag]() {
return 'Person'
}
}
const me = new Person('Me')
console.log(me.toString()) // "[object Person]"
console.log(Object.prototype.toString.call(me)) // "[object Person]"
Value | toString Tag |
---|---|
undefined | Undefined |
null | Null |
Array object | Array |
string object | String |
arguments | Arguments |
callable | Function |
error object | Error |
boolean object | Boolean |
number object | Number |
date object | Date |
regular expression object | RegExp |
(Otherwise) | Object |
Bigint
- Decimal:
123n
. - Binary:
0b1101n
. - Octal:
0o777n
. - Hexadecimal:
0xFFn
.
/**
* Takes a bigint as an argument and returns a bigint
*/
function nthPrime(nth) {
if (typeof nth !== 'bigint')
throw new TypeError('Not bigint')
function isPrime(p) {
for (let i = 2n; i < p; i++) {
if (p % i === 0n)
return false
}
return true
}
for (let i = 2n; ; i++) {
if (isPrime(i)) {
if (--nth === 0n)
return i
}
}
}
assert.deepEqual(
[1n, 2n, 3n, 4n, 5n].map(nth => nthPrime(nth)),
[2n, 3n, 5n, 7n, 11n]
)
Bigint Conversion
x | BigInt(x) |
---|---|
undefined | Throws TypeError |
null | Throws TypeError |
boolean | false → 0n , true → 1n |
number | 123 → 123n |
Non-integer → throws RangeError | |
bigint | x |
string | '123' → 123n |
Unparsable → throws SyntaxError | |
symbol | Throws TypeError |
object | Configurable ([Symbol.toPrimitive]() /valueOf() ) |
BigInt(undefined)
// TypeError: Cannot convert undefined to a BigInt
BigInt(null)
// TypeError: Cannot convert null to a BigInt
BigInt('abc')
// SyntaxError: Cannot convert abc to a BigInt
BigInt('123n')
// SyntaxError: Cannot convert 123n to a BigInt
BigInt('123')
// 123n
BigInt('0xFF')
// 255n
BigInt('0b1101')
// 13n
BigInt('0o777')
// 511n
BigInt(123.45)
// RangeError: The number 123.45 cannot be converted to a BigInt
BigInt(123)
// 123n
BigInt({
valueOf() {
return 123n
},
})
// 123n
Convert To | Explicit Conversion | Coercion (Implicit Conversion) |
---|---|---|
boolean | Boolean(0n) → false | !0n → true |
Boolean(int) → true | !int → false | |
number | Number(7n) → 7 | +int → TypeError |
string | String(7n) → '7' | ''+7n → '7' |
Bigint Static Properties
BigInt.asIntN(width, theInt)
: Casts theInt to width bits (signed).BigInt.asUintN(width, theInt)
: Casts theInt to width bits (unsigned).
const uint64a = BigInt.asUintN(64, 12345n)
const uint64b = BigInt.asUintN(64, 67890n)
const result = BigInt.asUintN(64, uint64a * uint64b)
Wrapper Objects for Primitives
Using the wrapper function without the new keyword is a useful way of coercing a value into a primitive type.
// Not recommended (primitive object wrapper):
// eslint-disable-next-line no-new-wrappers
const objectType = typeof new String(37) // object
// Safe (type coercion with wrapper function):
const stringType = typeof String(37) // string
// Primitive strings:
// eslint-disable-next-line no-self-compare
const truthy = '37' === '37' // true
// Object-wrapped string:
// eslint-disable-next-line no-new-wrappers
const falsy = new String(37) === '37' // false
// Type-coerced string:
const truthy = String(37) === '37' // true
// BAD!
// eslint-disable-next-line no-new-wrappers
const falseObject = new Boolean(false)
const result = falseObject && true
console.log(result) // true
console.log(typeof falseObject) // object
console.log(falseObject instanceof Boolean) // true
const prim = true
assert.equal(typeof prim, 'boolean')
assert.equal(prim instanceof Boolean, false)
const wrapped = Object(prim)
assert.equal(typeof wrapped, 'object')
assert.equal(wrapped instanceof Boolean, true)
assert.equal(wrapped.valueOf(), prim) // unwrap
Box and Unbox for primitive values:
- 自动创建的原始值包装对象可以让原始值拥有对象的行为.
- 自动创建的原始值包装对象只存在于访问它的那行代码执行期间.
- 常数值加括号