博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
你不懂js系列学习笔记-类型与文法- 03
阅读量:6946 次
发布时间:2019-06-27

本文共 5587 字,大约阅读时间需要 18 分钟。

第三章:原生类型

原文:

这是最常用的原生类型的一览:

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol() —— 在 ES6 中被加入的!

如你所见,这些原生类型实际上是内建函数。

如果你拥有像 Java 语言那样的背景,JavaScript 的 String() 看起来像是你曾经用来创建字符串值的 String(..) 构造器。所以,你很快就会观察到你可以做这样的事情:

var s = new String("Hello World!");console.log(s.toString()); // "Hello World!"复制代码

这些原生类型的每一种确实可以被用作一个原生类型的构造器。但是被构建的东西可能与你想象的不同:

var a = new String("abc");typeof a; // "object" ... 不是 "String"a instanceof String; // trueObject.prototype.toString.call(a); // "[object String]"复制代码

创建值的构造器形式(new String("abc"))的结果是一个基本类型值("abc")的包装器对象。

重要的是,typeof 显示这些对象不是它们自己的特殊 类型,而是 object 类型的子类型。

1. 内部 [[Class]]

typeof 的结果为 "object" 的值(比如数组)被额外地打上了一个内部的标签属性 [[Class]](请把它考虑为一个内部的分类方法,而非与传统的面向对象编码的类有关)。这个属性不能直接地被访问,但通常可以间接地通过在这个值上借用默认的 Object.prototype.toString(..) 方法调用来展示。举例来说:

Object.prototype.toString.call([1, 2, 3]); // "[object Array]"Object.prototype.toString.call(/regex-literal/i); // "[object RegExp]"复制代码

所以,对于这个例子中的数组来说,内部的 [[Class]] 值是 "Array",而对于正则表达式,它是 "RegExp"。在大多数情况下,这个内部的 [[Class]] 值对应于关联这个值的内建的原生类型构造器(见下面的讨论),但事实却不总是这样。

基本类型呢?首先,nullundefined

Object.prototype.toString.call(null); // "[object Null]"Object.prototype.toString.call(undefined); // "[object Undefined]"复制代码

你会注意到,不存在 Null()Undefined() 原生类型构造器,但不管怎样 "Null""Undefined" 是被暴露出来的内部 [[Class]] 值。

但是对于像 stringnumber、和 boolean 这样的简单基本类型,实际上会启动另一种行为,通常称为“封箱(boxing)”(见下一节“封箱包装器”):

Object.prototype.toString.call("abc"); // "[object String]"Object.prototype.toString.call(42); // "[object Number]"Object.prototype.toString.call(true); // "[object Boolean]"复制代码

在这个代码段中,每一个简单基本类型都自动地被它们分别对应的对象包装器封箱,这就是为什么 "String""Number"、和 "Boolean" 分别被显示为内部 [[Class]] 值。

2. 封箱包装器

这些对象包装器服务于一个非常重要的目的。基本类型值没有属性或方法,所以为了访问 .length.toString() 你需要这个值的对象包装器。值得庆幸的是,JS 将会自动地 封箱(也就是包装)基本类型值来满足这样的访问。

var a = "abc";a.length; // 3a.toUpperCase(); // "ABC"复制代码

那么,如果你想以通常的方式访问这些字符串值上的属性/方法,比如一个 for 循环的 i < a.length 条件,这么做看起来很有道理:一开始就得到一个这个值的对象形式,于是 JS 引擎就不需要隐含地为你创建一个。

但事实证明这是一个坏主意。浏览器们长久以来就对 .length 这样的常见情况进行性能优化,这意味着如果你试着直接使用对象形式(它们没有被优化过)进行“提前优化”,那么实际上你的程序将会 变慢

一般来说,基本上没有理由直接使用对象形式。让封箱在需要的地方隐含地发生会更好。换句话说,永远也不要做 new String("abc")new Number(42) 这样的事情 —— 应当总是偏向于使用基本类型字面量 "abc"42

3. 开箱

