JS高频面试题汇总

最近在准备春招的面试,JS的基础问题是每一家公司必问的方面,在准备JS的面试题的时候总结了下面几个经常出现的高频问题,希望可以帮助到大家

1. 闭包

**闭包函数:**声明在一个函数中的函数,叫做闭包函数。

**闭包:**内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后

闭包找到的是同一地址中父级函数中对应变量最终的值

缺点:容易造成内存泄漏,不会被垃圾回收机制回收

function funA(){
  var a = 10;  // funA的活动对象之中;
  return function(){   //匿名函数的活动对象;
        console.log(a);
  }
}
var b = funA();
b();  //10

2. JavaScript有几种类型的值?

栈:原始数据类型(Undefined,Null,Boolean,Number、String、Symbol)

堆:引用数据类型(对象、数组和函数)

两种类型的区别是:存储位置不同;

1,原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;

2,引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

在参数传递方式上,原始类型是按值传递,引用类型是按共享传递

3.new操作符具体干了什么呢?

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、最后隐式的返回 this 。

用new和不用new调用构造函数,有什么区别

一、没有return

    function Person(name) {
        this.name = name;
        console.log(this.name);
    }
    var a = Person('Tom'); // Tom 
    var b = new Person('LI') // LI
    console.log(a) // undefined
    console.log(b) // Person {name: "LI"}

1.不使用new:this对象指向window,并且,不会默认返回任何对象
2.使用new: this对象指向的是Person 实例

二、有return
1.简单数据类型:数值,字符串、布尔值、null、undefined

    function Person(name) {
        this.name = name;
        console.log(this.name);
        return 1
    }
    var a = Person('Tom') // Tom
    var b = new Person('LI') // LI 
    console.log(a) // 1
    console.log(b) // Person {name: "LI"} 
    console.log(a.name) // undefined
    console.log(b.name) // LI

2.复杂数据类型:对象即属性的集合(function、Array、Object)

    function Person(name) {
        this.name = name;
        console.log(this.name);
        return [1,2,3]
    }
    var a = Person('Tom') // Tom
    var b = new Person('LI') // LI 
    console.log(a) // [1,2,3]
    console.log(b) // [1,2,3]
    console.log(a.name) // undefined
    console.log(b.name) // undefined

(1)不使用new:return之后,得到的就是return的返回值
(2)使用new:return之后,就会出现两种情况,一种是返回的是简单数据类型,这时返回值会被自动忽略,返回的还是原来的实例。另一种是返回的是复杂数据类型,此时得到的就是return的返回对象。

4.箭头函数和普通函数的区别

  • 箭头函数是匿名函数,不能作为构造函数,不能使用new
  • 箭头函数不绑定arguments,取而代之用rest参数…解决
  • this的作用域不同,箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
  • 箭头函数没有原型属性

5.bind,call和apply

bind会创建一个新的函数,这个函数的this始终指向bind()方法传入的第一个参数

call和apply

共同点:
都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。
另一种说法,都能够改变方法的执行上下文(执行环境),将一个对象的方法交给另一个对象来执行,并且是立即执行。
不同点:
apply();//最多只能有两个参数–新this对象和一个数组argArray,如果给该方法传递多个参数,则把参数都写进这个数组里边,当然,即使只有一个参数,也要写进数组里边。
call();//可以接收多个参数,第一个参数apply()一样,后面则是一串参数列表。

6.设计模式

// 单例模式
class Singleton {
  constructor() {}
}

Singleton.getInstance = (function() {
  let instance
  return function() {
    if (!instance) {
      instance = new Singleton()
    }
    return instance
  }
})()

let s1 = Singleton.getInstance()
let s2 = Singleton.getInstance()
console.log(s1 === s2) // true

// 工厂模式
class Man {
  constructor(name) {
    this.name = name
  }
  alertName() {
    alert(this.name)
  }
}

class Factory {
  static create(name) {
    return new Man(name)
  }
}

Factory.create('yck').alertName()

// 适配器模式
class Plug {
  getName() {
    return '港版插头'
  }
}

class Target {
  constructor() {
    this.plug = new Plug()
  }
  getName() {
    return this.plug.getName() + ' 适配器转二脚插头'
  }
}

let target = new Target()
target.getName() // 港版插头 适配器转二脚插头


7.ES6 module和commonJS的区别

ES6模块

  • 不共享全局命名空间
  • 模块顶级this的值是undefined
  • 模块中的var声明不会添加到window对象
  • 异步加载和执行
  • 采用export导出,import导入

