前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[第13期] 掌握前端面试基础系列一: ES6

[第13期] 掌握前端面试基础系列一: ES6

作者头像
皮小蛋
发布2020-03-02 10:29:54
3920
发布2020-03-02 10:29:54
举报
文章被收录于专栏:前端皮小蛋

马上又到年底了,我又想起来18年准备面试的情景。

搜集资料, 整理, 面试, 再整理, 十分的辛苦。

其实无论面试哪家公司, 基础 都是重中之重。

最近准备把这些基础知识系统的整理一下,分享给大家,以后自己也能看。

刚好假期在家看了ES6相关的东西, 这一篇就从ES6开始吧。

正文


var, let, const

这三个东西, 经常会被问到。

汇总一下,基本上就是:

  • var, let, const 有什么区别?
  • let , const 有没有变量提升?
  • 什么是 TDZ ?

首先, 我们先整体的看下区别:

首先, 我们还是先了解一下变量提升.

看个例子:

代码语言:javascript
复制
console.log(a) // undefined
var a = 1

这里我们可以看到, 第一行中的a虽然还没声明, 但是我们用起来却不会报错。这种情况, 就是变量声明的提升。

其实也就是:

代码语言:javascript
复制
var a
console.log(a) // undefined
a = 1

但是, 如果是换成let , 情况就不一样了:

要理解这个现象, 首先需要搞清楚提升的本质,。

理解创建 javascript 变量的三个步骤

  1. 创建
  2. 初始化
  3. 赋值

为了便于理解, 我们先看看var创建、初始化和赋值 过程:

代码语言:javascript
复制
function foo(){
  var x = 1
  var y = 2
}

foo()

执行foo 时, 会有一些过程(部分过程):

  1. 进入 foo,为 foo 创建一个环境。
  2. 找到 foo 中所有用 var 声明的变量,在这个环境中「创建」这些变量(即 x 和 y)。
  3. 将这些变量「初始化」为 undefined。
  4. 执行代码
  5. x = 1 将 x 变量「赋值」为 1
  6. y = 2 将 y 变量「赋值」为 2

也就是说 var 声明, 会在代码执行之前就将 创建变量,并将其初始化为 undefined

这就解释了为什么在 var x = 1 之前 console.log(x) 会得到 undefined

接下来看 let 声明的「创建、初始化和赋值」过程

代码语言:javascript
复制
// ...
{
  let x = 1;
  x = 2
}

我们看一下过程:

  1. 找到所有用 let 声明的变量,在环境中创建这些变量
  2. 执行代码(注意现在还没有初始化)
  3. 执行 x = 1,将 x 「初始化」为 1(这并不是一次赋值,如果代码是 let x,就将 x 初始化为 undefined)
  4. 执行 x = 2,对 x 进行「赋值」

这就解释了为什么在 let x 之前使用 x 会报错:

代码语言:javascript
复制
let x = 'global'
{
  console.log(x) // Uncaught ReferenceError: x is not defined
  let x = 1
}

原因有两个:

  1. console.log(x) 中的 x 指的是下面的 x,而不是全局的 x.
  2. 执行 log 时 x 还没「初始化」,所以不能使用(也就是所谓的 TDZ, tempory dead zone, 暂时死区)

说到这里, 就都清楚了:

  1. let 的「创建」过程被提升了,但是初始化没有提升。
  2. var 的「创建」和「初始化」都被提升了。

function 也是类似的,而且function 的「创建」「初始化」和「赋值」都被提升了。

这一点, 感兴趣的朋友可以自己实验一下。

算了, 直接给个例子吧:

代码语言:javascript
复制
bar()

function  bar() {
  console.log(2)
}

