转行学前端的第 26 天 : 了解 ECMAScript 基础数据类型 (下篇)

800 阅读12分钟

我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。

今日学习目标

昨天基于一些页面搜索,学习了《JavaScirpt 高级程序设计》(第三版)  第 3 章节中的 ES 基础概念中语法和变量,今天主要是基于搜索来学习第 3 章节中的 ES 基础概念中基础数据类型,又是适合学习的一天,加油,小又又!!!!

原本准备用一篇文章进行发布的,但是,发布一直提示 Request Entity Too Large,就分两篇发布了~~~

今日学习概要在第一篇,总结在第二篇~~~~

BigInt

what

BigInt 是一种内置对象,它提供了一种方法来表示大于 2- 1 的整数。这原本是 Javascript中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。

可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数BigInt()

const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n
const hugeString = BigInt("9007199254740991");
// ↪ 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// ↪ 9007199254740991n
const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
// ↪ 9007199254740991n

它在某些方面类似于 Number ,但是也有几个关键的不同点:不能用于 Math 对象中的方法;不能和任何 Number 实例混合运算,两者必须转换成同一种类型。


在两种类型来回转换时要小心,因为 BigInt 变量在转换成 Number 变量时可能会丢失精度。


grammar

类型信息

使用 typeof 测试时, BigInt 对象返回 "bigint" :

typeof 1n === 'bigint'; // true
typeof BigInt('1') === 'bigint'; // true


使用 Object 包装后, BigInt 被认为是一个普通 "object" :

typeof Object(1n) === 'object'; // true

运算

以下操作符可以和 BigInt 一起使用: +*-**% 。除 >>> (无符号右移)之外的 位操作 也可
以支持。

因为 BigInt 都是有符号的, >>> (无符号右移)不能用于 BigInt为了兼容 asm.js BigInt 不支持单目 (+) 运算符。

const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER);
// ↪ 9007199254740991n
const maxPlusOne = previousMaxSafe + 1n;
// ↪ 9007199254740992n

const theFuture = previousMaxSafe + 2n; // ↪ 9007199254740993n, this works now! const multi = previousMaxSafe * 2n; // ↪ 18014398509481982n const subtr = multi – 10n; // ↪ 18014398509481972n const mod = multi % 10n; // ↪ 2n const bigN = 2n ** 54n; // ↪ 18014398509481984n bigN * -1n // ↪ –18014398509481984n


/ 操作符对于整数的运算也没问题。可是因为这些变量是 BigInt 而不是 BigDecimal ,该操作符结果会向零取整,也就是说不会返回小数部分。

当使用 BigInt 时,带小数的运算会被取整。

const expected = 4n / 2n;
// ↪ 2n
const rounded = 5n / 2n;
// ↪ 2n, not 2.5n

比较


BigIntNumber 不是严格相等的,但是宽松相等的。

0n === 0
// ↪ false
0n == 0
// ↪ true


NumberBigInt 可以进行比较。

1n < 2
// ↪ true
2n > 1
// ↪ true
2 > 2
// ↪ false
2n > 2
// ↪ false
2n >= 2
// ↪ true


两者也可以混在一个数组内并排序。

const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
// ↪  [4n, 6, -12n, 10, 4, 0, 0n]
mixed.sort();
// ↪ [-12n, 0, 0n, 10, 4n, 4, 6]


注意被  Object 包装的 BigInts 使用 object 的比较规则进行比较,只用同一个对象在比较时才会相等。

0n === Object(0n); // false
Object(0n) === Object(0n); // false
const o = Object(0n);
o === o // true


条件

BigInt 在需要转换成 Boolean 的时表现跟 Number 类似:如通过 Boolean 函数转换;用于 Logical Operators ||, &&, 和 ! 的操作数;或者用于在像 if statement 这样的条件语句中。

if (0n) {
  console.log('Hello from the if!');
} else {
  console.log('Hello from the else!');
}
// ↪ "Hello from the else!"
0n || 12n
// ↪ 12n
0n && 12n
// ↪ 0n
Boolean(0n)
// ↪ false
Boolean(12n)
// ↪ true
!12n
// ↪ false
!0n
// ↪ true


example


计算素数

function isPrime(p) {
  for (let i = 2n; i * i <= p; i++) {
    if (p % i === 0n) return false;
  }
  return true;
}

// Takes a BigInt as an argument and returns a BigInt
function nthPrime(nth) {
  let maybePrime = 2n;
  let prime = 0n;

while (nth >= 0n) { if (isPrime(maybePrime)) { nth -= 1n; prime = maybePrime; } maybePrime += 1n; }

return prime; } nthPrime(20n) // ↪ 73n


tip

转化

由于在 NumberBigInt 之间进行转换会损失精度,因而建议仅在值可能大于2 时使用 BigInt 类型,并且
不在两种类型之间进行相互转换。


