金三银四求职季-前端面试 - JS总结(2) - ES6 (let, 箭头函数, this) 作用域 闭包

124 阅读9分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、 ES6新特性

let const \ var

模板字面量和类的支持 简洁

箭头函数 简化了函数的书写

Promise 处理异步操作的一种模式

字符串模板(反引号) Hello ${name} !

For of值遍历 每次循环它提供的不是序号而是值。

块作用域

加强的对象字面

对象解构

模块

Symbol

代理(proxy)Set

二、let 和 const

es5有两种声明变量的方法:var, function

es6有6种声明变量的方法:var, function, let, const, import, class

1. var 和 let区别

块级作用域:let定义的变量有块级作用域,var声明的变量只有全局和函数作用域。

变量提升:let不存在变量提升,var存在变量提升。

重复声明:let不允许重复声明,var可以重复声明。

暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。

2. var和let 和const区别

(1)Var是ES5语法;var没有块的概念,可以跨块访问, 不能跨函数访问;var有变量提升(变量可以在使用后声明,也就是变量可以先使用再声明);

(2)let const是ES6语法;Let const有块级作用域,var没有,Var和let是变量可以修改;

(3)const是常量,不可修改,而且必须初始化,只能在块作用域里访问;不可修改指的是:const指针指向的地址不可以改变,指向地址的内容是可以改变的。因为const只是保证对象的指针不改变,而对象的内容改变不会影响到指针的改变,所以对象的属性内容是可以修改的

(4)let和const存在暂时性死区,var没有

三、this的指向

3. This

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁

(1)作为普通函数调用 -- 返回window

(2)使用call apply bind被调用 -- 传入什么绑定什么

(3)作为对象方法被调用 -- 返回对象本身(如果是隐式原型的方法 -- undefined)

(4)在class方法中被调用 -- 返回当前实例本身

(5)箭头函数 -- 永远会找上级作用域this的值来确定

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,

理解参考链接:www.cnblogs.com/pssp/p/5216…

4. this的规则优先级

首先是函数作为构造器被调用,指向创建的对象

其次是call、apply和bind,指向第一次bind的对象

然后是作为对象的方法被调用,指向该对象

最后是作为普通函数被调用,指向window

5. 函数定义的方法

参考链接:segmentfault.com/a/119000001…

四、箭头函数

6. 什么是箭头函数

使用ES6箭头函数语法定义函数,将原函数的“function”关键字和函数名都删掉,并使用“=>”连接参数列表和函数体。当函数参数只有一个,括号可以省略;但是没有参数时,括号不可以省略。箭头函数完全修复了this的指向,this总是指向词法作用域。 箭头函数没有自己的this,总是指向外部作用域

箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种只包含一个表达式,省略掉了{ ... }和return。还有一种可以包含多条语句,这时候就不能省略{ ... }和return

箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。(词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变 。)

funcName(2);// 4

7. 箭头函数与普通函数的区别

普通函数的声明在变量提升中是最高的,箭头函数没有函数提升

箭头函数没有属于自己的this,arguments

箭头函数不能作为构造函数,因为没有自己的this,没有prototype属性

箭头函数不可以使用 yield 命令

8. 箭头函数的优缺点

优点:ES6中箭头函数给我们带来了便利,写法很简洁,不用再定义this,因为箭头函数的this取决于上一级作用域。

缺点:this被定义好了,太简洁有时候难以阅读。不推荐使用this的场景:在对象中定义方法时, 在原型上定义方法时, 在构造函数内,需要动态上下文的地方

什么时候不需要使用箭头函数 参考链接

五、Bind call apply

9. 相似之处

它们的第一个参数都是this要指向的对象;

都是用来改变函数的this指向的;

都可以利用后继参数传参;

10. 区别

执行方面:使用call和apply之后函数可自动执行,但是使用bind需要手动执行;

参数接收方面: call和apply接收的第一个参数是在其中运行函数的作用域,不同的是在接收的第二个参数时,apply接收的是一个数组,而call接收的是参数列表,需要跟函数保持一一对应。

11. 应用场景

如果不需要关心具体有多少参数被传入函数的时候(或者参数较多情况下),可以选用apply;

如果确定函数可以接收多少个参数(或参数较少情况下),并且想一目了然的表达形参和实参的对象关系的,可以选用call;

如果想将来再调用方法,不需要立即得到函数的返回结果的,可以选用bind;

手写代码题目链接:blog.csdn.net/Sabrina_cc/…

六、作用域与闭包

12. 闭包定义

定义:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。即有权访问另一个函数作用域变量的函数都是闭包。 意义就是让我们可以间接访问函数内部的变量。闭包是块作用域(解决异步循环输出相同问题)

一个变量在当前作用域没有定义,但是被使用了,会向上级作用域,一层一层依次寻找,直到找到为止,如果到全局作用域都没有找到,则报错xx is not defined

机制:当我们调用一个闭包函数,在函数执行时,其上下文有个Scope属性,该属性作为一个作用域链包含有该函数被定义时所有外层的变量对象的引用,所以定义了闭包的函数虽然销毁了,但是其变量对象依然被绑定在函数inner上,保留在内存中。

影响:变量会常驻内存,得不到释放。因此闭包不要乱用,可能会影响性能

注意:所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方

13. 三级作用域

全局作用域(全局都可以使用,如document对象)函数作用域(一个函数中定义的只能在当前函数使用)块级作用域(ES新增)(块:包含if while等包含大括号,在大括号外使用会报错)

14. 闭包应用场景

函数作为参数被传递(函数在一个地方定义好之后,到另一个地方去执行)

函数作为返回值被返回(函数定义好之后会被返回到另一个地方执行)

15. 哪些操作会造成内存泄漏?

1.意外的全局变量 。由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。

2.被遗忘的计时器或回调函数。 设置了setInterval定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。

3.脱离 DOM 的引用。 获取一个DOM元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。

4.闭包 不合理的使用闭包,从而导致某些变量一直被留在内存当中。

16. 作用域与作用域链

作用域: 作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找。

作用域链: 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和 函数。

作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前 端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。

当我们查找一个变量时,如果当前执行环境中没有找到,我们可以沿着作用域链向后查找。

17. 作用域和执行上下文

作用域是在函数声明的时候就确定的一套变量访问规则,而执行上下文是函数执行时才产生的一系列变量的环境

也就是说,作用域定义了执行上下文中的变量的访问规则

作用域在函数定义时就已经确定,而执行上下文在每次执行时都重新确定

七、其他

18. Set的特点是什么?

Set的值都是唯一的

Set可以接收一个数组,实现对数组的去重

Set认为NaN等于自身

Set验证:两个对象总是不相等的