JavaScript 允许我们像处理对象一样处理基元(字符串、数字等)。它们还提供可调用的方法。我们很快会学习这些方法,但首先我们来看看它是如何工作的,因为当然,基元不是对象(这里我们会让它更清晰)。
我们来看看基元和对象之间的主要区别。
一个基元
- 是原始类型的值。
- 有 7 种原始类型:
string
、number
、bigint
、boolean
、symbol
、null
和undefined
。
一个对象
- 能够将多个值存储为属性。
- 可以使用
{}
创建,例如:{name: "John", age: 30}
。JavaScript 中还有其他类型的对象:例如,函数就是对象。
关于对象最好的事情之一是,我们可以将一个函数存储为其属性之一。
let john = {
name: "John",
sayHi: function() {
alert("Hi buddy!");
}
};
john.sayHi(); // Hi buddy!
所以这里我们使用 sayHi
方法创建了一个对象 john
。
许多内置对象已经存在,例如那些处理日期、错误、HTML 元素等的对象。它们具有不同的属性和方法。
但是,这些功能是有代价的!
对象比原始类型“更重”。它们需要额外的资源来支持内部机制。
一个作为对象的原始类型
这是 JavaScript 创建者面临的悖论
- 人们希望对原始类型(如字符串或数字)执行许多操作。使用这些方法访问它们会很棒。
- 原始类型必须尽可能快且轻量级。
解决方案看起来有点尴尬,但它是这样的
- 原始类型仍然是原始类型。一个单一的值,正如所愿。
- 该语言允许访问字符串、数字、布尔值和符号的方法和属性。
- 为了实现这一点,会创建一个提供额外功能的特殊“对象包装器”,然后将其销毁。
每个原始类型的“对象包装器”都不同,它们被称为:String
、Number
、Boolean
、Symbol
和 BigInt
。因此,它们提供了不同的方法集。
例如,存在一个字符串方法 str.toUpperCase(),它返回一个大写的 str
。
它的工作原理如下
let str = "Hello";
alert( str.toUpperCase() ); // HELLO
很简单,对吧?以下是 str.toUpperCase()
中实际发生的事情
- 字符串
str
是一个原始类型。因此在访问其属性的那一刻,会创建一个特殊对象,该对象知道该字符串的值,并具有有用的方法,如toUpperCase()
。 - 该方法运行并返回一个新字符串(由
alert
显示)。 - 特殊对象被销毁,只留下原始类型
str
。
因此,原始类型可以提供方法,但它们仍然保持轻量级。
JavaScript 引擎高度优化了这一过程。它甚至可能完全跳过创建额外的对象。但它仍必须遵守规范,并表现得好像它创建了一个对象一样。
一个数字有它自己的方法,例如,toFixed(n) 将数字四舍五入到给定的精度
let n = 1.23456;
alert( n.toFixed(2) ); // 1.23
String/Number/Boolean
仅供内部使用像 Java 这样的某些语言允许我们使用类似 new Number(1)
或 new Boolean(false)
的语法显式地为基元创建“包装对象”。
在 JavaScript 中,出于历史原因,这也是可能的,但强烈不推荐。很多地方都会变得混乱。
例如
alert( typeof 0 ); // "number"
alert( typeof new Number(0) ); // "object"!
对象在 if
中始终为真,因此此处警报将显示
let zero = new Number(0);
if (zero) { // zero is true, because it's an object
alert( "zero is truthy!?!" );
}
另一方面,在没有 new
的情况下使用相同的函数 String/Number/Boolean
完全没问题且有用。它们将值转换为相应类型:字符串、数字或布尔值(基元)。
例如,这是完全有效的
let num = Number("123"); // convert a string to number
特殊基元 null
和 undefined
是例外。它们没有相应的“包装对象”,也不提供任何方法。从某种意义上说,它们是“最原始的”。
尝试访问此类值的属性将导致错误
alert(null.test); // error
总结
- 除了
null
和undefined
之外的基元提供了许多有用的方法。我们将在即将到来的章节中学习这些方法。 - 从形式上讲,这些方法通过临时对象工作,但 JavaScript 引擎经过精心调整以在内部优化它们,因此调用它们并不昂贵。
评论
<code>
标记,对于多行代码,请将其包装在<pre>
标记中,对于 10 行以上的代码,请使用沙盒 (plnkr、jsbin、codepen...)