面试总结9.23

1、setTimeout

console.log('script start')    //1. 打印 script start
setTimeout(function(){
    console.log('settimeout')    // 4. 打印 settimeout
})    // 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end')    //3. 打印 script start
// 输出顺序:script start->script end->settimeout

2、Promise

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。

复制代码
console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout
复制代码

3、Async/Await

复制代码
async function async1(){
   console.log('async1 start');
    await async2();
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}

console.log('script start');
async1();
console.log('script end')

// 输出顺序:script start->async1 start->async2->script end->async1 end
复制代码

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。

 

2.=================================================================================================

ES6,新增数据结构WeakSet的用法

 

WeakSet和Set类似,同样是元素不重复的集合,它们的区别是WeakSet内的元素必须是对象,不能是其它类型。

特性:

1.元素必须是对象。

添加一个number类型的元素。

const ws = new WeakSet()
ws.add(1)

结果是报类型错误。

TypeError: Invalid value used in weak set

 

添加一个对象。

const ws = new WeakSet()
var a = {p1:'1', p2:'2'}
ws.add(a)
console.log(ws.has(a));

添加OK,结果显示:

true

可以通过WeakSet的has方法判断一个元素是否已在集合中。

如果不需要存储元素时,可以使用delete方法删除元素。

 

2.弱引用,不被计入垃圾回收

添加进WeakSet的元素对象,WeakSet不会对元素对像的引用计数加1,对于被添加进WeakSet的元素对象,只要该元素对象没有被除WeakSet以外的其他对象引用,就会被垃圾回收释放,在WeakSet中的该元素对象自动被释放,不会出现内存泄漏。

因为这一特性,其性能要比map要高,对于存储无顺序要求的,不重复的,临时存储的场景,可以使用它。

const ws = new WeakSet()
var a = {p1:'1', p2:'2'}

ws.add(a)
a = null
console.log(ws.has(a));

先将对象添加到WeakSet中,然后将对象设成null,然后再下面的has方法判定时,结果显示,表示WeakSet中已经不存在该对象。

false

 

3.不能遍历

因为其对内部的元素对象是弱引用,随时会被垃圾回收释放掉,所以其不支持size和forEach等遍历方法。

 

End

 

3.============================================================================================================================

01

Map代码示例

Map的键和值可以是任何数据类型,键值对按照插入顺序排列,下段代码演示了Map的一些用法:

let map = new Map();
let o = {n: 1};
map.set(o, "A"); //add
map.set("2", 9);
console.log(map.has("2")); //check if key exists
console.log(map.get(o)); //retrieve value associated with key
console.log(...map);
console.log(map);
map.delete("2"); //delete key and associated value
map.clear(); //delete everything
//create a map from iterable object
let map_1 = new Map([[1, 2], [4, 5]]);
console.log(map_1.size); //number of keys

上述代码将会输出:

true
A
[ { n: 1 }, 'A' ] [ '2', 9 ]
Map { { n: 1 } => 'A', '2' => 9 }
2

从上述代码中,我们可以看出使用new Map()语法进行声明,Map键的类型可以使用任意对象作为键(字符串,object类型),我们还可以直接以二维数组键值对的形传入到构建函数中,第一项为键,后一项为值。

 

Map中如果插入重复的键,会怎么样?如下段代码所示:

const map=new Map([['foo',1],['foo',2]]);
console.log(map);
console.log(map.get('foo'));

上述代码将会输出:

Map { 'foo' => 2 }
2

上述代码我们可以看出,如果存在相同的键,则会按照FIFO(First in First Out,先进先出)原则,后面的键值信息会覆盖前面的键值信息。

 

02

Map常用方法示例

以下表格罗列了Map相关的常用操作方法:

 

操作方法

内容描述

map.set(key,value)

添加键值对到映射中

map.get(key)

获取映射中某一个键的对应值

map.delete(key)

将某一键值对移除映射

map.clear()

清空映射中所有键值对

map.entries()

返回一个以二元数组(键值对)作为元素的数组

map.has(key)

检查映射中是否包含某一键值对

map.keys()

返回一个当前映射中所有键作为元素的可迭代对象

map.values()

返回一个当前映射中所有值作为元素的可迭代对象

map.size

映射中键值对的数量

 

增删键值对与清空MAP

let user={name:"Aaron",id:1234};
let userHobbyMap=new Map();
userHobbyMap.set(user,['Ice fishing','Family Outting']);//添加键值对
console.log(userHobbyMap);
userHobbyMap.delete(user);//删除键值对
userHobbyMap.clear(); //清空键值对
console.log(userHobbyMap);

上述代码将会输出:

Map { { name: 'Aaron', id: 1234 } => [ 'Ice fishing', 'Family Outting' ] }
Map {}

 

获取键值对

 

与Set集合对象不一样,集合对象的元素没有元素位置的标识,故没有办法获取集合某元素,但是映射对象由键值对组成,所以可以利用键来获取对应的值。

const map=new Map();
map.set('foo', 'bar');
console.log(map.get('foo')); //output bar

 

检查映射对象中是否存在某键

 

与Set集合一样,Map映射也可以使用has(键)的方法来检查是否包含某键。

