Skip to content

一、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不可修改