字符串的方法的总结和使用

前言

前些天写了篇数组的总结,其实我们再来看看字符串,和数组有异曲同工之妙,字符串也可以通过 split 来变成数组,然后使用数组的方法。

另外,对于字符串,其实我们关心的只有一个问题,返回值是什么

接下来,我会带大家一步步的使用 String 里面所拥有的方法和属性

字符串的属性

length

length属性表示一个字符串的长度。该属性返回字符串中字符编码单元的数量。JavaScript 使用 UTF-16 编码,该编码使用一个 16 比特的编码单元来表示大部分常见的字符,使用两个代码单元表示不常用的字符。因此 length 返回值可能与字符串中实际的字符数量不相同。

空字符串的 length 为 0。

静态属性 String.length 返回 1。

let str = "gating"
console.log(str.length) // 6

let empty = ""
console.log(empty.length) // 0  


// JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。
// 对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。
let double = "𪚥"
console.log(double.length) // 2

// 从数组的length我们可得知,数组的 length 来截断数组,字符串的可不可以呢?
// 很遗憾的说,不行,因为字符串的length只可读,不可写
let name = "gating"
name.length = 1
console.log(name,name.length) // gating,6

字符串的静态方法

字符的 Unicode 表示法

说到字符串,不得不说 JavaScript 中的字符串的 Unicode 表示形式,JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的Unicode码点。

但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。

// 比如
console.log("\u0061") // a
console.log("\uD842\uDFB7") // 𠮷
console.log("\u20BB7") // "₻7"

// 但是 es6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
console.log("\u{20BB7}")

参考链接 ECMAScript 6 入门 建议去了解一下,这里我就不做过多解释了,因为我用的真的不多(主要也是不会)

String.fromCharCode()

String.fromCharCode() 方法返回使用指定的Unicode值序列创建的字符串。

/**
 * String.fromCharCode(numN)
 * @param {Number/String} numN => 一组序列数字,表示 Unicode 值。
 * ----------------------------------------------
 * 返回值
 * @param {String} 该方法返回一个字符串,而不是一个 String 对象。
 */

console.log(String.fromCharCode(65)) // A
console.log(String.fromCharCode(65,66)) // AB
console.log(String.fromCharCode("----")) // " "

作用于高位编码(higher values)

尽管绝大部分常用的 Unicode 值可以用一个 16-bit 数字表示(正如 JavaScript 标准化过程早期),并且对于绝大部分值 fromCharCode() 返回一个字符(即对于绝大部分字符 UCS-2 值是 UTF-16 的子集),但是为了处理所有的 Unicode 值(至 21 bits),只用 fromCharCode() 是不足的。由于高位编码字符是用两个低位编码(lower value)表示形成的一个字符,因此String.fromCodePoint() (ES6 规范的一部分)被用来返回这样一对低位编码,从而可以完全表示这些高位编码字符。

String.fromCodePoint()

String.fromCodePoint() 静态方法返回使用指定的代码点序列创建的字符串。

/**
 * String.fromCodePoint(numN)
 * @param {Number/String} numN => 一串 Unicode 编码。如果传入无效的 Unicode 编码,将会抛出一个RangeError
 * ----------------------------------------------
 * 返回值
 * @param {String} 该方法返回一个字符串,而不是一个 String 对象。
 */
console.log(String.fromCodePoint(65)) // A
console.log(String.fromCodePoint(65,66)) // AB
console.log(String.fromCodePoint("----")) // RangeError

// String.fromCharCode() 方法不能单独获取在高代码点位上的字符
console.log(String.fromCharCode(0x2F804)) // 
console.log(String.fromCodePoint(0x2F804)) // 你

具体实际用理可以看看 MDN 这里不做阐述了,因为博主这两个方法用着不多 String.fromCharCode()String.fromCodePoint()

String.raw 和 模板字符串

模板字符串

