前言
本篇文章是作者在准备春招时学习ES6总结的,如果你对文章中的问题有着更好的解答,欢迎在评论区讨论。假如您也和我一样,在准备春招。欢迎加我微信shunwuyu ,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!”
1. let、const、var的区别
var、let、const三者区别可以围绕下面五点展开:
- 变量提升
- 暂时性死区
- 块级作用域
- 重复声明
- 修改声明的变量
- 使用
变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
// var
console.log(a) // undefined
var a = 10
// let
console.log(b) // Cannot access 'b' before initialization
let b = 10
// const
console.log(c) // Cannot access 'c' before initialization
const c = 10
暂时性死区
var不存在暂时性死区。let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
// var
console.log(a) // undefined
var a = 10
// let
console.log(b) // Cannot access 'b' before initialization
let b = 10
// const
console.log(c) // Cannot access 'c' before initialization
const c = 10
块级作用域
var不存在块级作用域,let和const存在块级作用域。
// var
{
var a = 20
}
console.log(a) // 20
// let
{
let b = 20
}
console.log(b) // Uncaught ReferenceError: b is not defined
// const
{
const c = 20
}
console.log(c) // Uncaught ReferenceError: c is not defined
重复声明
var允许重复声明变量,let和const在同一作用域不允许重复声明变量。
// var
var a = 10
var a = 20 // 20
// let
let b = 10
let b = 20 // Identifier 'b' has already been declared
// const
const c = 10
const c = 20 // Identifier 'c' has already been declared
修改声明的变量
var和let可以修改,const声明一个只读的常量。一旦声明,常量的值就不能改变。
// var
var a = 10
a = 20
console.log(a) // 20
//let
let b = 10
b = 20
console.log(b) // 20
// const
const c = 10
c = 20
console.log(c) // Uncaught TypeError: Assignment to constant variable
使用
能用const的情况尽量使用const,其他情况下大多数使用let,避免使用var。
2. const对象的属性可以修改吗
const保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。 对于基本类型的数据(数值、字符串、布尔值),其值就保存在变量指向的那个内存地址,因此等同于常量。但对于引用类型的数据(主要是对象和数组)来说,变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。
3. 箭头函数与普通函数的区别
(1)箭头函数比普通函数更加简洁
- 如果没有参数,就直接写一个空括号即可
- 如果只有一个参数,可以省去参数的括号
- 如果有多个参数,用逗号分割
- 如果函数体的返回值只有一句,可以省略大括号
(2)箭头函数没有自己的this
箭头函数不会创建自己的this, 所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了,之后不会改变。
(3)箭头函数继承来的this指向永远不会改变
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id);
}
};
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
new obj.a() // undefined
new obj.b() // Uncaught TypeError: obj.b is not a constructor
对象obj的方法b是使用箭头函数定义的,这个函数中的this就永远指向它定义时所处的全局执行环境中的this,即便这个函数是作为对象obj的方法调用,this依旧指向Window对象。需要注意,定义对象的大括号{}是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中。
(4)call()、apply()、bind()等方法不能改变箭头函数中this的指向
var id = 'Global';
let fun1 = () => {
console.log(this.id)
};
fun1(); // 'Global'
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'
(5)箭头函数不能作为构造函数使用
由于箭头函数没有自己的this的,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。
(6)箭头函数没有自己的arguments
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。
(7)箭头函数没有prototype
(8)箭头函数不能用作Generator函数,不能使用yeild关键字
4. 扩展运算符
(1)对象扩展运算符
对象的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。
let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }
上述方法实际上等价于:
let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。(如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性)。
同样,如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}
扩展运算符对对象实例的拷贝属于浅拷贝。
(2)数组扩展运算符
数组的扩展运算符可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。
console.log(...[1, 2, 3])
// 1 2 3
console.log(...[1, [2, 3, 4], 5])
// 1 [2, 3, 4] 5
下面是数组的扩展运算符的应用:
- 将数组转换为参数序列
function add(x, y) {
return x + y;
}
const numbers = [1, 2];
add(...numbers) // 3
- 复制数组
const arr1 = [1, 2];
const arr2 = [...arr1];
- 合并数组
const arr1 = ['two', 'three'];
const arr2 = ['one', ...arr1, 'four', 'five'];
// ["one", "two", "three", "four", "five"]
- 将字符串转为数组
[...'hello'] // [ "h", "e", "l", "l", "o" ]
- 任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组
// arguments对象
function foo() {
const args = [...arguments];
}
用于替换es5中的Array.prototype.slice.call(arguments)写法。
5. ES6中模板语法与字符串处理
ES6 提出了“模板语法”的概念。在 ES6 以前,拼接字符串是很麻烦的事情:
var name = 'css'
var career = 'coder'
var hobby = ['coding', 'writing']
var finalString = 'my name is ' + name + ', I work as a ' + career + ', I love ' + hobby[0] + ' and ' + hobby[1]
仅仅几个变量,写了这么多加号,还要时刻小心里面的空格和标点符号有没有跟错地方。但是有了模板字符串,拼接难度直线下降:
var name = 'css'
var career = 'coder'
var hobby = ['coding', 'writing']
var finalString = `my name is ${name}, I work as a ${career} I love ${hobby[0]} and ${hobby[1]}`
字符串不仅更容易拼了,也更易读了,代码整体的质量都变高了。这就是模板字符串的第一个优势——允许用${}的方式嵌入变量。但这还不是问题的关键,模板字符串的关键优势有两个:
- 在模板字符串中,空格、缩进、换行都会被保留
- 模板字符串完全支持“运算”式的表达式,可以在${}里完成一些计算
在模板字符串里无障碍地直接写 html 代码:
let list = `
<ul>
<li>列表项1</li>
<li>列表项2</li>
</ul>
`;
console.log(message); // 正确输出,不存在报错
把一些简单的计算和调用丢进 ${} 来做:
function add(a, b) {
const finalString = `${a} + ${b} = ${a+b}`
console.log(finalString)
}
add(1, 2) // 输出 '1 + 2 = 3'
除了模板语法外, ES6中还新增了一系列的字符串方法用于提升开发效率:
(1)存在判定:在过去,当判断一个字符/字符串是否在某字符串中时,只能用 indexOf > -1 来做。现在 ES6 提供了三个方法:includes、startsWith、endsWith,它们都会返回一个布尔值来告诉你是否存在。
- includes:判断字符串与子串的包含关系:
const son = 'haha'
const father = 'xixi haha hehe'
father.includes(son) // true
- startsWith:判断字符串是否以某个/某串字符开头:
const father = 'xixi haha hehe'
father.startsWith('haha') // false
father.startsWith('xixi') // true
- endsWith:判断字符串是否以某个/某串字符结尾:
const father = 'xixi haha hehe'
father.endsWith('hehe') // true
(2)自动重复:可以使用 repeat 方法来使同一个字符串输出多次(被连续复制多次):
const sourceCode = 'repeat for 3 times;'
const repeated = sourceCode.repeat(3)
console.log(repeated) // repeat for 3 times;repeat for 3 times;repeat for 3
6.Set和Map
一、Set
Set是es6新增的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值,我们一般称为集合。
Set本身是一个构造函数,用来生成 Set 数据结构
const s = new Set();
增删改查
Set的实例关于增删改查的方法:
- add()
- delete()
- has()
- clear()
add()
添加某个值,返回 Set 结构本身。
当添加实例中已经存在的元素,set不会进行处理添加
s.add(1).add(2).add(2); // 2只被添加了一次
delete()
删除某个值,返回一个布尔值,表示删除是否成功。
s.delete(1)
has()
返回一个布尔值,判断该值是否为Set的成员。
s.has(2)
clear()
清除所有成员,没有返回值。
s.clear()
遍历
Set实例遍历的方法有如下:
关于遍历的方法,有如下:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
Set的遍历顺序就是插入顺序
keys方法、values方法、entries方法返回的都是遍历器对象。
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
forEach()用于对每个成员执行某种操作,没有返回值,键值、键名都相等,同样的forEach方法有第二个参数,用于绑定处理函数的this。
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
扩展运算符和Set 结构相结合实现数组或字符串去重。
// 数组
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)]; // [3, 5, 2]
// 字符串
let str = "352255";
let unique = [...new Set(str)].join(""); // "352"
实现并集、交集、和差集。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
二、Map
Map类型是键值对的有序列表,相比对象而言键和值都可以是任意类型。
Map本身是一个构造函数,用来生成 Map 数据结构
const m = new Map()
增删改查
Map 结构的实例针对增删改查有以下属性和操作方法:
- size 属性
- set()
- get()
- has()
- delete()
- clear()
size
size属性返回 Map 结构的成员总数。
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
set()
设置键名key对应的键值为value,然后返回整个 Map 结构,如果key已经有值,则键值会被更新,否则就新生成该键。
同时返回的是当前Map对象,可采用链式写法。
const m = new Map();
m.set('edition', 6) // 键是字符串
m.set(262, 'standard') // 键是数值
m.set(undefined, 'nah') // 键是 undefined
m.set(1, 'a').set(2, 'b').set(3, 'c') // 链式操作
get()
get方法读取key对应的键值,如果找不到key,返回undefined。
const m = new Map();
const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 键是函数
m.get(hello) // Hello ES6!
has()
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
const m = new Map();
m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');
m.has('edition') // true
m.has('years') // false
m.has(262) // true
m.has(undefined) // true
delete()
delete方法删除某个键,返回true。如果删除失败,返回false。
const m = new Map();
m.set(undefined, 'nah');
m.has(undefined) // true
m.delete(undefined)
m.has(undefined) // false
clear()
clear方法清除所有成员,没有返回值。
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0
遍历
Map结构原生提供三个遍历器生成函数和一个遍历方法:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回所有成员的遍历器
- forEach():遍历 Map 的所有成员
遍历顺序就是插入顺序。
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
三、WeakSet 和 WeakMap
WeakSet
创建WeakSet实例
const ws = new WeakSet();
WeakSet可以接受一个具有 Iterable接口的对象作为参数
const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}
在API中WeakSet与Set有两个区别:
- 没有遍历操作的
API - 没有
size属性
WeakSet只能成员只能是引用类型,而不能是其他类型的值
let ws=new WeakSet();
// 成员不是引用类型
let weakSet=new WeakSet([2,3]);
console.log(weakSet) // 报错
// 成员为引用类型
let obj1={name:1}
let obj2={name:1}
let ws=new WeakSet([obj1,obj2]);
console.log(ws) //WeakSet {{…}, {…}}
WeakSet里面的引用只要在外部消失,它在 WeakSet里面的引用就会自动消失。
WeakMap
WeakMap结构与Map结构类似,也是用于生成键值对的集合。
在API中WeakMap与Map有两个区别:
- 没有遍历操作的
API - 没有
clear清空方法
// WeakMap 可以使用 set 方法添加成员
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 2);
wm1.get(key) // 2
// WeakMap 也可以接受一个数组,
// 作为构造函数的参数
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
const map = new WeakMap();
map.set(1, 2)
// TypeError: 1 is not an object!
map.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
map.set(null, 2)
// TypeError: Invalid value used as weak map key
WeakMap的键名所指向的对象,一旦不再需要,里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
举个场景例子:
在网页的 DOM 元素上添加数据,就可以使用WeakMap结构,当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
7.Promise
Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大,
优点:
- 链式操作减低了编码难度
- 代码可读性明显增强
下面我们正式来认识promise:
状态
promise对象仅有三种状态
pending(进行中)fulfilled(已成功)rejected(已失败)
特点
- 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态。
- 一旦状态改变(从
pending变为fulfilled和从pending变为rejected),就不会再变,任何时候都可以得到这个结果。
流程
认真阅读下图,我们能够轻松了解promise整个流程
用法
Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise(function(resolve, reject) {});
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”
实例方法
Promise构建出来的实例存在以下方法:
- then()
- catch()
- finally()
then()
then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数。
then方法返回的是一个新的Promise实例,也就是promise能链式书写的原因
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
catch()
catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
Promise对象抛出的错误不会传递到外层代码,即不会有任何反应
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程。
finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
构造函数方法
Promise构造函数存在以下方法:
- all()
- race()
- allSettled()
- resolve()
- reject()
- try()
all()
Promise.all()方法用于将多个 Promise实例,包装成一个新的 Promise实例
const p = Promise.all([p1, p2, p3]);
接受一个数组(迭代对象)作为参数,数组成员都应为Promise实例。
实例p的状态由p1、p2、p3决定,分为两种:
-
只有
p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数 -
只要
p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
race()
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.race([p1, p2, p3]);
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。率先改变的 Promise 实例的返回值则传递给p的回调函数。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
allSettled()
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例,只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
resolve()
将现有对象转为 Promise对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
参数可以分成四种情况,分别如下:
- 参数是一个 Promise 实例,
promise.resolve将不做任何修改、原封不动地返回这个实例 - 参数是一个
thenable对象,promise.resolve会将这个对象转为Promise对象,然后就立即执行thenable对象的then()方法 - 参数不是具有
then()方法的对象,或根本就不是对象,Promise.resolve()会返回一个新的 Promise 对象,状态为resolved - 没有参数时,直接返回一个
resolved状态的 Promise 对象
reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
Promise.reject()方法的参数,会原封不动地变成后续方法的参数
Promise.reject('出错了')
.catch(e => {
console.log(e === '出错了')
})
// true
8.异步解决方案有哪些
异步解决的方案共有以下几种:
- 回调函数
- Promise 对象
- generator 函数
- async/await
回调函数
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数。
fs.readFile('/etc/fstab', function (err, data) {
if (err) throw err;
console.log(data);
fs.readFile('/etc/shells', function (err, data) {
if (err) throw err;
console.log(data);
});
});
Promise
Promise就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用。
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
readFile('/etc/fstab').then(data =>{
console.log(data)
return readFile('/etc/shells')
}).then(data => {
console.log(data)
})
这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强。
generator
yield表达式可以暂停函数执行,next方法用于恢复函数执行,这使得Generator函数非常适合将异步任务同步化。
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async/await
将上面Generator函数改成async/await形式,更为简洁,语义化更强了。
const ReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
区别
通过上述代码进行分析,将promise、Generator、async/await进行比较:
promise和async/await是专门用于处理异步操作的。Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口等等)。promise编写代码相比Generator、async更为复杂化,且可读性也稍差。Generator、async需要与promise对象搭配处理异步情况。async实质是Generator的语法糖,相当于会自动执行Generator函数。async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案。
9. Proxy
定义: 用于定义基本操作的自定义行为
本质: 修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming)
Proxy用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
用法
Proxy为 构造函数,用来生成 Proxy实例
var proxy = new Proxy(target, handler)
参数
target表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数,甚至另一个代理))
handler通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为
handler解析
关于handler拦截属性,有如下:
- get(target,propKey,receiver):拦截对象属性的读取
- set(target,propKey,value,receiver):拦截对象属性的设置
- has(target,propKey):拦截
propKey in proxy的操作,返回一个布尔值 - deleteProperty(target,propKey):拦截
delete proxy[propKey]的操作,返回一个布尔值 - ownKeys(target):拦截
Object.keys(proxy)、for...in等循环,返回一个数组 - getOwnPropertyDescriptor(target, propKey):拦截
Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象 - defineProperty(target, propKey, propDesc):拦截
Object.defineProperty(proxy, propKey, propDesc),返回一个布尔值 - preventExtensions(target):拦截
Object.preventExtensions(proxy),返回一个布尔值 - getPrototypeOf(target):拦截
Object.getPrototypeOf(proxy),返回一个对象 - isExtensible(target):拦截
Object.isExtensible(proxy),返回一个布尔值 - setPrototypeOf(target, proto):拦截
Object.setPrototypeOf(proxy, proto),返回一个布尔值 - apply(target, object, args):拦截 Proxy 实例作为函数调用的操作
- construct(target, args):拦截 Proxy 实例作为构造函数调用的操作
10.Reflect
若需要在Proxy内部调用对象的默认行为,建议使用Reflect,其是ES6中操作对象而提供的新 API。
基本特点:
- 只要
Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在。 - 修改某些
Object方法的返回结果,让其变得更合理(定义不存在属性行为的时候不报错而是返回false) - 让
Object操作都变成函数行为
11.为什么需要模块化
- 代码抽象
- 代码封装
- 代码复用
- 依赖管理
如果没有模块化,我们代码会怎样?
- 变量和方法不容易维护,容易污染全局作用域。
- 加载资源的方式通过script标签从上到下。
- 依赖的环境主观逻辑偏重,代码较多就会比较复杂。
- 大型项目资源难以维护,特别是多人合作的情况下,资源的引入会让人奔溃。
因此,需要一种将JavaScript程序模块化的机制,如
- CommonJs (典型代表:node.js早期)
- AMD (典型代表:require.js)
- CMD (典型代表:sea.js)