关于面试题的总结 之 ES6

ES6 平时开发中经常使用,但是部分基础定义时常被忽略,借此,用笔记的方式记录一下,若有不对的地方,还请大家指出,谢谢!

let const var 的区别

变量的作用域,指的是,在编写的算法函数中,我们能访问变量(在使用函数作用域时,也可以是一个函数)的地方. 有局部变量和全局变量两种。

let const var 的区别

  • letconst 定义的的变量会常量是具有块级作用域的。
  • var 定义的变量没有块级作用域的概念,但是会有变量提升,即 可在变量声明之前使用变量,letconst 则不行。
  • constlet 声明的常量或变量不能再被重复声明(在同一个作用域内),var 可以重复声明变量,以最后定义的变量覆盖之前的原则定义。
  • const 定义常量,定义的时候就要赋初值,否则报错。let,var初始化时可以不赋初值。
  • const 定义的基本数据类型的常量不能被修改值,定义引用数据类型的常量引用数据的内部属性值是可以修改值的。

扩展问题 1 : JS有哪些作用域?什么是块级作用域?

JS中作用域采用了此法作用域机制。 包含有 全局作用域, 函数作用域块级作用域

全局作用域

即 window 全局作用域

函数作用域

一个 function就是一个作用与空间页脚局部作用域,局部作用于内的变量不予许在外部访问。

function中的嵌套结构当嵌套之后在当前作用域中无法找到某个变量的时候,编译引擎就会在外层嵌套作用域中继续查找(父级作用域指的是定义时的父级作用域并非运行时),知道找到这个变量。或是抵达最外层的作用域(也就是全局作用域)为止。

这个逐层向上查找机制就是作用域链的查找规则

如果在最外层也没找到该变量, 严格模式下会抛出异常, 非严格模式会在最外层(全局)定义该变量

块级作用域

使用let ,const 定义的变量或常量。会形成块级作用域。
块级作用域 指的是 在一个作用域空间中通过let ,const 声明的变量在该作用域外是不可见的。

常见的 function ,if , for , try/catch 都会形成一个作用域块级作用域。
注意: for 的小括号也是一个独立的作用域空间。

扩展问题 2 :什么是变量提升?

个人理解的是变量function声明之前就能使用的现象。

举个例子:

// 变量定义

console.log(a); //  undefined
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c);  //  Cannot access 'x1' before initialization

var a = 1;
let b = 2;
const c = 3;

var 定义的变量会变量提升,let ,const 进行的声明则不会

// 函数提升

console.log(fn1); //  undefined
console.log(fn2); //  ƒ fn2() {}

var fn1 = function() {};
function fn2() {}

由上可知,通过 function 关键字顶一个函数会被提升,但是函数表达式不会被提升

var num = 1;
function fn3() {
    console.log(num); //  undefined
    var num = 0;
}

fn3();
console.log(num); //  1

每个作用域里面都有提升操作,声明会被提升到所在作用域的顶部

console.log(g) // function g(){}
var g = 1
function g(){}
console.log(g) // 1

console.log(g) // function g(){}
function g(){}
var g = 1
console.log(g) // 1

函数function关键字提升的优先级要高于变量var关键字。
所以第一个console.log(g)打印出了g()方法,当代码继续向下运行遇到function g(){}g()函数已经声明过了不做任何处理,而是被 g=1 的赋值操作给覆盖,所以后面一个console.log(g)打印出了1

如果变量或函数有重复声明会以最后一次声明为主。

所以,变量和函数提升的特点是:

  • 通过var定义的变量会提升,而letconst进行的声明不会提升。
  • 通过function关键字定义的函数会被提升,而函数表达式则不会提升。
  • var声明本身会被提升,但包括函数表达式在内的赋值操作并不会提升
  • 每个作用域都会进行提升操作,声明会被提升到所在作用域的顶部
  • 函数function关键字提升的优先级要高于变量var关键字。

参考: JS的作用域

Set、Map、WeakSet和WeakMap的区别

Set、Map、WeakSet和WeakMap的详细说明以及使用
Set 和 Map 数据结构

Set

  • 是一种类数组的数据结构,成员的值都是唯一的,没有重复值
  • 接受一个数组(或类似数组的对象)作为参数
  • 可以遍历,遍历顺序就是插入顺序

WeakSet 结构类似Set, 也是不重复的值的集合

