前端面试基础-ES6

225 阅读1分钟

前言

记录前端ES6的学习
学习已完成

  • 1.模板字符串
  • 2.块级作用域
  • 3.for of
  • 4.箭头函数
  • 5.参数加强
  • 6.解构
  • 7.Class
  • 8.Promise

1.模板字符串

var uname = "小明";
console.log(`Welcome ${uname}`);
// 还可直接在 ${} 里面进行运算 ,如下
var sex = 1;
console.log(`性别:${sex == 1 ? "男" : "女"}`);

2.块级作用域

a.使用var进行声明时,声明会被提前
==> 声明提前会打乱写程序时的执行顺序,代码块内的变量会超出代码块范围,影响外部的变量\

b.使用let、const 有块级作用域则不会被提前\

什么是块级作用域?

  • 在程序块{}内的变量,出了所在的程序块{},就不能使用。举例如下
// 1 var
for (var i = 0; i < arr.length; i++) { }
console.log(i); // 正常执行

// 2 let 块级作用域
for (let i = 0; i < arr.length; i++) { }
console.log(i);//报错

// 1的for语句相当于下面
var i; // i被提取出来也可以理解为声明提前了
for (i = 0; i < arr.length; i++) { }
console.log(i); // 所以正常执行

// let i = 0 的底层会被翻译为 匿名函数自调
(function(){
    var i = 0 // 则不会提前声明
})()

同样的测试在 if(){}、直接 {}、forEach循环等里面也成立

a. 因为不会声明提前,所以不能在声明变量之前,提前使用该变量。
b. 在相同作用域内,禁止声明两个同名的变量!
c. 因为let底层相当于匿名函数自调,所以,即使在全局创建的let变量,在window中也找不到!

区别varletconst
产生”块级作用域”×✔️✔️
会被声明提前✔️××
保存在window中✔️××
相同作用域中能否重复声明变量✔️××
是否能提前使用✔️××
是否必须设置初始值××✔️
能否修改实际保存在变量中的原始类型值或引用类型地址✔️✔️×

3.for of

可直接遍历获得值,对于只关心元素值的循环可用

var arr = [1,2,3];
for (var i = 0; i < arr.length; i++) {
    console.log(arr[i])
}

for (var t of arr) {
    console.log(t)
}
// 输出结果均为 // 1 2 3

a. 无法获得下标位置i,只能获得元素值
b. 无法控制遍历的顺序或步调,只能从头到尾,一个挨一个的顺序遍历
c. 无法遍历下标名为自定义下标的对象和关联数组

4.箭头函数

function a(){} 等价于 var a = ()=>{}
注意:箭头函数可让函数内的this与函数外的this保持一致!自身没有this
箭头函数底层相当于function a(){}.bind(this) 永久绑定外部this,因此 call无法替换箭头函数中的this
箭头函数可简写,当只有一个返回值时
function a(){return 1} 等价于 var a = ()=> 1

5.参数加强

1) 形参默认值,function写形参时可写入默认值,当不传入实参时可以使用形参默认值

function order(a=1,b=2,c=3){ … }

2) 剩余参数 ...arr

由于箭头函数不支持普通function内置的arguments所以有了剩余参数
并且...arr返回的是纯正的数组而arguments是类数组,Array的一些方法不可使用,...arr全部可使用

// 定义一个函数,求任意多个数字的和
var add = function(){
    var result = 0;
    for (var n of arguments) { // arguments is not defined
        result += n;
    }
    return result;
}
var add = (...arr)=> {
    // arr=[所有实参值]
    var result = 0;
    for (var n of arr) { // 由于不存在arguments 则有了 剩余参数 ...arr
        result += n;
    }
    return result;
}
console.log(add(1, 2, 3)); // 6

• 构造函数不能用
• 对象的方法不能用
• 原型对象方法不能用
• DOM中事件处理函数不能用
• 箭头函数无法用call,apply,bind改变this
• 箭头函数不支持arguments
• 箭头函数没有prototype

3) 展开运算符

  • 抛出一个问题,比如求数组中的最小值
let val = Math.min(1,5,8,3) // 1
// Math.min()并不支持数组
let val = Math.min([1,5,8,3]) // NaN
// 解决:使用展开运算符 拆散数组
let val = Math.min(...[1,5,8,3])
console.log(val)

a. 定义函数时,形参列表中的...,表示收集
b. 调用函数时,实参列表中的...,表示拆散

4) 语法糖

