Appearance
前端面试八股文
1、箭头函数和普通函数的区别
1.箭头函数在一些情况下书写更简洁(比如只有一个参数、函数提直接返回的时候)
2.箭头函数没有自己的this,箭头函数内的this变量指向外层非箭头函数的函数的this,或者将该箭头函数作为属性的对象。箭头函数也不支持call()/apply()函数特性
3.箭头函数内部不可以使用arguments对象
4.箭头函数不可当作构造函数
为什么不能用作构造函数:
构造函数是通过new关键字来生成对象实例,生成对象实例的过程也是通过构造函数给实例绑定this的过程,而箭头函数没有自己的this。创建对象过程,new 首先会创建一个空对象,并将这个空对象的__proto__指向构造函数的prototype,从而继承原型上的方法,但是箭头函数没有prototype。因此不能使用箭头作为构造函数,也就不能通过new操作符来调用箭头函数。
2、apply、bind、call的作用|区别|实现
作用:call、apply、bind都是用来改变this执行那个的
区别:1、call和apply调用时候立即执行,bind调用返回新的函数
2、当需要传递参数时候,call直接写多个参数,apply将多个参数写成数组,bind在绑定时候需要固定参数时候,也是直接写多个参数
call
javascript
Function.prototype.apply = function(object){
object = object || window
let fn = Symbol(object)
object[fn] = this
let arg = [...arguments].slice(1)
object[fn](...arg)
delete object[fn]
}apply
javascript
Function.prototype.apply = function(object){
object = object || window
let fn = Symbol(object)
object[fn] = this
let arg = [...arguments].slice(1)
object[fn](arg)
delete object(fn)
}bind
javascript
Function.prototype.bind = function(object){
object = object || window
let oldArg = [..arguments].slice(1)
let self = this
return function(){
let newArg = [...arguments]
// 返回函数绑定this,传入两次保存的参数
// 考虑返回函数有返回值做了return
return self.apply(object,oldArg.concat(newArg))
}
}3、防抖、节流原理|实现
防抖:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行(在一段时间内多次触发,但只实现一次)
javascript
function debounce(fn,delay){
let time = null
return function(){
if(time){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this,argument)
time = null
},delay)
}
}节流:指定时间间隔内只会执行一次任务(一个时间段,只触发一次)
javascript
function throttle(fn,delay){
let time
return function(){
if(!time){
time = setTimeout(() => {
time = null
fn.apply(this,arguments)
},delay)
}
}
}4、实现深拷贝和浅拷贝
深拷贝
javascript
function checkType(any) {
return Object.prototype.toString.call(any).slice(8, -1)
}
function clone(any){
if(checkType(any) === 'Object') { // 拷贝对象
let o = {};
for(let key in any) {
o[key] = clone(any[key])
}
return o;
} else if(checkType(any) === 'Array') { // 拷贝数组
var arr = []
for(let i = 0,leng = any.length;i<leng;i++) {
arr[i] = clone(any[i])
}
return arr;
} else if(checkType(any) === 'Function') { // 拷贝函数
return new Function('return '+any.toString()).call(this)
} else if(checkType(any) === 'Date') { // 拷贝日期
return new Date(any.valueOf())
} else if(checkType(any) === 'RegExp') { // 拷贝正则
return new RegExp(any)
} else if(checkType(any) === 'Map') { // 拷贝Map 集合
let m = new Map()
any.forEach((v,k)=>{
m.set(k, clone(v))
})
return m
} else if(checkType(any) === 'Set') { // 拷贝Set 集合
let s = new Set()
for(let val of any.values()) {
s.add(clone(val))
}
return s
}
return any;
}浅拷贝
javascript
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}5、Event Loop
JavaScript事件循环
事件循环是js引擎执行js的机制,用来实现js的异步特性
事件循环的过程为:当执行栈空的时候,就会从任务队列中,取任务来来执行,共分3步:
1、取一个宏任务来执行。执行完毕后,下一步。
2、取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。
3、更新UI渲染。
宏任务:包括整体代码script,setTimeout,setInterval、setImmediate。
微任务:原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、Object.observe(已废弃)、 MutationObserver 记住就行了。
6、判断数组
javascript
// ES6中增加的数组方法
Array.isArray()
// 使用constructor判断
function isArray(arr){
return arr.constructor.toString().indexof("Array") > -1
}
function isArray(arr){
return arr.constructor === Array
}
//使用instance判断
function isArray(arr){
return arr instanceof Array
}7、js数据类型
js数据类型有6种,number,string,boolean,object,function,undefined
其中number、string、boolean、undefined是值类型,function和object是引用类型。
8、柯里化
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术
javascript
function curry(fn,firstArg){
// 返回一个新函数
return function(){
// 新函数调用时会继续传参
let resArgs = [].slice.call(arguments)
// 参数合并,通过apply调用原函数
return fn.apply(this,[firstArg,...resArgs])
}
}9、原型与原型链
构造函数有个prototype对象(原型对象),该对象有个constructor属性,指向构造函数。
每个对象都有一个"__proto__"属性,指向它的构造函数的prototype属性
构造函数的prototype属性,也有一个"__proto__"对象,它指向Object的prototype对象
当我们访问对象的属性时候,会先访问该对象中的本身属性(私有属性),如果访问不到会查找对象的proto指向的构造函数的prototype对象,如果其中有要访问的属性,就使用该值,否则继续访问prototype的proto,在其中查找要访问属性。这样一直上溯到Obejct对象,这个就是原型链
10、js继承
原型链继承
将子类原型对象指向父类的实例,根据原型链找到父类的方法和属性
缺点:多个子类实对父类引用类型的操作会被篡改,无法保持子类实例的个性
javascript
// 父类
function Parent(){
this.name = "父类"
this.introduce = function(){
console.log("my name is" + this.name)
}
}
// 子类
function Child(){
this.childName = "子类"
}
child.prototype = new Parent()
let child = new Child()构造函数继承
在子类的构造函数中,执行父类的构造函数,并且为其绑定子类的this
缺点:继承不到父类原型上的属性和方法
javascript
// 父类
function Parent(){
this.name = "父类"
this.introduce = function(){
console.log("my name is" + this.name)
}
}
Parent.prototype.sayHi = function(){
}
// 子类
function Child(){
this.childName = "子类"
Parent.call(this)
}
let child = new Child()
child.introduce()原型链加构造函数继承
缺点:使用子类创建实例对象的时候,其原型中会存在两份相同的属性和方法
javascript
// 父类
function Parent(){
this.name = "父类"
this.introduce = function(){
console.log("my name is" + this.name)
}
}
Parent.prototype.sayHi = function(){
}
// 子类
function Child(){
this.childName = "子类"
Parent.call(this)
}
Child.prototype = new Parent() // 第一次调用Parnet
let child = new Child()
child.introduce()
child.sayHi()原型式继承
利用一个空对象作为中介、将某个对象直接赋值给空对象构造函数的原型,其实就是使用Object.create()
缺点:跟原型链继承一样,多个子类实例的引用类型属性指向相同,可能会篡改
javascript
let Parent = {
name:'父类属性',
sayHi:function(){
console.log(this.name)
}
}
let child = Object.create(Parent)
child.name = '子类属性'
console.log(child)寄生组合式继承
javascript
// 父类
function Parent(){
this.name = "父类"
this.introduce = function(){
console.log("my name is" + this.name)
}
}
Parent.prototype.sayHi = function(){
}
// 子类
function Child(){
this.childName = "子类"
Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
const child = new Child()
child.name = '子类属性'
console.log(child)11、从输入url到看到界面的过程
12、http各个版本的改进
13、https的通信过程
14、https为什么是安全的
15、前端性能优化问题
页面展示可以分为3个阶段,请求页面,加载和解析页面,渲染
1、请求数据阶段主要指标是服务器相应时间,从服务器角度优化
2、加载和解析页面阶段,性能优化的主要思路是减少请求数量、降低资源的大小和避免阻塞
3、渲染阶段优化思路是避免重绘和重排
16、网络攻击有哪些
17、类型判断
typeof判断Null和数组会出错
javascript
console.log(typeof arr) //object x
console.log(typeof obj) //object √
console.log(typeof number) //number √
console.log(typeof string) //string √
console.log(typeof bool) //boolean √
console.log(typeof unl) // object x
console.log(typeof und) // undefined √
//判断数组方法
// 1、利用Array.isArray方法
console.log(Array.isArray(arr))
// 2、利用constructor和instanceof方法
function isArray(arr){
return arr.constructor === Array || arr instanceof Array
}
console.log(isArray(arr))18、null和undefined的区别
本身都表示"没有",但null表示引用类型的对象为空,undefined则表示变量未定义
在相等判断时候,null和undefined是相等的
但Null和Undefined在很多方面有区别
null 表示对象空指针,undeifined表示变量未定义
类型不同
javascript
typeof null // 'object'
typeof undefined // 'undefined'
Number(null) //0
Number(undefined) //NaNnew一个对象发生了什么
1、创建了一个空的js对象(即{}) 2、将空对象的原型prototype指向构造函数的原型 3、将空对象作为构造函数的上下文(改变this指向) 4、判断构造函数的返回值,以决定最终返回的结果 a.如果返回值是基础数据类型,则忽略返回值 b.如果返回值是引用数据类型,则使用return的返回,也就是new操作符无效