ES6面试题

​ 面试题:var、let、const区别

var

var声明的变量既是全局变量,也是顶层变量

注意:顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象

var a = 10;
console.log(window.a) // 10

使用var声明的变量存在变量提升的情况

console.log(a) // undefined
var a = 20

在编译阶段,编译器会将其变成以下执行

var a
console.log(a)
a = 20

使用var,我们能够对一个变量进行多次声明,后面声明的变量会覆盖前面的变量声明

var a = 20 
var a = 30
console.log(a) // 30

在函数中使用使用var声明变量时候,该变量是局部的

var a = 20
function change(){
    var a = 30
}
change()
console.log(a) // 20 

而如果在函数内不使用var,该变量是全局的

let

letES6新增的命令,用来声明变量

用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效

{
    let a = 20
}
console.log(a) // ReferenceError: a is not defined.

不存在变量提升

console.log(a) // 报错ReferenceError
let a = 2

这表示在声明它之前,变量a是不存在的,这时如果用到它,就会抛出一个错误

只要块级作用域内存在let命令,这个区域就不再受外部影响

var a = 123
if (true) {
    a = 'abc' // ReferenceError
    let a;
}

使用let声明变量前,该变量都不可用,也就是大家常说的“暂时性死区”

最后,let不允许在相同作用域中重复声明

let a = 20
let a = 30
// Uncaught SyntaxError: Identifier 'a' has already been declared

注意的是相同作用域,下面这种情况是不会报错的

let a = 20
{
    let a = 30
}

因此,我们不能在函数内部重新声明参数

function func(arg) {
  let arg;
}
func()
// Uncaught SyntaxError: Identifier 'arg' has already been declared

const

const声明一个只读的常量,一旦声明,常量的值就不能改变

const a = 1
a = 3
// TypeError: Assignment to constant variable.

这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值

const a;
// SyntaxError: Missing initializer in const declaration

如果之前用varlet声明过变量,再用const声明同样会报错

var a = 20
let b = 20
const a = 30
const b = 30
// 都会报错

const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动

对于简单类型的数据,值就保存在变量指向的那个内存地址,因此等同于常量

对于复杂类型的数据,变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,并不能确保改变量的结构不变

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

其它情况,constlet一致

参考文献:面试官:说说var、let、const之间的区别 | web前端面试 - 面试官系列 (vue3js.cn)

​ 面试题:将下列对象进行合并

 $.extend()

var obj1= {'a': 1};

var obj2= {'b': 1}

var c = $.extend(obj1, obj2);

​ 面试题:箭头函数和普通函数有什么区别?

1、箭头函数使用箭头定义,普通函数中没有

2、箭头函数都是匿名函数
        普通函数可以有匿名函数,也可以有具体名函数,但是箭头函数都是匿名函数。

3、箭头函数不能用于构造函数,不能使用new
        普通函数可以用于构造函数,以此创建对象实例。

4、箭头函数中this的指向不同
        在普通函数中,this总是指向调用它的对象,如果用作构造函数,this指向创建的对象实例。

5、箭头函数本身不创建this
        也可以说箭头函数本身没有this,但是它在声明时可以捕获其所在上下文的this供自己使用。
        注意:this一旦被捕获,就不再发生变化

6、箭头函数不绑定arguments,取而代之用rest参数解决
        每一个普通函数调用后都具有一个arguments对象,用来存储实际传递的参数。但是箭头函数并没有此对象。

总结
        (1).箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
        (2).普通函数的this指向调用它的那个对象

​ 面试题:Promise有几种状态以及解决回调地狱

三种状态:pending、fulfilled、rejected(未决定,履行,拒绝),同一时间只能存在一种状态,且状态一旦改变就不能再变。

promise是一个构造函数,promise对象有resolve和reject两个参数。

resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

promise的优缺点

​ 优点:

​ 1.Promise 分离了异步数据获取和业务逻辑,有利于代码复用。

​ 2.可以采用链式写法

​ 3.可以用于解决回调地狱。

​ 缺点:

1、无法取消Promise,一旦新建它就会立即执行,无法中途取消。

2、如果不设置回调函数,promise内部抛出的错误,不会反应到外部。

3、当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

回调地狱

就是一层一层嵌套,会极大的影响代码的可读性和逻辑,会引起代码难以维护等问题。

用promise解决回调地狱问题:

function getTea(){
	return new Promise(function(resolve,reject){
		setTimeout(() => {
			resolve('奶茶')    //通过resolve来将异步数据传出
		},1000)
	})
}
function getHotpot(){
	return new Promise(function(resolve,reject){
		setTimeout(() => {
			resolve('火锅')    //通过resolve来将异步数据传出
		},2000)
	})
}
//先吃火锅,后喝奶茶
getHotpot().then(function(data){    //通过then来拿到异步数据
	console.log(data);
	return getTea();
}).then(function(data){    //通过then来拿到异步数据
	console.log(data)
})

