前端,通过面试去学习,es6(es6 typescript 模块化开发)

es6


es6在面试中出现的频率也非常之高,另外typescript和es6的对比也经常被问到,es6的模块化与commonjs的模块化等等衍生问题也需要掌握


ES6新增特性

let const
  • let:块级作用域,没有变量提升
  • const: 恒量/常量;声明后不能修改内存地址,可修改属性成员
  • 最佳实践:不用var,主用const,配合let
export import

可以说模块化是js发展的最大进步,es6后,前端er终于可以自如的使用模块化了,针对js模块化的发展历程,之前有写一片笔记整理,JavaScript模块化开发的演进历程

es6.js

export function showVal(){
    let res = {
        data:{
            a:1,
            b:2
        },
        resCode:"0000"
    }
    const {data} = res;
    console.log(data);
}
export default showVal;

var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export { firstName, lastName, year };

index.js

import es6 ,{showVal,firstName, lastName, year}from './js/es6.js'
es6()
showVal()
console.log(firstName+"----"+lastName+"----"+year)

import * as es6 from './js/es6.js'
es6.showVal()
console.log(es6.firstName+"----"+es6.lastName+"----"+es6.year)
class

es5

function People (name) {
  // 设置实例属性
  this.name = name;
}
// 设置实例的共享方法
People.prototype.sayHi = function () {
  console.log(this.name)
}
let p = new People('tom')
p.sayHi()

es6

class Peopel {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(this.name)
  }
}
const p = new Peopel('tony')
p.say()

class 继承 extends

变量的解构赋值

let [a, b, c] = [1, 2, 3];
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

let x = 1;
let y = 2;
[x, y] = [y, x];

// 返回一个数组
function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象
function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

//提取 JSON 数据
let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

//遍历 Map 结构
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world

// 获取键名
for (let [key] of map) {
  // ...
}

// 获取键值
for (let [value] of map) {
  // ...
}

模板字符串 ``

let str = `生成一个随机数:${ Math.random() }`

字符串新增方法

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

ES2017,字符串的padEnd()、padStart()方法

函数的扩展

函数参数的默认值

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

rest 参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(2, 5, 3) // 10

展开运算符
提取a,b的value构成新对象

let a={x:1,y:2};
let b={z:3};
let ab1={...a,...b};
console.log(JSON.stringify(ab1))//{"x":1,"y":2,"z":3}
let ab2={a,...b}
console.log(JSON.stringify(ab2))//{"a":{"x":1,"y":2},"z":3}
let ab3={...a,b}
console.log(JSON.stringify(ab3))//{"x":1,"y":2,"b":{"z":3}}

解构赋值

let data = {
    a:1,
    b:2,
    c:3,
    d:4,
    e:5
}
function _dealObj({a,...other}){
    let _a = {a};
    let _other = {...other};
    console.log(JSON.stringify(_a))//{"a":1}
    console.log(JSON.stringify(_other))//{"b":2,"c":3,"d":4,"e":5}
    console.log(JSON.stringify({...other,a}))//{"b":2,"c":3,"d":4,"e":5,"a":1}
}

箭头函数
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

[1,2,3].map(function(x){
    return x*x
});
[1,2,3].map(
    x => x*x
)
[1,2,3].map(
    x => {return x*x}
)
数组扩展

由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了

// ES5 的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);

// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);

ES6 提供三个新的方法——entries()keys()values()——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

Array.prototype.sort()

const arr = [
  'peach',
  'straw',
  'apple',
  'spork'
];

const stableSorting = (s1, s2) => {
  if (s1[0] < s2[0]) return -1;
  return 1;
};

arr.sort(stableSorting)
// ["apple", "peach", "straw", "spork"]

上面代码对数组arr按照首字母进行排序。排序结果中,strawspork的前面,跟原始顺序一致,所以排序算法stableSorting是稳定排序。

