【ES6】字符串、数值、函数、数组的扩展

838 阅读7分钟

一、字符串的扩展

1. includes()、startWith()、endWith()方法

let str = 'Hello World';
console.log(str.includes('llo'));	//true
console.log(str.startWith('He'));	//true
console.log(str.endWith('ld'));		//true

2.repeat()方法

let str = 'abc';
console.log(str.repeat(3));		//abcabcabc

参数记得传整数,传小数会取整,数组对象什么的就别传了,别为难人家库函数了。

3.padStart()、padEnd()方法

padStart(n, str)/padEnd(n, str),在前面或者后面补字符串,直到整个字符串的长度 == n :

let str = 'x';
console.log(str.padStart(4, 'ab'));		//abax
console.log(str.padEnd(4, 'ab'));		//xaba

但是只有当原字符串的长度小于n时才会补,如果大于则不做操作:

let str = 'xxxxx';
console.log(str.padStart(4, 'ab'));		//xxxxx

应用情况:月份和日期补零:

let year = '2020';
let month = '9';
let day = '9';
console.log(year + month.padStart(2,'0') + day.padStart(2, '0'));		//20200909

4.字符串模板

{ }里可以是变量,也可以是表达式,也可以是函数调用

let [a, b, c] = [1, 2, 3];
let s = `<ul>
	<li>${a}</li>
    <li>${b}</li>
    <li>${c + b}</li>
</ul>`;
console.log(s);		

输出:

二、数值的扩展

1.二进制和八进制的表示方法

0b开头代表二进制

let a1 = 0b0101;
console.log(a1);		//5

0o开头代表八进制

let b1 = 0o77;
console.log(b1);		//63

2.parseInt() 和 parseFloat()

和Number.parseInt()的效果一样

console.log(Number.parseInt === parseInt);	//true

3.Number.isInteger()

JS的基本类型里整形和浮点型统一用Number表示,我们可以进一步用Number.isInterger()判断这个数是否是整数。

let a = 1;
let b = 1.5;
console.log(Number.isInteger(a), Number.isInteger(b));		//true false

实现原理,用向下取整进行判断

function myIsInteger(a){
	return Math.floor(a) === a;
}
console.log(myIsInteger(1.5)); 		//false

4.Number.EPSILON

表示极小的常量

console.log(Number.EPSILON);	//2.220446049250313e-16

常用来做误差检查

function withErrorMargin(left, right){
	return Math.abs(left - right) < Number.EPSILON;
}
withErrorMargin(0.1 + 0.2, 0.3);		//true

5. 安全整数Number.isSafeInteger()、Number.MAX_SAFE_INTEGER()

console.log(Number.isSafeInteger(5));	//true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1));		//false
console.log(Number.MAX_SAFE_INTEGER);	//9007199254740991

6. Math扩展出来的API

6.1 trunc()方法 去掉小数部分

ES5里的Math.floor()也可以取整,但是是向下取整,如果要操作的对象是负数的时候,和我们想要达到的效果不一样,所以有了更方便的方法trunc:

let a = 1.5;
let b = -1.5;
console.log(Math.floor(a), Math.floor(b));		//1 -2
console.log(Math.trunc(a), Math.trunc(b));		//1 -1

6.2 Math.cbrt()方法 立方根

console.log(Math.cbrt(27));		//3

ES5里求立方根的方法

console.log(Math.pow(27, 1/3))		//以27为底的1/3次方

6.3 Math.hypot() 对所有的数据求平方和,然后开方

2个参数时,相当于求直角三角形的斜边

console.log(Math.hypot(3, 4));	//5	

还有一些对数,正弦、余弦函数的方法,用到再查了。

三、函数的扩展

1.箭头函数

情况1:只有一个参数,一个返回

// ES5的写法
var f = function(a){
	return a + 1;
}

// ES6的写法
let f = a => a + 1;		//简化了很多

情况2:没有参数,只有一个返回

// ES5的写法
var f = function(){
	return 'a';
}

// ES6的写法
let f = () => 'a';

情况3:多个参数,1个返回

// ES5的写法
var f = function(a, b, c){
	return a + b + c;
}

// ES6的写法
let f = (a, b, c) => a + b + c;

情况4:函数里面有多条语句

// ES5的写法
var f1 = function(a, b){
    var c = 0;
    c += a * a;
    c += b * b;
    c = Math.sqrt(c);
    return c;
}

// ES6的写法
let f2 = (a, b) => {
    let c = 0;
    c += a * a;
    c += b * b;
    c = Math.sqrt(c);
    return c;
}

情况5:特例:当只有一个返回,且是一个对象的时候

// ES5
var f11 = function(a, b){
    return {a: a, b: b}
}

// ES6
// 如果直接返回一个对象,那么大括号外面要有小括号
let f21 = (a, b) => ({a: a, b: b});
console.log(f21(1,2));

情况6:箭头函数和解构赋值的结合

var f1 = function({a, b}){
    var c = Math.sqrt(a * a + b * b);
    return c;
}

var obj = {a: 3, b: 4};
console.log(f1(obj));

