js 基础-数据类型-数字/字符串

345 阅读8分钟

原始类型的方法

一个原始值:有 7 种原始类型:stringnumberbigintbooleansymbolnullundefined

一个对象:能够存储多个值作为属性。 {} 创建对象

对象包装器

被称为 StringNumberBooleanSymbol。它们提供了不同的方法。

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

通过包装器,原始类型可以提供方法。

null/undefined 没有任何方法 没有包装器 注意:

  • 不要用new Number(0) 方式创建,会返回对象,所有判断都是true
  • 应改用没有new 的 Number(0)方法调用直接返回基本类型 例如:
alert( typeof 0 ); // "number"
alert( typeof new Number(0) ); // "object"!
let zero = new Number(0);
if (zero) { // zero 为 true,因为它是一个对象, 始终为真, 
  alert( "xxx" );
}
//改用 直接调用方法
let zero = Number(0);
if (zero) { // zero => false
  alert( "xxxx" );
}

动态给包装类添加属性

let abc = "xxxx";
abc.name = 'jason'; // 这里包装类还在,可以赋值,但是在严格模式下会报错
alert(str.name); //  到这里 包装类已经被销毁,所以是 undefined

数字类型

number 包含两种

  1. 双精度浮点数
  2. BigInt

简写

1e3 = 1 * 1000 // "e" 把数字乘以 1 后面跟着给定数量的 0 的数字。
1.23e6 = 1.23 * 1000000
let ms = 0.000001;
let ms = 1e-6; // 1 的左边有 6 个 0 等价于 1 / 1000000 

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

//16进制
alert( 0xff ); // 255
alert( 0xFF ); // 255(一样,大小写没影响)
//二进制 8进制
let a = 0b11111111; // 二进制形式的 255
let b = 0o377; // 八进制形式的 255
alert( a == b ); // true,两边是相同的数字,都是 255

toString(base)

参数base是指用什么编码转化,最大36

let num = 255;
alert( num.toString() );  // 默认是10进制  255 
alert( num.toString(16) );  // 16进制 ff
alert( num.toString(2) );   // 二进制 11111111

直接数字转化,可以把长数字转短数字,方便url传输

alert( 123456..toString(36) ); // 2n9c 
//注意这里 需要使用.. 两个点。 因为一个点js以为是正常小数,两个点js能识别是特殊处理。
//等价于
(123456).toString(36)

舍入

Math.floor

向下舍入:3.1 变成 3-1.1 变成 -2

Math.ceil

向上舍入:3.1 变成 4-1.1 变成 -1

Math.round

向最近的整数舍入:3.1 变成 33.6 变成 4-1.1 变成 -1

Math.truncIE不支持)

都不舍入:3.1 变成 3-1.1 变成 -1

保留2位小数

