写在前面:平时开发中总是遇见相同的问题,但很多时候都需要重新查找相关资料才可以,不但浪费了时间,而且每次都有种重新开始的感觉。。。因此将这些常见问题总结在一起,后续再有相关问题,都将其归为一类进行总结对比学习。
参考资料
基本数据类型
typeof
鉴于ECMAScript是松散类型的,因此需要一种手段来检测变量的数据类型,typeof
就是负责提供这方面信息的操作符。
typeof 'hello' // 'string'
typeof true // 'boolean'
typeof 100 // 'number'
typeof undefined // 'undefined'
typeof (null) // 'object'
typeof function(){} // 'function'
引用数据类型
基本包装类型
.基本概念
为了便于操作基本类型值,ECMAScript
还提供了3个特殊的引用类型:Boolean、Number、String
。和其他引用类型相似但又不同。实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。
var s1 = 'some text';
var s2 = s1.substring(2); // "me text"
如上变量s1
包含一个字符串,当然就是基本类型值,而下一行就调用了substring()
方法。。。我们知道基本类型值不是对象,因而从逻辑上讲他们不应该有方法。
其实为了实现这种直观的操作,后台自动完成了一些列的处理。当第二行访问s1
时,访问过程处于一种读取模式,也就是要从内存中读取这个字符串的值,而在读取模式中访问字符串时,后台会自动完成下面操作:
- 创建
String
类型的一个实例(var s1 = new String('some text')
) - 在实例上调用指定的方法(
var s2 = s1.substring(2)
) - 销毁这个实例(
s1 = null
)
经过上面的处理,基本的字符串就变得跟对象一样了,而且上面的过程也分别适用于Boolean、Number
类型对应的布尔值和数字值。
引用类型与基本包装类型的主要区别就是对象的内存期。使用new
操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。意味着,我们不能在运行时会基本类型添加属性或方法,如下:
var s1 = 'some text';
s1.color = 'red';
s1.color // undefined
原因就是第二行创建的String
对象在执行第三行代码时已经被销毁了。第三行代码又创建自己的String
对象,而该对象没有color
属性。
当然可以显式调用String、Boolean、Number
来创建基本包装类型的对象,但最好不要这样做,因为很容易让人分不清自己处理的是基本类型还是引用类型的值。对基本包装类型的实例(用new构造)调用typeof
会返回'object'
,而且所有基本包装类型的对象都会被转换为布尔值true
(因为是对象了)
Object
构造函数也像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。
var str = new Object('test string');
str instanceof String; // true
var num = new Object(666);
num instanceof Number; // true
var booleanVal = new Object(true);
booleanVal instanceof Boolean; // true
注意:使用new
调用基本包装类型的构造函数,与直接调用同名的转型函数时不一样的。例如:
var val1 = Number('25'); // 转型函数
typeof val1; // 'number'
var val2 = new Number('25'); // 构造函数
typeof val2; // 'object'
.String类型
String
对象的方法可以在所有基本的字符串值中访问到,继承的valueOf()、toLocaleString()、toString()
方法都返回对象所表示的基本字符串值。且每个实例都有length
属性,String
类型还提供了很多方法,用于完成对字符串的解析和操作。
1. 字符方法
两个用于访问字符串特定字符的方法:charAt()、charCodeAt()
,参数都是一个基于0的字符位置。前者返回对应位置的字符,而后者返回字符的编码。
var str = 'hello world';
str.charAt(1); // 'e'
str[1]; // 'e'
str.charCodeAt(1); // 101
2. 字符串操作方法
// concat()返回拼接后的新字符串,不影响原有字符串
var str1 = 'hello';
str1.concat(' world'); // 'hello world'
str1; // 'hello'
slice()、substring()、substr()
三个方法都是基于子字符串创建新字符串的方法,返回新的子字符串,都接受1或2个参数,参数一指定子字符串的开始位置,参数二表示子字符串到哪里结束(不包含)。不同的是substr()
的参数二指定的是返回的字符个数,而不是位置。。。
var str2 = 'hello world';
str2.slice(3); // 'lo world'
str2.substring(3); // 'lo world'
str2.substr(3); // 'lo world'
str2.slice(3, 7); // 'lo w' ,从3开始,不包含索引为7的字符
str2.substring(3, 7); // 'lo w' ,同上
str2.slice(3, 7); // 'lo worl',从3开始,共7个字符
当slice()、substring()、substr()
参数里有负数时,行为就不尽相同了,其实slice()
方法会将传入的负数与字符串的长度相加。substr()
方法将的负的第一参数加上字符串的长度,而将负的第二个参数转换为0。substring()
则将所有负数都转换为0。
var str3 = 'hello';
str3.slice(-2); // 'lo'
str3.substr(-2); // 'lo'
str3.substring(-2); // 'hello'
str3.slice(-2, -1); // 'l', 等价于slice(3, 4)
str3.substr(-2, -1); // '', 等价于substr(3, 0)
str3.substring(-2, -1); // '', 等价于slice(0, 0)
3. 字符串位置方法
有两个从字符串中查找子字符串的方法:indexOf()、lastIndexOf()
,都是从一个字符串搜索给定的子字符串,然后返回子字符串的位置,没有找到则返回-1,前者是从字符串的开头向后搜索子字符串,而后者反之。都可选第二个参数,表示索引开始的位置。
var str4 = 'hello world';
str4.indexOf('o'); // 4
str4.lastIndexOf('o'); // 7
str4.indexOf('o', 5); // 7
str4.lastIndexOf('o', 8); // 7
str4.lastIndexOf('o', 1); // -1,索引的位置都是从前向后数的,即使从最后开始遍历
利用上面的特性,则可以循环获取所有匹配的项
var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = [];
var pos = stringValue.indexOf("e");
while(pos > -1){
positions.push(pos);
pos = stringValue.indexOf("e", pos + 1);
}
console.log(positions); // [3, 24, 32, 35, 52]
4. trim()方法
ECMAScipt 5
为所有字符串定义了trim()
方法,该方法会创建一个字符串的副本,并删除前置和后置的所有空格,然后返回结果。
5. 字符串大小写转换方法
ECMAScipt
中涉及字符串大小写转换的方法有四个:toLowerCase()、toLocaleLowerCase()、toUpperCase()、toLocaleUpperCase()
,其中toLowerCase()、toUpperCase()
是两个经典的方法,借鉴自java.lang.String
中的同名方法。而其他两个则是针对特定地区的实现。对有些地方,针对地区的方法和通用方法得到的结果相同,但少数语言(如土耳其语)会为Unicode
大小转换应用特殊的规则,这时候就必须使用针对特定地区的方法来保证实现正确的转换。
注意:一般来说,在不知道自己的代码将在那种语言环境中运行的情况下,还是使用针对地区的方法更加稳妥一些。
6. 字符串的模式匹配方法
String
类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是match()
,在字符串上调用这个方法,本质上与调用RegExp
的exec()
方法相同。match()
只接受一个参数,要么是一个正则表达式,要么是一个RegExp
对象。
var text = "cat, bat, sat, fat";
var pattern = /.at/;
var matches = text.match(pattern);// ["cat", index: 0, input: "cat, bat, sat, fat", groups: undefined]
matches.index; // 0
matches[0]; // "cat"
pattern.lastIndex; // 0,可以给正则表达式对象设置lastIndex,但对match方法无效,匹配总是从字符串的第一个字符开始
match()
返回一个数组,第一项是与整个模式匹配的字符串,之后的每一项(如果有)保存着匹配的字符串相关的数据。index
表示匹配到的字符串索引(.
匹配所有字符),input
则是目标字符串,groups
是?
var text = "cat, bat, sat, fat";
var pattern = /.at/g; // g开启全局搜索模式,匹配到所有匹配的项
pattern.lastIndex = 2;
var matches = text.match(pattern);// ["cat", "bat", "sat", "fat"],全局模式不含index,input等信息
matches.index; // undefined
pattern.lastIndex; // 0,上面设置无效
另一个用于查找的方法是search()
,参数为字符串或正则表达式,返回第一个匹配的索引,否则返回-1,始终从字符串开头查找。
为了简化替换子字符串的操作,ECMAScript
提供了replalce()
方法,两个参数:参数一是字符串或正则表达式,参数二可以是字符串或者一个函数。如果第一个参数是字符串,则只会替换第一个子字符串,要想替换所有的,则需要用正则,而且需要加上全局g
标识。
var text = "cat, bat, sat, fat";
var result = text.replace("at", "ond");
alert(result); //"cond, bat, sat, fat"
result = text.replace(/at/g, "ond");
alert(result); //"cond, bond, sond, fond"
如果第二个参数是字符串,那么还可以使用一些特殊的字符序列,将正则表达式得到的值插入到结果字符串中。如下表:
变量名 | 代表的值 |
---|---|
$$ | 插入一个’$’ |
$& | 插入匹配的子串 |
$` | 插入当前匹配的子串左边的内容 |
$’ | 插入当前匹配的子串右边的内容 |
$n | 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始 |
var text = "cat, bat, sat, fat";
text.replace(/(.at)/g, "word ($1)");
// "word (cat), word (bat), word (sat), word (fat)"
text.replace(/(.at)/, 'hello $&');
// "hello cat, bat, sat, fat"
text.replace(/(.at)/g, 'hello $&');
// "hello cat, hello bat, hello sat, hello fat"
text.replace(/(.at)/, 'hello $`')
// "hello , bat, sat, fat"
text.replace(/(.at)/g, 'hello $`')
// "hello , hello cat, , hello cat, bat, , hello cat, bat, sat, "
text.replace(/(.at)/, 'hello $\'') // 需转义,或双引号包裹
// "hello , bat, sat, fat, bat, sat, fat"
replace()
方法的第二个参数也可以是一个函数,在只有一个匹配项时,会向这个函数传递3个参数:模式的匹配项、模式匹配项的索引和原始字符串。
如果参数一定义了多个捕获组的情况下,传递给函数的参数依次为:模式的匹配项,第一个捕获组的匹配项、第二个捕获组的匹配项……,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串。
变量名 | 代表的值 |
---|---|
match | 匹配的子串。(对应于上述的$&。) |
p1,p2, … | 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+,p2 就是匹配的 \b+。 |
offset | 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 ‘abcd’,匹配到的子字符串是 ‘bc’,那么这个参数将会是 1) |
string | 被匹配的原字符串。 |
function replacer(match, p1, p2, p3, offset, string) {
return [p1, p2, p3].join(' - ');
}
// [^\d]中括号里的^表示除了,这里匹配除了数字项,因此匹配到'abc'
// ()表示分组,每一组作为一个整体进行匹配,(\d*)匹配到'12345'
// 同理([^\w]*)匹配特殊字符,这里匹配到'#$*%'
var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString); // abc - 12345 - #$*%
下面是一个转义HTML
代码的函数:
var text = "cat, bat, sat, fat";
function htmlEscape(text){
return text.replace(/[<>"&]/g, function(match, pos, originalText){
switch(match){
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "\"":
return """;
}
});
}
htmlEscape("<p class=\"greeting\">Hello world!</p>");
//<p class="greeting">Hello world!</p>
最后一个与模式匹配有关的方法是split()
方法,该方法可以基于指定的分隔符将一个字符串分割成多个字符串,并将结果放到一个数组里。分隔符还可以是正则,还可以接受第二个参数,用于指定数组的大小。
var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(",");
//["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); // 指定数组长度
//["red", "blue"]
colorText.replace(/[^\,]+/g, '$'); // [$,$,$,$]
var colors3 = colorText.split(/[^\,]+/);
//["", ",", ",", ",", ""]
注意:最后一次调用split()
返回的数组中,第一项和最后一项是两个空字符串,因为通过正则表达式指定的分隔符出现在了字符串的开头和末尾。
7. fromCharCode()方法
String
构造函数本身有一个静态方法:fromCharCode()
,接收一个或多个字符编码,然后将他们转换为字符串,其实就是charCodeAt()
的逆操作。。。
String.fromCharCode(104, 101, 108, 108, 111)
// "hello"