了解String.raw时,我们必须先了解一下什么是模板字符串,简单来说模板字符串是增强版的字符串,他用反引号(`)标识。

// 字符串中嵌入变量
// es6 之前,我们如果想要在字符串嵌入变量需要通过+号
let name = "gating", time = "today";
console.log("Hello "+name+", how are you " + time) // Hello gating, how are you today?

// 模板字符串后
let name = "gating", time = "today";
console.log(`Hello ${name}, how are you ${time}?`) // Hello gating, how are you today?

// 另外模板字符串可以表示多行字符串
var str = `<ul>
             <li>first</li>
             <li>second</li>
          </ul>`

String.raw

String.raw() 是一个模板字符串的标签函数,它的作用类似于 Python 中的字符串前缀 r 和 C# 中的字符串前缀 @,是用来获取一个模板字符串的原始字面量值的。

/**
 * String.raw(callSite, ...substitutions) || String.raw`templateString`
 * @param {Object} callSite => 一个模板字符串的“调用点对象”。类似{ raw: ['foo', 'bar', 'baz'] }。
 * @param {Any} ...substitutions => 任意个可选的参数,表示任意个内插表达式对应的值。
 * @param {String} templateString => 模板字符串。
 * ----------------------------------------------
 * 返回值
 * @param {String} 给定模板字符串的原始字面量值。
 */
// "Hi\\u000A!",这里得到的会是 \、u、0、0、0、A 6个字符
// 任何类型的转义形式都会失效
console.log(String.raw `Hi\u000A!`) // Hi\u000A!

let name = "Bob"
console.log(String.raw `Hi\n${name}!`) // Hi\nBob!

// 我认为你通常不需要把它当成普通函数来调用
console.log(String.raw({ raw: 'test' }, 0, 1, 2)) // t0e1s2t
console.log(String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2)) // t0e1s2t

作为函数,String.raw的代码实现基本如下。

String.raw = function (strings, ...values) {
  let output = '';
  let index;
  for (index = 0; index < values.length; index++) {
    output += strings.raw[index] + values[index];
  }

  output += strings.raw[index]
  return output;
}

ES6入门 String.raw()

正则相关

正则推荐观看文末的 正则表达式30分钟入门教程 ,写的非常好

split

split() 方法使用指定的分隔符字符串将一个String对象分割成字符串数组,以将字符串分隔为子字符串,以确定每个拆分的位置。

Tip: 如果空字符串(““)被用作分隔符,则字符串会在每个字符之间分割。

/**
 * str.split(separator,limit)
 * @param {String} separator => 一个介于0 和字符串长度减1之间的整数。 (0~length-1) 如果没有提供索引,charAt() 将使用 0。如果指定的 index 值超出了该范围,则返回一个空字符串。
 * @param {Number} limit => 一个整数,限定返回的分割片段数量。当提供此参数时,split 方法会在指定分隔符的每次出现时分割该字符串,但在限制条目已放入数组时停止。如果在达到指定限制之前达到字符串的末尾,它可能仍然包含少于限制的条目。新数组中不返回剩下的文本。
 * ----------------------------------------------
 * 返回值
 * @param {Array} 返回源字符串以分隔符出现位置分隔而成的一个 Array 
 */

let str = "gating"
console.log(str.split('')) // ["g", "a", "t", "i", "n", "g"]
console.log(str.split('',1)) // ["g"]
console.log(str.split('',8)) // ["g", "a", "t", "i", "n", "g"]

看到这里,是不是感觉可以用通过split转成数组,使用数组的方法啦?没错是的,还记得前些天我写过一篇数组的方法的总结吗?没错,重新看一遍就可以了O(∩_∩)O

不信,你看,我们可以通过 split 获取字符串的交集或差集哦

/**
 * 返回两个字符串的交集
 * @param {String} str => 字符串1 
 * @param {String} str => 字符串2
 */
function intersection(str1, str2) {
    return str1.split("").filter(v => str2.split("").includes(v)).toString().replace(/,/g, "")
}
console.log(intersection("abc","bcd")) // bc

是不是感觉自己已经对字符串了如指掌了吗?

好了,本次教程到此结束了

开个玩笑嘛,接下来我会陆陆续续带大家认识下 string 的方法的。

match

当一个字符串与一个正则表达式匹配时, match()方法检索匹配项

/**
 * str.match(regexp)
 * @param {RegExp} regexp => 个正则表达式对象。如果传入一个非正则表达式对象,则会隐式地使用 new RegExp(obj) 将其转换为一个 RegExp 。如果你未提供任何参数,直接使用 match() ,那么你会得到一个包含空字符串的 Array :[""] 。
 * ----------------------------------------------
 * 返回值
 * @param {String}  如果字符串匹配到了表达式,会返回一个数组,数组的第一项是进行匹配完整的字符串,之后的项是用圆括号捕获的结果。如果没有匹配到,返回null
 */

let str = "gating"
const res = str.match(/g/g)
console.log(res) // ["g", "g"]
console.log(str.match()) // [""]
console.log(str.match(/sadasd/)) // null

let str1 = "NaN means not a number. Infinity contains -Infinity and +Infinity in JavaScript.",
    str2 = "My grandfather is 65 years old and My grandmother is 63 years old.",
    str3 = "The contract was declared null and void.";
str1.match("number");   // "number" 是字符串。返回["number"]
str1.match(NaN);        // NaN的类型是number。返回["NaN"]
str1.match(Infinity);   // Infinity的类型是number。返回["Infinity"]
str1.match(+Infinity);  // 返回["Infinity"]
str1.match(-Infinity);  // 返回["-Infinity"]
str2.match(65);         // 返回["65"]
str2.match(+65);        // 有正号的number。返回["65"]
str3.match(null);       // 返回["null"]

replace

replace() 方法返回一个由替换值替换一些或所有匹配的模式后的新字符串。模式可以是一个字符串或者一个正则表达式, 替换值可以是一个字符串或者一个每次匹配都要调用的函数。

replace 十分重要,必须掌握

/**
 * str.replace(regexp|substr, newSubStr|function)
 *  @param {RegExp} regexp => 一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
 *  @param {String} substr => 一个要被 newSubStr 替换的字符串。其被视为一整个字符串,而不是一个正则表达式。仅仅是第一个匹配会被替换。
 *  @param {String} newSubStr =>  用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名 
 *  @param {Function} function => 一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果 
 * ----------------------------------------------
 * 返回值
 * @param {String}  一个部分或全部匹配由替代模式所取代的新的字符串
 */

// 特殊的变量名 和 function 在实例中 会更详细说明
let str = "gating"
// 如果用 substr ,只会替换找到的第一个字符,替换多个字符则需要用正则
console.log(str.replace('g','家庭')) // 家庭ating
console.log(str.replace(/g/,'家庭')) // 家庭ating
console.log(str.replace(/g/g,'家庭')) // 家庭atin家庭
console.log(str.replace(/g/i,'G')) // Gating

let word = "hello,gating"
// 单词首字母大写
// 这里的 word 参数时匹配的字符串,下文有说明
let res = word.replace(/\b\w+\b/g,(word)=>(word[0].toUpperCase()+word.slice(1)))
console.log(res) // Hello,Gating

实际应用场景中,replace 方法非常灵活,可以做很多我们想要的事,比如:😄

类似于我们的模板引擎 replace 就可以实现,不过这里我就不带着大家实现了= =

在写实例时,我希望可以先搞懂几个概念和用法

newSubStr 的特殊变量名

变量名 代表的值
$$ 插入一个 “$”。
$& 插入匹配的子串。
$` 插入当前匹配的子串左边的内容。
$' 插入当前匹配的子串右边的内容。
$*n* 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始

replace 中 function 的参数

变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, ... 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如, 如果是用 /(\a+)(\b+)/这个来匹配, p1就是匹配的 \a+, p2 就是匹配的 \b+。
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1)
string 被匹配的原字符串。

