《You Dont Know JS中卷》(三) ---原生函数

37 阅读3分钟

原生函数

原生函数指的是JS内置的一些函数,因为涉及的内容比较常见,相关资料很多,这里就不再重读赘述,所以本文只对该书作者描述内容做简单总结.

常见的原生函数

下面是一些常见的原生函数:

String()
Number()
Boolean()
Function()
Object()
Array()
Date()
RegExp()
Error()
Symbol()

不难发现他们好像对应了js中的一些数据类型

这些函数可以作为构造函数来调用,对于基本类型的数据而言,原生函数构造出一个封装了基本类型值的封装对象

const strObj = new String("abc")
typeof strObj // object

至于strObj这个封装对象和基本类型值有什么区别,将在封装和拆封一节中说明

内部属性[[class]]

所有typeof返回object的对象都包含一个内部属性[[Class]],这个属性只能通过Object.prototype.toString()来查看

Object.prototype.toString.call([1, 2, 3]) //[object Array]
Object.prototype.toString.call({a:1}) //[object Object]

有意思的是对基本数据类型使用这个方法,也能返回内部的[[Class]]属性:

两个比较特殊的:NullUndefined
Object.prototype.toString.call(null) //[object Null]
Object.prototype.toString.call(undefined) //[object Undefined]
虽然nullundefined对应的原生构造函数不存在但内部的[[Class]]属性值仍然是nullundefined
​
其他基本类型:
Object.prototype.toString.call("string") // [object String]

很奇怪,typeof对基本数据类型返回的并不是object,为什么还是能访问[[Class]]属性呢?这是因为其类型被各自封装了一层,将在拆封和封装一节中详细说明

封装和拆封

先思考一个问题,对于基本类型值来说,自身是没有.length,.toString方法的,可是在我们日常使用中经常会通过基本类型值来直接访问其没有的属性,如:

const str = 'abc'
console.log(str.length) // 3,即使str没有length属性,也同样能访问,这是为什么呢?

这是因为:

在访问属性之前,js会隐式的通过其对应的原生函数将基本类型值包装成一个封装对象,可以这样理解:

1.const str = 'abc';
2.将str包装成封装对象: new String('abc'),str变成了这个对象
3.console.log(str.length) 这里就正常访问了
4.销毁掉封装对象

既然有封装,那么就会有拆封: 有时候我们想得到封装对象中的基本数据类型值,可以使用valueOf()方法进行拆封:

const a = new String('abc');
console.log(a) // {0:'a',1:'b',2:'c',length:3}
console.log(a.valueOf()) // abc

在一些请情况下,js会隐式的进行拆封:

const a = new String('abc')
const b = a + ''
console.log(b) // abcconsole.log(typeof a) //object
console.log(typeof b) //string

具体过程涉及js的强制类型转换,将在下一章节中详细讨论

原生函数or字面量

既然原生函数可以作为构造函数,那么在定义变量的时候,应该使用那种方式呢?其中Symbol比较特殊,直接调用来生成符号,其他数据类型,除了Date,Error之外,优先使用字面量的方式定义变量,可以避免很多误解,比如:

const bool = new Boolean(false)
if(!bool){ // 这里的代码永远都不会执行}
​
使用Boolean()封装了false之后,typeof bool返回object是一个真值,永远返回true