常见的排序算法之中,插入排序、合并排序、冒泡排序等都是稳定的,堆排序、快速排序等是不稳定的。不稳定排序的主要缺点是,多重排序时可能会产生问题。假设有一个姓和名的列表,要求按照“姓氏为主要关键字,名字为次要关键字”进行排序。开发者可能会先按名字排序,再按姓氏进行排序。如果排序算法是稳定的,这样就可以达到“先姓氏,后名字”的排序效果。如果是不稳定的,就不行。

早先的 ECMAScript 没有规定,Array.prototype.sort()的默认排序算法是否稳定,留给浏览器自己决定,这导致某些实现是不稳定的。ES2019 明确规定,Array.prototype.sort()的默认排序算法必须稳定。这个规定已经做到了,现在 JavaScript 各个主要实现的默认排序算法都是稳定的。

链判断 运算符

ES2020新增特性,极大的减少了代码量

const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';
  
const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined
  
const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value
对象字面量的增强

如果key与value变量名相同,省略:value

对象的新增方法

Object.is()
ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

Proxy:代理对象,vue3采用proxy取代vue2的Object.defineProperty()

Object.values(),以数组的形式,返回对象所有的值

Set 和 Map 数据结构
Set

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。

let set = new Set();
set.add(1);
set.add(2);
set.add(3);

for(let s of set){
    console.log(s);//1,2,3
}

Set 结构的实例有以下属性

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。
  • Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
  • Set.prototype.clear():清除所有成员,没有返回值。

遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员
    需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

展运算符和 Set 结构相结合,就可以去除数组的重复成员。
而且,数组的mapfilter方法也可以间接用于 Set 了。

let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
// [3, 5, 2]

let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
// 返回Set结构:{2, 4, 6}

let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
// 返回Set结构:{2, 4}

因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
Map
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2

const m = new Map();
m.set('edition', 6)        // 键是字符串
m.set(262, 'standard')     // 键是数值
m.set(undefined, 'nah')    // 键是 undefined

//set方法返回的是当前的Map对象,因此可以采用链式写法。
let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');

const m = new Map();
const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 键是函数
m.get(hello)  // Hello ES6!

const m = new Map();
m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');
m.has('edition')     // true
m.has('years')       // false
m.has(262)           // true
m.has(undefined)     // true

const m = new Map();
m.set(undefined, 'nah');
m.has(undefined)     // true

m.delete(undefined)
m.has(undefined)       // false

let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0

遍历方法

Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map 的所有成员。
Promise 对象

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

  • 面试经常会问到为什么用promise,有什么优点,除了解决回调地狱和代码美观还有什么其他好处?
  • 可以从promise这个单词入手回答,承诺,一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected

手写promise,有兴趣和精力的可以去研究

async函数,await
typescript和es6区别

关于ts和es6的争论一直没有停过,【校招面试】关于Typescript和ES6的对比?

  • 就我个人而言,起初培训的java,入职公司后直接写angular2,用的就是typescript,感觉和java有些像,很多都借鉴了java或者其他服务端语言来设计,ts的js的超集,ts中的很多特性都被ECMAScript逐步收录
  • 至于ts写起来,一开始确实比js痛苦,写惯了随意的js代码,再让你加上各种类型,定义好各种属性,编译器各种红波浪线…
  • typescript设计思想是面向对象,实现以面向对象编程的方式使用Javascript,最后代码还是编译为Javascript,这个面试也会经常被问到哪一个是面向对象OOP,解释下面向对象,比如ts中的类、抽象类、接口等,我记得培训java是时候背过面向对象三大特性,封装 继承 多态…
  • ts不再像一个脚本语言,结合面向对象思想,以及强类型语言的优点,对服务端开发相对友好
  • 但是我觉得非常多的前端er用ts去开发,基本上只是用到了强类型type,并没有面向对象的思想在里面,主要还是js的语言设计和应用场景导致的,前端更多的是一种终端,解决用户需求,实现交互,呈现功能,面向过程开发的思想更重一些
  • 也许es永远不会引入强类型,谁知道呢…
参考博客
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值