6. 字符串
JavaScript 字符串与 PHP 中的字符串非常相似。

6.1. 脚本 [str-01]
首先需要理解的是,一旦字符串创建完成,就无法再对其进行修改。虽然有许多方法可以基于原始字符串创建新的字符串,但原始字符串本身保持不变。此外,字符串有两种类型:
- [string]:当使用字符串字面量初始化时;
- [object] 当其作为 [String] 类的实例创建时;
'use strict';
// strings are read-only (cannot be modified)
// a chain
const chaîne1 = "abcd ";
// type
console.log("typeof(chaîne1)=", typeof (chaîne1));
// character n° 2
console.log("chaîne1[2]=", chaîne1[2]);
// causes an error
chaîne1[2] = "0";
执行
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Temp\19-09-01\javascript\strings\str-01.js"
typeof(chaîne1)= string
chaîne1[2]= c
c:\Temp\19-09-01\javascript\strings\str-01.js:1
TypeError: Cannot assign to read only property '2' of string 'abcd '
at Object.<anonymous> (c:\Temp\19-09-01\javascript\strings\str-01.js:12:12)
at Generator.next (<anonymous>)
6.2. 脚本 [str-02]
本脚本演示了字符串可以通过两种方式构建。
'use strict';
// strings can be of two types
// a literal string
const chaîne1 = "abcd ";
// type
console.log("typeof(chaîne1)=", typeof (chaîne1));
// string instance
const chaîne2 = new String("xyzt");
// type
console.log("typeof(chaîne2)=", typeof (chaîne2));
// other entry (without new) - type [string] not [object]
const chaîne3 = String("12 34");
// type
console.log("typeof(chaîne3)=", typeof (chaîne3));
// type [string] and type [object] offer the same methods, those of the String class
console.log("chaîne1.length=", chaîne1.length);
console.log("chaîne2.length=", chaîne2.length);
注释
- 第 6 行:定义字符串的标准方法。[string1] 的类型为 [string];
- 第 10 行:可以使用 [String] 类的构造函数来构建字符串。[string2] 的类型将是 [object];
执行
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Temp\19-09-01\javascript\strings\str-02.js"
typeof(chaîne1)= string
typeof(chaîne2)= object
typeof(chaîne3)= string
chaîne1.length= 5
chaîne2.length= 4
[string] 类型继承了 [String] 类的所有方法。
6.3. 脚本 [str-03]
此脚本通过变量插值显示特定字符串。
'use strict';
// chain
const chaîne = "Introduction à Javascript par l'exemple";
// chain with variable interpolation
const str = `[${chaîne}].substr(3, 2)=` + chaîne.substr(3, 2)
console.log(str);
注释
- 第 6 行:字符串中可以包含 ${变量} 表达式,这些表达式会被替换为变量的值。这与 PHP 字符串中的 $ 变量遵循相同的逻辑。请注意此类字符串的语法:它必须用反引号(法语键盘上的 AltGr-7)括起来;
执行
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Temp\19-09-01\javascript\strings\tempCodeRunnerFile.js"
[Introduction à Javascript par l'exemple].substr(3, 2)=ro
6.4. 脚本 [str-04]
带变量插值的字符串仍显不足。无法在 ${variable} 表达式中用表达式替换变量。对于有 C 语言编程经验的人来说,没有什么能比得上 [printf, sprintf] 函数来编写或构造格式化字符串。成百上千的开发者创建了数千个 JavaScript 包,形成了庞大的生态系统。 当 JavaScript 原生功能无法满足您的需求时,就该寻找能实现该功能的包了。为此,我们使用包管理器 [npm]。它提供了一个 [搜索] 选项,允许您在包描述中搜索特定字符串。 [npm] 会返回符合搜索条件的包列表。因此,我们将搜索包描述中是否包含字符串 [sprintf]:

- 第 [4] 列为第 [3] 列中包的关键词;
- 在第 [5] 列中,显示第 [3] 列中包的描述;
下一步是访问 [npm] 网站 [https://www.npmjs.com/],并阅读包的描述:

在 [3] 中,我们查看包列表并选择一个。

在包的描述中,你会找到安装和使用的说明:

我们在 [VSCode] 终端中安装 [sprintf-js] 包:

此安装操作将修改位于 [javascript] 文件夹根目录下的 [package.json] 文件 [2]:

如上所示,该包已安装在 [dependencies] 中,即运行项目所需的包。请记住,仅在项目开发期间需要的包会被放置在 [devDependencies] 中。这些包在运行时不会被使用。在创建项目最终版本以进行生产部署时,这一区别至关重要。有工具可用于:
- 将执行所需的所有 JavaScript 文件合并为一个文件。因此,[devDependencies] 中的包不会包含在此最终文件中;
- 对其进行压缩,即尽可能减小文件大小。例如,此过程会移除所有注释;
- “混淆”代码以增加其可读性难度。例如,变量 rate、salary 和 tax 将被替换为变量 a、b 和 c;
- 执行其他优化操作;
这种对 JavaScript 项目最终文件的优化在 Web 编程中被广泛应用。一个 Web 应用程序可能依赖于大量 JavaScript 文件。在浏览器中加载这些文件可能会减慢应用程序首页的显示速度。上述优化旨在缩短这种加载时间。如果用户觉得加载时间过长,他们将不会使用该应用程序。
既然我们已经有了 [sprintf-js] 包,就需要使用它。以下是 [str-04] 脚本:
'use strict';
// use of an external package to provide the sprintf function
import { sprintf } from 'sprintf-js';
// chain
const chaîne = "Introduction à Javascript par l'exemple";
// method
console.log(sprintf("[%s].substr(3,2)=[%s]", chaîne, chaîne.substr(3, 2)));
在 ECMAScript 6 中,我们使用 [import] 关键字来导入包导出的对象。要了解包导出了什么,可以查看其代码:

- 在 [1] 中,右键单击导入的包;
- 在 [2] 中,我们希望查看其定义;
- 在 [3-4] 中,我们可以看到该包导出了一个名为 [sprintf] 的函数;
通过以下语句导入来自 [sprintf-js] 包的 [sprintf] 函数:
import { sprintf } from 'sprintf-js';
完整代码:
'use strict';
// use of an external package to provide the sprintf function
import { sprintf } from 'sprintf-js';
// chain
const chaîne = "Introduction à Javascript par l'exemple";
// method
console.log(sprintf("[%s].substr(3,2)=[%s]", chaîne, chaîne.substr(3, 2)));
产生以下结果:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe "c:\Temp\19-09-01\javascript\strings\str-04.js"
c:\Temp\19-09-01\javascript\strings\str-04.js:3
import { sprintf } from 'sprintf-js';
^
SyntaxError: Unexpected token {
at new Script (vm.js:79:7)
at createScript (vm.js:251:10)
at Object.runInThisContext (vm.js:303:10)
at Module._compile (internal/modules/cjs/loader.js:657:28)
第 3 行:无法识别 [import] 语句。这是因为本课程使用的 [node.js] 10.15.1 版本(2019 年 9 月)尚未支持名为 modules 的 ECMAScript 包导入标准。 2019 年,[node.js] 支持名为 CommonJS 的模块标准。[node.js] 计划于 2020 年集成 ECMAScript 模块。开发者们再次挺身而出,创建了允许在 [node.js] 中立即(2019 年)使用 ES6 模块的包。
我们将使用一个名为 [esm](ECMAScript Modules)的包。我们在终端中为 [javascript] 项目安装它:

在 [4] 中,我们可以看到安装 [esm] 包 [1-3] 已修改了 [javascript/package.json] 文件。
我们还没完成。为了让 [node.js] 能够使用 [esm] 模块,我们需要使用 [-r esm] 参数来运行它。
因此,我们需要修改 [VSCode] 中 [Code Runner] 扩展的配置:


在 [11] 处,我们添加 [-r esm] 参数,并保存(Ctrl-S)配置。
现在我们可以运行 [str-04] 脚本:
'use strict';
// use of an external package to provide the sprintf function
import { sprintf } from 'sprintf-js';
// chain
const chaîne = "Introduction à Javascript par l'exemple";
// substr method
console.log(sprintf("[%s].substr(3,2)=[%s]", chaîne, chaîne.substr(3, 2)));

6.5. 脚本 [str-05]
以下是文档中关于 [sprintf] 函数的说明:
格式字符串中的占位符以 % 开头,后跟一个或多个以下元素,顺序如下:
- 一个可选的数字,后跟 $ 符号,用于指定从哪个参数索引获取值。如果未指定,参数将按与输入字符串中占位符相同的顺序放置。
- 一个可选的 + 符号,用于强制在数值结果前添加正号或负号。默认情况下,仅对负数使用 - 符号。
- 一个可选的填充指定符,用于指定填充字符(若指定)。可能的值为 0 或任何以 '(单引号)开头的字符。默认使用空格进行填充。
- 一个可选的 - 符号,用于使 sprintf 将此占位符的结果左对齐。默认情况下,结果为右对齐。
- 一个可选数字,指定结果应包含的字符数。如果要返回的值短于此数字,结果将被补齐。当与 j(JSON)类型指定符一起使用时,补齐长度指定用于缩进的制表符大小。
- 一个可选的精度修饰符,由 .(点)后跟一个数字组成,用于指定浮点数应显示的位数。当与 g 类型指定符一起使用时,它指定有效位数。当用于字符串时,会导致结果被截断。
- 类型指定符,可以是以下任意一种:
- % — 生成字符 %
- b — 返回一个二进制整数
- c — 返回一个整数,表示具有该 ASCII 值的字符
- d 或 i — 返回一个带符号的十进制整数
- e — 返回采用科学记数法的浮点数
- u — 返回一个无符号十进制整数
- f — 直接返回浮点数;参见上文关于精度的说明
- g — 原样返回浮点数;参见上文关于精度的说明
- o — 返回一个八进制整数
- s — 原样返回字符串
- t — 返回 true 或 false
- T — 返回参数1的类型
- v — 返回指定参数的原始值
- x — 将整数以十六进制形式返回(小写)
- X — 返回一个十六进制数(大写)
- j — 返回一个 JavaScript 对象或数组,以 JSON 编码的字符串形式呈现
脚本 [script-05] 实现了其中部分格式:
'use strict';
// use of an external package to provide the sprintf function
import { sprintf } from 'sprintf-js';
// chain
const chaîne = "Javascript";
// character strings
console.log(sprintf("[%s, %%s]=>[%s]", chaîne, chaîne));
console.log(sprintf("[%s, %%20s]=>[%20s]", chaîne, chaîne));
console.log(sprintf("[%s, %%-20s]=>[%-20s]", chaîne, chaîne));
// integers
console.log(sprintf("[%d, %%d]=>[%d]", 10, 10));
console.log(sprintf("[%d, %%4d]=>[%4d]", 10, 10));
console.log(sprintf("[%d, %%-4d]=>[%-4d]", 10, 10));
console.log(sprintf("[%d, %%04d]=>[%04d]", 10, 10));
// real
console.log(sprintf("[%f, %%f]=>[%f]", -10.5, -10.5));
console.log(sprintf("[%f, %%10.2f]=>[%10.2f]", -10.5, -10.5));
console.log(sprintf("[%f, %%-10.2f]=>[%-10.2f]", -10.5, -10.5));
console.log(sprintf("[%f, %%010.3f]=>[%010.3f]", -10.5, -10.5));
// json
console.log(sprintf("personne (%%j)=%j", { nom: "mathieu", âge: 34 }));
// type
console.log(sprintf("type personne (%%T)=%T", { nom: "mathieu", âge: 34 }));
// boolean
console.log(sprintf("booléen (%%t)=%t", 4 === 4));
执行
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\strings\str-05.js"
[Javascript, %s]=>[Javascript]
[Javascript, %20s]=>[ Javascript]
[Javascript, %-20s]=>[Javascript ]
[10, %d]=>[10]
[10, %4d]=>[ 10]
[10, %-4d]=>[10 ]
[10, %04d]=>[0010]
[-10.5, %f]=>[-10.5]
[-10.5, %10.2f]=>[ -10.50]
[-10.5, %-10.2f]=>[-10.50 ]
[-10.5, %010.3f]=>[-00010.500]
personne (%j)={"nom":"mathieu","âge":34}
type personne (%T)=object
booléen (%t)=true
6.6. 脚本 [str-06]
[str-06] 脚本演示了 [String] 类的某些方法,这些方法也可用于 [string] 类型:
'use strict';
// use of an external package to provide the sprintf function
import { sprintf } from 'sprintf-js';
// chain
const chaîne = " Introduction à Javascript ";
// a few methods
// substr(10,2): 2 characters starting from number 10
console.log(sprintf("[%s].substr(10,2)=[%s]", chaîne, chaîne.substr(10, 2)));
// trim: eliminates blanks at the beginning and end of a chain (blank=b \t \r \n \f)
console.log(sprintf("[%s].trim()=[%s]", chaîne, chaîne.trim()));
// toLowerCase: transformation to lower case
console.log(sprintf("[%s].toLowerCase=[%s]", chaîne, chaîne.toLowerCase()));
// toUpperCase: transformation into uppercase letters
console.log(sprintf("[%s].toUpperCase=[%s]", chaîne, chaîne.toUpperCase()));
// indexOf: position of a searched string within the string, -1 if the substring doesn't exist
console.log(sprintf("[%s].indexOf('Java')=[%s]", chaîne, chaîne.indexOf('Java')));
console.log(sprintf("[%s].trim().indexOf('abcd')=[%s]", chaîne, chaîne.indexOf('abcd')));
// includes: true if the string searched for is in the string
console.log(sprintf("[%s].includes('Java')=[%s]", chaîne, chaîne.includes('Java')));
// length: string length - not a method but a property
console.log(sprintf("[%s].length=[%s]", chaîne, chaîne.length));
// slice (7,10): strings of characters 7 to 9
console.log(sprintf("[%s].slice(7,10)=[%s]", chaîne, chaîne.slice(7, 10)));
// match: searches for an expression in the string - this expression can be a regular expression
// /intro/i: regular expression designating the string [intro] in upper or lower case
// returns the string found
console.log(sprintf("[%s].match(/intro/i)=[%s]", chaîne, chaîne.match(/intro/i)));
// replace: replaces string1 with string2 in string
// replaces the 1st occurrence of i with x
console.log(sprintf("[%s].replace('i','x')=[%s]", chaîne, chaîne.replace('i', 'x')));
// replaces all occurrences of i with x
// /i/g is a regular expression designating all (g) occurrences of i
console.log(sprintf("[%s].replace(/i/g,'x')=[%s]", chaîne, chaîne.replace(/i/g, 'x')));
// split: splits the string into words separated by the split parameter
// renders the table of these words
// /\s*/ : words separated by 0 or more spaces
console.log(sprintf("[%s].split(/\\s*/)=[%s]", chaîne, chaîne.split(/\s*/)));
// /\s+/ : words separated by one or more spaces
console.log(sprintf("[%s].split(/\\s+/)=[%s]", chaîne, chaîne.split(/\s+/)));
执行
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\strings\str-06.js"
[ Introduction à Javascript ].substr(10,2)=[ti]
[ Introduction à Javascript ].trim()=[Introduction à Javascript]
[ Introduction à Javascript ].toLowerCase=[ introduction à javascript ]
[ Introduction à Javascript ].toUpperCase=[ INTRODUCTION À JAVASCRIPT ]
[ Introduction à Javascript ].indexOf('Java')=[17]
[ Introduction à Javascript ].trim().indexOf('abcd')=[-1]
[ Introduction à Javascript ].includes('Java')=[true]
[ Introduction à Javascript ].length=[28]
[ Introduction à Javascript ].slice(7,10)=[duc]
[ Introduction à Javascript ].match(/intro/i)=[Intro]
[ Introduction à Javascript ].replace('i','x')=[ Introductxon à Javascript ]
[ Introduction à Javascript ].replace(/i/g,'x')=[ Introductxon à Javascrxpt ]
[ Introduction à Javascript ].split(/\s*/)=[,I,n,t,r,o,d,u,c,t,i,o,n,à,J,a,v,a,s,c,r,i,p,t,]
[ Introduction à Javascript ].split(/\s+/)=[,Introduction,à,Javascript,]