星空网 > 软件开发 > Java

原生JS:String对象详解

String对象

本文参考MDN做的详细整理,方便大家参考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)

JavaScript中的 String 类型用于表示文本型的数据. 它是由无符号整数值(16bit)作为元素而组成的集合. 字符串中的每个元素在字符串中占据一个位置. 第一个元素的index值是0, 下一个元素的index值是1, 以此类推. 字符串的长度就是字符串中所含的元素个数.

String 对象是对原始string类型的封装,你可以在String字面值上使用String对象的任何方法—JavaScript自动把String字面值转换为一个临时的String对象, 然后调用其相应方法,最后丢弃此临时对象.在String字面值上也可以使用String.length属性


访问字符串的单个字符方法:

  • 获取字符串的某一单个字符有两种方法。 第一种是使用 charAt 方法:
  1. return 'cat'.charAt(1); // returns "a"

  • 另一种方法 (在ECMAScript 5中有所介绍) 是把字符串当作一个类数组对象,其中的每个字符对应一个数值索引:
  1. return 'cat'[1]; // returns "a"

使用括号访问字符串不可以对其进行删除或添加,因为对应属性并不是可读或可写的。



你可以通过String字面值或者String对象两种方式创建一个字符串.
  • 通常,我们都使用字符串的字面量写法
  • 可以使用 String 函数来将其他值 生成或转换 成字符串:
  1. String(thing)
  2. new String(thing)

字符串比较:可以用‘>’ 或'<'来对两个字符、字符串进行比较。

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

Code

Output

\0

the NUL character

\'

single quote

\"

double quote

\\

backslash

\n

new line

\r

carriage return

\v

vertical tab

\t

tab

\b

backspace

\f

form feed

\uXXXX

unicode codepoint

\u{X} ... \u{XXXXXX}

unicode codepoint  escapes (ES6)

\xXX

the Latin-1 character




  • 16进制转义序列在\x之后的数值将被认为是一个16进制数:'\xA9' // "©"
  • Unicode转义序列在\u之后需要至少4个字符:'\u00A9' // "©"
  • Unicode code point escapes:这是ECMAScript 6中的新特性. 有了Unicode code point escapes, 任何字符都可以用16进制数转义, 这使得通过Unicode转义表示大于0x10FFFF的字符成为可能. 
  • 使用简单的Unicode转义时通常需要分别写字符相应的两个部分(译注:大于0x10FFFF的字符需要拆分为相应的两个小于0x10FFFF的部分)来达到同样的效果.请参阅 String.fromCodePoint() 或 String.prototype.codePointAt().
  1. '\u{2F804}'
  2. // the same with simple Unicode escapes
  3. '\uD87E\uDC04'



String对象的方法(各个方法详解见本文底部)

方法

描述

charAt, charCodeAt, codePointAt

返回字符串指定位置的字符或者字符编码。

indexOf, lastIndexOf

分别返回字符串中指定子串的位置或最后位置。

startsWith, endsWith, includes

返回字符串是否以指定字符串开始、结束或包含指定字符串。

concat

连接两个字符串并返回新的字符串。

fromCharCode, fromCodePoint

从指定的Unicode值序列构造一个字符串。这是一个String类方法,不是实例方法。

split

通过将字符串分离成一个个子串来把一个String对象分裂到一个字符串数组中。

slice

从一个字符串提取片段并作为新字符串返回。

substring, substr

分别通过指定起始和结束位置,起始位置和长度来返回字符串的指定子集。

match, replace, search

通过正则表达式来工作.

toLowerCase, toUpperCase

分别返回字符串的小写表示和大写表示。

normalize

Returns the Unicode Normalization Form of the calling string value.

repeat

将字符串内容重复指定次数后返回。

trim

去掉字符串开头和结尾的空白字符。




模板字符串:ES6新特性

模板字符串允许嵌入表达式,并且支持多行字符串和字符串插补特性。

语法

  1. `string text`
  2. `string text line 1
  3. string text line 2`
  4. `string text ${expression} string text`
  5. tag `string text ${expression} string text`

