JavaScript 原生函数

2023-05-17 15:27:56 浏览数 (1)

常用原生函数:

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

原生函数可以被当作构造函数来使用,但其构造出来的对象可能会和我们设想的有所出入:

代码语言:javascript复制
var str = new String('Hello');
typeof str; // 'object' 不是 'string'

str instanceof String; // true
Object.prototype.toString.call(str); // '[object String]'

通过构造函数(如 new String("abc") )创建出来的是封装了基本类型值(如 "Hello" )的封装对象。

# 内部属性 [[Class]]

所有 typeof 返回值为 "object" 的对象(如数组)都包含一个内部属性 [[Class]] (可以把它看作一个内部的分类,而非传统的面向对象意义上的类)。这个属性无法直接访问,一般通过 Object.prototype.toString() 来查看。

代码语言:javascript复制
Object.prototype.toString.call([]); // '[object Array]'
Object.prototype.toString.call({}); // '[object Object]'
Object.prototype.toString.call(function(){}); // '[object Function]'
Object.prototype.toString.call(/d /); // '[object RegExp]'
Object.prototype.toString.call(new Date()); // '[object Date]'
Object.prototype.toString.call(new Error()); // '[object Error]'
Object.prototype.toString.call(Symbol()); // '[object Symbol]'

多数情况下,对象的内部 [[Class]] 属性和创建该对象的内建原生构造函数相对应,但并非总是如此。

代码语言:javascript复制
// 虽然 Null() 和 Undefined() 这样的原生构造函数并不存在
// 但是内部 [[Class]] 属性值仍然是 "Null" 和"Undefined" 
Object.prototype.toString.call(null); // '[object Null]'
Object.prototype.toString.call(undefined); // '[object Undefined]'

# 封装对象包装

封装对象(object wrapper)扮演着十分重要的角色。由于基本类型值没有 .length.toString() 这样的属性和方法,需要通过封装对象才能访问,此时 JavaScript 会自动为基本类型值包装 (box 或者 wrap)一个封装对象:

代码语言:javascript复制
var str = 'Hello';
str.length; // 5
str.toUpperCase(); // 'HELLO'

一般情况下,不需要直接使用封装对象。最好的办法是让 JavaScript 引擎自己决定什么时候应该使用封装对象。换句话说,就是应该优先考虑使用 "abc" 和 42 这样的基本类型值,而非 new String("abc")new Number(42)

# 需要注意的地方

为 false 创建了一个封装对象,然而该对象是真值。

代码语言:javascript复制
var b = new Boolean(false);

if (!b) {
  console.log('false'); // 无法执行到这里
}

如果想要自行封装基本类型值,可以使用 Object() 函数(不带 new 关键字):

代码语言:javascript复制
var a = 'abc';
var b = new String('abc');
var c = Object('abc');

typeof a; // 'string'
typeof b; // 'object'
typeof c; // 'object'

b instanceof String; // true
c instanceof String; // true

Object.prototype.toString.call(b); // '[object String]'
Object.prototype.toString.call(c); // '[object String]'

# 拆封

如果想要得到封装对象中的基本类型值,可以使用 valueOf() 函数:

代码语言:javascript复制
var a = new String('abc');
var b = new Number(123);
var c = new Boolean(true);

a.valueOf(); // 'abc'
b.valueOf(); // 123
c.valueOf(); // true

在需要用到封装对象中的基本类型值的地方会发生隐式拆封。

代码语言:javascript复制
var a = new String('abc');
var b = a   ''; // 'abc'

typeof a; // 'object'
typeof b; // 'string'

# 原生函数作为构造函数

# Array()

Array 构造函数只带一个数字参数的时候,该参数会被作为数组的预设长度(length),而非只充当数组中的一个元素。

数组并没有预设长度这个概念,这样创建出来的只是一个空数组,只不过它的 length 属性被设置成了指定的值。

代码语言:javascript复制
var a = new Array(1, 2, 3);
a; // [1, 2, 3]

var b = [1, 2, 3];
b; // [1, 2, 3]

构造函数 Array() 不要求必须带 new 关键字。不带时,它会被自动补上。

# Object()、Function() 和 RegExp()

除非万不得已,否则尽量不要使用 Object()Function()RegExp()

在实际情况中没有必要使用 new Object() 来创建对象,因为这样就无法像常量形式那样一次设定多个属性,而必须逐一设定。

构造函数 Function 只在极少数情况下很有用,比如动态定义函数参数和函数体的时候。不要把 Function() 当作 eval() 的替代品,基本上不会通过这种方式来定义函数。

建议使用常量形式(如 /^a*b /g )来定义正则表达式,这样不仅语法简单,执行效率也更高,因为 JavaScript 引擎在代码执行前会对它们进行预编译和缓存。RegExp() 有时还是很有用的,比如动态定义正则表达式时:

代码语言:javascript复制
var name = 'Kyle';
var namePattern = new RegExp('\b(?:'   name   ') \b', 'ig');

var matches = someText.match(namePattern);

# Date() 和 Error()

Date()Error() 的用处要大很多,因为没有对应的常量形式来作为它们的替代。

创建日期对象必须使用 new Date()Date() 可以带参数,用来指定日期和时间,而不带参数的话则使用当前的日期和时间。

构造函数 Error() (与 Array() 类似)带不带 new 关键字都可。

创建错误对象(error object)主要是为了获得当前运行栈的上下文(大部分 JavaScript 引擎通过只读属性 .stack 来访问)。栈上下文信息包括函数调用栈信息和产生错误的代码行号,以便于调试(debug)。

# Symbol()

Symbol 是具有唯一性的特殊值(并非绝对),用它来命名对象属性不容易导致重名。该类型的引入主要源于 ES6 的一些特殊构造,此外 Symbol 也可以自行定义。

Symbol 可以用作属性名,但无论是在代码还是开发控制台中都无法查看和访问它的值,只会显示为诸如 Symbol(Symbol.create) 这样的值。

可以使用 Symbol() 原生构造函数来自定义符号。但它比较特殊,不能带 new 关键字,否则会出错:

代码语言:javascript复制
var s = Symbol('my symbol');
s; // Symbol(my symbol)
s.toString(); // 'Symbol(my symbol)'
typeof s; // 'symbol'

var a = {};
a[s] = 'value';

Object.getOwnPropertySymbols(a); // [Symbol(my symbol)]

# 原生类型

原生构造函数有自己的 .prototype 对象,如 Array.prototypeString.prototype 等。这些对象包含其对应子类型所特有的行为特征。

如,将字符串值封装为字符串对象之后,就能访问 String.prototype 中定义的方法。

0 人点赞