你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。 (注意: 上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。

在实际应用中,replace的使用可以满足大部分的操作字符串场景,特别是function的引入,极大的增强了replace的实力,从而使得我们操作字符游刃有余.

在写实例的时,我们需要搞懂几个newSubStr特殊变量名,他在实际应用中也很有作用

let name = 'gating'
// $& 表示匹配到的字串,这里匹配到了 gating ,所以 $& 就等于 gating
console.log(name.replace(/\w+/,'$&-$&')) // gating-gating

let str = "gating say hello for mosen"
// $1 表示匹配第一个括号里的内容 也就是 $1 就是 gating 
console.log(str.replace(/(^\w+)(.*?)(\w+)$/,'$3$2$1')) // mosen say hello for gating\

let hello = "hello,gating"
// $`当前匹配的子串左边的内容。
console.log(hello.replace(/gating/,"&.$`mosen")) // hello,gating.hello,mosen
// $'当前匹配的子串右边的内容。(不是例子的例子)
console.log(hello.replace(/hello,/,"$' say hi for $`")) // gating say hi for gating

// function 的 用法
let paragraph = "what is this?"
let res = paragraph.replace(" is this",(match,offset,string)=>{
    // match 匹配的子串
    // offset 偏移量 这是是4 也就是 偏移了what4个字符
    // string 元字符
    console.log(match,offset,string) //  is this 4 what is this?
    return " is that"
})
console.log(res) // what is that?

了解了这些,那么我们就可以做写点比较实际的了

// js 隐藏手机中间5位号码
let phone='13700000137'
console.log(phone.replace(/(\d{3})\d+(\d{3})/, '$1****$2')) // 137****137

// 将阿拉伯数字每三位一逗号分隔,如:1000转化为1,000
let price =  '3521.08'
console.log(price.replace(/(?=(?!^)(?:\d{3})+(?:\.|$))(\d{3}(\.\d+$)?)/g,',$1')) // 3,521.08

// html 转义,防止用户注入
let html = '<div>"hello & world"</div>';
let res = html.replace(/[<>\"\'\&']/g,function(a){
    switch(a){
        case '<':
            return '&lt;';
        case '>':
            return '&gt;';
        case '\"':
            return '&quot;';
        case '\'':
            return '&#39;';
        case '\&':
            return '&amp;';
    }
})
console.log(res) // &lt;div&gt;&quot;hello &amp; world&quot;&lt;/div&gt;


// 简易的字符串模板引擎
let template = "My name is {name}"
const obj = {name:'gating'}
const tmpl = (template,obj)=> template.replace(/([^{}]*){(.*)}/g,(match,p1,p2) => (p1+obj[p2]))
console.log(tmpl(template,obj)) // My name is gating

字符编码相关

charAt

charAt() 方法从一个字符串中返回指定的字符

/**
 * str.charAt(index)
 * @param {Number} index => 一个介于0 和字符串长度减1之间的整数。 (0~length-1) 如果没有提供索引,charAt() 将使用 0。如果指定的 index 值超出了该范围,则返回一个空字符串。
 * ----------------------------------------------
 * 返回值
 * @param {String} 当前索引的字符串
 */

let str = "gating"
console.log(str.charAt(0)) // g
console.log(str.charAt(6)) // ""

// charAt 和 fromCharCode 用同样的问题,他不能识别高位编码
console.log("𠮷".charAt(0)) // �

charCodeAt

charCodeAt() 方法返回0到65535之间的整数,表示给定索引处的UTF-16代码单元 (在 Unicode 编码单元表示一个单一的 UTF-16 编码单元的情况下,UTF-16 编码单元匹配 Unicode 编码单元。但在——例如 Unicode 编码单元 > 0x10000 的这种——不能被一个 UTF-16 编码单元单独表示的情况下,只能匹配 Unicode 代理对的第一个编码单元) 。如果你想要整个代码点的值,使用 codePointAt()。

/**
 * str.charCodeAt(index)
 * @param {Number} index => 一个大于等于 0,小于字符串长度的整数。如果不是一个数值,则默认为 0。
 * ----------------------------------------------
 * 返回值
 * @param {Number} 返回值是一表示给定索引处(String中index索引处)字符的 UTF-16 代码单元值的数字;如果索引超出范围,则返回 NaN。
 */
console.log("ABC".charCodeAt(0)) // 65
console.log("ABC".charCodeAt(1)) // 66
console.log("ABC".charCodeAt(3)) // NaN

// 针对高位编码,他会拆分成两个双字节的形式再来获取
// 也就是 "\uD842\uDFB7".charCodeAt(0)
console.log("𠮷".charCodeAt(0)) // 55362
console.log("𠮷".charCodeAt(1)) // 57271

codePointAt

codePointAt() 方法返回 一个 Unicode 编码点值的非负整数。

/**
 * str.codePointAt(pos)
 * @param {Number} pos => 这个字符串中需要转码的元素的位置。
 * ----------------------------------------------
 * 返回值
 * @param {String} 返回值是在字符串中的给定索引的编码单元体现的数字(10进制数),如果在索引处没找到元素则返回 undefined 。
 */
console.log("ABC".charCodeAt(0)) // 65
console.log("ABC".charCodeAt(3)) // undefined
console.log("𠮷".codePointAt(0)) // 134071
console.log("𠮷".codePointAt(1)) // 57271

汉字“𠮷”(注意,这个字不是“吉祥”的“吉”)的码点是0x20BB7,UTF-16 编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt方法无法读取整个字符,charCodeAt方法只能分别返回前两个字节和后两个字节的值。

codePointAt 方法在第一个字符上,正确地识别了“𠮷”,返回了它的十进制码点 134071(即十六进制的20BB7),在第二个字符(即“𠮷”的后两个字节)codePointAt方法的结果与charCodeAt方法相同。

codePointAt方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString(16)方法转换一下。

let s = '𠮷a'
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97

// 你可能注意到了,codePointAt方法的参数,仍然是不正确的。
// 比如,上面代码中,字符a在字符串s的正确位置序号应该是 1,但是必须向codePointAt方法传入 2。
// 解决这个问题的一个办法是使用for...of循环,因为它会正确识别 32 位的 UTF-16 字符。
for (let ch of s) {
  console.log(ch.codePointAt(0).toString(16)) // 20bb7 61
}
console.log("\u{20bb7}") // 𠮷

normalize

normalize() 方法会按照指定的一种 Unicode 正规形式将当前字符串正规化.

许多欧洲语言有语调符号和重音符号。为了表示它们,Unicode 提供了两种方法。一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。另一种是提供合成符号(combining character),即原字符与重音符号的合成,两个字符合成一个字符,比如O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)。