模板字符串使用反引号 (` `) 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${expression})的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来,如果一个模板字符串由表达式开头,则该字符串被称为带标签的模板字符串,该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前,你都可以在通过该函数对模板字符串来进行操作处理。

多行字符串、表达式插补

使用普通字符串,换行需要\n,插入表达式需要分段:

  1. var a = 5;
  2. var b = 10;
  3. console.log("Fifteen is " + (a + b) + " and\nnot " + (2 * a + b) + ".");
  4. // "Fifteen is 15 and
  5. // not 20."

现在通过模板字符串,我们可以使用一种更优雅的方式来表示(换行也可以用\n):
  1. var a = 5;
  2. var b = 10;
  3. console.log(`Fifteen is ${a + b} and
  4. not ${2 * a + b}.`);
  5. // "Fifteen is 15 and
  6. // not 20."

带标签的模板字符串


模板字符串的一种更高级的形式称为带标签的模板字符串。它允许您通过标签函数修改模板字符串的输出。标签函数的第一个参数是一个包含了字符串字面值的数组(在本例中分别为“Hello”和“world”);第二个参数,在第一个参数后的每一个参数,都是已经被处理好的替换表达式(在这里分别为“15”和“50”)。 最后,标签函数返回处理好的字符串。在后面的示例中,标签函数的名称可以为任意的合法标示符。


  1. var a = 5;
  2. var b = 10;
  3. function tag(strings, ...values) {
  4. console.log(strings[0]); // "Hello "
  5. console.log(strings[1]); // " world "
  6. console.log(values[0]); // 15
  7. console.log(values[1]); // 50
  8. return "Bazinga!";
  9. }
  10. tag`Hello ${ a + b } world ${ a * b}`;
  11. // "Bazinga!"

原始字符串


在标签函数的第一个参数中,存在一个特殊的属性raw ,我们可以通过它来访问模板字符串的原始字符串。

  1. function tag(strings, ...values) {
  2. console.log(strings.raw[0]);
  3. // "string text line 1 \\n string text line 2"
  4. }
  5. tag`string text line 1 \n string text line 2`;

另外,使用String.raw() 方法创建原始字符串和使用默认模板函数和字符串连接创建是一样的。

  1. String.raw`Hi\n${2+3}!`;
  2. // "Hi\\n5!"

安全性

由于模板字符串能够访问变量和函数,因此不能由不受信任的用户来构造。

  1. `${console.warn("this is",this)}`; // "this is" Window
  2. let a = 10;
  3. console.warn(`${a+=20}`); // "30"
  4. console.warn(a); // 30

Feature

Chrome

Firefox (Gecko)

Internet Explorer

Opera

Safari

Basic support

41

34 (34)

12/Edge (modern.ie)

未实现

未实现






String对象的方法:

静态方法

String.fromCharCode(num1, ..., numN)

根据指定的 Unicode 编码中的序号值来返回一个字符串,该方法返回一个字符串,而不是一个 String 对象。

  • 尽管绝大部分常用的 Unicode 值可以用一个 16-bit 数字表示,并且对于绝大部分值 fromCharCode() 返回一个字符(即对于绝大部分字符 UCS-2 值是 UTF-16 的子集),但是为了处理所有的 Unicode 值(至 21 bits),只用 fromCharCode() 是不足的。
  • 该方法不支持Unicode码点大于0xFFFF的字符,即传入的参数不能大于0xFFFF,根本原因在于,码点大于0xFFFF的字符(高位编码字符)占用四个字节,而JavaScript只支持两个字节的字符。必须把码点大于0xFFFF的字符拆成两个字符表示。
  • 由于高位编码字符是用两个低位编码(lower value)表示形成的一个字符,因此String.fromCodePoint() (ES6 草案的一部分)被用来返回这样一对低位编码,从而可以完全表示这些高位编码字符。

String.fromCodePoint(num1[, ...[, numN]])  ES6