let f2 = ({a, b}) => {
    let c = Math.sqrt(a * a + b * b);
    return c;
}
console.log(f1(obj));

情况7:常见的应用

map()里参数的处理

console.log([1, 2, 3].map(function(x){
    return x * x;
}));

console.log([1, 2, 3].map(x => x * x));		//1 4 9

sort()的简化

console.log([1, 5, 2, -3].sort(function(a, b){
    return a - b;
}));

console.log([1, 5, 2, -3].sort((a, b) => a - b));	//[-3, 1, 2, 5]

情况8:rest参数

let [a, ...args] = [1, 2, 3, 4];
console.log(args);		//[2, 3, 4]
//1赋值给了a,其他剩下的值赋给args

用于函数的简化

// ES5的写法
function f1(a, ...args){
    return args;
}

// ES6的写法
let f2 = (a, ...args) => args;

情况9:箭头函数的this是在定义时确定的,不能再调用时确定

// ES5
function f(){
    setTimeout(function(){
        console.log(this.id);	//undefined
    }, 1000);
}
f.call({id:5});		

因为f.call的调用id是给了f,settimeout里的function函数有自己的this,所以输出undefined

// ES6
function f(){
    setTimeout(() => {console.log(this.id)}, 1000); 	//1
}

let obj = {id: 1}

f.call(obj);

因为箭头函数不初始化this,所以会向上父作用域里找this,最后输出1。

情况10:箭头函数不可以作为构造函数,否则报错

因为箭头函数里没有this

let f2 = () => {
    this.a = 1;
    this.b = 2;
};

let a = new f2();

情况11:箭头函数不能使用arguments对象,需使用rest参数

// ES5
var f1 = function(){
    console.log(arguments);		// []
}

f1(1, 2, 3, 4);

// ES6
let f2 = (...args) => {
    console.log(args);
}

f2(1, 2, 3, 4);

情况12:嵌套的箭头函数:箭头函数内嵌套箭头函数

也就是所说的管道机制

// ES5
function f1(...args1){
    console.log(args1);
    return {f2: function(...args2){
        console.log(args2);
        return {f3: function(...args3){
            console.log(args3);
        }};
    }};
}

var c = f1(1, 2, 3).f2(4, 5, 6).f3(7, 8, 9);		//[1, 2, 3]  [4, 5, 6]  [7, 8, 9]

// ES6
let f1 = (...args) => ({f2: (...args) => ({f3: (...args3) => {console.log(args3);}})})

var c = f1(1, 2, 3).f2(4, 5, 6).f3(7, 8, 9);	// [7, 8, 9]

2.函数参数的默认值

情况1:默认值的基本用法

不设置默认值的写法

function f(x, y){
    x = x || 'Hello';
    y = y || 'World';
    console.log(x, y);
}

f();	//Hello World
f('Lao Wang');		// Lao Wang World
f('Lao Wang', '');		//问题1:希望输出是空,实际输出Lao Wang World

为解决上面的问题1,可以通过设置默认值解决

function f2(x = 'Hello', y = 'World'){
    console.log(x, y);
}
f2('Lao Wang', '');		//Lao Wang
f2('Lao Wang');			//Lao Wang World

情况2:函数默认值的参数不能重复声明

2.1 不能声明和形参一样的变量

function f(x = 5){
    const x = 3;
}
f();	// 运行时会报错

2.2 当参数有了默认值后,不能重复声明

function f(x, x, y = 1){
    console.log(x, y);
}
f(3, 4, 5);		// 报错

想要重复声明,参数就不能赋默认值

function f(x, x, y){
    console.log(x, y);
}
f(3, 4, 5);		//4 5

2.3 参数默认值的计算是惰性的

每次调用都会重新计算参数里的表达式

let x = 9;
function f(y = x + 1){
    console.log(y);
}
f();	//10
x = 10;
f();	// 11

情况3:函数默认值与解构默认值的结合使用

function f({a = 0, b = 0} = {a: 3}){
    console.log(a, b);
}

f({a: 3, b: 4});	//3 4
f({a: 3});		//3 0
f({});			//0 0
f();			//3 0

函数默认是:a = 0, b = 0 结构默认值是:{a: 3}

情况4:实参省略的方法

在解构赋值里参数省略的方法

let [, b, c] = [1, 2, 3];
console.log(b, c);		//2 3

但是在函数里不能这样省略

function f(x, y = 2, z){
    console.log(x, y);
}
f( , 4, 3);		//错误传参
f(undefined, 4, 3);		//正确传参

情况5:函数的length:这个函数需要的必须参数的个数

console.log((function(a, b, c){}).length);	//3
console.log((function(){}).length);			//0
console.log((function(a, b, c = 0){}).length);		//2
console.log((function(a = 1, b, c = 0, d ){}).length);		//2

如果出现默认值,从默认值出现开始往后,都不算进length里

情况6:函数的name

函数的声明

function f(){
    console.log('hello');
}

console.log(f.name);		// f

函数的表达式

let f2 = function(){
    console.log('hello');
} 