如果你有一个包装器对象,而你想要取出底层的基本类型值,你可以使用 valueOf() 方法:

var a = new String("abc");var b = new Number(42);var c = new Boolean(true);a.valueOf(); // "abc"b.valueOf(); // 42c.valueOf(); // true复制代码

当以一种查询基本类型值的方式使用对象包装器时,开箱也会隐含地发生。这个处理的过程(强制转换)将会在第四章中更详细地讲解,但简单地说:

var a = new String("abc");var b = a + ""; // `b` 拥有开箱后的基本类型值"abc"typeof a; // "object"typeof b; // "string"复制代码

4. 原生类型作为构造器

Array(..)

var a = new Array(1, 2, 3);a; // [1, 2, 3]var b = [1, 2, 3];b; // [1, 2, 3]复制代码

注意: Array(..) 构造器不要求在它前面使用 new 关键字。如果你省略它,它也会像你已经使用了一样动作。所以 Array(1,2,3)new Array(1,2,3) 的结果是一样的。

Array 构造器有一种特殊形式,如果它仅仅被传入一个 number 参数,与将这个值作为数组的 内容 不同,它会被认为是用来“预定数组大小”(嗯,某种意义上)用的长度。

但更重要的是,其实没有预定数组大小这样的东西。你所创建的是一个空数组,并将这个数组的 length 属性设置为那个指定的数字值。

一个数组在它的值槽上没有明确的值,但是有一个 length 属性意味着这些值槽是存在的,在 JS 中这是一个诡异的数据结构,它带有一些非常奇怪且令人困惑的行为。可以创建这样的值的能力,完全源自于老旧的、已经废弃的、仅具有历史意义的功能(比如arguments 这样的“类数组对象”)。

注意: 带有至少一个“空值槽”的数组经常被称为“稀散数组”。

要观察这种不同,试试这段代码:

var a = new Array(3);var b = [undefined, undefined, undefined];var c = [];c.length = 3;a;b;c;复制代码

注意: 正如你在这个例子中看到的 c,数组中的空值槽可以在数组的创建之后发生。将数组的 length 改变为超过它实际定义的槽值的数目,你就隐含地引入了空值槽。事实上,你甚至可以在上面的代码段中调用 delete b[1],而这么做将会在 b的中间引入一个空值槽。

Object(..)Function(..)RegExp(..)

Object(..)/Function(..)/RegExp(..) 构造器一般来说也是可选的(因此除非是特别的目的,应当避免使用):

var c = new Object();c.foo = "bar";c; // { foo: "bar" }var d = { foo: "bar" };d; // { foo: "bar" }var e = new Function("a", "return a * 2;");var f = function(a) {  return a * 2;};function g(a) {  return a * 2;}var h = new RegExp("^a*b+", "g");var i = /^a*b+/g;复制代码

几乎没有理由使用 new Object() 构造器形式,尤其因为它强迫你一个一个地添加属性,而不是像对象的字面形式那样一次添加许多。

Function 构造器仅在最最罕见的情况下有用,也就是你需要动态地定义一个函数的参数和/或它的函数体。不要将 Function(..) 仅仅作为另一种形式的 eval(..)。你几乎永远不会需要用这种方式动态定义一个函数。

用字面量形式(/^a*b+/g)定义正则表达式是被大力采用的,不仅因为语法简单,而且还有性能的原因 —— JS 引擎会在代码执行前预编译并缓存它们。和我们迄今看到的其他构造器形式不同,RegExp(..) 有一些合理的用途:用来动态定义一个正则表达式的范例。

var name = "Kyle";var namePattern = new RegExp("\\b(?:" + name + ")+\\b", "ig");var matches = someText.match(namePattern);复制代码

这样的场景在 JS 程序中一次又一次地合法出现,所以你有需要使用 new RegExp("pattern","flags") 形式。

Date(..)Error(..)

Date(..)Error(..) 原生类型构造器要比其他种类的原生类型有用得多,因为它们没有字面量形式。

要创建一个日期对象值,你必须使用 new Date()Date(..) 构造器接收可选参数值来指定要使用的日期/时间,但是如果省略的话,就会使用当前的日期/时间。