使用指定的unicode参数返回一个primitive的字符串。与fromCharCode功能类似,但是支持的Unicode字符更全,但是浏览器支持情况不是太好,目前ie以及safari还不支持。另外,性能并没有fromCharCode快。因为是ES6当中的新定义的特性,所以目前还不适合放到正式产品环境中去使用。

  • 如果传入无效的Unicode编码,将会抛出一个RangeError

String.raw(callSite, ...substitutions)  ES6


是一个模板字符串的标签函数,是用来获取一个模板字符串的原始字面量值的。


  • callSite 一个模板字符串的“调用点对象”。
  • ...substitutions 任意个可选的参数,表示任意个内插表达式对应的值。
  • 如果第一个参数没有传入一个格式良好的调用点对象,则会抛出 TypeError 异常。

  • 像所有的标签函数一样,你通常不需要把它看成一个普通函数,你只需要把它放在模板字符串前面就可以了,而不是在它后面加个括号和一堆参数来调用它,引擎会替你去调用它:

  1. String.raw `Hi\n!`;
  2. // "Hi\\n!",这里得到的不是 Hi 后面跟个换行符,而是跟着 \\ 和 n 两个字符
  3. String.raw `Hi\u000A!`;
  4. // "Hi\\u000A!",同上,这里得到的会是 \\、u、0、0、0、A 6个字符,
  5. // 任何类型的转义形式都会失效,保留原样输出,不信你试试.length
  6. let name = "Bob";
  7. String.raw `Hi\n${name}!`;
  8. // "Hi\\nBob!",内插表达式还可以正常运行
  9. String.raw({raw: "test"}, 0, 1, 2);
  10. // "t0e1s2t",我认为你通常不需要把它当成普通函数来调用


Feature

Chrome

Firefox (Gecko)

Internet Explorer

Opera

Safari

Basic support

41 

34 (34)

未实现

未实现

未实现







实例方法:

跟HTML无关的方法

String.prototype.charAt(index)

返回字符的特定位置,index :0~length-1。如果指定的 index 值超出了该范围,则返回一个空字符串。

String.prototype.charCodeAt(index)

返回表示给定索引的字符的Unicode的值。是String.fromCharCode()方法的反操作

  • index 一个大于等于 0,小于字符串长度的整数。如果不是一个数值,则默认为 0。
  • 如果指定的 index 小于 0 或不小于字符串的长度,则 charCodeAt 返回 NaN。

  • 返回0到65535之间的整数,代表索引处字符的UTF-16编码单元(在Unicode编码单元表示一个单一的UTF-16编码单元的情况下,UTF-16编码单元匹配Unicode编码单元。否则,比如Unicode 编码单元 > 0x10000 的情况下,只能匹配Unicode代理对的第一个编码单元)。如果你希望得到整点编码值,使用codePointAt()
  • 注意,charCodeAt 总是返回一个小于 65,536 的值。这是因为高位编码单元(higher code point)使用一对(低位编码(lower valued))代理伪字符("surrogate" pseudo-characters)来表示,从而构成一个真正的字符。因此,为了查看或复制(reproduce)65536 及以上编码字符的完整字符,不仅需要获取 charCodeAt(i) 的值,也需要获取 charCodeAt(i+1) 的值

String.prototype.concat(string2, string3[, ..., stringN])

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

  • 为了提高性能,强烈建议使用 赋值操作符(+, +=)代替 concat 方法

String.prototype.indexOf(searchValue[, fromIndex])

从字符串对象中返回首个被发现的给定值的索引值,如果没有找到则返回-1。indexOf 方法区分大小写

  • fromIndex 可选 表示调用该方法的字符串中开始查找的位置。可以是任意整数。默认值为 0。如果 fromIndex < 0 则查找整个字符串(如同传进了 0)。如果 fromIndex >= str.length,则该方法返回 -1,除非被查找的字符串是一个空字符串,此时返回 str.length。

String.prototype.lastIndexOf(searchValue[, fromIndex])

