1. ES6 Proxy与Reflect
1.1 概述
Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
1.2 基本用法
1.2.1 Proxy(代理)
proxy,代理,使用代理,我们可以操作对象时,需要先经过代理,才能到达对象,这样就可以起到'拦截'的作用。
一个 Proxy 对象由两个部分组成: target 、 handler 。在通过 Proxy 构造函数生成实例对象时,需要提供这两个参数。 target 即目标对象, handler 是一个对象,声明了代理 target 的指定行为,拦截过滤代理操作的。
let target = {
name: 'zhangmeili',
age: 24
}
let handler = {
get: function(target, key) {
console.log('getting '+key);
return target[key]; // 不是target.key
},
set: function(target, key, value) {
console.log('setting '+key);
target[key] = value;
}
}
let proxy = new Proxy(target, handler)
proxy.name // 实际执行 handler.get
proxy.age = 25 // 实际执行 handler.set
总结proxy的用处:
实现拦截和监视外部对对象的访问。
降低函数和类的复杂度,优雅的写出代理代码。
在复杂操作前对操作进行校验或对所需资源进行管理。
场景:
-
抽离校验模块。
-
私有属性。
-
预警和拦截。
-
过滤操作。
-
中断代理。
1.2.2 Reflect(反射)
ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。
Reflect 对象对某些方法的返回结果进行了修改,使其更合理。
Reflect 对象使用函数的方式实现了 Object 的命令式操作。
静态方法
Reflect.get(target, key, receiver)
查找并返回 target 对象的 key属性。
let person = {
name: "zhangmeili",
age: 24,
get info(){
return this.name + this.age;
}
}
Reflect.get(person, 'name'); // "zhangmeili"
// 当 target 对象中存在 name 属性的 getter 方法, getter 方法的 this 会绑定 // receiver
let receiver = {
name: "wangshuai",
age: 20
}
Reflect.get(person, 'info', receiver); // wangshuai
// 当 name 为不存在于 target 对象的属性时,返回 undefined
Reflect.get(person, 'birth'); // undefined
// 当 target 不是对象时,会报错
Reflect.get(1, 'name'); // TypeError
let person = {
name: "zhangmeili",
age: 24,
get info() {
return this.name + this.age;
}
};
Reflect.set(person,'sex','男');
console.log(person);
总结reflect的用处
reflect有的方法object都有,es6希望数据和逻辑代码分离,那么object就是纯数据,所有的逻辑都放到reflect上,对象对某些方法的返回结果进行了修改,使其更合理。
场景:
- 当object的工具类来用。
1.2.3 组合使用
Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作。
let person = {
name: "zhangmeili",
age: 24
}
let handler = {
get: function(target, key){
console.log("getting "+key);
return Reflect.get(target,key);
},
set: function(target, key, value){
console.log("setting "+key+" to "+value)
Reflect.set(target, key, value);
}
}
let proxy = new Proxy(person, handler)
// 设置
proxy.name = "wangshuai"
//获取
console.log(proxy.name);
2. ES6 字符串
2.1 子串的识别
ES6 之前判断字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的识别方法。
-
includes():返回布尔值,判断是否找到参数字符串。
-
startsWith():返回布尔值,判断参数字符串是否在原字符串的头部。
-
endsWith():返回布尔值,判断参数字符串是否在原字符串的尾部。
以上三个方法都可以接受两个参数,需要搜索的字符串,和可选的搜索起始位置索引。
let string = "apple,banana,orange";
string.includes("banana"); // true
string.startsWith("apple"); // true
string.endsWith("apple"); // false
string.startsWith("banana",6) // true
注意:
-
这三个方法只返回布尔值,如果需要知道子串的位置,还是得用 indexOf 和 lastIndexOf 。
-
这三个方法如果传入了正则表达式而不是字符串,会抛出错误。而 indexOf 和 lastIndexOf 这两个方法,它们会将正则表达式转换为字符串并搜索它。
2.2 字符串重复
repeat():返回新的字符串,表示将字符串重复指定次数返回。
console.log("Hello,".repeat(2)); // "Hello,Hello,"
2.3 字符串补全
-
padStart(targetLength [, padString]):返回新的字符串,表示用参数字符串从头部(左侧)补全原字符串。
-
padEnd(targetLength [, padString]):返回新的字符串,表示用参数字符串从尾部(右侧)补全原字符串。
以上两个方法接受两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。如果没有指定第二个参数,默认用空格填充。
console.log("h".padStart(5,"o")); // "ooooh"
console.log("h".padEnd(5,"o")); // "hoooo"
console.log("h".padStart(5)); // " h"
2.4 模板字符串
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。
let string = `Hello'\n'world`;
console.log(string);
// "Hello'"
// "'world"
//模板字符串 ``反引号
var num = 123
var fn = function () {
return "hello"
}
let str66 = "hello \n everyone"
console.log(str66);
let str666 = `hello everyone ${num + 10} ${fn()}`
console.log(str666);
let str2=`hello`
3. ES6 数值
3.1 数值的表示(了解)
二进制表示法新写法: 前缀 0b 或 0B 。
console.log(0b11 === 3); // true
console.log(0B11 === 3); // true
八进制表示法新写法: 前缀 0o 或 0O 或0。
console.log(0o11 === 9); // true
console.log(0O11 === 9); // true
3.2 Number对象新方法
-
Number.isNaN():用于检查一个值是否为 NaN 。
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('true'/0)); // true// 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换
// 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false
Number.isNaN("NaN"); // false
Number.isNaN(undefined); // false
Number.isNaN({}); // false
Number.isNaN("true"); // false
3.2 指数运算符(了解)
1 ** 2; // 1
// 右结合,从右至左计算
2 ** 2 ** 3; // 256
// **=
let power = 2;
power **= 3; // 8
3.3 parseInt(string, radix)方法补充
parseInt(string, radix)
string | 必需。要解析的字符串。 |
---|---|
radix | 可选。代表要使用的数字系统的数字(从 2 到 36)。如果是0,radix 被假定10 (十进制) |
面试题:
["1", "2", "3"].map(parseInt) 为何返回[1,NaN,NaN]?
//解析:
arr.map(function(value,index){})
parseInt('1',0); //1
parseInt('2',1);//NaN
parseInt('3',2);//NaN
//例如
parseInt('11',16);//17
4. ES6 对象(重点)
4.1 属性的简洁表示法
const age = 18;
const name = "zhangmeili";
const person = {age, name};
console.log(person) //{age: 18, name: "zhangmeili"}
//等同于
const person = {age: age, name: name}
4.2 方法名也可以简写
const person = {
sayHi(){
console.log("Hi");
}
}
person.sayHi(); //"Hi"
//等同于
const person = {
sayHi:function(){
console.log("Hi");
}
}
person.sayHi();//"Hi"
4.3 属性名表达式
ES6允许用表达式作为属性名,但是一定要将表达式放在方括号[ ]
内。
const obj = {
["he"+"llo"](){
return "Hi";
}
}
obj.hello(); //"Hi"
注意点:属性的简洁表示法和属性名表达式不能同时使用,否则会报错。
const hello = "Hello";
const obj = {
[hello]
};
//SyntaxError: Unexpected token }
console.log(obj);
const hello = "Hello";
const obj = {
[hello+"2"]:"world"
};
//{Hello2: "world"}
console.log(obj);
4.4 扩展运算符-对象中的用法(重点)
扩展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
-
基本用法-拷贝对象
let person = {name: "zhangmeili", age: 18};
let pcopy = { ...person };
console.log(pcopy); //{name: "zhangmeili", age: 15} -
可用于合并两个对象
let age = {age: 18};
let name = {name: "zhangmeili"};
let person= {...age, ...name};
console.log(person) //{age: 18, name: "zhangmeili"}
注意:
自定义的属性和扩展运算符对象里面属性的相同的时候:自定义的属性在扩展运算符后面,则扩展运算符对象内部同名的属性将被覆盖掉。
4.5 对象的新方法
Object.is(value1, value2) 用来比较两个值是否严格相等,与(===)基本类似。
Object.is("q","q"); // true
Object.is(1,1); // true
Object.is([1],[1]); // false
Object.is({q:1},{q:1}); // false
Object.is(NaN, NaN);// true
5. ES6 数组
5.1 数组创建
-
Array.of()将参数中所有值作为元素形成数组。
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
// 参数值可为不同类型
console.log(Array.of(1, '2', true)); // [1, '2', true]
// 参数为空时返回空数组
console.log(Array.of()); // []
-
Array.from()将类数组对象或可迭代对象转化为数组。
// 浅拷贝数组
var arr=[1,2,3];
console.log(Array.from(arr)); //[1,2,3]
// 参数为数组,返回与原数组一样的数组
console.log(Array.from([1, 2])); // [1, 2]
5.2 数组的新方法
-
arr.findIndex(callback)查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
let arr = Array.of(2,5,6,-7,2);
console.log(arr.findIndex(function(value,index){
if(value < 0){
return index;
}
})); -
arr.flat([depth]) 嵌套数组转指定深度的数组。
let arr = [1,2,4,[5,6,77,[2,3,45,'你好啊']]];
arr2 = arr.flat(1);
console.log(arr2);
5.3 扩展运算符-数组中的用法(重点)
-
复制数组
let arr1 = [1, 2];
let arr2 = [...arr1];
console.log(arr2); // [1, 2] -
合并数组
let arr = [1,2,4];
let arr2 = ["你好","world",'qqqq',true];
let arr3 = [...arr,...arr2];
console.log(arr3); -
将伪数组转换为数组
var boxes = document.querySelectorAll('div');
console.log(boxes);
console.log([...boxes]);
5.4 剩余运算符和扩展运算符的区别
剩余运算符 会收集 这些集合数据,扩展运算符 是将数组或对象拆分成元素的集合 ,它们是相反的。
//剩余运算符收集数据
function fun1(a, b, ...arr1) {
console.log(arr1.length);
}
fun1(1,23,4);
//扩展运算符--合并对象/数组
let age = {age: 18};
let name = {name: "zhangmeili"};
let person= {...age, ...name};
console.log(person) //{age: 18, name: "zhangmeili"}