Javascript基础之 数据类型(number,string)

286 阅读10分钟

原始类型的方法

以下是JavaScript创建者面临的悖论:

  • 人们可能想对诸如字符串或数字之类的原始类型执行很多操作,最好将它们作为方法来访问。
  • 原始类型必须尽可能的简单轻量。 解决方案,如下:
  1. 原始类型仍然是原始的。与预期相同,提供单个值
  2. JavaScript允许访问字符串,数字,布尔值和symbol的方法和属性。
  3. 为了使它们起作用,创建了提供额外功能的特殊“对象包装器”,使用后即被销毁。

"对象包装器"对于每种原始类型哦都是不同的,它们被称为String, Number,BooleanSymbol。因此,它们提供了不同的方法。

举个例子:

let str = "Hello";

alert( str.toUpperCase() ); // HELLO

以下是实际str.toUpperCase()中实际发生的情况:

  1. 字符串strs是一个原始值。因此,在访问其属性时,会创建一个包含字符串字面量的特殊对象,并且具有有用的方法,例如toUpperCase()
  2. 该方法运行并返回一个新的字符串(由alert显示)。
  3. 特殊对象被销毁,只留下原始值str.

所以原始类型可以提供方法,但它们依然时轻量级的。

构造器Stirng/Number/Boolean仅提供内部使用
像Java这样的一些语言允许我们使用new Number(1) 或 new Boolean(true)等语法,明确地为原始类型创建“对象包装器”。
在JavaScript中,由于历史原因,这也是可以,但极其不推荐。因为这样会出问题

let zero = new Number(0);
if(zero) { alert("zero is truthy?!") };

因为在if中始终为真,因此此处的alert将显示

另一方面,调用不带new关键字的String/Number/Boolean函数是完全理智和有用的。它们将一个值转换为相应的类型:转成字符串,数字或布尔值(原始类型)。
例如,下面完全是有效的:

let num = Number("123");

null和undefined

特殊的原始类型nullundefined是例外。它们没有对应的“对象包装器”,也没有提供任何方法。从某种意义上说,它们是最原始的。

原文链接:zh.javascript.info/primitives-…

数字类型

在现代JavaScript中,数字有两种类型:

  1. JavaScript中常规数字以64位的格式IEEE-754存储,也被称为“双精度浮点数”。这是我们大多数时候所使用的数字。
  2. Bigint数字,用于表示任意长度的整数。有时会需要它们,因为常规数字不能超过2<sup>53</sup>或小于-2<sup>53</sup>。(另讲)
  3. Infinity(-Infinity)
  4. NaN

我们将主要讨论常规数字。

科学计数法

在JavaScript中,我们通过在数字后附加字母e,并指定零的数量来缩短数字。

let billion = 1e9;
alert(billion); // 1000000000
alert(7.3e9);   // 7300000000

e后面可以接负数,表示除以1后面跟着给定数量的0的数字

alert(1e-3);     // 0.001 
alert(1.23e-6);  // 0.00000123

十六进制,二进制和八进制数字

十六进制,二进制和八进制可以使用0x,0b0o前缀表示。

// 十六进制
alert( 0xff );  // 255
alert( 0x5f );  // 95

// 二进制
alert( 0b111101 );  // 61
alert( 0b1111 );    // 15

// 八进制
alert( 0o377 );   // 255
alert( 0o4523 );  // 2387

只有这三种进制支持这种写法。对于其他进制,我们应该使用函数parseInt

toString(base)

num.toString(base) 返回在给定base进制数字系统中num的字符串表示形式。

base的范围是 2 ~ 36 的整数。默认是 10 。

let num = 255;

alert(num.toString(2));   // 11111111
alert(num.toString(16));  // ff

使用两个点调用toString()方法
如果我们想直接在一个数字上调用一个方法,我们需要在它后面放置两个点..

alert(123456..toString(36)); //2n9c 
// 相当于 alert( (123456).toString(36) );

如果放置一个点,会出错。因为JavaScript语法隐含了第一个点之后的部分位小数部分。如果再放一个点,那么JavaScript就知道小数部分为空,现在使用该方法。