从字符串对象中返回最后一个被发现的给定值的索引值,如果没有找到则返回-1。

  • fromIndex 从调用该方法字符串的此位置处开始查找。可以是任意整数。默认值为 str.length。如果为负值,则被看作 0。如果 fromIndex > str.length,则 fromIndex 被看作 str.length。

String.prototype.localeCompare(compareStr[, locales[, options]])   新的 locales 、 options 参数要求IE11,Safari不支持

返回一个数字来表明调用该函数的字符串的排列顺序是否在某个给定的字符串compareStr的前面或者后面,或者是一样的(编码中的位置)。在 compareStr 前面时返回负数,作 compareStr 后面时返回正数,相同位置时返回0

  • 新的 locales 、 options 参数能让应用程序定制函数的行为即指定用来排序的语言。  locales 和 options 参数是依赖于具体实现的,在旧的实现中这两个参数是完全被忽略的
  • 查阅浏览器支持部分来确定哪些浏览器支持 locales 参数和 options 参数, 在功能检测中检查对 locales 、options 参数的支持。locales 和 options 参数还没有被所有阅览器所支持。检查是否被支持, 可以给第二个参数传递一个 "i" , 判断是否有异常 RangeError抛出
  • 性能: 当比较大量字符串时, 比如比较大量数组时, 最好创建一个Intl.Collator 对象并使用compare 属性所提供的函数
  • 该方**考虑自然语言的排序情况,如将B排在a的前面,而JavaScript采用的是Unicode码点比较'B'<'a'

String.prototype.match(regexp)