现在用解决回调地狱更好的办法,就是用async和await方法:

//async函数
async function getData(){
	//直接获取resolve传出来的异步数据
	let hotPot = await getHotpot();
	console.log(hotPot);
	let Tea = await getTea();
	console.log(Tea);
}
getData();

Promise.all

        Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个 Promise实例, 那个输入的所有promise的resolve回调的结果是一个数组。此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

好处是可以异步的完成调用,可以节约时间,比如一个界面调用三个接口,调用第一个接口需要一秒钟,第二个需要两秒钟,第三个需要三秒钟,但是如果使用promise.all,就不会造成时间叠加,而是只需要三秒钟就可以了。缺点是如果有一个接口调用失败了,就会导致全部接口调用失败,而且只会返回第一个失败的promise。

​ 面试题:find和filter的区别

find和filter都是数组的查找方法,但是会有一些细节的差别:
find

  • 返回值是一个对象,只返回符合条件的第一项
  • 找不到会返回undefined

filter

  • 返回值是一个数组,数组里面是符合条件的每一个对象以逗号隔开,可以有多个
  • 找不到会返回空数组 [ ],不会返回undefined

总结:
find和filter的返回值是大家比较容易搞混的,我们可以这样记:

find字面意思是寻找,但只会寻找第一个符合条件的,既然最终是返回一个,那么肯定就是找的的这一项;找不到就只能返回undefined。

filter字面意思是筛选,肯定是把所有符合条件的都找出来,这么多符合项肯定是要放在一个容器里面的,那这个容器就是一个数组,找不到自然是返回空数组呗。
 

​ 面试题:some和every的区别

every()与some()方法都是JS中数组的迭代方法。

every()是对数组中每一项运行给定函数,如果该函数对每一返回true,则返回true。

some()是对数组中每一项运行给定函数,如果该函数对任一返回true,则返回true。

webpack面试题

面试题:webpack是什么?

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器,可以使用它管理项目中的模块依赖,并编译输出模块所需的静态文件。它可以很好地管理、打包开发中所用到的HTML,CSS,JavaScript和静态文件(图片,字体)等,让开发更高效。对于不同类型的依赖,webpack有对应的模块加载器,而且会分析模块间的依赖关系,最后合并生成优化的静态资源。

面试题:有哪些常见的Loader?他们是解决什么问题的?

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • image-loader:加载并且压缩图片文件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码

面试题:有哪些常见的Plugin?他们是解决什么问题的?

  • define-plugin:定义环境变量
  • commons-chunk-plugin:提取公共代码
  • uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码

面试题:Loader和Plugin的不同?

  • Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
  • Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

出处:关于webpack的面试题总结 - 知乎 (zhihu.com)

Git面试题

面试题:Git是什么?

GIT,全称是分布式版本控制系统,git通常在编程中会用到,并且git支持分布式部署,可以有效、高速的处理从很小到非常大的项目版本管理。分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一个完整的Git仓库。

说白了就是一个版本控制工具,帮助大家管理自己的代码。

版本库又名仓库(repository),可以简单理解成一个目录(存放好多版本的目录),目录里所有文件都被Git管理起来,每个文件的修改,删除,Git都会跟踪,以便任何时候都可以追踪历史或者在将来某一时刻可以还原修改。

​ 面试题:git常用命令

git init:初始化一个git仓库

git clone:clone一个git仓库

git add file或者git add  . :新增文件的命令

git commit 将缓存区内容添加到仓库中,可以在后面加-m选项,以在命令行中提供提交注释,格式如下:

git commit -m "第一次版本提交"

如果你觉得 每次 commit之前要add一下,想跳过add这一步,可以直接使用 -a选项,如:

git commit -am "第一次版本提交"

​ 面试题:解决冲突

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。

解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。

git log --graph命令可以看到分支合并图。

​ 面试题:GitFlow

Git Flow 的常用分支

  • 生产分支(master)

Master分支是仓库的主分支,这个分支包含最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改‌

  • 补丁分支(hotfix)

当我们在生产环境发现新的Bug时候,我们需要基于master分支创建一个Hotfix分支,然后在Hotfix分支上修复bug,完成Hotfix后,我们要把hotfix分支合并回Master和Develop分支‌

  • 发布分支(release)

当你需要发布一个新功能的时候,要基于Develop分支创建一个Release分支,在Release分支测试并修复bug,完成release后,把release合并到master和develop分支‌

  • 开发分支(develop)

这个分支是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支‌

  • 功能分支(feature)

feature分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release‌

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