parseInt 和 parseFloat

parseIntparseFloat的作用:

  1. 可以从字符串中“读取”数字,直到无法读取位置。如果发生error,则返回收集到的数字。函数parseInt返回一个整数,函数parseFloat返回一个浮点数
  2. parseInt函数具有可选的第二个参数,制定了数字系统的基数,可以解析十六进制数字等字符串。
// 第一个作用

alert( parseInt("100px") );  // 100
alert( parseFloat("12.5rem") );  //12.5

alert( parseInt("12.8") );   // 12 , 只有整数部分返回
alert( parseFloat("12.3.4") );  // 12.3 , 在第二个点处停止了读取

alert( parseInt("a123") ); //NaN 第一个符号停止了读取
// 第二个作用

alert( parseInt("0xff", 16) );  // 255
alert( parseInt("ff", 16) );    // 255 , 没有0x前缀仍然有效

alert( parseInt("2n9c", 36) );  // 123456

小数的舍入

  • Math.floor() 向下舍入
  • Math.ceil() 向上舍入
  • Math.round() 四舍五入
  • Math.trunc() 保留整数(※IE浏览器不支持这个方法)
Math.floor()Math.ceil()Math.round()Math.trunc()
3.13433
3.63443
-1.1-2-1-1-1
-1.6-2-1-2-1
  • 保留小数点后n位:
  1. 乘除法
let num = 1.23456'
alert( Math.floor(num * 100) / 100 ); // 1.23
  1. toFixed(n)方法,以 字符串 形式返回结果
let num = 12.34;
alert( num.toFixed(1) ); // "12.3"

num = 12.36;
alert( num.toFixed(1) ); // "12.4"

// 如果小数部分比所需要的短,则在结尾添加0
alert( num.toFixed(5) ); // "12.36000"

isFinite 和 isNaN

  • isFinity(value) 将其参数转换为数字,如果是常规数字,则返回true
alert( isFinity("15") ); //true
alert( isFinity("str") ); //false, 因为是一个特殊的值 NaN
alert( isFinity(Infinity) ); //false 因为是一个特殊的值 Infinity
  • isNaN(value) 将其参数转换为数字,然后测试它是否为NaN
alert( isNaN(NaN) ); // true
alert( isNaN("str") ); // true

// NaN === NaN
// 值NaN是独一无二的,它不等于任何东西,包括它自身
alert( NaN === NaN ); // false

在所有数字函数(包括isFinity)中,空字符串或仅有空格的字符串均被视为 0

Object.is方法 它类似于 ===一样对值进行比较,但它对于两种边缘情况更可靠:

  1. 适用于NaN: Object.is(NaN,NaN) === true
  2. 值 0 和 -0 是不同的:Object.is(0,-0) === false

不精确的计算

在内部,数字是以 64 位格式 IEEE-754 表示的,所以正好有 64 位可以存储一个数字:其中 52 位被用于存储这些数字,其中 11 位用于存储小数点的位置(对于整数,它们为 0 ),而 1 位用于符号。

溢出

如果一个数字太大,则会溢出64位存储,并可能导致无穷大:

alert( 1e500 ); // Infinity

精度的损失

alert( 0.1 + 0.2 == 0.3 ); // false

alert( 0.1 + 0.2 ) // 0.30000000000000004

为什么会出现这样的情况呢?
一个数字以其二进制的形式存储在内存中。在十进制数字系统中开起来很简单的0.1,0.2这样的小数,实际上在二进制行驶中是无限循环小数。

在十进制数字系统中,可以保证以10的整数次幂作为除数能够正常工作,但是以3作为除数则不能。也是同样的原因,在二进制数字系统中,可以保证以2的整数次幂作为除数时能够正常工作,但1/10就变成了一个无限循环的二进制小数。
IEEE-754数字格式通过将数字舍入到最接近的可能数字来解决此问题,这些舍入规则通常不允许我们看到“极小的精度损失”,但是它确实存在。

最可靠的方法时借助方法 toFixed(n)对结果进行舍入,再将其强制转换位数字:

let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) );  // 0.3

尝试运行下面的这段代码:

alert( 9999999999999999 ) // 显示 10000000000000000

出现了同样的问题:精度损失。有64位来表示数字,其中52位可用于存储整数,但这还不够,所以最不重要的数字就消失了 JavaScript不会在此类事件中触发error。它会尽最大努力使数字符合所需的格式。

其他数学函数

  • Math.random(): 返回一个从 0 到 1 的随机数(不包括 1 )
  • Math.max(a, b...)/Math.min(a, b...): 从任意数量的参数中返回最大/最小值
  • Math.pow(n, power): 返回 n 的给定 power 次幂 其他数学函数,点击这里查看

原文链接:zh.javascript.info/number

字符串

字符串的表示

可以包含在单引号,双引号或反引号中,反引号允许我们通过${...}将任何表达式嵌入到字符串中,允许跨行。

let single = 'single-quoted';
let double = "double-quoted";

function sum(a, b){
    return a + b;
}

let guestList = `Guest:
  * John
  * Pete
  * Mary
  `;
  
 alert(single); // single-quoted
 alert(double); // double-quoted
 
 /*
 Guest:
  * John
  * Pete
  * Mary
 */
 alert(guestList);
 alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.

特殊字符

常用特殊字符

字符描述
\n换行
\r回车:不单独使用。Windows文本文件使用两个字符\r\n的组合来表示换行。
\'``\"引号
\\反斜线
\t制表符
\xXX具有给定十六进制 Unicode XX 的 Unicode 字符
\uXXXX以 UTF-16 编码的十六进制代码 XXXX 的 unicode 字符
\U{XXXX..}(1到6个十六进制字符)具有给定 UTF-32 编码的 unicode 符号。一些罕见的字符用两个 unicode 符号编码

字符串的长度

Length属性表示字符串的长度

alert(`My\n`.length); // 3

\n是一个单独的“特殊”字符,所以长度是 3.

访问字符

  • 使用方括号[]
  • 调用str.charAt(pos)方法 ❗
  1. 第一个字符下标为 0
  2. 如果没有找到字符,[]返回undefined,charAt返回一个空字符串
  3. 可以使用for..of遍历字符
for(let char of "Hello"){
    alert(char); // H e l l o
}

常用方法

改变大小写

str.toUpperCase() 转换大写
str.toLowerCase() 转换小写

查找字符串

str.indexOf(substr [, pos])

  • 从给定位置pos开始,在str中查找substr,如果没有找到,则返回-1,否则返回匹配成功的位置。

str.lastIndexOf(substr [, pos])

  • 从给定位置pos开始向前搜索,默认为字符串末尾

str.inclueds(substr [, pos])

  • 返回布尔值,判断是否存在substr
  • 第二个可选参数是开始搜索的起始位置

str.startsWith(substr)

  • 判断是否以substr开头

str.endsWith(substr)

  • 判断是否以substr结尾

获取子字符串

str.slice(start [, end])

  • 返回字符串从start到(但不包括)end的部分
  • 如果没有第二个参数,slice会一直运行到字符串末尾
  • start/end也有可能是负值
let str = "stringify";

// 从右边的第四个位置开始,在右边的第一个位置结束
alert( str.slice(-4, -1) ); // "gif"

str.substring(start [, end])

  • 返回字符串从start到(但不包括)end的部分
  • 允许start大于end
  • 不允许负参数,负参数视为 0

str.substr(start [, length])

  • 返回字符串从start开始获取length的字符串
  • 允许start负值

比较字符串

str.codePointAt(pos)

  • 返回pos位置的字符UTF-16编码

String.fromCodePoint(code)

  • 通过数字code创建字符
alert( String.fromCodePoint(90) ); // Z

str.localeCompare(str2)

  • 根据语言规则返回一个整数,这个整数能指示字符串str在排序顺序中排在字符串str2前面,后面,还是相同:
    • 如果str排在str2前面,则返回负数。
    • 如果str排在str2后面,则返回正数。
    • 如果它们在相同位置,则返回 0

其他

str.trim()

  • 删除字符串前后的空格

str.repeat(n)

  • 重复字符串 n 次

原文链接:zh.javascript.info/string