这两种表示方法,在视觉和语义上都等价,但是 JavaScript 不能识别。

/**
 * str.normalize(form)
 * @param {String} form => 四种 Unicode 正规形式 "NFC", "NFD", "NFKC", 以及 "NFKD" 其中的一个, 默认值为 "NFC".
 * ----------------------------------------------
 * 返回值
 * @param {String} 正规化后的字符串。如果给 form 传入了非法的参数值, 则会抛出 RangeError 异常.
 */
console.log('\u01D1'==='\u004F\u030C') // false
console.log('\u01D1'.normalize() === '\u004F\u030C'.normalize()) // true

// NFC参数返回字符的合成形式,NFD参数返回字符的分解形式。
'\u004F\u030C'.normalize('NFC').length // 1
'\u004F\u030C'.normalize('NFD').length // 2
  1. NFC,默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。

  2. NFD,表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。

  3. NFKC,表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来举例,normalize方法不能识别中文。)

  4. NFKD,表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。

不过,normalize方法目前不能识别三个或三个以上字符的合成。这种情况下,还是只能使用正则表达式,通过 Unicode 编号区间判断。

此内容参考了 阮老师的normalize()

合并填充相关

concat

concat() 方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。

强烈建议使用 赋值操作符(+, +=)代替 concat 方法。因为 concat 性能比较垃圾

