字符串扩展
模板字符串
const xiaoming = {
name:'小明',
age:14,
say1:function(){
console.log('我叫' + this.name + ',我今年' + this.age + '岁了');
},
say2:function(){
console.log(`我叫${this.name},我今年${this.age}岁了`); // 这就是模板字符串!
}
}
xiaoming.say1(); // 我叫小明,我今年14岁了
xiaoming.say2(); // 我叫小明,我今年14岁了
新的方法
- padStart(参数1,参数2):从头部开始补全
// padStart(参数1,参数2):从头部开始补全
// 参数1:补全后的字符串长度
// 参数2:用来补全的字符串
let str = 'i';
let str1 = str.padStart(5,'mooc');
let str2 = str.padStart(8,'mooc');
console.log(str1); // mooci
console.log(str2); // moocmooi
// 常用于日期自动补全,比如 3 自动补全成 03
- padEnd(参数1,参数2):从尾部开始补全
// padEnd(参数1,参数2):从尾部开始补全
// 参数1:补全后的字符串长度
// 参数2:用来补全的字符串
let str = 'i';
let str3 = str.padEnd(5,'mooc');
let str4 = str.padEnd(8,'mooc');
console.log(str3); // imooc
console.log(str4); // imoocmoo
- repeat(参数:重复次数)
// repeat(参数:重复次数)
console.log('i'.repeat(10)); // iiiiiiiiii
- startsWith(参数:字符串):判断是否以参数中的字符串开始
- endsWith(参数:字符串):判断是否以参数中的字符串结束
const str = 'A promise is a promise';
console.log(str.startsWith('B')); // false
console.log(str.startsWith('A pro')); // true
console.log(str.endsWith('A')); // false
console.log(str.endsWith('promise')); // true
- includes(参数:字符串):判断是否包含参数中的字符串
const str = 'A promise is a promise';
if(str.indexOf('promise') !== -1){
console.log('存在1'); // 存在1
}
if(str.includes('promise')){
console.log('存在2'); // 存在2
}
- String.raw:在
"\"
之前自动添加转义字符"\"
console.log(String.raw`Hello\nworld`); // Hello\nworld
console.log(`Hello\nworld`); // Hello
// world
for-of遍历字符串
const words = "promise";
for(let word of words){
console.log(word);
}
// 输出结果:p r o m i s e
unicode表示法
unicode是一项标准,包括字符集,编码方案等。
它是为了解决传统的字符编码方案的局限而产生的,为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言,跨平台进行文本转移,处理的要求。
// 超出两个字节(0xFFFF)的unicode,要用{}包裹起来,否则不能正确显示
console.log(`\u0061`); // a 正确表示
console.log(`\u20BB7`); // ₻7 没有正确表示
console.log(`\u{20BB7}`); // 正确表示
// charCodeAt不能正确取得字符长度超过2个字节的字符所对应的Unicode码
// 请看下面的codePointAt方法
let s = '';
// .length属性的计算规则:unicode长度是两个字节,.length的结果是1
// 该字符unicode长度查过0xFFFF,被认为是4个字节,所以.length的结果是2
console.log(s.length); // 2
console.log(s.charAt(0)); // � 乱码,前两个字节的unicode码对应的字符
console.log(s.charAt(1)); // � 乱码,后两个字节的unicode码对应的字符
console.log(s.charCodeAt(0)); // 55362 前两个字节的unicode码
console.log(s.charCodeAt(1)); // 57271 后两个字节的unicode码
// codePointAt可以正确取得字符长度超过2个字节的字符所对应的Unicode码
let s = 'a';
console.log(s.length); // 3 字符''长度2,字符'a'长度1,所以结果是3
console.log(s.codePointAt(0)); // 134071 字符''的unicode码
console.log(s.codePointAt(0).toString(16)); // 20bb7 字符''的unicode码的16进制表示
console.log(s.codePointAt(1)); // 57271 字符''的后两个字节的unicode码,没有什么意义
console.log(s.codePointAt(2)); // 97 字符'a'的unicode码
console.log(String.fromCharCode('0x20bb7')); // ஷ 乱码,不能正确处理超过2个字节的字符
console.log(String.fromCodePoint('0x20bb7')); // 正确显示,可以正确处理超过2个字节的字符
正则扩展
- 新的写法
// 以下三种是ES5中就已经有的写法
const regex1 = new RegExp('xyz','i');
const regex2 = new RegExp(/xyz/i);
const regex3 = /xyz/i;
console.log(regex1.exec('xyz123xyz')); // ["xyz", index: 0
console.log(regex2.exec('xyz123xyz')); // ["xyz", index: 0
console.log(regex3.exec('xyz123xyz')); // ["xyz", index: 0
// 以下是ES6中的新写法,允许第一个参数是字面量,然后再加第二个参数
// 此时第二个参数"i",会覆盖第一个参数的"ig"
const regex4 = new RegExp(/xyz/ig,'i');
console.log(regex4.exec('xyz123xyz')); // ["xyz", index: 0
- u unicode修饰符
// 如果所要匹配的字符串超过2个字节,需要用u修饰符
// .只能匹配0xFF之内的字符,超出0xFF需要用u修饰符去匹配
// 字符串'\ud83d\udc36',这一个整体表示的unicode,只有加上u之后匹配才算真正匹配到
console.log('\ud83d\udc36'); //
console.log(/^\ud83d/.test('\ud83d\udc36')); // true,这里把'\ud83d\udc36'当成2个字符
console.log(/^\ud83d/u.test('\ud83d\udc36')); // false,这里把'\ud83d\udc36'当成1个字符
console.log(/^\ud83d\udc36/u.test('\ud83d\udc36')); // true,这里把'\ud83d\udc36'当成1个字符
// 如果{}包裹的是unicode编码,需要用u修饰符才能识别
console.log(/\u{61}/.test('a')); // false
console.log(/\u{61}/u.test('a')); // true,只有加u修饰符,才能识别
console.log(`\u{20BB7}`); //
const str = '';
// .并不能匹配到超出两个字节的字符,需要加u修饰符才行
console.log(/^.$/.test(str)); // false
console.log(/^.$/u.test(str)); // true
// {2}并不能匹配到超出两个字节的字符的数量,需要加u修饰符才行
console.log(/{2}/.test('')); // false
console.log(/{2}/u.test('')); // true
- y 黏连修饰符
const r1 = /imooc/g;
const r2 = /imooc/y;
const str = 'imoocimooc-imooc';
console.log(r1.exec(str));
// 找到第一个:["imooc", index: 0, input: "imoocimooc-imooc", groups: undefined]
console.log(r1.exec(str));
// 找到第二个:["imooc", index: 5, input: "imoocimooc-imooc", groups: undefined]
console.log(r1.exec(str));
// 找到第三个:["imooc", index: 11, input: "imoocimooc-imooc", groups: undefined]
console.log(r1.exec(str));
// 没有了:null
// y修饰符是要黏连住的才能找到
// 当找到第二个之后,接下来的字符是"-",不符合"imooc",所以就找不到了
console.log(r2.exec(str));
// 找到第一个:["imooc", index: 0, input: "imoocimooc-imooc", groups: undefined]
console.log(r2.exec(str));
// 找到第二个:["imooc", index: 5, input: "imoocimooc-imooc", groups: undefined]
console.log(r2.exec(str));
// 没有了:null
// 判断某一个正则是否有y修饰符
console.log(r1.sticky); // false
console.log(r2.sticky); // true
- s修饰符(ES6中还未实现,期待。。)
可用于查找换行符,回车符,行分隔符,段分隔符
数值扩展
新的进制表示法
console.log(016); // 14
// 以上其实是八进制的16,转换成十进制就是14
// 是不是容易不好理解,容易引起歧义?所以不建议这么写
// 以下是建议的书写方式
// 0o 0O octonary 八进制
// 0b 0B binart 二进制
console.log(0o16); // 14
console.log(0b1111); // 15
新的方法
在ES6中,以下方法挂载在Number对象上,所以使用的时候最好用Number.方法()的形式。
- parseInt():取整(常用于字符串取整)
// 字符串必须以数字开头才能取到数值
console.log(Number.parseInt('123abc')); // 123
console.log(Number.parseInt('abc123')); // NaN
console.log(Number.parseInt(1.23)); // 1 这样用也是可以的
- parseFloat():取小数(常用于字符串取小数)
// 字符串必须以数字开头才能取到数值
console.log(Number.parseFloat('1.23abc')); // 1.23
console.log(Number.parseFloat('abc1.23')); // NaN
console.log(Number.parseFloat(1.23)); // 1.23 这样用也是可以的
- isNaN():判断其参数是否是NaN
// 只有当值是NaN的时候,才会返回true
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(-NaN)); // true
console.log(Number.isNaN(1)); // false
console.log(Number.isNaN('1')); // false
console.log(Number.isNaN(true)); // false
- isFinite():判断其参数是否是有限的
// 只有当其参数是一个正儿八经的有限数时,才会返回true
console.log(Number.isFinite(2/0)); // false
console.log(Number.isFinite(2/4)); // true
console.log(Number.isFinite(1234)); // true
console.log(Number.isFinite('1234')); // false
console.log(Number.isFinite(true)); // false
console.log(Number.isFinite(NaN)); // false
- isInteger():判断其参数是否是整数
console.log(Number.isInteger(25)); // true
console.log(Number.isInteger(25.0)); // true 25.0的值跟25是一样的,所以结果为true
console.log(Number.isInteger(25.1)); // false
console.log(Number.isInteger('25')); // false 首先参数得是数字,否则结果都为false
- isSafeInteger():判断其参数是否是安全数 (处于JS能表示的数值范围之内)
console.log(Number.MAX_SAFE_INTEGER); // JS能表示的最大值:9007199254740991(2的53次 - 1)
console.log(Number.MIN_SAFE_INTEGER); // JS能表示的最小值:-9007199254740991(-2的53次 + 1)
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER - 1)); // true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false
console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER + 1)); // true
console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1)); // false
- Math.trunc():取其参数的整数部分的值
console.log(Math.trunc(4.1)); // 4
console.log(Math.trunc(4.9)); // 4
- Math.sign():判断其参数大于0,等于0,还是小于0
console.log(Math.sign(-5)); // -1
console.log(Math.sign(0)); // 0
console.log(Math.sign(5)); // 1
console.log(Math.sign("5")); // 1 把字符"5"转换成了数字5,所以结果是1
console.log(Math.sign("-5")); // -1 把字符"-5"转换成了数字-5,所以结果是-1
console.log(Math.sign("abc")); // NaN 非数字,结果为NaN
- Math.cbrt():取其参数的立方根
console.log(Math.cbrt(8)); // 2
幂运算
console.log(2 ** 3); // 8(2的3次方)
// 幂运算是右结合的
// 以下两种结果是一样的
console.log(2 ** 3 ** 0); // 2
console.log(2 ** (3 ** 0)); // 2
函数扩展
默认参数
function add1(a, b = 2) {
console.log(a, b); // 1 2
}
add1(1);
// 这样写b的默认值也是可以的
function add2(a, b = a + 2) {
console.log(a, b); // 1 3
}
add2(1);
剩余参数...
我们通过一个例子来解释什么是剩余参数...,它与扩展运算符的概念又有什么不同。
// 任务:把字符串装换为数组
const str = 'abcde';
// 方式1:使用扩展运算符...
// 这里的...叫扩展运算符,是负责扩展的
const arr1 = [...str];
console.log(arr1); // ["a", "b", "c", "d", "e"]
// 方式2:使用剩余参数...
// 这里的...叫剩余参数,是负责聚合的
const [...arr2] = str;
console.log(arr2); // ["a", "b", "c", "d", "e"]
// 方式3:使用Array原型对象的方法slice
const arr3 = Array.prototype.slice.call(str);
console.log(arr3); // ["a", "b", "c", "d", "e"]
箭头函数(=>)
- 一个简单的箭头函数
// 以下两种方式定义的函数是一模一样的
// 方式1:传统方式
function add1(a, b) {
return a + b;
}
// 方式2:箭头函数
const add2 = (a, b) => a + b;
console.log(add1(2, 2)); // 4
console.log(add2(2, 2)); // 4
- 不带返回值的箭头函数
const pop1 = arr => arr.pop(); // 运行arr.pop()处理,且返回处理后的值
const pop2 = arr => void arr.pop(); // 运行arr.pop()处理,不返回
console.log(pop1([1, 2, 3])); // 3
console.log(pop2([1, 2, 3])); // undefined
- 箭头函数是没有arguments的
const log = () => {
console.log(arguments); // 报错:arguments is not defined
}
log(1,2,3);
- 箭头函数中的this,是定义自身的环境中的this
// 例子1:对象中的方法要取得自身属性的话,还是不要用箭头函数了
// 方法可以使用简洁表示法,请继续往下看
const xiaoming = {
name:'小明',
say1:function(){
console.log(this.name); // 小明 this指向的是对象本身
},
say2:() => {
console.log(this.name); // 空 this指向的是window对象
}
}
xiaoming.say1();
xiaoming.say2();
// 例子2:模拟取得ajax数据
// 方式1:传统方式,必须保存this然后才能使用
const xiaoming1 = {
name:'xiaoming',
age:null,
getAge:function(){
const _this = this;
// ajax...
setTimeout(function(){
_this.age = 14;
}, 1000);
}
}
// 方式2:箭头函数,可以直接使用this
const xiaoming2 = {
name:'xiaoming',
age:null,
getAge:function(){
// ajax...
setTimeout(() => {
this.age = 14; // this指向的是定义自己的环境中的this,也就是方式1中的_this
}, 1000);
}
}
xiaoming1.getAge(); // {name: "xiaoming", age: 14, getAge: ƒ}
xiaoming2.getAge(); // {name: "xiaoming", age: 14, getAge: ƒ}
尾调用
// 以下就是尾调用
// 当有多个函数嵌套使用的时候,可以用于提升性能
function tail(x){
console.log('tail',x);
}
function fx(x){
return tail(x);
}
fx(123); // tail 123
对象扩展
简洁表示法
- 传统方式
const getUserInfo1 = (id = 1) => {
// Ajax...
const name = 'xiaoming';
const age = 10;
return {
name:name,
age:age,
say:function(){
console.log(this.name + this.age);
}
}
}
const xiaoming1 = getUserInfo1();
- 简洁表示
const getUserInfo2 = (id = 1) => {
// Ajax...
const name = 'xiaoming';
const age = 10;
return {
name,
age,
say(){
console.log(this.name + this.age);
}
}
}
const xiaoming2 = getUserInfo2();
属性名表达式
const key = 'age';
const xiaoming = {
name:'xiaoming',
[key]:14
}
// 跟以下内容是一模一样的
const xiaoming = {
name:'xiaoming',
age:14
}
复制对象
const obj = {
a: 1,
b: 2,
c: {
aa: 11,
bb: 22
}
};
// 复制对象
const cObj = { ...obj };
console.log(cObj); // {a: 1, b: 2, c: {aa:11,bb:22}}
// 可以看到,obj.aa改变了,cObj.aa也改变了。
// 这里的复制是浅复制。
obj.aa = 33;
console.log(cObj); // {a: 1, b: 2, c: {aa:11,bb:33}}
新的方法和属性
- Object.is():与===几乎一样
// 与===不同的地方是这两个
console.log(Object.is(+0,-0)); // false
console.log(+0 === -0); // true
console.log(Object.is(NaN,NaN)); // true
console.log(NaN === NaN); // false
- Object.assign():合并对象
// 注意:这里的合并也是浅拷贝!!
// 只拷贝自身的属性和方法,无法拷贝继承的属性和方法
const obj = Object.assign(
{ a: 1 },
{ b: 2 },
{ c: 3 },
{ d: 4, e: 5 }
);
console.log(obj); // {a: 1, b: 2, c: 3, d: 4, e: 5}
- Object.keys():取得对象的key并组成数组
- Object.values():取得对象的value并组成数组
- Object.entries():不知道怎么描述,还是看下面的输出结果体会吧
const obj = {
a:1,
b:2,
c:3,
d:4
};
console.log(Object.keys(obj)); // ["a", "b", "c", "d"]
console.log(Object.values(obj)); // [1, 2, 3, 4]
console.log(Object.entries(obj)); // ["a", 1],["b", 2],["c", 3],["d", 4]
- for-in:遍历对象
// 顺便提下,最好能记住
// for-in是遍历对象的
// for-of是遍历数组和字符串,这种拥有迭代器的集合
// for-in
const obj = {
a: 1,
b: 2,
c: 3
};
for (let key in obj) {
console.log(key); // a b c
}
// for-of
const arr = [1,2,3];
for(let i of arr){
console.log(i); // 1 2 3
}
- Object.setPrototypeOf(参数1:目标对象,参数2:原型对象):设定原型对象
- Object.getPrototypeOf(参数:目标对象):取得原型对象
const obj1 = {
a: 1
};
const obj2 = {
b: 2
};
const obj = Object.create(obj1);
console.log(obj.__proto__); // {a: 1}
Object.setPrototypeOf(obj, obj2); // 设定obj的原型对象为obj2
console.log(obj.__proto__); // {b: 2}
// Object.getPrototypeOf(obj) 和 obj.__proto__是一样的
console.log(Object.getPrototypeOf(obj)); // {b: 2}
console.log(obj.__proto__); // {b: 2}
console.log(Object.getPrototypeOf(obj) === obj.__proto__); // true
- super:可以访问到原型对象
const obj = { name: "xiaoming " };
const cObj = {
say() {
console.log(`My name is ${super.name}`);
}
};
Object.setPrototypeOf(cObj, obj);
cObj.say();
数组扩展
扩展
- 结合扩展运算符
const arr = [1, 2, 233, 3, 4, 5];
console.log(Math.max(...arr)); // 233
console.log(Math.max.apply(null, arr)); // 233
- Set构造方法
// Set方法构造出来的内容,不会有重复
// 所以,可以用来做去重处理
const set = new Set([1, 2, 2, 3]);
const num = [...set];
console.log(num); // [1, 2, 3]
新的方法
- Array.indexOf(参数:查找目标):查找数组中的目标元素
var fruits = ["Banana", "Orange", "Apple", "Mango"];
var a = fruits.indexOf("Apple"); // 2
- Array.from(参数:原对象,回调):将一个ArrayLike对象或者Iterable对象转换成一个Array
// 注意:类数组对象的属性名必须为数值型或字符串型的数字,就像下面这样
const obj = {
0: 1,
1: "22",
length: 2
};
console.log(Array.from(obj, item => item * 2)); // [2, 44]
- Array.of(各种参数):把参数合成一个数组
console.log(Array.of(1, 2, "123", false)); // [1, 2, "123", false]
console.log(Array.of()); // [] 空数组
- Array#fill(content,start,end):填充数组
let arr1 = new Array(10).fill(0);
console.log(arr1); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
let arr2 = new Array(10).fill(0,0,3);
console.log(arr2); // [0, 0, 0, empty × 7] 注意结束位置是end-1
console.log([1,2,3].fill(0)); // [0, 0, 0]
console.log(['a','b','c','d'].fill(7,1,3)); // ["a", 7, 7, "d"] 注意结束位置是end-1
- Array#includes:检测数组中是否包含某一项(与字符串的includes方法类似)
const arr = [1, 2, 3, 4, NaN];
console.log(arr.includes(1)); // true
console.log(arr.includes(55)); // false
console.log(arr.includes(NaN)); // true 这个就很厉害了
- Array#keys():取得数组的key
- Array#values():取得数组的value
- Array#entries():取得数组的key和value
const arr = [1, 2, 3, 444];
console.log(arr.keys()); // Array Iterator {}:迭代器
for (const i of arr.keys()) {
console.log(i); // 0 1 2 3
}
for (const v of arr.values()) {
console.log(v); // 1 2 3 444
}
for (const item of arr.entries()) {
console.log(item); // [0, 1] [1, 2] [1, 2] [2, 3] [3, 444]
}
- find():根据条件(回调) 按顺序遍历数组 当回调返回true时 就返回当前遍历到的值
const arr = [1, 7, 6, 3, 8];
const rst = arr.find((value, index, arr) => value % 2 === 0);
console.log(rst); // 6 找到符合条件的内容,就返回了,不再继续找了
- findIndex():根据条件(回调) 按顺序遍历数组 当回调返回true时 就返回当前遍历到的下标
// 功能与indexOf有点类似
const arr = [1, 7, 6, 3, 8];
const rst = arr.findIndex((value, index, arr) => value % 2 === 0);
console.log(rst); // 2
Symbol数据类型
概念:Symbol数据类型提供一个独一无二的值
// 声明方式1:Symbol()
let a1 = Symbol();
let a2 = Symbol();
console.log(a1 === a2); // false a1和a2永远都不会相等
// 声明方式2:Symbol().for(key)
// 先检查key值是否已经注册
// 如果key值没有注册,那么生成一个新的独一无二的值
// 如果key值已经注册,那么返回那个已经生成的独一无二的值
let a3 = Symbol.for('a3');
let a4 = Symbol.for('a3');
console.log(a3 === a4); // true a3已经被注册了,所有这里就返回那个值
作用:在定义对象属性的时候,可以不用考虑属性重复问题
let a1 = Symbol.for('abc'); // 好处:不用考虑其他人也在这个对象里使用'abc'属性了
let obj = {
[a1]: '123',
'abc': 345,
'c': 456
}
console.log(obj);
// abc: 345
// c: 456
// Symbol(abc): "123"
// 只能取到非Symbol类型的数据
for (let [key, value] of Object.entries(obj)) {
console.log(key, value);
// abc 345
// c 456
}
// 只能取到Symbol类型的数据
Object.getOwnPropertySymbols(obj).forEach(item => {
console.log(item, obj[item]);
// Symbol(abc) 123
})
// 可以取到所有类型的数据
Reflect.ownKeys(obj).forEach(item => {
console.log(item, obj[item]);
// abc 345
// c 456
// Symbol(abc) 123
})
类扩展
定义
class MyApp{
constructor(){
this.name = 'imooc';
}
sayHello(){
console.log(`hello ${this.name}!`);
}
}
// 实例化一个对象
const app = new MyApp();
// 使用对象的sayHello方法
app.sayHello(); // hello imooc
继承
class Parent {
constructor(name = 'song') {
this.name = name;
}
}
class Child extends Parent {}
const child = new Child();
console.log(child); // {name: "song"}
继承(传递参数)
class Parent {
constructor(name = 'song') {
this.name = name;
}
}
class Child extends Parent {
constructor(name = 'child') {
super(name); // 这个要放在第一行,会覆盖父元素的name属性
this.type = 'child';
}
}
const child1 = new Child();
const child2 = new Child('hello');
console.log(child1); // {name: "child", type: "child"}
console.log(child2); // {name: "hello", type: "child"}
set,get
class Parent {
constructor(name = 'song') {
this.name = name;
}
// 注意:longName是一个属性
get longName() {
return 'mk' + this.name;
}
set longName(value) {
this.name = value;
}
}
let v = new Parent();
console.log(v.longName); // mksong
v.longName = 'hello';
console.log(v.longName); // mkhello
静态方法
class Parent {
constructor(name = 'song') {
this.name = name;
}
// 定义一个静态方法tell
static tell() {
console.log('tell');
}
}
// 静态方法的调用方式:只能通过类调用
Parent.tell(); // tell
静态属性
class Parent {
constructor(name = 'song') {
this.name = name;
}
// 定义一个静态方法tell
static tell() {
console.log('tell');
}
}
// 定义一个静态属性type
Parent.type = 'test';
// 静态属性的访问方式:只能通过类调用
console.log(Parent.type); // test