做者:Alexjavascript
译者:前端小智html
来源:dev.to前端
阿里云最近在作活动,低至2折,真心以为很划算了,能够点击本条内容或者连接进行参与: promotion.aliyun.com/ntms/yunpar…java
ECMAScript 6(如下简称ES6)是 JS 语言的下一代标准,已经在2015
年6
月正式发布了。它的目标,是使得 JS 语言能够用来编写复杂的大型应用程序,成为企业级开发语言。接下来我们来看看 20 道棘手的面试题,经过作题,顺带提高一下我们的 JS 的技能。git
ES5
和ES6
的区别吗?主题: JavaScript 难度: ⭐⭐⭐es6
ECMAScript 5 (ES5):ECMAScript 的第五版,于2009年标准化,该标准已在全部现代浏览器中彻底支持。github
ECMAScript 6 (ES6)/ ECMAScript 2015 (ES2015):ECMAscript 第 6 版,2015 年标准化。这个标准已经在大多数现代浏览器中部分实现。面试
如下是ES5和ES6之间的一些主要区别:数据库
箭头函数和字符串插值设计模式
const greetings = (name) => {
return `hello ${name}`;
}
复制代码
也能够这样写:
const greetings = name => `hello ${name}`;
复制代码
const:const
表示没法修改变量的原始值。须要注意的是,const
表示对值的常量引用,我们能够改变被引用的对象的属性值,但不能改变引用自己。
const NAMES = [];
NAMES.push("Jim");
console.log(NAMES.length === 1); // true
NAMES = ["Steve", "John"]; // error
复制代码
块做用域:ES6 中 let
, const
会建立块级做用域,不会像 var
声明变量同样会被提高。
默认参数:默认参数使我们可使用默认值初始化函数。当参数省略或 undefined
时使用默认参数值。
function multiply (a, b = 2) {
return a * b;
}
multiply(5); // 10
复制代码
类定义与继承
ES6 引入了对类(class
关键字)、构造函数(constructor
关键字)和 extend
关键字(用于继承)的语言支持。
for-of 运算符
for...of
语句建立一个遍历可迭代对象的循环。
展开操做符
const obj1 = { a: 1, b: 2 }
const obj2 = { a: 2, c: 3, d: 4}
const obj3 = {...obj1, ...obj2}
复制代码
Promises: Promises 提供了一种机制来处理异步操做的结果和错误。可使用回调来完成相同的事情,可是Promises
经过方法连接和简洁的错误处理来提升可读性。
const isGreater = (a, b) => {
return new Promise ((resolve, reject) => {
if(a > b) {
resolve(true)
} else {
reject(false)
}
})
}
isGreater(1, 2)
.then(result => {
console.log('greater')
})
.catch(result => {
console.log('smaller')
})
复制代码
模块导出
const myModule = { x: 1, y: () => { console.log('This is ES5') }}
export default myModule;
复制代码
和导入
import myModule from './myModule';
复制代码
主题: JavaScript 难度: ⭐⭐⭐
IIFE
是一个当即调用的函数表达式,它在建立后当即执行
(function IIFE(){
console.log( "Hello!" );
})();
// "Hello!"
复制代码
经常使用此模式来避免污染全局命名空间,由于在IIFE
中使用的全部变量(与任何其余普通函数同样)在其做用域以外都是不可见的。
主题: JavaScript 难度: ⭐⭐⭐
如下是一些经验分享:
在全局做用域内和Object.prototype
属性中使用 function
。
为对象构造函数使用 class
。
其它状况使用箭头函数。
为啥大多数状况都使用箭头函数?
做用域安全性:当箭头函数被一导致用时,全部东西都保证使用与根对象相同的thisObject
。若是一个标准函数回调与一堆箭头函数混合在一块儿,那么做用域就有可能变得混乱。
紧凑性:箭头函数更容易读写。
清晰度:使用箭头函数可明确知道当前 this
指向。
主题: JavaScript 难度: ⭐⭐⭐
Symbol
是一种新的、特殊的对象,能够用做对象中唯一的属性名。使用 Symbol
替换string
能够避免不一样的模块属性的冲突。还能够将Symbol
设置为私有,以便尚无直接访问Symbol
权限的任何人都不能访问它们的属性。
Symbol
是JS新的基本数据类型。与number
、string
和boolean
原始类型同样,Symbol
也有一个用于建立它们的函数。与其余原始类型不一样,Symbol
没有字面量语法。建立它们的惟一方法是使用如下方法中的Symbol
构造函数
let symbol = Symbol();
复制代码
主题: JavaScript 难度: ⭐⭐⭐
ES6 的展开语法在以函数形式进行编码时很是有用,由于我们能够轻松地建立数组或对象的副本,而无需求助于Object.create,slice
或库函数。Redux
和rx.js
项目中常用此特性。
function putDookieInAnyArray(arr) {
return [...arr, 'dookie'];
}
const result = putDookieInAnyArray(['I', 'really', "don't", 'like']);
// ["I", "really", "don't", "like", "dookie"]
const person = {
name: 'Todd',
age: 29,
};
const copyOfTodd = { ...person };
复制代码
ES6 的 rest 语法提供了一种捷径,其中包括要传递给函数的任意数量的参数。
就像展开语法的逆过程同样,它将数据放入并填充到数组中而不是展开数组,而且它在函数变量以及数组和对象解构分中也常常用到。
function addFiveToABunchOfNumbers(...numbers) {
return numbers.map(x => x + 5);
}
const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10);
// [9, 10, 11, 12, 13, 14, 15]
const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4]
const { e, f, ...others } = {
e: 1,
f: 2,
g: 3,
h: 4,
}; // e: 1, f: 2, others: { g: 3, h: 4 }
复制代码
主题: JavaScript 难度: ⭐⭐⭐
// ES5 Function Constructor
function Person(name) {
this.name = name;
}
// ES6 Class
class Person {
constructor(name) {
this.name = name;
}
}
复制代码
对于简单的构造函数,它们看起来很是类似。
构造函数的主要区别在于使用继承。若是我们建立一个继承Person
类的Student
子类并添加一个studentId
字段,如下是两种方式的使用:
// ES5 Function Constructor
function Student(name, studentID) {
// 调用你类的构造函数以初始化你类派生的成员。
Person.call(this. name)
// 初始化子类的成员。
this.studentId = studentId
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
// ES6 Class
class Student extends Person {
constructor(name, studentId) {
super(name)
this.studentId = studentId
}
}
复制代码
在 ES5 中使用继承要复杂得多,并且 ES6 版本更容易理解和记住。
.call
和 .apply
区别是啥?主题: JavaScript 难度: ⭐⭐⭐
.call
和.apply
均用于调用函数,而且第一个参数将用做函数中this
的值。可是,.call
将逗号分隔的参数做为下一个参数,而.apply
将参数数组做为下一个参数。简单记忆法:C用于call
和逗号分隔,A用于apply
和参数数组。
function add(a, b) {
return a + b;
}
console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3
复制代码
主题: JavaScript 难度: ⭐⭐⭐
选择使用类的一些缘由:
语法更简单,更不容易出错。
使用新语法比使用旧语法更容易(并且更不易出错)地设置继承层次结构。
class
能够避免构造函数中使用new的常见错误(若是构造函数不是有效的对象,则使构造函数抛出异常)。
用新语法调用父原型方法的版本比旧语法要简单得多,用super.method()
代替ParentConstructor.prototype.method.call(this)
或Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)
考虑下面代码:
使用 ES6 实现上述功能:
主题: JavaScript 难度: ⭐⭐⭐
能够 Object.freeze
来实现枚举
var DaysEnum = Object.freeze({
"monday": 1,
"tuesday": 2,
"wednesday": 3,
...
})
复制代码
或者
var DaysEnum = {
"monday": 1,
"tuesday": 2,
"wednesday": 3,
...
}
Object.freeze(DaysEnum)
复制代码
可是,这阻止我们把值分配给变量:
let day = DaysEnum.tuesday
day = 298832342 // 不会报错
复制代码
Object.freeze()
和 const
的区别主题: JavaScript 难度: ⭐⭐⭐
const
和Object.freeze
是两个彻底不一样的概念。
const
声明一个只读的变量,一旦声明,常量的值就不可改变:
const person = {
name: "Leonardo"
};
let animal = {
species: "snake"
};
person = animal; // ERROR "person" is read-only
复制代码
Object.freeze
适用于值,更具体地说,适用于对象值,它使对象不可变,即不能更改其属性。
let person = {
name: "Leonardo"
};
let animal = {
species: "snake"
};
Object.freeze(person);
person.name = "Lima"; //TypeError: Cannot assign to read only property 'name' of object
console.log(person);
复制代码
主题: JavaScript 难度: ⭐⭐⭐⭐
提高是指 JS 解释器将全部变量和函数声明移动到当前做用域顶部的操做,提高有两种类型
只要一个var
(或函数声明)出如今一个做用域内,这个声明就被认为属于整个做用域,而且能够在任何地方访问。
var a = 2
foo() // 正常运行, foo 已被提高
function foo() {
a = 3
console.log(a) // 3
var a
}
console.log( a ) // 2
复制代码
主题: JavaScript 难度: ⭐⭐⭐⭐
原型模式会建立新的对象,而不是建立未初始化的对象,它会返回使用从原型或样本对象复制的值进行初始化的对象。原型模式也称为属性模式。
原型模式有用的一个例子是使用与数据库中的默认值匹配的值初始化业务对象。原型对象保留默认值,这些默认值将被复制到新建立的业务对象中。
传统语言不多使用原型模式,可是JavaScript做为一种原型语言,在构建新对象及其原型时使用这种模式。
主题: JavaScript 难度: ⭐⭐⭐⭐
在 ES6 中,let
和const
跟 var
、class
和function
同样也会被提高,只是在进入做用域和被声明之间有一段时间不能访问它们,这段时间是临时死区(TDZ)。
//console.log(aLet) // would throw ReferenceError
let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10
复制代码
主题: JavaScript 难度: ⭐⭐⭐⭐
不该该使用箭头函数一些状况:
当想要函数被提高时(箭头函数是匿名的)
要在函数中使用this/arguments
时,因为箭头函数自己不具备this/arguments
,所以它们取决于外部上下文
使用命名函数(箭头函数是匿名的)
使用函数做为构造函数时(箭头函数没有构造函数)
当想在对象字面是以将函数做为属性添加并在其中使用对象时,由于我们没法访问 this
即对象自己。
主题: JavaScript 难度: ⭐⭐⭐⭐
WeakMaps 提供了一种从外部扩展对象而不影响垃圾收集的方法。当我们想要扩展一个对象,可是由于它是封闭的或者来自外部源而不能扩展时,能够应用WeakMap
。
WeakMap只适用于 ES6 或以上版本。WeakMap是键和值对的集合,其中键必须是对象。
var map = new WeakMap();
var pavloHero = {
first: "Pavlo",
last: "Hero"
};
var gabrielFranco = {
first: "Gabriel",
last: "Franco"
};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero)); //This is Hero
复制代码
WeakMaps的有趣之处在于,它包含了对map
内部键的弱引用。弱引用意味着若是对象被销毁,垃圾收集器将从WeakMap
中删除整个条目,从而释放内存。
主题: JavaScript 难度: ⭐⭐⭐⭐
function foo(){ }();
复制代码
IIFE
表明当即调用的函数表达式。JS解析器读取函数foo(){}();
做为函数foo(){}
和();
,前者是一个函数声明,后者(一对括号)是尝试调用一个函数,但没有指定名称,所以它抛出Uncaught SyntaxError: Unexpected token
异常。
我们可使用void
操做符:void function foo(){ }();
。不幸的是,这种方法有一个问题。给定表达式的求值老是undefined
的,因此若是IIFE 函数有返回值,则不能使用它,以下所示:
const foo = void
function bar() {
console.log('前端小智')
return 'foo';
}();
console.log(foo); // undefined
复制代码
主题: JavaScript 难度: ⭐⭐⭐⭐
模块模式一般用于命名空间,在该模式中,使用单个实例做为存储来对相关函数和对象进行分组。这是一个不一样于原型设计的用例,它们并非相互排斥,我们能够同时使用它们(例如,将一个构造函数放在一个模块中,并使用new MyNamespace.MyModule.MyClass(arguments)
)。
构造函数和原型是实现类和实例的合理方法之一。它们与模型并不彻底对应,所以一般须要选择一个特定的scheme
或辅助方法来实现原型中的类。
主题: JavaScript 难度: ⭐⭐⭐⭐⭐
当它们的键/值引用的对象被删除时,它们的行为都不一样,如下面的代码为例:
var map = new Map()
var weakmap = new WeakMap()
(function() {
var a = {
x: 12
};
var b = {
y: 12
};
map.set(a, 1);
weakmap.set(b, 2);
})()
复制代码
执行上面的 IIFE,就没法再引用{x:12}
和{y:12}
。垃圾收集器继续运行,并从 WeakMa
中删除键b
指针,还从内存中删除了{y:12}
。
但在使用 Map
的状况下,垃圾收集器不会从Map
中删除指针,也不会从内存中删除{x:12}
。
WeakMap
容许垃圾收集器执行其回收任务,但Map
不容许。对于手动编写的 Map
,数组将保留对键对象的引用,以防止被垃圾回收。但在WeakMap
中,对键对象的引用被“弱”保留,这意味着在没有其余对象引用的状况下,它们不会阻止垃圾回收。
主题: JavaScript 难度: ⭐⭐⭐⭐⭐
柯里化是一种模式,其中一个具备多个参数的函数被分解成多个函数,当被串联调用时,这些函数将一次累加一个所需的全部参数。这种技术有助于使用函数式编写的代码更容易阅读和编写。须要注意的是,要实现一个函数,它须要从一个函数开始,而后分解成一系列函数,每一个函数接受一个参数。
function curry(fn) {
if (fn.length === 0) {
return fn;
}
function _curried(depth, args) {
return function(newArgument) {
if (depth - 1 === 0) {
return fn(...args, newArgument);
}
return _curried(depth - 1, [...args, newArgument]);
};
}
return _curried(fn.length, []);
}
function add(a, b) {
return a + b;
}
var curriedAdd = curry(add);
var addFive = curriedAdd(5);
var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]
复制代码
主题: JavaScript 难度: ⭐⭐⭐⭐⭐
若是我们想要确保对象被深冻结,就必须建立一个递归函数来冻结对象类型的每一个属性:
没有深冻结
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
Object.freeze(person);
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }
复制代码
深冻结
function deepFreeze(object) {
let propNames = Object.getOwnPropertyNames(object);
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === "object" ?
deepFreeze(value) : value;
}
return Object.freeze(object);
}
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object
复制代码
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
阿里云最近在作活动,低至2折,有兴趣能够看看:promotion.aliyun.com/ntms/yunpar…
干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。
由于篇幅的限制,今天的分享只到这里。若是你们想了解更多的内容的话,能够去扫一扫每篇文章最下面的二维码,而后关注我们的微信公众号,了解更多的资讯和有价值的内容。
每次整理文章,通常都到2点才睡觉,一周4次左右,挺苦的,还望支持,给点鼓励