使用正则表达式与字符串相比较,返回一个包含匹配结果的数组,如果没有匹配项,则返回 null。当字符串匹配到正则表达式时,match() 方**提取匹配项。

  • 参数regexp 是一个正则表达式对象。如果传入一个非正则表达式对象,则会隐式地使用 new RegExp(obj) 将其转换为正则表达式对象
  • 如果正则表达式没有 g 标志,返回和 RegExp.exec(str) 相同的结果。而且返回的数组拥有一个额外的 input 属性,该属性包含原始字符串。另外,还拥有一个 index 属性,该属性表示匹配结果在原字符串中的索引(以0开始)。

    String.prototype.replace(regexp|substr, newSubStr| function)

    被用来在正则表达式和字符串直接比较,然后用新的子串来替换被匹配的子串。该方法并不改变调用它的字符串本身,而只是返回一个新的替换后的字符串。

    • regexp (pattern)一个 RegExp 对象。该正则所匹配的内容会被第二个参数的返回值替换掉。
    • substr (pattern)一个要被 newSubStr 替换的字符串。
    • newSubStr (replacement)用于替换掉第一个参数在原字符串中的匹配部分的 String。该字符串中可以内插一些特殊的变量名。参考下面的使用字符串作为参数。
    • function (replacement)一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考下面的指定一个函数作为参数。

    使用字符串作为参数: 替换字符串newSubStr可以插入下面的特殊变量名:

    变量名

    代表的值

    $$

    插入一个 "$"。

    $&

    插入匹配的子串。

    $`

    插入当前匹配的子串左边的内容。

    $'

    插入当前匹配的子串右边的内容。

    $n

    假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。



    下面的例子交换一个字符串中两个单词的位置,这个脚本使用$1 和 $2 代替替换文本

      
    1. var re = /(\w+)\s(\w+)/;
    2. var str = "John Smith";
    3. var newstr = str.replace(re, "$2, $1");
    4. console.log(newstr);


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

    下面是该函数的参数:

    变量名

    代表的值

    match

    匹配的子串。(对应于上述的$&。)

    p1,p2, ...

    假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)

    offset

    匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串时“bc”,那么这个参数将时1)

    string

    被匹配的原字符串。



    (精确的参数个数依赖于replace()的第一个参数是否是一个正则表达式对象, 以及这个正则表达式中指定了多少个括号子串。)下面的例子使 newString 变成'abc - 12345 - #$*%':

      
    1. function replacer(match, p1, p2, p3, offset, string) {
    2. // p1 is nondigits, p2 digits, and p3 non-alphanumerics
    3. return [p1, p2, p3].join(' - ');
    4. }
    5. var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);

    String.prototype.search(regexp)

    对正则表达式和指定字符串进行匹配搜索,返回第一个出现的匹配项的下标。如果匹配成功,则 search() 返回正则表达式在字符串中首次匹配项的索引。否则,返回 -1。如果参数是字符串,和indexOf方法一样

    • regexp 查找的字符串或者正则表达式对象。如果传入一个非正则表达式对象,则会使用 new RegExp(obj) 隐式地将其转换为正则表达式对象。

    String.prototype.slice(beginSlice[, endSlice])

    摘取一个字符串区域(从beginSlice到endSlice,不含endSlice),,返回一个新的字符串。

    • beginSlice从该索引(以 0 为基数)处开始提取原字符串中的字符。如果值为负数,会被当做 sourceLength + beginSlice 看待,这里的sourceLength 是字符串的长度 (例如, 如果beginSlice 是 -3 则看作是: sourceLength - 3)
    • endSlice可选。在该索引(以 0 为基数)处结束提取字符串。如果省略该参数,slice会一直提取到字符串末尾。如果该参数为负数,则被看作是 sourceLength + endSlice,这里的 sourceLength 就是字符串的长度(例如,如果 endSlice 是 -3,则是, sourceLength - 3)。

    String.prototype.split([separator][, limit])

    通过separator将字符串分离成子串,返回由子串组成的数组。

    • separator指定用来分割字符串的字符(串)。separator 可以是一个字符串或正则表达式。 当找到一个 seperator时,separator 会从字符串中被移除,返回存进一个数组当中的子字符串。如果忽略 separator 参数,则返回的数组只包含一个元素,该元素是原字符串。如果 separator 是一个空字符串,则 str 将被转换为由字符串中字符组成的一个数组。
    • 如果 separator 是一个正则表达式,且包含捕获括号(capturing parentheses),则每次匹配到 separator 时,捕获括号匹配的结果将会插入到返回的数组中。然而,不是所有浏览器都支持该特性

    • limit一个整数,限定返回的分割片段数量。split 方法仍然分割每一个匹配的 separator,但是返回的数组只会截取最多 limit 个元素。
    • 当字符串为空时,split 返回一个包含一个空字符串的数组,而不是一个空数组。

    String.prototype.substr(start[, length])

    返回字符串中从指定位置开始的指定长度的子字符串.

    • start开始提取字符的位置。如果为负值,则被看作 strLength + start,其中 strLength 为字符串的长度(例如,如果 start 为 -3,则被看作 strLength-3)。注意负的 start 参数不被 Microsoft JScript 所支持,需要Polyfill支持

    • length可选。提取的字符数。
    • 如果 start 为正值,且大于或等于字符串的长度,则 substr 返回一个空字符串
    • 如果 length 为 0 或负值,则 substr 返回一个空字符串。如果忽略 length,则 substr 提取字符,直到字符串末尾。

    String.prototype.substring(indexStart[, indexEnd])

    返回字符串两个索引之间(或到字符串末尾)的子串。

    substring 提取从 indexStart 到 indexEnd(不包括)之间的字符。特别地:

    • 如果 indexStart 等于 indexEnd,substring 返回一个空字符串。
    • 如果省略 indexEnd,substring 提取字符一直到字符串末尾。
    • 如果任一参数小于 0 或为 NaN,则被当作 0。
    • 如果任一参数大于 stringName.length,则被当作 stringName.length。
    • 如果 indexStart 大于 indexEnd,则 substring 的执行效果就像两个参数调换了一样。例如,str.substring(1, 0) == str.substring(0, 1),
    • 因此不建议使用这个方法,优先使用slice

    String.prototype.toLocaleLowerCase()

    根据当前区域设置,将符串中的字符转换成小写。对于大多数语言来说,toLowerCase的返回值是一致的。

    String.prototype.toLocaleUpperCase()

    根据当前区域设置,将字符串中的字符转换成大写,对于大多数语言来说,toUpperCase的返回值是一致的。

    String.prototype.toLowerCase()

    将字符串转换成小写并返回。

    String.prototype.toString()

    返回用字符串表示的特定对象。重写 Object.prototype.toString 方法。

    String.prototype.toUpperCase()

    将字符串转换成大写并返回。

    String.prototype.trim()  IE9

    返回一个删除了字符串两端的空白字符的新字符串,包括所有的空格字符 (space, tab, no-break space 等)以及所有的行结束符(如 LF,CR)。

    兼容写法:

      
    1. if (!String.prototype.trim) {
    2. String.prototype.trim = function () {
    3. return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
    4. };
    5. }

    String.prototype.valueOf()

    返回特定对象的原始值。重写 Object.prototype.valueOf 方法。

    HTML wrapper methods

    下面的方法被限制使用,因为只对可用的HTML表情和属性提供部分支持。



    String.prototype.startsWith(searchString [, position])  ES6 IE无 有Polyfill

    判断字符串的起始位置是否匹配其他字符串中的字符。

    • searchString要搜索的子字符串。
    • position在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。

    String.prototype.endsWith(searchString [, position]) ES6

    判断当前字符串是否是以另外一个给定的子字符串“结尾”的。可选的position 在 str 中搜索 子串searchString 的结束位置,默认值为 str.length,也就是真正的字符串结尾处。

    String.prototype.codePointAt() ES6

    返回使用UTF-16编码的给定位置的值的非负整数。


    String.prototype.includes(searchString[, position])  ES6

    判断一个字符串是否包含另一个子字符串,如果是返回true,否则返回false。position 可选,从当前字符串的哪个索引位置开始搜寻子字符串;默认为0。includes() 是区分大小写的。Polyfill:

      
    1. if (!String.prototype.includes) {
    2. String.prototype.includes = function(search, start) {
    3. 'use strict';
    4. if (typeof start !== 'number') {
    5. start = 0;
    6. }
    7. if (start + search.length > this.length) {
    8. return false;
    9. } else {
    10. return this.indexOf(search, start) !== -1;
    11. }
    12. };
    13. }


    String.prototype.normalize()  ES6

    返回调用字符串值的Unicode标准化形式。

    String.prototype.repeat(count)  ES6 有Polyfill

    构造并返回一个重复当前字符串若干次数的新字符串对象。count 介于0和正无穷大之间的整数 : [0, +∞) 。表示在新构造的字符串中重复了多少遍原字符串。

    • RangeError: 重复次数不能为负数。
    • RangeError: 重复次数必须小于 infinity,且长度不会大于最长的字符串。


    下面的方法被限制使用,因为只对可用的HTML表情和属性提供部分支持:


    String.prototype.anchor(name)

    创建一个 <a> HTML 锚元素,被用作超文本靶标,name表示被创建的标签的 name 属性。

    • 使用 anchor 方法能够以编程方式在一个文档中创建和展现一个锚链接。

    <a name="name"> text</a>
    • 语法上来讲,字符串表示你想让用户看到的文本。name 字符串参数表示 <a> 元素的 name 属性。
    • 使用 anchor 方法创建的锚点(anchors)将会成为 document.anchors 数组的元素

    String.prototype.bold()

    创建 HTML 元素 “b”,并将字符串加粗展示。将一个字符串嵌入到<b></b>标记中

    String.prototype.link(url)

    使用 link 方法创建一个超链接 HTML 片段。返回的字符串可以通过 document.write 或 element.innerHTML 方法添加到文档中。

    • url任何能够指定 a 标签的 href 属性的字符串;它应当是有效的 URL(相对或绝对),任何 & 字符将会被转义为 &amp;,任何 " 字符将会被转义为 &quot;。
    • 使用 link 方法创建的链接将会成为 document.links 数组中的元素。查看 document.links。











    原标题:原生JS:String对象详解

    关键词:JS

    JS
    *特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。
    相关文章
    我的浏览记录
    最新相关资讯
    海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流