console.log(f2.name);		//f2
console.log((f2.bind({})).name);	//bound f2

情况7:this的绑定和调用

call、aplay、bind的使用和区别

let obj = {
    a: 25
};

function f(a, b){
    console.log(this.a, a, b);
}

f.call(obj, 1, 2);	// 25 1 2
f.apply(obj, [1, 2]);	// 25 1 2
f.bind(obj)(1,2);		// 25 1 2

四、数组的扩展

1.扩展运算符...(rest参数)

用于数组的解构赋值

let [a, ...args] = [1, 2, 3, 4];
console.log(args);		// 1 2 3 4

用于函数参数的传递

let f = (a, ...args) => {console.log(args)};
f(1, 2, 3, 4);		//1 2 3 4

数组的逆向读取

console.log(...[1, 2, 3]);		//1 2 3 

2. ...常用于替代apply

function f(){
    console.log(arguments);
}

let args = [3, 4, 5];
f(args[0], args[1], args[2]);	// 3 4 5
f.apply(null, args);		// 3 4 5
f(...args);					// 3 4 5

应用1: 求数组里的最大值

let a = [1, 2, 3];
console.log(Math.max(1, 3, 5));		
console.log(Math.max.apply(null, a));
console.log(Math.max(...a));

应用2: 拼接两个数组

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];

Array.prototype.push.apply(arr1, arr2);
arr1.push(...arr2);
console.log(arr1);

console.log(arr1.concat(arr2));
console.log([...arr1, ...arr2]);

3.扩展的变量必须是最后一个,否则报错

let [a, ...b] = [1, 2, 3, 4];		//正确
let [a, ...b, c] = [1, 2, 3, 4];	//报错

4.字符串也可以使用扩展变量

console.log([...'hello']);	// ['h', 'h', 'l', 'o', 'o']

//想要实现同样的效果,ES5是这样操作的
var str = 'hello';
var strArr = str.split('');

5.把类数组变成数组

5.1自定义的类数组

let arrayLike = {
    '0': '1',
    '1': '25',
    '2': 'abc',
    length: 3
}

// ES5
// let array = Array.prototype.slice.call(arrayLike);
let array = [].slice.call(arrayLike);
console.log(array);		//  ["1", "25", "abc"]

// ES6
let array2 = Array.from(arrayLike);
console.log(array2);	//  ["1", "25", "abc"]

5.2 DOM NodeList

let divs = document.querySelectorAll('div');
console.log(divs.__proto__.constructor.name);	// NodeList
console.log(divs);		

let array = Array.from(divs);
console.log(array.__proto__.constructor.name);		//Array
console.log(array);

5.3 函数的 arguments

function f(){
    console.log(arguments.__proto__.constructor.name);		//object
    console.log(arguments);

    let args = Array.from(arguments);
    console.log(args.__proto__.constructor.name);		// Array
    console.log(args);
}

f(1, 2, 3, 4);

6.Array.of: 把零散的数据变为数组

let a = [1, 2, 3];		// [1, 2, 3] 
let b = new Array(3);	//长度为3,但值为空的数组
let c = Array.of(3);	// [3]
console.log(a, b, c);	// [1, 2, 3] 

7.实例实例的find(), findIndex() 写一个回调函数

find()里可以是一个函数,找到符合条件的值

let a = [1, 4, -3, 9];
console.log(a.find(x => x < 0));		// -3
console.log(a.find((value, index, arr) => {
    console.log(value, index, arr);
    return value < 0;
}));

找到符合条件值对应的索引

console.log(a.findIndex((value, index, arr) => {
    console.log(value, index, arr);
    return value < 0;
}));

找到NaN的值,这是在ES5里比较难做到的

let b = [1, 4, NaN];
console.log(b.findIndex((value, index, arr) => {
    console.log(value, index, arr);
    return Object.is(NaN, value);	
}));

需要用Object.is,不能使用isNan,因为NaN == NaN 是false

8. 数组实例的填充fill()

let a = new Array(5);
a.fill(0);	
console.log(a);		// [0, 0, 0, 0, 0]

let b = new Array(5);
b.fill(0, 1, 3);	// (填充的值,从第几个开始填充,填充到第几个之前)
console.log(b);		// [ , 0, 0, , ]

9. 数组实例的entries(), keys(), values()

遍历下标

let a = [1, 2, 3, 4];
for(let index of a.keys()){
    console.log(index);		// 0 1 2 3
}

遍历值

for(let elem of a.values()){
    console.log(elem);		// 1 2 3 4	
}

遍历下标和值

for(let [index, elem] of a.entries()){
    console.log(index, elem);		// 0 1, 1 2, 2 3, 3 4
}

逐个输出下标和值

let entries = a.entries();
console.log(entries.next().value);		// 0 1 
console.log(entries.next().value);		// 1 2

10.数组实例的includes()

let a = [1, 2, 5, NaN];
console.log(a.includes(2));		// true
console.log(a.includes(10));	// false
console.log(a.includes(NaN));	// true

比indexOf好的地方:1. 语义化, 2. NaN