// a. 简写:复制一个数组 \
var arr1 = [1,2,3]
var arr2 = [...arr1]
// b. 简写:合并多个数组
var arr1=[1,2,3];
var arr2=[5,6,7];
var arr3=[...arr1,4,...arr2,8];
// c. 简写:克隆对象
var lilei={ sname:"Li Lei", sage:11 };
var lilei2={...lilei};
// d. 简写:合并多个对象和属性
var obj1={ x:1, y:2 };
var obj2={ i:4, j:5 };
var obj3={...obj1, z:3, ...obj2, k:6};

6.解构

数组结构、对象结构、参数结构

// 数组结构
let arr = [1,2,4]
let [a,b,c] = arr // a=1,b=2,c=4
// 对象结构
let obj = { a:1, b:2 }
let { a:a, b:b } = obj
let {a,b} = obj // 简写 a=1,b=2
// 参数结构 本质还是对象解构
function fns({a=1,b=2,c=3}){
    console.log(a,b,c)
}
fns({a:4,c:5}) // 4,2,5

7. Class & 继承

Class 还是语法糖,本质还是 构造函数 + 原型对象

1) 构造函数和原型对象

在旧JS中构造函数和原型对象是分开定义的,并不符合面向对象封装

// 定义一个学生类型,描述所有学生的统一结构和功能
function Student(sname, sage){
    this.sname=sname;
    this.sage=sage;
}
Student.prototype.intro=function(){
    console.log(`I'm ${this.sname}, I'm ${this.sage}`);
}
// 实例化一个学生类型的对象
var xiaoming = new Student("Xiao Ming",18);
console.log(xiaoming);
xiaoming.intro();

2) Class

优点:程序中专门集中保存; 一种类型的所有子对象; 统一的属性结构和方法定义
改造 构造函数+原形对象的形式为class

// 1. 用class{}包裹原构造函数+原型对象方法
// 2. 原构造函数名升级为整个class的名字,所有构造函数统一更名为"constructor“
// 3. 原型对象中的方法,不用再加prototype前缀,也不用=function,直接简写为: 方法名(){ ...}
class Student {
    constructor(sname, sage) {
        this.sname = sname;
        this.sage = sage;
    }
    intro() { // 直接在class中定义的方法 都默认保存在原型对象中。
        console.log(`I'm ${this.sname}, I'm ${this.sage}`);
    }
}

问题1:多个子对象共用一个属性值,应该放在那里?
旧js中:是和共有方法一起放在原型对象中
es6中:直接在class中定义的方法都默认保存在原型对象中。 不过直接在class中定义的属性,却不会成为共有属性,不会保存在原型对象中,而是成为每个子对象的自有属性。

class Student{
    constructor(){ ... }
    className = '高三一班' // 如此新增属性不会成为共有属性
    intro(){ ... }
}

为了和其他主流开发语言尽量保持一致,ES6的class放弃在原型对象中保存共有属性,改用静态属性保存

class Student{
    constructor(){ ... }
    static className = '高三一班' // 改用静态属性,所有子对象都可使用一个共同的属性值,都要用静态属性代替原来的原型对象属性
    intro(){ ... }
}
console.log('Student: ', Student.className); // 访问 得到 高三一班

标有static的静态属性,都是保存在构造函数对象身上。因为构造函数在程序中不会重复!所以,静态属性,也不会重复!任何时候,任何地点,访问一个类型的静态属性,永远访问的都是同一份!

3) 继承

此处可简写 可在constructor中使用super 访问父级的属性写法

class Group extends Student {
    constructor(sname,sage,height){
        super(sname,sage) // 继承时可用super调用访问父类的属性
        this.height = height;
    }
    intro() { // 方法重写了,若不重写则直接继承父类方法
        console.log(`I'm ${this.sname}, I'm ${this.sage}, I'm ${this.height}`);
    }
}
var xiaohong = new Group("Xiao Hong", 18, 160);
console.log(xiaohong);
xiaohong.intro();

8. Promise

Promise的出现是为了解决回调地狱
异步函数几乎同时执行,各自执行各自的,互不干扰,互相之间也不会等待。当我们需要等待一个异步函数执行完后再执行下一个执行函数时就不得要在异步函数的回调里面重复嵌套写逻辑,陷入了回调地狱
Promise的基础使用较为简单,可参考该文章# Promise不会??看这里!!!史上最通俗易懂的Promise!!!

后续将学习尝试手写Promise # 手写promise

最后

以上的方式总结只是自己学习总结,有其他方式欢迎各位大佬评论
渣渣一个,欢迎各路大神多多指正,不求赞,只求监督指正( ̄. ̄)
有关该文章经常被面试问到的可以帮忙留下言,小弟也能补充完善完善一起交流学习,感谢各位大佬(~ ̄▽ ̄)~