commonJS模块

  • 模块加载是模块系统执行的同步操作
  • 使用require()指定依赖
  • 导出挂载到module.exports对象上

8.Set和Map

  • Map是一组键值对的结构,具有极快的查找速度
  • Set和Map类似,也是一组Key的集合,但不存储Value。在Set中不存在重复的Key

9.节流和防抖

节流和防抖分别是什么?

  • 函数节流:规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效; 典型的案例就是鼠标不断点击触发,规定在n秒内多次点击只有一次生效
  • 函数防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时;典型的案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时。
function throttle (fn, delay) {
  if(typeof fn !== 'function'){
    throw new TypeError('fn is not a function')
  }
  //记录上一次函数触发的时间
  let lastTime = Date.now()
  return function(...args){
    let now = Date.now()
    if(now - lastTime >= delay){
      fn.call(this, ...args)
      lastTime = Date.now()
    }
  }
}

function debounce(fn, delay) {
  if(typeof fn !== 'function'){
    throw new TypeError('fn is not a function')
  }
  let timer = null
  return function(...args){
    if(timer){
    	clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.call(this, ...args)
    }, delay)
  }	
}

10.手写Promise.all和Promise.race

function myPromiseAll(promiseArr){ 
  const resultArr = []
  return new Promise((resolve, reject) => {
    promiseArr.forEach((promiseItem) => {
      Promise.resolve(promiseItem).then((res) => {
        resultArr.push(res)
      }, (rej) => {
        reject(rej)
      })
    })
  if(restArr.length === promiseArr.length){
    resolve(resultArr)
  } 
  })
}

function myPromiseRace(promiseArr){
  return new Promise((resove, reject) => {
  	promiseArr.forEach((item) => {
  		Promise.solve(item).then((res) => {
  			resolve(res)
  		})
  		.catch((err) => {
  			reject(err)
  		})
    })
}

11.获取url中的参数并转化为对象

  const url = "http:www.xxx.net/x/x.html?id=898602B8261890349226&aa=123&bb=456"
  const params = url.split("?")[1].split("&")
  const obj = {}
  params.map(v => obj[v.split("=")[0]] = v.split("=")[1])   
  console.log(obj) //{id: "898602B8261890349226", aa: "123", bb: "456"}

12.手写发布订阅模式

// 发布订阅模式
class EventEmitter {
    constructor() {
        // 事件对象,存放订阅的名字和事件
        this.events = {};
    }
    // 订阅事件的方法
    on(eventName,callback) {
       if (!this.events[eventName]) {
           // 注意时数据,一个名字可以订阅多个事件函数
           this.events[eventName] = [callback]
       } else  {
          // 存在则push到指定数组的尾部保存
           this.events[eventName].push(callback)
       }
    }
    // 触发事件的方法
    emit(eventName) {
        // 遍历执行所有订阅的事件
       this.events[eventName].forEach(cb => cb());
    }
    // 移除订阅事件
    removeListener(eventName, callback) {
        if (this.events[eventName]) {
            this.events[eventName] = this.events[eventName].filter(cb => cb != callback)
        }
    }
    // 只执行一次订阅的事件,然后移除
    once(eventName,callback) {
        // 绑定的时fn, 执行的时候会触发fn函数
        let fn = () => {
           callback(); // fn函数中调用原有的callback
           this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
        }
        this.on(eventName,fn)
    }
}

// 观察者模式
class Subject {
  observers = []

  addObserver(observer) {
    this.observers.push(observer)
  }
  removeObserver(observer) {
    let index = this.observers.indexOf(observer)
    if(index > -1){
      this.observers.splice(index, 1)
    }
  }
  notify() {
    this.observers.forEach(observer => {
      observer.update()
    })
  }
}


class Observer{
  update() {}
  subscribeTo(subject) {
    subject.addObserver(this)
  }
} 

13.面向对象三大概念及特点

  • 封装
  • 继承
  • 多态
  • 一种面向对象语言需要向开发者提供四种基本能力:
    1. 封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
    2. 聚集 - 把一个对象存储在另一个对象内的能力
    3. 继承 - 由另一个类(或多个类)得来类的属性和方法的能力
    4. 多态 - 编写能以多种方法运行的函数或方法的能力

14.判断是否是数组

一共四种方法

let arr = []

1.  instanceof 

    arr  instanceof Array

2. __proto__

    arr.__proto__  === Array.prototype

3. constructor

    arr.constructor === Array

4. Object.prototype.toString

   Object.prototype.toString.call(arr) === '[object Array]'

5. Array.isArray

    Array.isArray(arr)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值