目前你构建一个日期对象的最常见的理由是要得到当前的时间戳(一个有符号整数,从 1970 年 1 月 1 日开始算起的毫秒数)。你可以在一个日期对象实例上调用 getTime() 得到它。

但是在 ES5 中,一个更简单的方法是调用定义为 Date.now() 的静态帮助函数。而且在前 ES5 中填补它很容易:

if (!Date.now) {  Date.now = function() {    return new Date().getTime();  };}复制代码

Error(..) 构造器(很像上面的 Array())在有 new 与没有 new 时的行为是相同的。

你想要创建 error 对象的主要原因是,它会将当前的执行栈上下文捕捉进对象中(在大多数 JS 引擎中,在创建后使用只读的 .stack 属性表示)。这个栈上下文包含函数调用栈和 error 对象被创建时的行号,这使调试这个错误更简单。

典型地,你将与 throw 操作符一起使用这样的 error 对象:

function foo(x) {  if (!x) {    throw new Error("x wasn't provided");  }  // ..}复制代码

Error 对象实例一般拥有至少一个 message 属性,有时还有其他属性(你应当将它们作为只读的),比如 type。然而,与其检视上面提到的 stack 属性,最好是在 error 对象上调用 toString()(明确地调用,或者是通过强制转换隐含地调用 —— 见第四章)来得到一个格式友好的错误消息。

提示: 技术上讲,除了一般的 Error(..) 原生类型以外,还有几种特定错误的原生类型:EvalError(..)RangeError(..)ReferenceError(..)SyntaxError(..)TypeError(..)URIError(..)。但是手动使用这些特定错误原生类型十分少见。如果你的程序确实遭受了一个真实的异常,它们是会自动地被使用的(比如引用一个未声明的变量而得到一个 ReferenceError 错误)。

复习

JavaScript 为基本类型提供了对象包装器,被称为原生类型(StringNumberBoolean 等等)。这些对象包装器使这些值可以访问每种对象子类型的恰当行为(String#trim()Array#concat(..))。

如果你有一个像 "abc" 这样的简单基本类型标量,而且你想要访问它的 length 属性或某些 String.prototype 方法,JS 会自动地“封箱”这个值(用它所对应种类的对象包装器把它包起来),以满足这样的属性/方法访问。

转载于:https://juejin.im/post/5ae95aa96fb9a07abf725254

你可能感兴趣的文章
LVS的DR模型的进阶应用——管理MySqL数据库
查看>>
制作 OpenStack Linux 镜像 - 每天5分钟玩转 OpenStack(151)
查看>>
使用ip-masq-agent灵活的控制容器服务Kubernetes集群的SNAT规则
查看>>
MySQL数据库的基本概述和基本应用
查看>>
IntelliJ IDEA Export to Eclipse Android工程不能正常被Eclipse识别的解决方法
查看>>
Windows Phone 7 程序菜单栏ApplicationBar
查看>>
浏览器对象BOM模型开发示意图分析(应用设计)
查看>>
磁盘IOPS计算与测量
查看>>
以色列网络安全行业崛起的秘密
查看>>
分层设计与分层测试
查看>>
全球大数据和业务分析收入预计到2019年突破1870亿美元
查看>>
删除管理员权限可屏蔽96%的 Windows 漏洞
查看>>
Xen 漏洞曝光
查看>>
《数据科学:R语言实现》——3.3 转换数据类型
查看>>
《MATLAB智能算法超级学习手册》一一2.2 种群竞争模型的讨论
查看>>
《海外社交媒体营销》一一1.3 海外社交媒体营销的技巧
查看>>
测试并发应用 (一)监控Lock接口
查看>>
《众妙之门——网页设计专业之道》——2.2 上下文精确导航
查看>>
《SOA与REST:用REST构建企业级SOA解决方案》—第2章2.2节案例研究背景之一:中西部大学联盟(MUA)...
查看>>
【转】SQL Server 的三种自定义函数(用户定义的函数)
查看>>