1.2345=> `1.23

//方法1: 先变整数处理,然后再除回去
let num = 1.23456;
alert( Math.floor(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
//方式2:toFixed 会-四舍五入
let num = 12.34;
alert( num.toFixed(1) ); // "12.3" //
//内容长会补零
let num = 12.34;
alert( num.toFixed(5) ); // "12.34000",在结尾添加了 0

长度问题

数字是以 64 位格式IEEE-754 存储一个数字:

  • 52 位被用于存储这些数字,
  • 11 位用于存储小数点的位置(对于整数,它们为零),
  • 1 位用于符号
//长度太大
alert( 1e500 ); // Infinity 
//精度问题
alert( 0.1 + 0.2 ); // 0.30000000000000004
alert( 0.1 + 0.2 == 0.3 ); // false 
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
//精度解决方案
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // 0.30
//利用乘法放大
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3

js 无法描述三分之一,在数学里,这是不可描述,是个无理数。

isNaN

  • NaN 代表一个 error。:
alert( isNaN(NaN) ); // true
alert( isNaN("str") ); // true
//不能直接NaN与NaN比较, “NaN” 是独一无二
alert( NaN === NaN ); // false

isFinite

isFinite(value) 将其参数转换为数字,是否为常规数

alert( isFinite("15") ); // true
alert( isFinite("str") ); // false,因为是一个特殊的值:NaN
alert( isFinite(Infinity) ); // false,因为是一个特殊的值:Infinity

Object.is

Object.is(NaN,NaN) === true //true
Object.is0,-0)=== false //false //因为数字的符号位可能会不同

parseInt 和 parseFloat

alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12,只有整数部分被返回了
alert( parseFloat('12.3.4') ); // 12.3,在第二个点出停止了读取
//读不到的情况
alert( parseInt('a123') ); // NaN,第一个符号停止了读取
//第二个参数是 以什么进制读取
alert( parseInt('0xff', 16) ); // 16进制读取, 255 

其他 Math函数

alert( Math.random() ); // 0.1234567894322
alert( Math.random() ); // 0.5435252343232
alert( Math.random() ); // ... (任何随机数)
Math.max(a, b, c...)` / `Math.min(a, b, c...)
alert( Math.max(3, 5, -10, 0, 1) ); // 5
alert( Math.min(1, 2) ); // 1
Math.pow(n, power)
alert( Math.pow(2, 10) ); // 2 的 10 次幂 = 1024

蛋疼的 精度问题

alert( 1.35.toFixed(1) ); // 1.4
//实际结果
alert( 1.35.toFixed(20) ); // 1.35000000000000008882
alert( 6.35.toFixed(1) ); // 6.3
//实际结果
alert( 6.35.toFixed(20) ); // 6.34999999999999964473
//解决方案 
alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
// 小数部分 0.5 实际上是 1/2,2 的整数次幂为分母的小数在二进制数字系统中可以被精确地表示

min 到 max 的随机数

alert( random(1, 5) ); // 1.2345623452
alert( random(1, 5) ); // 3.7894332423
alert( random(1, 5) ); // 4.3435234525
function random(min,max) {
    return min +  Math.random() * (min+max) 
}
//方法
function randomInteger(min, max) {
  // 现在范围是从  (min-0.5) 到 (max+0.5)
  let rand = min - 0.5 + Math.random() * (max - min + 1);
  return Math.round(rand);
}
alert( randomInteger(1, 3) );
//方法2
function randomInteger(min, max) {
  // here rand is from min to (max+1)
  let rand = min + Math.random() * (max + 1 - min);
  return Math.floor(rand);
}
alert( randomInteger(1, 3) );

字符串

  • 文本数据被以字符串形式存储,单个字符没有单独的类型。
  • 字符串的内部格式始终是utf-16

特殊字符

特殊字符都以反斜杠字符 `` 开始。它也被称为“转义字符”。

let list = "aaa:\n * bbb\n * ccc\n * ddd";
alert(list); // 多个换行显示
字符描述
\n换行
\r回车:不单独使用。Windows 文本文件使用两个字符 \r\n 的组合来表示换行。
', "引号
\反斜线
\t制表符
\b, \f, \v退格,换页,垂直标签 —— 为了兼容性,现在已经不使用了。
\xXX具有给定十六进制 Unicode XX 的 Unicode 字符,例如:'\x7A''z' 相同。
\uXXXX以 UTF-16 编码的十六进制代码 XXXX 的 unicode 字符,例如 \u00A9 —— 是版权符号 © 的 unicode。它必须正好是 4 个十六进制数字。
\u{X…XXXXXX}(1 到 6 个十六进制字符)具有给定 UTF-32 编码的 unicode 符号。一些罕见的字符用两个 unicode 符号编码,占用 4 个字节。这样我们就可以插入长代码了。
alert( "\u00A9" ); // ©
//字符长度
alert( `My\n`.length ); // 3 ,\n算一个字符

访问字符

let str = `Hello`; 
alert( str[0] ); // H
//等价于
alert( str.charAt(0) ); // H 
//没有找到
alert( str[1000] ); // undefined
alert( str.charAt(1000) ); // '' 日常开发建议用charAt

字符串是不可变的

let str = 'Hi';
str[0] = 'h'; // 不报错
alert( str[0] ); // 但还是H
//可以整个重新赋值解决
str = 'hi';

查找子字符串

  • str.indexOf(substr, pos) //pos 默认为0
  • str.lastIndexOf(substr, pos) //相反查询
let str = 'Widget with id';
alert( str.indexOf('Widget') ); // 0,因为 'Widget' 一开始就被找到
alert( str.indexOf('widget') ); // -1,没有找到,检索是大小写敏感的
alert( str.indexOf("id") ); // 1,"id" 在位置 1 处(……idget 和 id)
//从第二个id开始查找
let str = 'Widget with id';
alert( str.indexOf('id', 2) ) // 12
//判断是否包括应该使用
if (str.indexOf("Widget") != -1) {
    alert("xxxx"); 
}

按位(bitwise)NOT 技巧

它将数字转换为 32-bit 整数(如果存在小数部分,则删除小数部分),然后对其二进制表示形式中的所有位均取反

alert( ~2 ); // -3,和 -(2+1) 相同
alert( ~1 ); // -2,和 -(1+1) 相同
alert( ~0 ); // -1,和 -(0+1) 相同
alert( ~-1 ); // 0,和 -(-1+1) 相同
let str = "Widget";
if (~str.indexOf("Widget")) {
  alert( 'xxxx' ); //ok
}

includes,startsWith,endsWith

alert( "Widget with id".includes("Widget") ); // true
alert( "Hello".includes("Bye") ); // false
alert( "Widget".startsWith("Wid") ); // true,"Widget" 以 "Wid" 开始
alert( "Widget".endsWith("get") ); // true,"Widget" 以 "get" 结束

获取子字符串

substringsubstrslice

str.slice(start [, end])

返回字符串从 start 到(但不包括)end 的部分。注意游标从0开始

let str = "stringify";
alert( str.slice(0, 5) ); // 'strin',从 0 到 5 的子字符串(不包括 5)
alert( str.slice(2) ); // ringify 从第二个位置直到结束
//start/end 也有可能是负值。它们的意思是起始位置从字符串结尾计算:  
alert( str.slice(-4, -1) ); // 'gif' // 从右边的第四个位置开始,在右边的第一个位置结束

str.substring(start [, end])

与str.slice 区别在于允许 start 大于 end。相当于不用考虑谁是头谁是尾

let str = "stringify";
// 这些对于 substring 是相同的
alert( str.substring(2, 6) ); // "ring"
alert( str.substring(6, 2) ); // "ring"
// ……但对 slice 是不同的:
alert( str.slice(2, 6) ); // "ring"(一样)
alert( str.slice(6, 2) ); // ""(空字符串)

str.substr(start [, length])

区别允许我们指定 length 而不是具体结束位置

let str = "stringify";
alert( str.substr(2, 4) ); // 'ring',从位置 2 开始,获取 4 个字符

第一个参数可能是负数,从结尾算起:(注意负数是从-1开始计算)

let str = "stringify";
alert( str.substr(-4, 2) ); // 'gi',从第 4 位获取 2 个字符

开发中优先选择slice

方法选择方式……负值参数
slice(start, end)startend(不含 end允许
substring(start, end)startend 之间(包括 start,但不包括 end负值代表 0
substr(start, length)start 开始获取长为 length 的字符串允许 start 为负数

比较字符串

str.codePointAt(pos) //返回在 pos 位置的字符代码 :

alert( 'a' > 'Z' ); // true
// 不同的字母有不同的代码,最终比较的是 UTF-16 编码
alert( "z".codePointAt(0) ); // 122
alert( "Z".codePointAt(0) ); // 90
//把代码转为文本
alert( String.fromCodePoint(90) ); // 

总结

  • 有 3 种类型的引号。反引号允许字符串跨越多行并可以使用 ${…} 在字符串中嵌入表达式。
  • JavaScript 中的字符串使用的是 UTF-16 编码。
  • 我们可以使用像 \n 这样的特殊字符或通过使用 \u... 来操作它们的 unicode 进行字符插入。
  • 获取字符时,使用 []
  • 获取子字符串,使用 slicesubstring
  • 字符串的大/小写转换,使用:toLowerCase/toUpperCase
  • 查找子字符串时,使用 indexOfincludes/startsWith/endsWith 进行简单检查。
  • 根据语言比较字符串时使用 localeCompare,否则将按字符代码进行比较。