WeakSet 和 Set 的区别

  • WeakSet的成员只能是对象,而不能是其他类型的值
  • WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的,也没有size属性。

WeakSet的一个用处,是储存DOM节点,而不用担心这些节点从文档移除时,会引发内存泄漏。

Map

  • 是一种类似对象的数据结构,也是键值对的集合,“”的范围不限于字符串各种类型的值(包括对象)都可以当作键
  • 可以接受一个数组作为参数,该数组的成员是一个个表示键值对的数组
  • Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。
  • 如果Map的键是一个简单数据类型的值(数字,布尔,字符串),则只要两个值严格相等,Map将其视为一个键
  • Map的遍历顺序就是插入顺序

WeakMap 与Map 的区别

  • WeakMap只接受对象作为键名(null除外)
  • 键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收
  • 是没有遍历操作(即没有key()values()entries()方法),也没有size属性;
  • 是无法清空,即不支持clear方法。这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。因此,WeakMap只有四个方法可用:get()、set()、has()、delete()

基本上,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。

说一说箭头函数和普通函数的区别?

  • 箭头函数在语法上比普通函数更简洁
  • 箭头函数没有 prototype(原型),所以箭头函数本身没有this
  • 箭头函数的 this 指向在函数定义时就继承自外层第一个普通函数的this,所以箭头函数的this指向在定义的时候就已经确定,且之后永远不会改变。
  • 使用 call,apply,bind 都不能改变箭头函数中的this指向
  • 因为箭头函数没有子集的this且不会改变,所以箭头函数不能用定义构造函数,否则使用new 关键字会报错
  • 箭头函数内部没有arguments ,而是rest 参数(...)代替arguments对象来访问箭头函数的参数列表
  • 箭头函数不能用作 Generator 函数,不能使用 yield 关键字

针对 arguments 的实例:

// 普通函数
function A(a){
  console.log(arguments);
}
A(1,2,3,4);  //  [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]

// 箭头函数
let B = (b)=>{
  console.log(arguments);
}
B(2,92,32);   // Uncaught ReferenceError: arguments is not defined

// rest参数...
let C = (...c) => {
  console.log(c);
}
C(3,82,32);  // [3, 82, 32,]

说一说你对Promise理解

Promise,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。Promise对象提供统一的接口,使得控制异步操作更加容易

Promise的缺点:

  • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

Promise的状态

参考 ECMAScript 6 入门 ,Promise有三种状态:

  1. 等待中(pending)
  2. 完成了(resolved)
  3. 拒绝了(rejected)

Promise 解决了什么问题

Promise 的出现解决了 之前的回调地狱问题,并且Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise , 并且是一个全新的Promise 。是因为Promise 的状态不可变。如果你在then中使用了return ,那么 return 的值会被 Promise .resolve 包装。

Promise怎么拦截错误

  • .catch
    使用catch方法 捕捉错误,但是catch 方法只能捕捉到同步错误,异步错误捕捉不到。
  • 使用 reject 抛出错误,错误会被不停的返回到下一个,必须在每一个then里面使用 thow 将错误抛出去,不然不能被catch 捕捉到,其实也可以不用再次thow 错误,在promise 正常catch 就好,在异步中reject一下在最后就能catch到。

Promise 中的错误不会影响到外层的运行,window.onerror 也是无法检测到的。

关于 Promise的错误拦截详细描述请移步到 关于 Promise的错误拦截

模块化开发

将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。
模块功能主要有两个命令构成 : exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

export 命令

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

使用:

// 用法 1
export var name = 'lisa'

// 用法2
var name= 'lisa';
var age= 18;

export { name, age};
 
 // 用法 3
 function fn1() { ... }
function fn2() { ... }

 export { 
    fn1 as checkNanme1,
    fn2 as checkNanme2
}  // 使用 as 重命名

如果处于块级作用域内,就会报错,import命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。

import 命令

使用 import 命令加载模块

注意:
import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口(如果导入的是对象,改写对象的属性是可以的,但是不建议轻易修改它的值)。

export default 命令

为模块指定默认输出

export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

本质上,export default就是输出一个叫做default的变量或方法(指定对外接口为default),然后系统允许你为它取任意名字。所以它后面不能跟变量声明语句。

参考:
关于箭头函数的普通函数
promise的错误处理

你可能感兴趣的:(关于面试题的总结 之 ES6)