密码学

由于对 BigInt 的操作不是常数时间的,因而 BigInt 不适合用于密码学


在 JSON 中使用

对任何 BigInt 值使用 JSON.stringify() 都会引发 TypeError,因为默认情况下 BigInt 值不会在 JSON 中序列化。但是,如果需要,可以实现 toJSON 方法:

BigInt.prototype.toJSON = function() { return this.toString(); }


JSON.stringify 现在生成如下字符串,而不是抛出异常:

JSON.stringify(BigInt(1));
// '"1"'

String

what

String 全局对象是一个用于字符串或一个字符序列的构造函数。


attribute


String.prototype 属性的属性特性:
writable false
enumerable false
configurable false

grammar

字符串字面量


字符串可以是引号中的任意文本。可以使用单引号或双引号,也可以在字符串中使用引号,只要不匹配包围字符串的引号即可。

'string text' 
"string text"
"中文/汉语" 
"español"
"English "
"हिन्दी"
"العربية"
"português"
"বাংলা"
"русский" 
"日本語"
"ਪੰਜਾਬੀ"
"한국어"

String 对象

  • 创建 String 对象的语法
String(thing)
new String(thing)
  • 参数

thing任何可以被转换成字符串的值。


模板字面量

从 ECMAScript 2015 开始,字符串字面量也可以称为模板字面量

`hello world` `hello! world!` `hello ${who}` escape `<a>${who}</a>`

转义字符


除了普通的可打印字符以外,一些特殊有特殊功能的字符可以通过转义字符的形式放入字符串中

Code Output
\0 空字符
\' 单引号
\" 双引号
\\ 反斜杠
\n 换行
\r 回车
\v 垂直制表符
\t 水平制表符
\b 退格
\f 换页
\uXXXX unicode 码
\u{X} ... \u{XXXXXX} unicode codepoint
\xXX Latin-1 字符(x小写)


和其他语言不同,javascript 的字符串不区分单引号和双引号,所以不论是单引号还是双引号的字符串,上面的转义字符都能运行 。


长字符串

有时,你的代码可能含有很长的字符串。你可能想将这样的字符串写成多行,而不是让这一行无限延长或着被编辑器折叠。有两种方法可以做到这一点。

其一,可以使用 + 运算符将多个字符串连接起来,如下所示

let longString = "This is a very long string which needs " +
                 "to wrap across multiple lines because " +
                 "otherwise my code is unreadable.";


其二,可以在每行末尾使用反斜杠字符(“\”),以指示字符串将在下一行继续。确保反斜杠后面没有空格或任何除换行符之外的字符或缩进; 否则反斜杠将不会工作。 如下所示

let longString = "This is a very long string which needs \
to wrap across multiple lines because \
otherwise my code is unreadable.";


使用这两种方式会创建相同的字符串。


example

从字符串中获取单个字符

获取字符串的某个字符有两种方法。 第一种是使用 charAt 方法

return 'cat'.charAt(1); // returns "a"


另一种 (在ECMAScript 5中有所介绍) 是把字符串当作一个类似数组的对象,其中的每个字符对应一个数值索引

return 'cat'[1]; // returns "a"


使用括号访问字符串不可以对其进行删除或添加,因为字符串对应未知的属性并不是可读或配置的。 (更多的信息请参阅 Object.defineProperty。 )


字符串比较

熟练使用 C 语言的开发者经常使用 strcmp 函数来比较字符串,但在 JavaScript 中,你只需要使用比较操作符(>/</>=/<=)

var a = "a";
var b = "b";
if (a < b) // true
  print(a + " is less than " + b);
else if (a > b)
  print(a + " is greater than " + b);
else
  print(a + " and " + b + " are equal.");


使用从字符串实例继承而来的 localeCompare 方法也能达到同样的效果。


tip

基本字符串和字符串对象的区别~~~

请注意区分 JavaScript 字符串对象和基本字符串值 . ( 对于 BooleanNumbers 也同样如此.)

字符串字面量 (通过单引号或双引号定义) 和 直接调用 String 方法(没有通过 new 生成字符串对象实例)的字符串都是基本字符串。

JavaScript会自动将基本字符串转换为字符串对象,只有将基本字符串转化为字符串对象之后才可以使用字符串对象的方法。

当基本字符串需要调用一个字符串对象才有的方法或者查询值的时候(基本字符串是没有这些方法的),JavaScript 会自动将基本字符串转化为字符串对象并且调用相应的方法或者执行查询。

var s_prim = "foo";
var s_obj = new String(s_prim);
console.log(typeof s_prim); // Logs "string"
console.log(typeof s_obj);  // Logs "object"

当使用 eval时,基本字符串和字符串对象也会产生不同的结果。eval 会将基本字符串作为源代码处理; 而字符串对象则被看作对象处理, 返回对象。 例如:

s1 = "2 + 2";               // creates a string primitive
s2 = new String("2 + 2");   // creates a String object
console.log(eval(s1));      // returns the number 4
console.log(eval(s2));      // returns the string "2 + 2"


由于上述原因, 当一段代码在需要使用基本字符串的时候却使用了字符串对象就会导致执行失败(虽然一般情况下程序员们并不需要考虑这样的问题)。

利用 valueOf 方法,我们可以将字符串对象转换为其对应的基本字符串。

console.log(eval(s2.valueOf())); // returns the number 4

注意: 其他的将字符串对象转换成基本字符串的方法可以及参考 StringView – a C-like representation of strings based on typed arrays.


Symbol

what

symbol 是一种基本数据类型 (primitive data type)。

每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。更进一步的解析见—— glossary entry for Symbol

const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

console.log(typeof symbol1);
// expected output: "symbol"

console.log(symbol3.toString());
// expected output: "Symbol(foo)"

console.log(Symbol('foo') === Symbol('foo'));
// expected output: false



attribute

Symbol.prototype 属性的属性特性:
writable false
enumerable false
configurable false

grammar

创建 Symbol 对象

Symbol([description])

参数


description 可选的,字符串类型。对 symbol 的描述,可用于调试但不是访问 symbol 本身。


example

对 symbol 使用 typeof 运算符

typeof运算符能帮助你识别 symbol 类型

typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'

Symbols 与 for...in 迭代

Symbols 在 for...in 迭代中不可枚举。另外,Object.getOwnPropertyNames() 不会返回 symbol 对象的属性,但是你能使用 Object.getOwnPropertySymbols() 得到它们。

var obj = {};
obj[Symbol("a")] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";
for (var i in obj) {
   console.log(i); // logs "c" and "d"
}

Symbols 与 JSON.stringify()



当使用 JSON.stringify() 时,以 symbol 值作为键的属性会被完全忽略:
JSON.stringify({[Symbol("foo")]: "foo"});                 
// '{}'


更多细节,请看 JSON.stringify()


Symbol 包装器对象作为属性的键



当一个 Symbol 包装器对象作为一个属性的键时,这个对象将被强制转换为它包装过的 symbol 值
var sym = Symbol("foo");
var obj = {[sym]: 1};
obj[sym];            // 1
obj[Object(sym)];    // still 1

tip

直接使用Symbol()创建新的symbol类型,并用一个可选的字符串作为其描述。

var sym1 = Symbol();
var sym2 = Symbol('foo');
var sym3 = Symbol('foo');


上面的代码创建了三个新的 symbol 类型。 注意,Symbol("foo") 不会强制将字符串 “foo” 转换成 symbol 类型。它每次都会创建一个新的 symbol 类型:

Symbol("foo") === Symbol("foo"); // false

下面带有 new 运算符的语法将抛出 TypeError 错误:

var sym = new Symbol(); // TypeError

这会阻止创建一个显式的 Symbol 包装器对象而不是一个 Symbol 值。围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持。 然而,现有的原始包装器对象,如 new Booleannew String以及new Number,因为遗留原因仍可被创建。
如果你真的想创建一个 Symbol 包装器对象 (Symbol wrapper object),你可以使用 Object() 函数:

var sym = Symbol("foo");
typeof sym;     // "symbol"
var symObj = Object(sym);
typeof symObj;  // "object"

全局共享的 Symbol



上面使用`Symbol()` 函数的语法,不会在你的整个代码库中创建一个可用的全局?symbol类型。 要创建跨文件可用的symbol,甚至跨域(每个都有它自己的全局作用域) , 使用 [`Symbol.for()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) 方法和  [`Symbol.keyFor()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/keyFor) 方法从全局的symbol注册?表设置和取得symbol。

在对象中查找 Symbol 属性

Object.getOwnPropertySymbols() 方法让你在查找一个给定对象的符号属性时返回一个?symbol类型的数组。注意,每个初始化的对象都是没有自己的symbol属性的,因此这个数组可能为空,除非你已经在对象上设置了symbol属性。


Symbol 类型转换



当使用 symbol 值进行类型转换时需要注意一些事情:
  • 尝试将一个 symbol 值转换为一个 number 值时,会抛出一个 TypeError 错误  (e.g. +sym or sym | 0).
  • 使用宽松相等时, Object(sym) == sym returns true.
  • 这会阻止你从一个 symbol 值隐式地创建一个新的 string 类型的属性名。例如,Symbol("foo") + "bar" 将抛出一个 TypeError (can't convert symbol to string).
  • "safer" String(sym) conversion 的作用会像symbol类型调用 Symbol.prototype.toString() 一样,但是注意 new String(sym) 将抛出异常。

今日学习总结


今日心情


今日学习了ES 基础概念中 七种 基础数据类型,感觉很不错,希望明天学习到更多东西~~~~

本文使用 mdnice 排版