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
bar
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();
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
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')
log('Hello', 'China')
log('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)
展开运算符 提取a,b的value构成新对象
let a={x:1,y:2};
let b={z:3};
let ab1={...a,...b};
console.log(JSON.stringify(ab1))
let ab2={a,...b}
console.log(JSON.stringify(ab2))
let ab3={...a,b}
console.log(JSON.stringify(ab3))
解构赋值
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))
console.log(JSON.stringify(_other))
console.log(JSON.stringify({...other,a}))
}
箭头函数 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用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 方法,将数组转为函数的参数了
function f(x, y, z) {
}
var args = [0, 1, 2];
f.apply(null, args);
function f(x, y, z) {
}
let args = [0, 1, 2];
f(...args);
Math.max.apply(null, [14, 3, 77])
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);
}
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
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)
上面代码对数组arr 按照首字母进行排序。排序结果中,straw 在spork 的前面,跟原始顺序一致,所以排序算法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')
Object.is({}, {})
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target
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);
}
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 结构相结合,就可以去除数组的重复成员。 而且,数组的map 和filter 方法也可以间接用于 Set 了。
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
let union = new Set([...a, ...b]);
let intersect = new Set([...a].filter(x => b.has(x)));
let difference = new Set([...a].filter(x => !b.has(x)));
Map
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size
const m = new Map();
m.set('edition', 6)
m.set(262, 'standard')
m.set(undefined, 'nah')
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)
const m = new Map();
m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');
m.has('edition')
m.has('years')
m.has(262)
m.has(undefined)
const m = new Map();
m.set(undefined, 'nah');
m.has(undefined)
m.delete(undefined)
m.has(undefined)
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size
map.clear()
map.size
遍历方法
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永远不会引入强类型,谁知道呢…
参考博客
|