这是ES5的入门篇教程的笔记,网址在上一篇文章中,以下内容中黑体表示大标题,还有一些重点;斜体表示对于自身,还需要下功夫学习的内容。这里面有一些自己的见解,所以若是发现问题,欢迎指出~
继续标准库
Array对象
Array构造函数有一个很大的缺陷,就是不同的参数,会导致它的行为不一样(这我知道,所以我都直接赋值的,就是怕出现意想不到的结果,推荐直接赋值呀,哈哈哈)。
// 无参数时,返回一个空数组
new Array() // []
// 单个正整数参数,表示返回的新数组的长度
new Array(1) // [empty]
new Array(2) // [empty * 2]
// 非正整数的数值作为参数,会报错
new Array(3.2) // RangeError: Invalid array length
new Array(-3) // RangeError: Invalid array length
// 单个非数值(比如字符串、布尔值、对象等)作为参数,
// 则该参数时返回的新数组的成员
new Array('abc') // ['abc']
new Array([1]) // [Array[1]]
// 多参数时,所有参数都是返回的新数组的成员
new Array(1, 2) // [1, 2]
new Array('a', 'b', 'c') // ['a', 'b', 'c']
使用Array.isArray()方法可以返回一个布尔值,表示参数是否为数组,它可以弥补typeof运算符的不足(一般会用在从后端接收数据时,防止被挖坑,如果想要接收的是数组,可以先预判断防止报错)。
push()和pop()是一对。push方法用于在数组的末端添加一个或多个元素(原来还可以添加多个,一直以来都以为是一个),并返回添加新元素后的数组长度;pop方法用于删除数组的最后一个元素,并返回该元素,两种方法都会改变原数组。
shift()和unshift()是一对。shift()用于删除数组的第一个元素,并返回该元素;unshift()方法用于在数组的第一个位置添加一个或多个元素,并返回添加新元素后的数组长度,这两种方法也会改变原数组。
push和pop结合使用,就构成了“后进先出”的栈结构(stack);push和shift结合使用,就构成了“先进先出”的队列结构(queue)。
let arr = [];
arr.push(1) // 1
arr.push('a') // 2
arr.push(true, {}) // 4
arr // [1, 'a', true, {}]
arr.pop() // {}
arr // [1, 'a', true]
let a = ['a', 'b', 'c'];
a.shift() // 'a'
a // ['b', 'c']
a = ['c', 'd'];
a.unshift('a', 'b') // 4
a // ['a', 'b', 'c', 'd']
join()方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回,如果不提供参数,默认用逗号分隔。(这个方法不怎么用,差点都忘了还有这个方法)
通过call方法,可以用于字符串或类似数组的对象。
let a = [1, 2, 3, 4];
a.join(' ') // '1 2 3 4'
a.join(' | ') // '1 | 2 | 3 | 4'
a.join() // '1,2,3,4'
Array.prototype.join.call('hello', '-') // 'h-e-l-l-o'
let obj = { 0: 'a', 1: 'b', ; length: 2 };
Array.prototype.join.call(obj, '-') // 'a-b'
concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新的数组,原数组不变。
除了数组作为参数,concat也接受其他类型的值作为参数,添加到目标数组尾部。
如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是新数组拷贝的是对象的引用。
[].concat({a: 1}, {b: 2}) // [{a: 1}, {b: 2}]
[2].concat({a: 1}) // [2, {a: 1}]
[1, 2, 3].concat(4, 5, 6) // [1, 2, 3, 4, 5, 6]
let obj = {a: 1};
let oldArray = [obj];
let newArray = oldArray.concat();
obj.a = 2;
newArray[0].a // 2
reverse方法用于颠倒排列数组元素,返回改变后的数组,该方法将改变原数组。
let a = ['a', 'b', 'c'];
a.reverse() // ["c", "b", "a"]
a // ["c", "b", "a"]
slice方法用于提取目标数组的一部分,返回一个新数组,原数组不变。
第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员。
slice方法的一个重要应用,是将类似数组的对象转为真正的数组。(并没有用到过,也没有适用场景)
arr.slice(start, end);
a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice() // ["a", "b", "c"]
Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2}) // ['a', 'b']
Array.prototype.slice.call(arguments);
splice方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。该方法回改变原数组。(splice和slice,split一直都是傻傻分不清楚,每次要拆分数组或是删除数组元素的时候,都要百度,感受到了无力感。。。slice中文意思是切片,不改变原数组;splice表示的是拼接,改变原数组;split表示的是分裂。)
第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素的个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
arr.splice(start, count, addElement1, addElement2, …);
let a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]
sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将改变。
sort方法不是按照大小排序,而是按照字段顺序,也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以101排在11的前面。
sort方法也可以按照自定义方式排序,可以传入一个函数作为参数。
sort的参数函数本身可以接受两个参数,表示进行比较的两个数组成员。如果该函数的返回值大于0,表示第一个成员排在第二个成员后面;其他情况下(小于等于0),都是第一个元素排在第二个元素前面。
['d', 'c', 'b', 'a'].sort() // ['a', 'b', 'c', 'd']
[4, 3, 2, 1].sort() // [1, 2, 3, 4]
[11, 101].sort() // [101, 11]
[{name: '张三', age: 30},
{name: '李四', age: 24},
{name: '王五', age: 28}].sort(function (o1, o2) {
return o1.age - o2.age;
})
// [
// { name: "李四", age: 24 },
// { name: "王五", age: 28 },
// { name: "张三", age: 30 }
// ]
map方法将数组的所有成员一次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
map方法接受一个函数作为参数,调用该函数时,map方法向它传入三个参数:当前成员、当前位置和数组本身。
map方法还可以接受第二个参数,用来绑定回调函数内部的this变量。
let numbers = [1, 2, 3];
numbers.map(function (n) {
return n + 1;
}); // [2, 3, 4]
numbers // [1, 2, 3]
let arr = ['a', 'b', 'c'];
[1, 2].map(function (e) {
return this(e);
}, arr) // ['b', 'c'] 第二个参数,将回调函数内部的this对象,指向arr数组
forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数,但是forEach方法不返回值,只用来操作数据。=》如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法。
forEach方法无法终端执行,总是会将所有成员遍历完,如果希望符合某种条件时,就中断遍历,要使用for循环,这也是for和forEach最明显的区别。
filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回,原数组不会改变。
some()、every(),这两个方法类似“断言”(assert, 没听说,只听说过断点。。。。。有点尴尬),返回一个布尔值,表示判断数组成员是否符合某种条件。
some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false。
every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false。
对于空数组,some方法返回false,every方法返回true,回调函数都不会执行。(这个真奇怪)
let arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
return elem >= 3;
}); // true
arr.every(function (elem, index, arr) {
return elem >= 3;
)}; // false
function isEven(x) { return x % 2 === 0 }
[].some(isEven) // false
[].every(isEven) // true
indexOf方法返回给定元素在数组中第一次出现
的位置,如果没有出现则返回-1.
indexOf方法还可以接受第二个参数,表示搜索的开始位置。
lastIndexOf方法返回给定元素在数组中最后一次出现
的位置,如果没有出现则返回-1.
这两个方法不能用来搜索NaN的位置,也就说它们无法确定数组成员是否包含NaN。
let a = ['a', 'b', 'c'];
a.indexOf('b') // 1
a.indexOf('y') // -1
a.indexOf('a', 1) // -1
a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1
这些数组方法之中,有不少返回的还是数组,所以可以链式使用。
包装对象
对象是JavaScript语言最主要的数据类型,三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”(wrapper)。
所谓的“包装对象”,指的是与数字、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。
包装对象设计的目的在于,首先使得“对象”这种类型可以覆盖JavaScript所有的值,整门语言有一个通用的数据模型,其次是使得原始类型的值也有办法调用自己的方法。
let v1 = new Number(123);
let v2 = new String('abc');
let v3 = new Boolean(true);
typeof v1 // "object"
typeof v2 // "object"
typeof v3 // "object"
v1 === 123 // false
v2 === 'abc' // false
v3 === true // false
原始类型与实例对象的自动转换
某些场合,原始类型的值会自动当作包装对象调用,即调用包装对象的属性和方法。这时,JavaScript引擎会自动将原始类型的值转为包装对象实例,并在使用后立刻销毁
实例。自动转换生成的对象是只读的,无法修改,也就是说,字符串无法添加新属性。
let str = 'abc';
str.length // 3 abc是一个字符串,本身不是对象,不能调用length属性,引擎自动将其转为包装对象,在这个对象上调用length属性。调用结束后,这个临时对象就会被销毁,这就是原始类型与实例对象的自动转换。
// 等同于
let strObj = new String(str);
// String {
// 0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"
// }
strObj.length // 3
str.x = 123;
str.x // undefined 自动转换生成的包装对象是只读的
let newStr = new String('new');
newStr.x = 123;
newStr // 123
Boolean对象
对于一些特殊值,Boolean对象前面加不加new,会得到完全相反的结果!可以用!!可以将任意值转为对应的布尔值。
if (Boolean(false)) {
console.log('true');
} // 无输出 这里没有new,表示的是将任意值转为布尔值
if (new Boolean(false)) {
console.log('true');
} // true 前面加了new,表示生成了一个Boolean对象的实例,只要是对象,返回都为true
if (Boolean(null)) {
console.log('true');
} // 无输出
if (new Boolean(null)) {
console.log('true');
} // true
Number
Number.prototype.toFixed()方法先将一个数转为指定位数的小数,然后返回这个小数对应的字符串。
该方法的参数为小数位数,有效范围为0~20,超出这范围将会抛出RangeError错误。
由于浮点数不是精确储存的原因,小数5的四舍五入是不确定的(上次使用的时候,就发现了这个问题,终于知道原因了,当时因为这个原因,都是先把值放大到n10倍,用Math.round取整,再除以(n10)。因为整数取整没有这个问题),使用的时候必须小心。
Number.prototype.toExponential()方法用于将一个数转为科学计数法形式。
该方法的参数是小数位数,有效范围为0~20.
(10).toFixed(2) // "10.00"
10.005.toFixed(2) // "10.01"
// 理由同上一个
let n = 1;
n.x = 1;
n.x // undefined 自动转为Number的实例对象。调用结束后,该对象自动销毁。
n = new Number(1);
n.x = 1;
n.x // 1
String对象字符串对象
是一个类似数组的对象(很想数组,但不是数组,其他地方又称为“类数组”。)需要注意的是只有字符串对象才是,原生字符串不是,因为字符串对象中有过一个length属性,而类数组最显著的特征就是length属性。
new String('abc')
// String {0: "a", 1: "b", 2: "c", length: 3}
原来字符串也有concat实例方法,用于连接两个字符串,返回一个新字符串,不改变原字符串。
emmm字符串也有slice方法,又要开始晕头了
slice方法用来从原字符串取出子字符串并返回,不改变原字符串,最多参数为两个。
toLowerCase(),toUpperCase()分别用于将一个字符串全部转为小写、大写,返回的都是一个新字符串,不改变原字符串。
match方法用于确定源字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串,否则,返回null。match方法还可以匹配正则表达式。
'cat, bat, sat'.match('at') // ["at"]
'cat, bat, sat'.match('xt') // null
replace方法用于替换匹配的子字符串,一般情况下只替换第一个匹配(除非使用带有g'修饰符的正则表达式)。
split方法按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组。split方法还可以接受第二个参数,限定返回数组的最大成员数。
'a|b|c'.split('|') // ["a", "b", "c"]
'a|b|c'.split('') // ["a", "|", "b", "|", "c"]
'a|b|c'.split('|', 3) // ["a", "b", "c"]
Math对象
这些方法都比较常用,就没仔细看了。
Math.abs():绝对值
Math.ceil():向上取整
Math.floor():向下取整
Math.max():最大值
Math.min():最小值
Math.pow():指数运算
Math.sqrt():平方根
Math.log():自然对数
Math.exp():e的指数
Math.round():四舍五入
Math.random():随机数
// 还有三角函数,平时不怎么用到
Math.sing(0) // 0
Math.cos(0) // 1
Math.tan(0) // 0
Math.sin(Math.PI / 2) // 1
Math.asin(1) // 1.5707963267948966
Math.acos(1) // 0
Math.atan(1) // 0.7853981633974483
// round
Math.round(1.5) // 2
Math.round(-1.5) // -1
RegExp对象
终于到了正则表达式内容了,激动人心的时刻到了,在这之前还没好好梳理过正则表达式呢!
正则表达式(regular expression)是一种表达文本模式(即字符串结构)的方法,有点像字符串的模板,常常用来按照“给定模式”匹配文本。
新建正则表达式有两种方法,一种是使用字面量,以斜杆表示开始和结束;另一种使用RegExp构造函数。
第一种方法在引擎编译代码时,就会新建正则表达式;第二种方法在运行时新建正则表达式。第一种的效率较高,且比较便利和直观,所以实际应用中,基本上都采用字面量定义正则表达式。
正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。
let regex = /xyz/;
let regex = new RegExp('xyz'); // 这两种写法都是等价的,都新建了一个内容为xyz的正则表达式对象。
/cat/.test('cats and dogs') // true
与正则表达式有关的字符串实例方法:
String.prototype.match():返回一个数组,成员是所有匹配的子字符串。(跟test的区别在于,test是正则实例对象的方法,返回是布尔值;match是字符串实例对象的方法,返回的是数组。)
String.prototype.search():按照给定的正则表达式进行搜索,返回一个整数,表示匹配开始的位置。
String.prototype.replace():按照给定的正则表达式进行替换,返回替换后的字符串。
String.prototype.split():按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员。
let s = '_x_x';
let r1 = /x/;
let r2 = /y/;
s.match(r1); // ["x"]
s.match(r2); // null
'_x_x'.search(/x/) // 1
// 字符串对象的replace方法可以替换匹配的值。可以接受两个参数,第一个是正则表达式,表示搜索模式,第二个是替换的内容。 str.replace(search, replacement)。正则表达式如果不加g修饰符,就替换第一个匹配成功的值,否则替换所有匹配成功的值。
'aaa'.replace('a', 'b') // "baa"
'aaa'.replace(/a/, 'b') // "baa"
'aaa'.replace(/a/g, 'b') // "bbb"
// split可以接受两个参数,第一个参数是正则表达式,表示分割规则,第二个参数是返回数组的最大成员数
'a, b,c, d'.split(',') // ['a', ' b', 'c', ' d']
'a, b,c, d'.split(/, */) / ['a', 'b', 'c', 'd'] 去除多余的空格
'aaa**a*'.split(/a*/) // ["", "*", "*", "*"] 分割规则是0次或多次的a,由于正则默认是贪婪匹配,第一个分隔符是aaa,第二个分隔符是0个a(即空字符),第三个分隔符是a,所以将字符串分成四个部分。
// 如果正则表达式带有括号,则括号匹配的部分也会作为数组成员返回。
'aaa*a*'.split(/(a*)/)// [ '', 'aaa', '*', 'a', '*' ] 代码的正则表达式使用了括号,第一个组匹配是aaa,第二个组匹配是a,它们都作为数组成员返 回。
正则表达式中的字面量字符和元字符
大部分字符在正则表达式中,就是字面的含义,比如/a/匹配a,/b/匹配b。
如果在正则表达式中,某个字符只表示它字面的含义(比如/a/匹配a),那么他们就叫做“字面量字符”。
除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思,它们叫做“元字符”。
/dog/.test('old dog') // true
// **元字符**主要有以下几个
// 1、点字符. 匹配除回车\r、换行\n、行分隔符\u2028和段分隔符\u2029以外的所有字符。需要注意的是,对于码点大于0xFFFF字符,点字符不能正确匹配,会认为这是两个字符。
/c.t/ // c.t匹配c和t之间包含任意一个字符的情况,只要这三个字符在同一行,比如cat、c2t、c-t等等,但是不匹配coot。
// 2、位置字符 用来提示字符所处的位置,主要有两个字符。 ^表示字符串的开始位置;$表字符串的结束位置。
/^test/.test('test123') // true 表示test必须出现在开始位置
/test$/.test('new test') // true 表示test必须出现在结束位置
/^test$/.test('test') // true 表示从开始位置到结束位置**只有test**
/^test$/.test('test test') // false
// 3.选择符 | 在正则表达式中表示“或关系”(OR),即cat|dog表示匹配cat或dog。
/11|22/.test('911') // true 正则表达式指定必须匹配11或22,选择会包括它前后的多个字符,比如/11|22/指的是匹配11或22,而不是指匹配1或2,如果先要修改这个行为,可以使用圆括号。
/a( |\t)b/.test('a\tb') // true 表示的是a和b之间有一个空格或者一个制表符
// 4.转义符 正则表达式中那些特殊含义的元字符,如果要匹配它们本身,就需要在它们前面加上反斜杠
/1+1/.test('1+1') // false 加号是元字符,不代表自身
/1+1/.test('11') // true
/1\+1/.test('1+1') // true
// 如果使用RegExp方法生成正则对象,转义需要使用两个斜杆,因为RegExp作为构造函数,参数是一个字符串,但是在字符串内部,反斜杠也是转义字符,所以它会先被反斜杠转义一次,然后再被正则表达式转义一次,因此需要两个反斜杠转义。
字符类表示有一系列字符可供选择,只要匹配其中一个就可以了,所有可供选择的字符都放在方括号内,比如[xyz]表示x、y、z之中任选一个匹配。
脱字符 ^ 表示除了字符表之中的字符,其他字符都可以匹配。
连字符 - 用来提供简写形式,表示字符的连续范围,比如[123456789]可以写成[0-9]。连字符只有在方括号之中,才表示连续的字符序列,不出现在方括号之中,就不具备简写的作用。
/[abc]/.test('hello world') // false
/[abc]/.test('apple') // true 该字符串包含字母a,所以返回true
/[^abc]/.test('hello world') // true 表示除了a、b、c之外都可以匹配。
/[^abc]/.test('bbc') // false
// [^] 表示匹配一切字符,其中包括换行符。相比之下,点字符(.)是不包括换行符的
// 脱字符只有在字符类的第一个位置才有特殊含义,否则就是字面含义。
/a-z/.test('b') // false
/[a-z]/.test('b') // true
[a-z0-9]
[1-31] // 不代表1到31,只代表1到3
/[1-31]/.test(17) // true 1到3或者1,17匹配到了1,所以返回true,所以/[1-31]/.test(17777)也是true
记住,正则表达式遇到换行符(n)就会停止匹配。
一些简写方式:
\d 匹配0-9之间的任一数字,相当于[0-9]
\D 匹配所有0-9以外的字符,相当于[^0-9]
\w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9]
\W 除字母、数字和下划线以外的字符,相当于[^A-Za-z0-9]
\s 匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f]
\S 匹配非空格的字符,相当于[^\t\r\n\v\f]
\b 匹配词的边界
\B 匹配非词边界,即在词的内部
重复类 模式的精确匹配次数,使用大括号({})表示。{n}表示恰好重复n次, {n,}表示至少重复n次, {n,m}表示重复不少于n次,不多于m次。
/lo{2}k/.test('look') // true
/lo{2,5}k/.test('looook') // true 注意逗号','后面不要加空格
量词符 用来设定某个模式出现的次数。
? 问号表示某个模式出现0次或1次,等同于{0,1}
* 星号表示某个模式出现0次或多次,等同于{0,}
+ 加号表示某个模式出现1次或多次,等同于{1,}
// 上面的是贪婪模式的,就是最大可能匹配,直到下一个字符不满足匹配规则为止,默认是贪婪模式
// 下面的是非贪婪模式
+? 表示某个模式出现1次或多次,匹配是采用非贪婪模式(也就是出现1次)
*? 表示某个模式出现0次或多次,匹配时采用非贪婪模式(也就是出现0次)
?? 表示某个模式出现0次或1次,匹配时采用非贪婪模式(也就是出现0次)
修饰符 表示模式的附加规则,放在这种正则模式的最尾部。
g修饰符 表示全局匹配(global)
i修饰符 表示忽略大小写(ignoreCase)默认正则对象是区分字母的大小写
m修饰符 表示多行模式,用于识别换行符
JSON对象
json对值得类型和格式的规定:
1、复合类型的值只能是数组或对象,不能是函数、正则表达式对象、日期对象;
2、原始类型的值只有四种:字符串、数值(必须以十进制表示)、布尔值和null(不**能使用NaN, Infinity, -Infinity和undefined**);(null、空数组和空对象都是合法的JSON值)
3、字符串必须使用**双引号**表示,不能使用单引号;
4、**对象的键名必须放在双引号里面**;
5、数组或对象最后一个成员的后面,**不能加逗号**。
JSON对象的两个静态方法
JSON.stringify方法用于将一个值转为JSON字符串.该字符串符合JSON格式,并且可以被JSON.parse方法还原。
JSON.parse方法用于将JSON字符串转换成对应的值。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。