/**
 * str.concat(stringN)
 * @param {String} stringN => 和原字符串连接的多个字符串
 * ----------------------------------------------
 * 返回值
 * @param {String} 连接后的字符串
 */
let str = "hello,"
console.log(str.concat("gating")) // hello,gating

padEnd

padEnd() 方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

/**
 * str.padEnd(targetLength,padString)
 * @param {Number} targetLength => 当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
 * @param {String} padString  => 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的缺省值为 " "(U+0020)。
 * ----------------------------------------------
 * 返回值
 * @param {String} 在原字符串末尾填充指定的填充字符串直到目标长度所形成的新字符串。
 */

let str = "abc"
console.log(str.padEnd(1)) // abc
console.log(str.padEnd(1,"123")) // abc
console.log(str.padEnd(4,"123")) // abc1
console.log(str.padEnd(7,"123")) // abc1231

padStart

padStart() 方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。

/**
 * str.padStart(targetLength,padString)
 * @param {Number} targetLength => 当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
 * @param {String} padString  => 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的缺省值为 " "(U+0020)。
 * ----------------------------------------------
 * 返回值
 * @param {String} 在原字符串开头填充指定的填充字符串直到目标长度所形成的新字符串。
 */

let str = "abc"
console.log(str.padStart(1)) // abc
console.log(str.padStart(3,'123')) // abc
console.log(str.padStart(6,'123')) // 123abc
console.log(str.padStart(7,'123')) // 1231abc

padStart()的常见用途是为数值补全指定位数和提示字符串格式。

// 生成 10 位的数值字符串
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

// 提示字符串格式
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

repeat

repeat() 构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。

/**
 * str.repeat(count)
 * @param {Number} count => 介于0和正无穷大之间的整数 : [0, +∞) 。表示在新构造的字符串中重复了多少遍原字符串。重复次数不能为负数。重复次数必须小于 infinity,且长度不会大于最长的字符串。
 * ----------------------------------------------
 * 返回值
 * @param {String} 包含指定字符串的指定数量副本的新字符串。
 */
let str = "gating"
console.log(str.repeat(2)) // gatinggating
// 参数count将会被自动转换成整数(向下取整)
console.log(str.repeat(3.5)) // gatinggatinggating
console.log(str.repeat(3.4)) // gatinggatinggating
// 但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。
console.log(str.repeat(-0.5)) // ""
console.log(str.repeat(0)) // ""
// 参数NaN等同于 0。
console.log(str.repeat(NaN)) // ""
// 如果repeat的参数是字符串,则会先转换成数字
console.log(str.repeat("aaa")) // ""

// RangeError: repeat count must be positive and less than inifinity
console.log(str.repeat(-1)) 
console.log(str.repeat(1/0))

repeat 其实很适合我们调试的时候生成多份测试数据,比如

let newData = ["1","2","3"]

// 假设我们想生成 ["1","1","2","2","3","3"] 的双份数据,那么我们就可以通过 repeat 来实现
let newData = myDate.map(item=>item.repeat(2).split('')).reduce((arr,val)=>arr.concat(val))
console.log(newData)) // ["1","1","2","2","3","3"]

搜索查找相关

indexOf

indexOf() 方法返回调用 String 对象中第一次出现的指定值的索引,开始在 fromIndex进行搜索。如果未找到该值,则返回-1。

/**
 * str.indexOf(searchValue,fromIndex)
 * @param {String} searchValue => 一个字符串表示被查找的值。
 * @param {String/Number} fromIndex => 表示调用该方法的字符串中开始查找的位置。可以是任意整数。默认值为 0。如果 fromIndex < 0 则查找整个字符串(如同传进了 0)。如果 fromIndex >= str.length,则该方法返回 -1。当被查找的字符串是一个空字符串,fromIndex <= 0时返回0,0 < fromIndex <= str.length时返回fromIndex,fromIndex > str.length时返回str.length。
 * ----------------------------------------------
 * 返回值
 * @param {Number} 指定值的第一次出现的索引; 如果没有找到 -1。
 */

let str = "gating"
console.log(str.indexOf("g")) // 0
// 这里的 -1 也就 fromIndex < 0 时,str.indexOf("a",-1) 等同于 str.indexOf("a",0)
console.log(str.indexOf("a",-1)) // 1
// 这里的 8 也就 fromIndex > str.length 时,str.indexOf("a",8) 等同于 str.indexOf("a",6)
console.log(str.indexOf("g",8)) // -1

// 区分大小写
console.log(str.indexOf("G")) // -1 

实际应用场景中,我们可以使用使用 indexOf 统计一个字符串中某个字符出现的次数

let str = "gating"

function countStr(str,value){
    let count = 0
    let pos = str.indexOf('g');
    while (pos !== -1) {
        count++;
        pos = str.indexOf('g', pos + 1);
    }
    return count
}

