一、var、let、const的区别
const不是变量的值不能改动,而是变量指向的哪个内存地址所保存的数据不能改动
1、变量提升
var可以在声明之前调用;let和const需要在声明后才能使用;
2、块级作用域
js
// 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
3、重复声明
var允许重复声明;let和const不允许重复声明
js
// 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
4、修改声明的变量
js
// 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
二、ES6中新增数组新增了哪些扩展
1、扩展运算符
将数组转为逗号分隔的参数序列
js
console.log(...[1,2,3,4,5]) // 1 2 3 4 5
js
// 主函数调用的时候,将一个数组变为参数序列
// 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
js
// 将某些数据结构转为数组
[...document.querySelectAll('div')]
js
// 实现数组的复制--浅拷贝
const a1 = [1,2]
const [...a2] = a1
js
// 也可以合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
js
// 和解构赋值结合,用于生成数组,需要放在会后一位,不然会报错
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
js
// 字符串转为数组
[...'hello'] // ['h', 'e', 'l', 'l', 'o']
2、构造函数新增
js
// Arrary.form,将类似数组的对象和可遍历的对象,比如Set、Map,变为真正的数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.form([1,2,3], (x) => x * x) // [1, 4, 9]
js
// Array.of,将一组值,转换为数组
Array.of(3, 11, 8) // [3, 11, 8]
3、实例对象新增
js
[1, 2, 3, 4, 5].copyWithin(0, 3, 5) // 按照索引,左闭右开
// [4, 5, 3, 4, 5]
js
[1, 5, 10, 15].find(function(value, index, arr) { // 当前值,当前索引,原数组
return value > 9;
}) // 10,返回数据
js
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2,返回索引
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);
// 26,接收第二个参数来绑定回调函数的this
js
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
['a', 'b', 'c'].fill(7, 1, 2) // 左闭右开
// ['a', 7, 'c']
js
for (let index of ['a', 'b'].keys()) { // 对键名的遍历
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) { // 对键值的遍历
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) { // 对键值对的遍历
console.log(index, elem);
}
// 0 "a"
js
// 判断是否包含给定的值
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
[1, 2, 3].includes(3, -1); // true 第二个参数代表搜索的起始位置,负数代表倒数的位置
js
// flat,对数组进行扁平化处理,返回一个新数组
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
js
// [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
4、空值处理
5、sort()排序算法稳定性
三、函数新增的扩展
1、参数设置默认值
允许为函数的入参设置默认值
js
function log(x, y = 'world') { console.log(x, y) }
js
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
2、属性
js
// length 返回没有指定默认值的参数个数
(function (a, b, c = 5) {}).length // 2
js
// name 返回该函数的函数名
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
const bar = function baz() {};
bar.name // "baz"
3、作用域
设置参数默认值之后,函数进行声明初始化的时候,参数会形成一个单独的作用域
js
let x = 1;
function f(y = x) {
// let y = x
let x = 2;
console.log(y);
}
f() // 1
4、严格模式
5、箭头函数
四、对象新增的扩展
1、属性的简写
当对象键名与对应值名相等的时候,可以简写
2、属性名表达式
js
let lastWord = 'last word';
const a = {
'first word': 'hello',
[lastWord]: 'world'
};
a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"
let obj = {
['h' + 'ello']() {
return 'hi';
}
};
obj.hello() // hi
3、super关键字
4、扩展运算符的应用
js
let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2,浅拷贝
5、属性的遍历
js
for ... in
Object.keys()
6、对象新增的方法
js
// Object.is 严格判单两个值是否相等
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
js
// Object.assign 用于对象的合并,浅拷贝到目标对象
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
js
// Object.getOwnPropertyDescriptors 返回指定对象所有自身属性的描述对象
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
js
// Object.setProtorypeOf() 设置一个对象的原型对象
Object.setPrototypeOf(object, prototype)
const o = Object.setPrototypeOf({}, null);
js
// Object.getProtorypeOf() 获取一个对象的原型对象
js
// Object.keys()
js
// Object.values()
js
// Object.entries()
js
// Object.formEntries() 将键值对数组,转为对象
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
五、理解Promise
1、promise的状态
pending(进行中)、fufilled(已成功)、rejected(已失败)
2、用法
js
const promise = new Promise(function(resolve, reject) {})
js
// .then 返回的是新的Promise,因此promise可以链式书写
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
js
// .catch
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
js
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
3、构造函数方法
js
// Promise.all(),全部为fulfilled,all()的状态才会是fulfilled
const p = Promise.all([p1, p2, p3])
js
// 参数的Promise自己定义了catch方法,一旦被reject,不会触发Promise.all的catch方法
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ['hello', Error: 报错了]
js
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result);
const p2 = new Promise((resolve, reject) => {
throw new Error(' ');
})
.then(result => result);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了
js
// Promise.race() 有一个promise的示例变化了状态,则整个的就会发生变
js
// Promise.allsettled()
js
// Promise.resolve()
Promise.resolve('foo') // 等价于
new Promise((resolve) => resolve('foo'))
js
// Promise.reject()
Promise.reject('报错了') // 等价于
new Promise((resolve, reject) => resolve('报错了'))
六、怎么理解ES6中的Module
模块是能够单独命名并独立地完整一定功能的程序语句的集合。
1、模块化
代码抽象、代码封装、代码复用、依赖管理
CommonJS、AMD、CMD
js
// AMD Asynchronous ModuleDefinition,异步模块定义
/** main.js
//
/ **/
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min", //
"underscore": "underscore.min"
}
});
require(["jquery","underscore"],function($,_){
// some code here
});
js
// CommonJS 是一套JavaScript模块规范,用于服务端
// a.js
module.exports={ foo , bar}
// b.js
const { foo,bar } = require('./a.js')
七、ES6中Generator
Generator函数是ES6提供的一种异步编程解决方案,其他异步解决方案有:回调函数、promise
1、Generator
js
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
2、使用
js
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
var c = foo(5)
c.next() // { value:6, done:false }
c.next(6) // { value:4, done:false }
c.next(4) // { value:21, done:true }
3、异步解决方案
- 回调函数
- Promise对象
- Generator函数
- async/await
js
// 回调函数
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);
});
});
js
// 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)
})
js
// Generator函数
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
js
// async、await
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
八、ES6中Decorator
Decorator,即装饰器
js
class soldier{}
function strong(target) {
target.AK = true
}
// 使用装饰器对士兵进行增强
@strong
class soldier {}
soldier.AK // true
1、类的装饰
js
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
2、类属性的装饰
对类属性进行装饰,会接收三个参数
- 类的原型对象
- 需要装饰的属性名
- 装饰属性名的描述对象
js
function readonly(target, name, descriptor){
descriptor.writable = false; //
return descriptor;
}
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
readonly(Person.prototype, 'name', descriptor)
js
function dec(id){
console.log('evaluated', id);
return (target, property, descriptor) =>console.log('executed', id);
}
class Example {
@dec(1)
@dec(2)
method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1
js
function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list);
};
}
//
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
九、ES6中Set、Map数据结构
Set是一种叫做集合的数据结构
集合是由一堆无序的、相关联的,且不重复的内存结构组成的组合
Map是一种叫做字典的数据结构
是一些元素的集合。每个元素有一个称作key的域,不同元素的key各不相同
1、Set
js
const s = new Set()
js
// add()
s.add(1).add(2).add(2) // 2只被添加了一次
// delete() 返回布尔值,表示删除是否成功
s.delete(1)
// has() 返回布尔值,判断是否为Set的成员
s.has(2)
// clear() 清除所有的成员
s.clear()
js
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"]
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
js
// 实现数组或者字符串去重
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"
js
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}
2、Map
js
const m = new Map()
js
// size()
m.set('foo', true);
m.set('bar', false);
m.size // 2
// set()
m.set('edition', 6)
m.set(262, 'standard')
m.set(undefined, 'nah')
m.set(1, 'a').set(2, 'b').set(3, 'c')
// get()
m.get(262) // standard
// has()
m.has(262) // true
m.has('years') // false
// delete()
m.delete(262)
m.has(262) // false
// clear
m.clear()
m.size() // 0
js
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);
});
3、WeekSet
4、WeekMap
十、ES6中Proxy
用于定义基本操作的自定义行为
js
var proxy = new Proxy(target, handler)
1、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(target) ,返回一个布尔值 |
getPrototypeOf(target) | 拦截Object.getPrototypeOf(target) 返回一个对象 |
isExtensible(target) | 拦截Object.isExtensible(target) ,返回一个布尔值 |
setPrototypeOf(target, proto) | 拦截Object.setPrototypeOf(target, proto) ,返回一个布尔值 |
apply(target, object, args) | 拦截Proxy实例作为函数调用的操作 |
construct(target, args) | 拦截Proxy实例作为构造函数调用的操作 |
2、Reflect
可以使用Reflect在Proxy内部调用对象的默认行为
3、get()
三个参数:目标对象、属性名、proxy实例本身
js
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
return Reflect.get(target, propKey)
}
})
proxy.name // 张三
js
// get可以对数组增删改查进行拦截,下面实现了数组读取读取负数的索引
function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver);
}
};
let target = [];
target.push(...elements);
return new Proxy(target, handler);
}
let arr = createArray('a', 'b', 'c');
arr[-1] // c
4、set()
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象,属性名,属性值和proxy实例本身
js
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的age属性以及其他属性,直接保存
obj[prop] = value;
}
};
person.age = 100;
let person = new Proxy({}, validator);
person.age // 100
person.age = 'young' //
person.age = 300
js
const obj = {};
Object.defineProperty(obj, 'foo', {
value: 'bar',
writable: false, // 目标对象的某个属性不可写不可配置,那么set方法将不起作用
});
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = 'baz';
}
};
const proxy = new Proxy(obj, handler);
proxy.foo = 'baz';
proxy.foo // "bar"
5、deleteProperty()
deleteProperty
方法用于delete
操作,如果这个方法抛出错误或者返回false
,当前属性就无法被delete
命令删除
js
var handler = {
deleteProperty: function(target, key) {
invariant(key, 'delete')
Reflect.delete(target, key)
}
}
function invariant(key, action) {
if(key[0] === '_' && action === 'delete') {
throw new Error('无法删除私有属性')
}
}
const target = { _prop: 'foo' }
const proxy = new Proxy(target, handler)
delete proxy._prop // 无法删除私有属性
6、取消代理
js
Proxy.revocable(target, handler);
7、应用
js
let api = {
_apiKey: '123abc456def',
getUsers: function () {
},
getUser: function (userId) {
},
setUser: function (userId, config) {
}
};
const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {
get(target, key, proxy) {
if (RESTRICTED.indexOf(key) > -1) {
throw Error(`${key} .`);
}
return Reflect.get(target, key, proxy);
},
set(target, key, value, proxy) {
if (RESTRICTED.indexOf(key) > -1) {
throw Error(`${key} `);
}
return Reflect.get(target, key, value, proxy);
}
});
console.log(api._apiKey) // _apiKey不可访问
api._apiKey = '987654321' // _apiKey不可修改