JS面试题个人总结与解析 #
Map和Set的区别,Map和Object的区别 #
Map和Set区别 #
- Map存储的数据是键值对结构,而Set存储的则是值结构。
- Set存储的value是唯一的,而Map存储的是keyValue,所以key是唯一的
Map和Object的区别 #
Map存储的是键值对结构,但是Map的键可以是任意类型,而Object的key只能是字符串和symbol还有数值(但数值会被自动转换为string)
Map中每一个键值对按照插入顺序进行排序,而Object不是。
filter、every、flat的作用? #
filter #
filter的作用是能够筛选出所有满足指定条件的数组元素,并返回这个经过筛选的数组元素集合。
every #
every的作用是能够测试一个数组是否全部满足一个指定的条件,所有数组元素都满足条件(每一次回调的返回值是true)
flat #
flat的作用是能够扁平化一个数组,并返回这个经过扁平化的数组。执行flat所传递的参数决定扁平的层级数。
ES6都有哪些新特性? #
解构赋值
函数参数的默认值
模板字符串
对象方法的简写形式
Promise
新增内置构造class类
新增内置构造Set和Map还有WeakSet和WeakMap
箭头函数
ES6模块化(import,export)
块作用域
说一下对Promise的了解 #
Promise本身是一个状态机,每一个Promise实例只能有三个状态:pending、fulfilled、rejected。分别代表:等待处理状态、处理成功状态、处理失败状态,并且状态的变更只能通过pending到fulfilled,peding到rejected两种。并且状态变更后都是不可逆的。
Promise的then方法能够对变更状态后的Promise进行处理,并返回一个promise对象,并且可以被连续调用多次。
Promise支持链式调用
Promise的then保存着一个状态处理后的参数,如果是成功状态处理,则返回上一次执行resolve或返回的值,如果是失败状态处理,则返回错误信息。
Promise的all和race有什么区别? #
Promise.all #
Promise的all方法所返回的promise会等到所有传递的promise数组集合都处理为完成状态或有一个失败状态时异步的从pending转变为fulfilled或rejected
Promise.race #
Promise的race方法执行时可传递一个promise迭代器,并返回一个promise,异步的转变状态。一旦迭代器中的任意一个promise返回成功或失败,那么返回的promise则转变为成功或失败状态,并在then或catch的参数中返回对应的成功参数或失败参数。一句话总结:race总是返回promise迭代器中最先处理完成的那个处理结果。
区别: #
all会在所有promise集合都为完成状态或第一个失败状态时变更状态为对应完成或失败状态。而race会在promise集合中的第一个promise处理完成后(不管是失败还是成功)直接变更返回的promise对应的状态。
all和race的相同之处在于他们都是在迭代promise集合时,集合中任意一个promise返回失败状态,那么最终返回的promise就直接返回失败状态。
箭头函数和普通函数的区别? #
箭头函数相当于匿名函数,没有命名,而普通函数不使用函数表达式时,需要命名。
箭头函数在只有一行执行语句时,可直接省略大括号
{}
并直接写上表达式。箭头函数不绑定
this
,普通函数的this取决于调用时的具体情况,而箭头函数的this总是在当前所在上下文的this(将上下文的this作为自己的this)箭头函数是匿名函数,所以并不能被作为构造函数(不能使用
new
)。箭头函数没有arguments参数,箭头函数用一个形参接收多个实参应该用rest参数...解决
...args
箭头函数没有原型(prototype)
let、var、const区别?希望const内的属性不能被修改该怎么做? #
let和const与var的区别: #
let和const都不能先使用后定义,必须先定义再使用。而var允许先使用后定义。
let和const定义都拥有块作用域特性,而var没有。
typescript{ var name = 'alex' const age = 19 } console.log(name) // alex console.log(age) // 错误,没有找到age
或以下这种情况:
typescriptfor(var i = 0;i<10;i++){} console.log(i) // 10 for(let m = 0;m<10;m++){} console.log(m) // 错误,没有找到m
const定义的为常量,let和var定义为变量。常量值如果是基础值,则值不可以被更改。常量值如果是引用值,则引用不可以被更改,但引用内部的属性或方法可以被更改。
希望const内的属性不能被修改该怎么做? #
如果希望const内的属性不能被修改(增删改),则可以通过方法Object.freeze
将该对象冻结。
typescript
const obj = {name:'alex',age:19}
Object.freeze(obj)
obj.name = 'jack'
console.log(obj) // {name:'alex',age:19} 没有发生改变
堆和栈的区别 #
基本概念: #
- 栈:栈一般存储着基本数据类型,自动分配的内存空间,每个内存空间固定大小,由系统自动释放。方法内定义的所有变量都是分配到栈中。引用数据类型存放在栈中只是一个引用地址,而不是具体的值,具体的值被分配到堆内存中,栈只是存放了该引用类型的地址。
- 堆:堆一般存储着引用数据类型具体的值,是动态分配的空间,大小不定也不会自动释放。只要当没有任何一个强引用在引用它时,那么垃圾回收机制才会回收。
区别: #
- 栈一般存放着基本类型和引用类型的地址,而堆只存放引用类型具体的值。
- 栈存放的每一块内存空间大小都是固定的,而堆存放的内存空间大小不定。
- 栈存放的每一块内存空间都是自动分配,并且由系统自动释放,堆存放的内存空间则是动态分配,并且不会自动释放。
闭包的原理 #
new的原理 #
函数执行时发生了什么? #
- 创建执行期上下文
- 创建作用域链,复制函数的[[scope]]属性并放入作用域链中
- 创建一个AO并将其推入至作用域链顶端,然后在函数内部获取arguments和其他变量并初始化到AO中。
- 函数执行。
- 函数执行完毕,局部的AO销毁(闭包会保留对函数的作用域的引用)
new一个构造函数时发生了什么? #
- 创建一个普通对象
- 将构造函数的this指向这个对象
- 执行这个构造函数(按照以上函数执行时的解释执行)
- 为该普通对象创建一个属性
__proto__
并指向这个构造函数的prototype
属性 - 返回这个对象(实例),如果手动return了一个引用,则返回该引用,否则覆盖该return。
数据类型有哪些?如何判断一个数据是否是数组 #
数据类型有哪些? #
基本数据类型 #
- Number
- String
- Boolean
- Undefined
- Null
- Symbol
引用数据类型 #
- Object
- Array
- Function
- Date
jQuery链式调用原理? #
jQuery链式调用原理实际上是通过在每一个方法的末尾都return this
来实现的。
分别介绍一下原型、原型链、作用域和作用域链的含义和使用场景 #
含义: #
原型: #
原型分为隐式原型和显式原型,隐式原型是__proto__
,显式原型是:prototype
。两者分别指向同一个引用:原型对象。原型对象能够存储属性和方法,原型对象通过不同的对象类型中拥有不同的属性和方法,同样也可以为原型添加自定义属性和方法。这些原型上的属性和方法,new出来的实例都可以访问的到。
原型链: #
原型链是原型继承的表现,我们通过一个构造函数去new一个实例时,在我们想要访问某一个属性或方法时,首先按照自身实例身上查找,如果没有查找到则在构造函数的原型身上查找,如果还是没有找到,则在该构造函数的原型的原型身上查找,直到原型为null时,才会停止。这种多个原型之间形成的关系就叫做原型链。
作用域: #
全局作用域:
在JS里,全局作用域意为程序的任何地方都能够访问其变量的作用域,不在任何函数内定义的变量都被称为全局变量。全局作用域在页面关闭时才会销毁。
函数作用域:
函数作用域指的是在函数内作用域,也可以理解为局部作用域。函数作用域内定义的变量只有当前函数内或“子函数”内可以访问,这能够避免不污染全局变量,能够常常看到立即执行函数的存在。
块作用域:
块作用域是ES6新增的作用域,在此前没有块作用域的特性。块作用域是指在一个代码块中的作用域:
{}
,在该代码块中通过let或const定义的变量拥有块作用域特性,而使用var则没有该特性。
作用域链: #
每一个函数执行期间都会产生一个作用域链,这个作用域链主要组成部分由父级函数的[[scope]]
它的作用域链和当前函数执行时产生的执行上下文的变量对象(AO)所组成的,变量对象AO会被塞到作用域链中的顶端,这样在当前函数内访问一个变量时,会根据作用域链查找变量,从顶至下依次查找,直到找不到时则会报错。一般最低下的作用域是GO,即全局作用域。
使用场景 #
原型和原型链的使用场景: #
原型的使用场景一般在于定义构造函数期间,为了能够让每个构造出的实例都能够拥有一些共同的属性或方法所以会在该构造函数原型上为添加一些自定义的属性或方法。
原型链的使用场景一般在于使用ES5去实现继承时所应用到。在ES5中还没有class时,如果要实现继承的特性,就需要将指定的目标继承对象的原型赋值给当前构造函数的原型的原型。将原本为Object.prototype的原型替换为指定的那个构造函数的原型,这时就实现了继承的特性。这主要的工作原理就是基于原型链的实现。
作用域和作用域链的使用场景: #
灵活使用作用域可以避免污染全局变量,比如for循环中使用let去定义的变量就拥有块作用域,这比使用var定义的变量好很多,因为如果for循环是全局环境下的,那么就意味着var定义的变量因为没有块作用域特性从而导致在全局定义,从而污染全局变量。使用let定义拥有块作用域特性便可避免这种情况的发生。
同时还有一些其他的概念涉及到作用域,比如立即构造函数、闭包等都是基于作用域的。