console.log(countStr(str,'g')) // 2

lastIndexOf

lastIndexOf() 方法返回指定值在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。从该字符串的后面向前查找,从 fromIndex 处开始。

/**
 * str.indexOf(searchValue,fromIndex)
 * @param {String} searchValue => 一个字符串表示被查找的值。
 * @param {String/Number} fromIndex => 从调用该方法字符串的此位置处开始查找。可以是任意整数。默认值为 str.length。如果为负值,则被看作 0。如果 fromIndex > str.length,则 fromIndex 被看作 str.length。
 * ----------------------------------------------
 * 返回值
 * @param {Number} 指定值的第一次出现的索引(从后往前数); 如果没有找到 -1。
 */
let str = "gating"
console.log(str.lastIndexOf('g')) // 5
console.log(str.lastIndexOf('g',2)) // 0 
// 这里的 8 也就 fromIndex > str.length 时,str.lastIndexOf("a",8) 等同于 str.lastIndexOf("a",6) 也就是 str.lastIndexOf("a")
console.log(str.lastIndexOf('g',8)) // 5 
console.log(str.lastIndexOf('g',-1)) // -1
console.log(str.lastIndexOf('g',0)) // 0

search() 方法执行正则表达式和 String对象之间的一个搜索匹配。

/**
 * str.search(regexp)
 * @param {Regexp} regexp => 一个正则表达式(regular expression)对象。如果传入一个非正则表达式对象,则会使用 new RegExp(obj) 隐式地将其转换为正则表达式对象。
 * ----------------------------------------------
 * 返回值
 * @param {Number} 如果匹配成功,则 search() 返回正则表达式在字符串中首次匹配项的索引。否则,返回 -1。
 */

let str = "gating"
console.log(str.search("g")) // 0
console.log(str.search(/g/)) // 0

includes

includes() 方法用于判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false。

/**
 * str.includes(searchString,position)
 * @param {String} searchString => 要在此字符串中搜索的字符串。
 * @param {Number} position => 可选。从当前字符串的哪个索引位置开始搜寻子字符串,默认值为0。
 * ----------------------------------------------
 * 返回值
 * @param {Boolean} 如果当前字符串包含被搜寻的字符串,就返回 true;否则返回 false。
 */
let str = "gating"
console.log(str.includes("a")) // true
console.log(str.includes("a",1)) // true
console.log(str.includes("a",2)) // false

在 Firefox 18 - 39中,这个方法的名称叫 contains()。 具体原因请查看

startsWith

startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的,根据判断结果返回 true 或 false。

/**
 * str.startsWith(searchString,position)
 * @param {String} searchString =>要搜索的子字符串。
 * @param {Number} position => 在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。
 * ----------------------------------------------
 * 返回值
 * @param {Boolean} 如果当前字符串以搜素字符串开头,就返回 true;否则返回 false。
 */

let str = "gating"
console.log(str.startsWith('g')) // true
console.log(str.startsWith('g',1)) // false
console.log(str.startsWith('g',7)) // false

endsWith

endsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的,根据判断结果返回 true 或 false。

/**
 * str.endsWith(searchString,position)
 * @param {String} searchString => 要搜索的子字符串。
 * @param {Number} position => 可选。作为str的长度,默认值为 str.length。
 * ----------------------------------------------
 * 返回值
 * @param {Boolean}  如果传入的子字符串在搜索字符串的末尾就返回true;否则将返回 false
 */

let str = "gating"
console.log(str.endsWith('g')) // true
// 这里 position 为 2 表示 str 的值就是 ga,也就是相当于 ga.endsWith('g')
console.log(str.endsWith('g',2)) // false
console.log(str.endsWith('g',7)) // true

截取相关

slice

slice() 方法提取一个字符串的一部分,并返回一新的字符串。

/**
 * str.slice(beginSlice,endSlice)
 * @param {Number} beginSlice => 从该索引(以 0 为基数)处开始提取原字符串中的字符。如果值为负数,会被当做 sourceLength + beginSlice 看待,这里的sourceLength 是字符串的长度 (例如, 如果beginSlice 是 -3 则看作是: sourceLength - 3)
 * @param {Number} endSlice => 可选。可选。在该索引(以 0 为基数)处结束提取字符串。如果省略该参数,slice会一直提取到字符串末尾。如果该参数为负数,则被看作是 sourceLength + endSlice,这里的 sourceLength 就是字符串的长度(例如,如果 endSlice 是 -3,则是, sourceLength - 3)。
 * ----------------------------------------------
 * 返回值
 * @param {Number}  返回一个从原字符串中提取出来的新字符串
 */
let str = "gating"
console.log(str.slice(1)) // ating
// 这里的-1实际上就是 (6-1) 也就是 5 即 str.slice(5)
console.log(str.slice(-1)) // g

// 这里的-1实际上就是 (6-1) 也就是 5 即 str.slice(0,5)
console.log(str.slice(0,-1)) // gatin
console.log(str.slice(-2,-1)) // n
console.log(str.slice(-1,-2)) // ""

