文章目录
- 更好的 Unicode 支持
- 更多的字符串 API
- 3-3. [扩展]正则中的粘连标记
- 模板字符串
- 3-5. [扩展]模板字符串标记
- 4-1. 参数默认值
- 使用
- [扩展]对 arguments 的影响
- [扩展]留意暂时性死区
- 4-2. 剩余参数
- 4-3. 展开运算符
- 对数组展开 ES6
- 对对象展开 ES7
- 函数柯里化
- 4-5. 明确函数的双重用途
- 4-6. 箭头函数
- 使用语法
- 注意细节
- 应用场景
- 数组的扩展
- 含义
更好的 Unicode 支持
早期,由于存储空间宝贵,Unicode 使用 16 位二进制来存储文字。我们将一个 16 位的二进制编码叫做一个码元(Code Unit)。
后来,由于技术的发展,Unicode 对文字编码进行了扩展,将某些文字扩展到了 32 位(占用两个码元),并且,将某个文字对应的二进制数字叫做码点(Code Point)。
ES6 为了解决这个困扰,为字符串提供了方法:codePointAt,根据字符串码元的位置得到其码点。
同时,ES6 为正则表达式添加了一个 flag: u,如果添加了该配置,则匹配时,使用码点匹配
更多的字符串 API
以下均为字符串的实例(原型)方法
- includes
判断字符串中是否包含指定的子字符串
- startsWith
判断字符串中是否以指定的字符串开始
- endsWith
判断字符串中是否以指定的字符串结尾
- repeat
将字符串重复指定的次数,然后返回一个新字符串。
3-3. [扩展]正则中的粘连标记
标记名:y
含义:匹配时,完全按照正则对象中的 lastIndex 位置开始匹配,并且匹配的位置必须在 lastIndex(默认为 0)位置。
模板字符串
ES6 之前处理字符串繁琐的两个方面:
- 多行字符串
- 字符串拼接
在 ES6 中,提供了模板字符串的书写,可以非常方便的换行和拼接,要做的,仅仅是将字符串的开始或结尾改为反引号 ` 符号
如果要在字符串中拼接 js 表达式,只需要在模板字符串中使用${JS表达式}
var love1 = "秋葵";
var love2 = "香菜";
var text = `邓哥喜欢${love1}
邓哥也喜欢${love2}
表达式可以是任何有意义的数据${1 + 3 * 2 / 0.5}
表达式是可以嵌套的:${`表达式中的模板字符串${love1 + love2}`}
\n\n
奥布瓦的发顺丰
在模板字符串中使用\${JS表达式}可以进行插值
`;
console.log(text);
3-5. [扩展]模板字符串标记
在模板字符串书写之前,可以加上标记:
标记名`模板字符串`;
var love1 = "秋葵";
var love2 = "香菜";
var text = myTag`邓哥喜欢${love1},邓哥也喜欢${love2}。`;
//相当于:
// var text = myTag(["邓哥喜欢", ",邓哥也喜欢", "。"], "秋葵", "香菜")
function myTag(parts) {
console.log(parts);
}
console.log(text);
标记是一个函数,函数参数如下:
- 参数 1:被插值分割的字符串数组
- 后续参数:所有的插值
模板字符串的实现原理:
var love1 = "秋葵";
var love2 = "香菜";
var text = myTag`邓哥喜欢${love1},邓哥也喜欢${love2}。`;
function myTag(parts) {
const values = Array.prototype.slice.apply(arguments).slice(1);
// console.log(values);
let str = "";
for (let i = 0; i < values.length; i++) {
str += `${parts[i]}:${values[i]}`;
if (i === values.length - 1) {
str += parts[i + 1];
}
}
return str;
}
4-1. 参数默认值
使用
在书写形参时,直接给形参赋值,附的值即为默认值
这样一来,当调用函数时,如果没有给对应的参数赋值(给它的值是 undefined),则会自动使用默认值。
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
参数变量是默认声明的,所以不能用let或const再次声明。
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
上面代码中,参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。
与解构赋值默认值结合使用
参数默认值可以与解构赋值的默认值,结合起来使用。
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
[扩展]对 arguments 的影响
只要给函数加上参数默认值,该函数会自动变量严格模式下的规则:arguments 和形参脱离
[扩展]留意暂时性死区
形参和 ES6 中的 let 或 const 声明一样,具有作用域,并且根据参数的声明顺序,存在暂时性死区。
4-2. 剩余参数
arguments 的缺陷:
- 如果和形参配合使用,容易导致混乱
- 从语义上,使用 arguments 获取参数,由于形参缺失,无法从函数定义上理解函数的真实意图
ES6 的剩余参数专门用于收集末尾的所有参数,将其放置到一个形参数组中。
语法:
function (...形参名){
}
细节:
- 一个函数,仅能出现一个剩余参数
- 一个函数,如果有剩余参数,剩余参数必须是最后一个参数
4-3. 展开运算符
使用方式:...要展开的东西
/**
* 对所有数字求和
* @param {...any} args
*/
function sum(...args) {
let sum = 0;
for (let i = 0; i < args.length; i++) {
sum += args[i];
}
return sum;
}
/**
* 获取一个指定长度的随机数组成的数组
* @param {*} length
*/
function getRandomNumbers(length) {
const arr = [];
for (let i = 0; i < length; i++) {
arr.push(Math.random());
}
return arr;
}
const numbers = getRandomNumbers(10);
//将数组的每一项展开,依次作为参数传递,而不是把整个数组作为一个参数传递
// sum(numbers)
console.log(sum(...numbers)); //相当于传递了10个参数
console.log(sum(1, 3, ...numbers, 3, 5));
对数组展开 ES6
const arr1 = [3, 67, 8, 5];
//克隆arr1数组到arr2
const arr2 = [0, ...arr1, 1];
console.log(arr2, arr1 === arr2);
// [0, 3, 67, 8, 5, 1] false
对对象展开 ES7
const obj1 = {
name: "成哥",
age: 18,
love: "邓嫂",
address: {
country: "中国",
province: "黑龙江",
city: "哈尔滨",
},
};
// 浅克隆到obj2
const obj2 = {
...obj1,
name: "邓哥",
};
console.log(obj2);
console.log(obj1.address === obj2.address); //true
// {name: '邓哥', age: 18, love: '邓嫂', address: {…}}address: {country: '中国', province: '黑龙江', city: '哈尔滨'}age: 18love: "邓嫂"name: "邓哥"[[Prototype]]: Object
// true
函数柯里化
function cal(a, b, c, d) {
return a + b * c - d;
}
//curry:柯里化,用户固定某个函数的前面的参数,得到一个新的函数,新的函数调用时,接收剩余的参数
function curry(func, ...args) {
//第一个参数是函数,第二个参数是乘余参数
return function (...subArgs) {
//返回一个函数(高阶函数),接收乘余参数
const allArgs = [...args, ...subArgs]; //拼接参数
if (allArgs.length >= func.length) {
//判断参数与函数参数个数
return func(...allArgs); //参数够了,执行函数
} else {
//参数不够,继续固定
return curry(func, ...allArgs);
}
};
}
const newCal = curry(cal, 1, 2);
console.log(newCal(3, 4)); // 1+2*3-4
console.log(newCal(4, 5)); // 1+2*4-5
console.log(newCal(5, 6)); // 1+2*5-6
console.log(newCal(6, 7)); // 1+2*6-7
4-5. 明确函数的双重用途
ES6 提供了一个特殊的 API,可以使用该 API 在函数内部,判断该函数是否使用了 new 来调用
new.target
//该表达式,得到的是:如果没有使用new来调用函数,则返回undefined
//如果使用new调用函数,则得到的是new关键字后面的函数本身
function Person(firstName, lastName) {
//判断是否是使用new的方式来调用的函数
// //过去的判断方式
// if (!(this instanceof Person)) {
// throw new Error("该函数没有使用new来调用")
// }
if (new.target === undefined) {
throw new Error("该函数没有使用new来调用")
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
const p1 = new Person("袁", "进");
console.log(p1)
4-6. 箭头函数
回顾:this 指向
- 通过对象调用函数,this 指向对象
- 直接调用函数,this 指向全局对象
- 如果通过 new 调用函数,this 指向新创建的对象
- 如果通过 apply、call、bind 调用函数,this 指向指定的数据
- 如果是 DOM 事件函数,this 指向事件源
使用语法
箭头函数是一个函数表达式,理论上,任何使用函数表达式的场景都可以使用箭头函数
完整语法:
(参数1, 参数2, ...)=>{
//函数体
}
如果参数只有一个,可以省略小括号
(参数) => {};
const sum = (a, b) => ({
a: a,
b: b,
sum: a + b,
});
console.log(sum(3, 5));
如果箭
注意细节
- 箭头函数的函数体中的 this,取决于箭头函数定义的位置的 this 指向,而与如何调用无关
- 箭头函数中,不存在 this、arguments、new.target,如果使用了,则使用的是函数外层的对应的 this、arguments、new.target
- 箭头函数没有原型
- 箭头函数不能作用构造函数使用
应用场景
- 临时性使用的函数,并不会可以调用它,比如:
- 事件处理函数
- 异步处理函数
- 其他临时性的函数
- 为了绑定外层 this 的函数
- 在不影响其他代码的情况下,保持代码的简洁,最常见的,数组方法中的回调函数
const obj = {
count: 0,
start: function () {
setInterval(() => {
this.count++;
console.log(this);
}, 1000);
},
regEvent: function () {
window.onclick = () => {
console.log(this);
};
},
// 这里this不用解决
print: function () {
console.log(this);
console.log(this.count);
},
};
// 在函数里面用函数出现this指向问题
obj.start();
obj.regEvent();
obj.print();
数组的扩展
- 扩展运算符
- Array.from()
- Array.of()
- 数组实例的 copyWithin()
- 数组实例的 find() 和 findIndex()
- 数组实例的 fill()
- 数组实例的 entries(),keys() 和 values()
- 数组实例的 includes()
- 数组实例的 flat(),flatMap()
- 数组的空位
- Array.prototype.sort() 的排序稳定性
含义
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
(...[1, 2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number
console.log(...[1, 2])
// 1 2