JS 引擎会有一下过程:

  1. 找到所有用 function 声明的变量,在环境中「创建」这些变量。
  2. 将这些变量「初始化」并「赋值」为 function(){ console.log(2) }
  3. 开始执行代码 fn2()`

也就是说 function 声明会在代码执行之前就「创建、初始化并赋值」。

简单的总结:

  1. 函数提升优先于变量提升. 函数提升会把整个函数挪到作用域顶部变量提升只会把声明挪到作用域顶部
  2. var存在提升,我们能在声明之前使用。
  3. let, const 因为存在暂时性死区不能在声明前使用
  4. var全局作用域下声明变量, 会导致变量挂载在window上, 而另外两者不会
  5. letconst的作用基本一致,但是后者声明的变量不能再次赋值。

箭头函数

这个也是 ES6 里比较好用的feature, 我们每天也都会用到。

箭头函数是 ES6 中新的函数定义形式:

代码语言:javascript
复制
function name(arg1, arg2) {}

// 可以使用

(arg1, arg2) => {}

// 来定义。

箭头函数,

一方面看起来比较简洁, 另一方面, 解决了ES5时代 this 的问题.

看个例子:

代码语言:javascript
复制

function fn() {
    console.log(this)  // {a: 100} ,该作用域下的 this 的真实的值
    var arr = [1, 2, 3]
    
    // ES5
    arr.map(function (item) {
        console.log(this)  // window
    })
    
    // 箭头函数
    arr.map(item => {
        console.log(this)  // {a: 100} 这里打印的就是父作用域的 this
    })
}
fn.call({a: 100})

模块化

模块化的好处是十分明显的:

  1. 解决命名冲突
  2. 提供复用性
  3. 提高代码可维护性

ES6 之前也有模块化的方案: AMD, CMD, 就简单的提一下, 不是本文讨论的主要内容。

AMD, CMD

代码语言:javascript
复制
// AMD
define(['./a', './b'], function(a, b) {
  a.do()
  b.do()
})

// CMD
define(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
})

IIFE

也有一种IIFE的 形式:

代码语言:javascript
复制
(function(globalVariable){
   // 形成一个独立的作用域,不会污染全局
   // ...
})(globalVariable)

CommonJS

CommonJS 最早是 Node.js 在使用,目前也仍然广泛使用。

看个例子:

代码语言:javascript
复制
// a.js
module.exports = {
  a: 1
}

// b.js
var module = require('./a.js')
module.a // 1

ES6 Module

ES Module 是原生实现的模块化方案, 提供了一种新的, 可以文件作为模块的开发方式。

使用方式就是我们常见的:

代码语言:javascript
复制
// a.js
export function a() {}
export default function() {}

//b.js
import XXX from './a.js'
import { XXX } from './a.js'

如果只是输出一个唯一的对象,使用export default即可:

代码语言:javascript
复制
// util1.js
export default {
    a: 100
}

// index.js 文件
import obj from './util1.js'
console.log(obj) // { a: 100 }

如果想要输出许多个对象,就不能用 default了,而且 import 时候要加 { ... },代码如下:

代码语言:javascript
复制
// foo.js
export function fn1() {
  alert('fn1')
}

export function fn2() {
  alert('fn2')
}

// index.js
import { fn1, fn2 } from './foo.js'
fn1()
fn2()

Class

Class 其实一直是Js的保留字,直到 ES6 才正式的用到。

(Js中并不存在类,class 只是个语法糖。本质上还是函数, 其实就是要模拟面向对象的语法, 你懂的)

ES6 的 class 就是取代之前构造函数初始化对象的形式,从语法上更加接近面向对象的写法。

例如:

代码语言:javascript
复制
// ES5
function MathHandle(x, y) {
  this.x = x;
  this.y = y;
}

MathHandle.prototype.add = function () {
  return this.x + this.y;
};

var method = new MathHandle(1, 2);
console.log(method.add())

ES6 class 的写法:

代码语言:javascript
复制
class MathHandle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  add() {
    return this.x + this.y;
  }
}
const method = new MathHandle(1, 2);
console.log(method.add())

注意以下几点:

  • class 是一种新的语法形式,是class Name {...} 这种形式,和函数的写法完全不一样.
  • 两者对比,构造函数函数体的内容要放在 class 中的constructor函数中,constructor即构造器,初始化实例时默认执行.
  • class 中函数的写法是add() {...}这种形式,并没有function关键字.

原型继承和 Class 继承

使用 Class 来实现继承就更加简单了,至少比构造函数实现继承简单很多:

代码语言:javascript
复制
// 动物
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}

// 狗
function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}

Dog.prototype = new Animal()

var husky = new Dog()
husky.bark() // dog bark

ES6 写法:

代码语言:javascript
复制
class Animal {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat`)
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name)
        this.name = name
    }
    
    bark() {
        console.log(`${this.name} say`)
    }
}

const husky = new Dog('哈士奇')
husky.bark()

注意以下两点:

  • 使用extends即可实现继承,更加符合经典面向对象语言的语法.
  • 子类的constructor一定要执行super(),调用父类的constructor.

以上这几个方面都是面试中经常问的,ES6的内容远远不止这些。

好用的特性还有很多, 比如:

  • ... 」 操作符
  • 解构
  • Set
  • Map
  • 等等

这里就不一一介绍了。

... 」 操作符 这个可以参考我的这篇文章:

深入了解强大的 ES6 「 ... 」 运算符

地址:https://segmentfault.com/a/1190000020612737

ES6 面试问的比较多的大概就是以上这几点, 可能有所纰漏, 后面再做补充吧。

希望对大家有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端皮小蛋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AMD, CMD
  • IIFE
  • CommonJS
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档