注意:slice() 提取的新字符串包括beginSlice但不包括 endSlice。

substr

substr() 方法返回一个字符串中从指定位置开始到指定字符数的字符。

/**
 * str.substr(start,length)
 * @param {Number} searchString => 开始提取字符的位置。如果为负值,则被看作 strLength + start,其中 strLength 为字符串的长度(例如,如果 start 为 -3,则被看作 strLength + (-3))
 * @param {Number} position => 可选。提取的字符数。默认是strLength
 * ----------------------------------------------
 * 返回值
 * @param {String}  返回一个从原字符串中提取出来的新字符串
 */

let str = "gating"
console.log(str.substr(1)) // ating
// 这里的-1实际上就是 (6-1) 也就是 5 即 str.slice(5)
console.log(str.substr(-1)) // g

// 因为长度不能小于0,所以返回""
console.log(str.substr(-1,-2)) // ""

注意:在IE8下,substr()方法传递负值会返回原始的字符串,IE9修复了此BUG

substring

substring() 方法返回一个字符串在开始索引到结束索引之间的一个子集, 或从开始索引直到字符串的末尾的一个子集。

/**
 * str.substring(indexStart,indexEnd)
 * @param {Number} indexStart => 需要截取的第一个字符的索引,该字符作为返回的字符串的首字母。
 * @param {Number} indexEnd => 可选。一个 0 到字符串长度之间的整数,以该数字为索引的字符不包含在截取的字符串内。
 * ----------------------------------------------
 * 返回值
 * @param {String}  包含给定字符串的指定部分的新字符串。
 */
let str = "gating"
console.log(str.substring(0,3)) // gat
// 如果 indexStart 大于 indexEnd,则会变成 str.substring(0,3)
console.log(str.substring(3,0)) // gat

// 如果任一参数小于 0 或为 NaN,则被当作 0。
console.log(str.substring(-2,3)) // gat
console.log(str.substring(NaN,3)) // gat

console.log(str.substring(4,4)) // ""
console.log(str.substring(0)) // gating
console.log(str.substring(0,10)) // gating

substring 提取从 indexStart 到 indexEnd(不包括)

  • 如果 indexStart 等于 indexEnd,substring 返回一个空字符串。
  • 如果省略 indexEnd,substring 提取字符一直到字符串末尾。
  • 如果任一参数小于 0 或为 NaN,则被当作 0。
  • 如果任一参数大于 stringName.length,则被当作 stringName.length。
  • 如果 indexStart 大于 indexEnd,则 substring 的执行效果就像两个参数调换了一样。

转换大小写相关

注意: toLocaleLowerCase、toLocaleUpperCase 按照本地方式把字符串转换为小写。只有几种语言(如土耳其语)具有地方特有的大小写映射。

toLowerCase

toLowerCase() 会将调用该方法的字符串值转为小写形式,并返回。

/**
 * str.toLowerCase()
 * ----------------------------------------------
 * 返回值
 * @param {String}  一个新的字符串,表示串转换为小写的调用字符。
 */
let str = "GATING"
console.log(str.toLowerCase()) // gating

toLocaleLowerCase

toLocaleLowerCase() 根据任何特定于语言环境的案例映射,将表示调用字符串的新字符串转换为小写。

/**
 * str.toLocaleLowerCase()
 * ----------------------------------------------
 * 返回值
 * @param {String}  根据任何特定于语言环境的案例映射,将表示调用字符串的新字符串转换为小写。
 */
let str = "GATING"
console.log(str.toLocaleLowerCase()) // gating

toUpperCase

toUpperCase() 将调用该方法的字符串值转换为大写形式,并返回。

/**
 * str.toUpperCase()
 * ----------------------------------------------
 * 返回值
 * @param {String}  将调用该方法的字符串值转换为大写形式
 */
let str = "gating"
console.log(str.toUpperCase()) // GATING

toLocaleUpperCase

toLocaleUpperCase() 将调用该方法的字符串值转换为大写形式,并返回。

/**
 * str.toLocaleUpperCase()
 * ----------------------------------------------
 * 返回值
 * @param {String}  一个新的字符串,即根据本地化的大小写映射规则将输入的字符串转化成大写形式的结果。
 */
let str = "gating"
console.log(str.toLocaleUpperCase()) // GATING

删除空白字符相关

trim

trim() 方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符 (space, tab, no-break space 等) 以及所有行终止符字符(如 LF,CR)

/**
 * str.trim()
 * ----------------------------------------------
 * 返回值
 * @param {String}  返回方法移除原字符串两端端的连续空白符
 */
let str = "   gating   "
console.log(str.trim()) // "gating"

trimRight

trimRight() 方法从一个字符串的右端移除空白字符。

/**
 * str.trimRight()
 * ----------------------------------------------
 * 返回值
 * @param {String}  返回方法移除原字符串右端的连续空白符
 */