const map=new Map([['foo',1]]);
console.log(map.has('foo'));//output true
console.log(map.has('bar'));//output false

 

遍历映射中的键值对

 

映射对象在设计上同样也是一种可迭代的对象,可以通过for-of循环对其遍历,同时也可以使用foreach进行遍历。

 

映射对象中带有entries()方法,用于返回包含所有键值对的可迭代的二元数组对象,而for-of和foreach便是先利用entries()方法先将映射对象转换成一个类数组对象,然年再进行迭代。

const map=new Map([['foo',1],['bar',2]]);
console.log(Array.from(map.entries()));
//output
//[ [ 'foo', 1 ], [ 'bar', 2 ] ]
for(const [key,value] of map){
console.log(`${key}:${value}`);
}
//output
//foo:1
//bar:2
map.forEach((value,key,map)=>
console.log(`${key}:${value}`))
//output
//foo:1
//bar:2

 

03

Map与Object的区别

说了这么多映射对象的方法,Map和Object对象有哪些区别呢?以下表格进行了总结:

 

对比项

映射对象Map

Object对象

存储键值对

遍历所有的键值对

检查是否包含指定的键值对

使用字符串作为键

使用Symbol作为键

使用任意对象作为键

 

可以很方便的得知键值对的数量

 

 

从中我们可以看出Map对象可以使用任何对象作为键,这就解决了我们实际应用中一个很大的痛点,比如现在有一个DOM对象作为键时,Object就不是那么好用了。

 

 

04

WeakMap

与集合类型(Set)一样,映射类型也有一个Weak版本的WeakMap。WeakMap和WeakSet很相似,只不过WeakMap的键会检查变量的引用,只要其中任意一个引用被释放,该键值对就会被删除。

 

以下三点是Map和WeakMap的主要区别:

  1. Map对象的键可以是任何类型,但WeakMap对象中的键只能是对象引用

  2. WeakMap不能包含无引用的对象,否则会被自动清除出集合(垃圾回收机制)。

  3. WeakSet对象是不可枚举的,无法获取大小。

 

下段代码示例验证了WeakMap的以上特性:

let weakmap = new WeakMap();
(function(){
let o = {n: 1};
weakmap.set(o, "A");
})(); // here 'o' key is garbage collected
let s = {m: 1};
weakmap.set(s, "B");
console.log(weakmap.get(s));
console.log(...weakmap); // exception thrown
weakmap.delete(s);
weakmap.clear(); // Exception, no such function
let weakmap_1 = new WeakMap([[{}, 2], [{}, 5]]); //this works
console.log(weakmap_1.size); //undefined”
const weakmap=new WeakMap();
let keyObject={id:1};
const valObject={score:100};
weakmap.set(keyObject, valObject);
console.log(weakmap.get(keyObject));
//output { score: 100 }
keyObject=null;
console.log(weakmap.has(keyObject));
//output false

 

05

小节

今天的内容就介绍到这里,我们明白了Map是一个键值对的映射对象,相比Object来说可以使用任何键做为键值,并且能够很方便的获取键值对。WeakMap相对于Map是一个不可枚举的对象,必须使用对象作为键值。如何更好的使用Map和WeakMap还需要具体结合我们实际的业务场景进行灵活使用。

 =====================================================================================================

4.闭包

闭包(closure)是javascript的一大难点,也是它的特色。很多高级应用都要依靠闭包来实现。

1、变量作用域

要理解闭包,首先要理解javascript的特殊的变量作用域。

变量的作用域无非就两种:全局变量和局部变量。

javascript语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。

注意点:在函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明的是一个全局变量!

2、如何从外部读取函数内部的局部变量?

出于种种原因,我们有时候需要获取到函数内部的局部变量。但是,上面已经说过了,正常情况下,这是办不到的!只有通过变通的方法才能实现。

那就是在函数内部,再定义一个函数。

function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。

这就是Javascript语言特有的"链式作用域"结构(chain scope),

子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

3、闭包的概念

上面代码中的f2函数,就是闭包。

各种专业文献的闭包定义都非常抽象,我的理解是: 闭包就是能够读取其他函数内部变量的函数。

由于在javascript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成“定义在一个函数内部的函数“。

所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

 4、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

5、闭包的优点

(1)逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑而单独编写额外逻辑。

(2)方便调用上下文的局部变量。

(3)加强封装性,是第2点的延伸,可以达到对变量的保护作用。

6、使用闭包的注意点(缺点)

(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

7、闭包的特性

(1)作为函数变量的一个引用。当函数返回时,其处于激活状态。

(2)闭包就是当一个函数返回时,并没有释放资源的栈区。

8、闭包对页面的影响

通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅、更简洁的表达出代码;在某些方面提升代码的执行效率。

9、闭包的工作原理

因为闭包只有在被调用时才执行操作,所以它可以被用来定义控制结构。多个函数可以使用同一个环境,这使得他们可以通过改变那个环境相互交流。

10、使用场景

(1)采用函数引用方式的setTimeout调用。 例子

(2)将函数关联到对象的实例方法。

(3)封装相关的功能集。

 

posted @ 2020-09-23 16:13  yaqian96  阅读(17)  评论(0)    收藏  举报