let str = "gating   "
console.log(str.trimRight()) // "gating"

// 兼容生产环境
const trimRight = (str)=> str.replace(/(\s*$)/g, "")

该特性是非标准的,请尽量不要在生产环境中使用它!

trimLeft

trimLeft() 方法从一个字符串的左端移除空白字符。

/**
 * str.trimLeft()
 * ----------------------------------------------
 * 返回值
 * @param {String}  返回方法移除原字符串左端的连续空白符
 */
let str = "  gating"
console.log(str.trimLeft()) // "gating"

// 兼容生产环境
const trimLeft = (str)=> str.replace(/(^\s*)/g, "")

该特性是非标准的,请尽量不要在生产环境中使用它!

两个不知名的方法

toString

toString() 方法返回指定对象的字符串形式。

String 对象覆盖了Object 对象的 toString 方法;并没有继承 Object.toString()。对于 String 对象,toString 方法返回该对象的字符串形式,和 String.prototype.valueOf() 方法返回值一样

/**
 * str.toString()
 * ----------------------------------------------
 * 返回值
 * @param {String}  返回指定对象的字符串形式
 */

let str = new String("gating");
console.log(str.toString()) // gating

valueOf

valueOf() 方法返回一个String对象的原始值(primitive value)。

String 对象的 valueOf 方法返回一个String对象的原始值。该值等同于String.prototype.toString()。

该方法通常在 JavaScript 内部被调用,而不是在代码里显示调用。

/**
 * str.valueOf()
 * ----------------------------------------------
 * 返回值
 * @param {String}  返回指定对象的字符串形式
 */

let str = new String("gating");
console.log(str.valueOf()) // gating

排序相关

localeCompare

localeCompare() 方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。

新的 locales 、 options 参数能让应用程序定制函数的行为即指定用来排序的语言。 locales 和 options 参数是依赖于具体实现的,在旧的实现中这两个参数是完全被忽略的。

/**
 * str.localeCompare(compareString,locales,options)
 * @param {String} compareString => 用来比较的字符串
 * @param {locales} locales => 可选。用来表示一种或多种语言或区域的一个符合 BCP 47 标准的字符串或一个字符串数组。locales参数的一般形式与解释, 详情请参考 MDN
 * @param {options} options => 可选。 支持下列的一些或全部属性的一个对象: 详情请参考 MDN
 * ----------------------------------------------
 * 返回值
 * @param {String}  如果引用字符存在于比较字符之前则为负数; 如果引用字符存在于比较字符之后则为正数; 相等的时候返回 0 .不同浏览器之间(以及不同浏览器版本之间)返回的正负数的值各有不同
 */

let str = "b"
console.log(str.localeCompare("b")) // 0
console.log(str.localeCompare("a")) // 1
console.log(str.localeCompare("c")) // -1

实际上,我们就可以通过 localeCompare 实现中文排序了。

let arr = ["北京","上海","深圳","广州"]
let res1 = arr.sort((a,b)=>a-b)
// 传统的排序方法,你会发现顺序是有问题的
console.log(res1) // ["北京", "上海", "广州", "深圳"]

// 通过 localeCompare 我们就实现了中文的排序了
let res2 = arr.sort((a,b)=>a.localeCompare(String(b)))
console.log(res2) // ["北京", "广州", "上海", "深圳"]

// 但是其实这个还是没有实现我们的需求,假设我们 数组既有数字、英文、中文、特殊符号的话,我们的排序就不能这么简单的排序了
let myData = ["北京","上海","深圳","广州",null,undefined,10,2,"apple","bird","banana"]
let result = myData.sort(function(a,b){
    var r = a - b;
    if(isNaN(r)){
        r = String(a).localeCompare(String(b));
    };
    return r;
})
console.log(result)

localeCompare方法,十分建议看看MDN,因为我实在太菜了 ┭┮﹏┭┮

当比较大量字符串时, 比如比较大量数组时, 最好创建一个 [Intl.Collator](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Collator) 对象并使用compare 属性所提供的函数。

// Intl.Collator 是用于语言敏感字符串比较的 collators构造函数。
function letterSort(lang, letters) {
  letters.sort(new Intl.Collator(lang).compare)
  return letters;
}

// 具体请看上面 MDN 的说明
console.log(letterSort('de', ['a','z','ä'])) // ["a", "ä", "z"]
console.log(letterSort('sv', ['a','z','ä'])) // ["a", "z", "ä"]

文中某些知识点参考链接

阮老师的es6入门,字符串的扩展 推荐!

localeCompare方法,十分建议看看MDN

Intl.Collator

Array.sort高级用法

JavaScript 正则表达式匹配汉字

正则表达式30分钟入门教程 推荐

总结

这里我就不做总结了,留个小作业给各位观众老爷,自己总结下咯

最后,感谢各位观众老爷观看啦O(∩_∩)O

这是我的原创文章,如果觉得不错,可以打个赏~
0%