JavaScript基础面试题

关于前端面试的题,最近整理了一些干货,经常被问到的一些问题,出现频率比较高的问题,如有不足之处,请高调指出,(⭐代表难度,星星越多越难,以次类推)

文章目录

  • JavaScript篇
    • 3.1、从数据类型引申到事件循环(10道)
      • 1、讲讲js内的数据类型?⭐
      • 2、JS有几种判断变量数据类型的方法?哪种方法最优?⭐⭐
      • 3、== 和 === 的区别⭐
      • 4、null 和 undefined 的区别 ?⭐⭐
      • 5、讲讲var let const 的区别?⭐⭐
      • 6、为什么使用const定义的对象,里面的值可以被修改?⭐⭐
      • 7、请用多种方式实现一个深拷贝;⭐⭐
      • 8、扩展运算符是深拷贝还是浅拷贝?
      • 9、讲讲前端的栈和堆?⭐⭐⭐
      • 10、请讲讲浏览器的事件循环(eventloop)?⭐⭐⭐
    • 3.2、从闭包引申到垃圾机制回收(4道)
      • 1、讲讲闭包的优缺点和实际使用场景?⭐⭐
      • 2、实现防抖和节流函数?⭐⭐
      • 3、什么是内存泄漏,怎么导致怎么防止?⭐⭐
      • 4、讲讲垃圾机制回收⭐⭐⭐
    • 3.4、从原型原型链引导到this和继承(8道)
      • 1、请讲述什么是原型和原型链?⭐
      • 2、讲讲JS内哪些方式可以实现继承?⭐⭐
      • 3、如何实现一个New?⭐⭐
      • 4、讲讲箭头函数和普通函数的区别?⭐
      • 5、为什么箭头函数不能new?⭐
      • 6、对this的理解,请讲述改变call apply bind的异同?⭐
      • 7、如何去实现一个call?⭐⭐⭐
      • 8、如何去实现一个apply?⭐⭐⭐
    • 3.5、从数组操作方向引申
      • 1、讲讲一些数组操作方式⭐
      • 2、数组方法 forEach 和 map 的区别 ?⭐
      • 3、Set 和 Map 的原理 ?⭐⭐
      • 4、如何实现数组去重?⭐⭐
      • 5、如何实现数组排序?⭐⭐⭐
      • 6、给定一个整数数组 `nums` 和一个整数目标值 `target`,请你在该数组中找出 和为目标值 `target` 的那 两个 整数,并返回它们的`数组下标`你可以假设每种输入只会对应一个答案。但是,数组中`同一个元素在答案里不能重复`出现。例:输入nums [2,7,9,11] target 11 ,输出[0,2]⭐⭐⭐
      • 7、如何实现数组扁平化?⭐⭐⭐
      • 8、forEach里面的async await有什么问题吗⭐⭐
      • 9、什么是高阶函数?⭐⭐
      • 10、什么是函数柯里化?⭐⭐
    • 3.6


JavaScript篇

3.1、从数据类型引申到事件循环(10道)

1、讲讲js内的数据类型?⭐

  • 基本数据类型:Number、String、Boolean、Null、Undefined、Symbol
  • 复杂数据类型:Object(Function、Array、Date、RegExp…)
    可参考文献:https://www.runoob.com/js/js-datatypes.html

2、JS有几种判断变量数据类型的方法?哪种方法最优?⭐⭐

1、typeof

console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof undefined); // undefined
console.log(typeof []); // object
console.log(typeof{}); // object
console.log(typeof function(){}); //function
console.log(typeof null); // object
// 优点:能够快速区分基本数据类型 + function
// 缺点:不能将Object、Array和Null区分,都返回object

2、instanceof

console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
//优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象
//缺点:Number,Boolean,String基本数据类型不能判断

3、Object.prototype.toString.call()

console.log(toString.call(2)); //[object Number]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call('str')); //[object String]
console.log(toString.call([])); //[object Array]
console.log(toString.call(function(){})); //[object Function]
console.log(toString.call({})); //[object Object]
console.log(toString.call(undefined)); //[object Undefined]
console.log(toString.call(null)); //[object Null]
//优点:精准判断数据类型
//缺点:写法繁琐不容易记,推荐进行封装后使用

3、== 和 === 的区别⭐

叫做相等运算符,= 叫做严格运算符,
前者会自动转换类型,再判断是否相等,后者不会自动类型转换,直接去比较

4、null 和 undefined 的区别 ?⭐⭐

undefined null
用于变量、属性和方法 用于对象
空值 空值
变量未没有定义 定义未指向
隐藏式 声明式
typeof null // ‘object’ typeof undefined // ‘undefined’
null == undefined  // true
null === undefined  // false
!!null === !!undefined  // true

可参考文献:https://zhuanlan.zhihu.com/p/463090509

5、讲讲var let const 的区别?⭐⭐

var let const
声明变量 声明变量 声明变量\常量
变量提升 存在 不存在 不存在
暂时性死区 不存在 存在 存在
重复声明 不允许 不允许 不允许
块级作用域 不存在 存在 存在

tip:其实在JS中只要是声明一个变量,就会存在变量的提升,但是为什么大家都会说,let和const不存在提升呢,其实是因为在ES6 中明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错也就是暂时性死区(temporal dead zone,简称 TDZ),其实是TDZ组织了变量的提升,不能说他没有提升,只是用报错的方式阻止了。

可参考文献:https://es6.ruanyifeng.com/#docs/let

6、为什么使用const定义的对象,里面的值可以被修改?⭐⭐

因为 对象和数组是引用数据类型 ,我们使用const定义的对象保存的仅是对象在栈中的指针,这就意味着,const仅保证指针不发生改变,修改对象的值不会改变对象的指针,所以是被允许的。

7、请用多种方式实现一个深拷贝;⭐⭐

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
浅拷贝只复制指向某个对象的地址,而不复制对象本身,新旧对象还是共享在堆中的内存,相互影响。
1、序列化和反序列

JSON.parse(   JSON.stringify()   ) 

JSON在执行字符串化的这个过程时,会先进行一个JSON格式化,获得安全的JSON值。因此如果是非安全的JSON值,就会被丢弃掉。其中undefined、function、symbol这三种类型的值就是非安全的(包括该对象的属性循环赋值该对象),所以格式化后,就被过滤掉了,而set、map这种数据格式的对象,也并没有被正确处理,而是处理成了一个空对象。

2、 Object.assign(target, source1, source2)
es6新增的方法,可用于对象合并,将源对象的所有可枚举属性,复制到目标对象上。
3、迭代递归方法

8、扩展运算符是深拷贝还是浅拷贝?

扩展运算符只是部分深拷贝,只是对第一层进行了深拷贝,其他都是浅拷贝

9、讲讲前端的栈和堆?⭐⭐⭐

栈(stack):

  • 栈是自动分配相对固定大小的内存空间,并由系统自动释放
  • 乒乓球结构
  • 先进后出,后进先出

堆(heap):

  • 堆是动态分配内存,内存大小不固定,也不会自动释放,
  • 堆数据结构是一种无序的树状结构,还满足key-value键值对的存储方式

基本数据类型:Undefined,String,Boolean,Null,Number,都是直接按值存放在栈内存中,占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说,更加容易管理内存空间且采用值传递。
引用数据类型:指那些可能由多个值构成的对象,如对象(Object)、数组(Array)、函数(Function) ,它们是通过拷贝和new出来的,这样的数据存储于堆中且采用地址传递。

10、请讲讲浏览器的事件循环(eventloop)?⭐⭐⭐

JS 的 事件执行机制 先同步 —> 所有异步 微任务 —> 异步宏任务 异步 微任务:promise 回调(then , .catch)
异步 宏任务: setTimeOut SetInterval 注意: 有哪些是 一创建就立即执行的

事件循环

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行 检查是否存在 Microtask,如果存在则不停的执行,直至清空
microtask 队列 更新render(每一次事件循环,浏览器都可能会去更新渲染) 重复以上步骤

console.log(1)
setTimeout(() => {
  console.log("2")
}, 0)
console.log(3)

let p = new Promise((resolve, reject) => {
  console.log(4)
  resolve("此处为成功的 信息")
})

p.then(
  (res) => {
    console.log(5)
    //此处为接收成功的信息
  },
  (res) => {
    console.log('6 :>> ', 6)
    //此处为接收失败的信息
  }
)

console.log(7)
//1,3,4,7,5,2

3.2、从闭包引申到垃圾机制回收(4道)

1、讲讲闭包的优缺点和实际使用场景?⭐⭐

概念:闭包是在另一个函数(称为父函数)中定义的函数,并且可以访问在父函数作用域中声明和定义的变量。
优点:
1.可以让我们函数外部能够访问到函数内部的变量。
2.闭包函数保留了使用的变量对象的引用,保证变量对象不会被回收
缺点:不当的时用会造成内存泄漏
这里要补充一下,目前大部分浏览器真实开发场景下,闭包并不会引起内存泄漏,只是由于IE9之前的版本对JavaScript对象和COM对象(IE9之前BOM和DOM中的对象是C++实现的组件对象模型对象,简称COM对象)使用不同的垃圾收集,从而导致内存无法进行回收,这是IE的问题,所以闭包和内存泄漏没半毛钱关系。

2、实现防抖和节流函数?⭐⭐

防抖是控制次数,节流是控制频率
1.防抖 - 防抖是控制次数

function debounce(func, delay) {
    var timeout;
    return function(e) {
        console.log("清除",timeout,e.target.value)
        clearTimeout(timeout);
        var context = this, args = arguments
        console.log("新的",timeout, e.target.value)
        timeout = setTimeout(function(){
          console.log("----")
          func.apply(context, args);
        },delay)
    };
};

var validate = debounce(function(e) {
    console.log("change", e.target.value, new Date-0)
}, 380);

// 绑定监听
document.querySelector("input").addEventListener('input', validate);

2.节流 - 节流是控制频率

function throttle(fn, threshhold) {
 var timeout
 var start = new Date;
 var threshhold = threshhold || 160
 return function () {

 var context = this, args = arguments, curr = new Date() - 0
 
 clearTimeout(timeout)//总是干掉事件回调
 if(curr - start >= threshhold){ 
     console.log("now", curr, curr - start)//注意这里相减的结果,都差不多是160左右
     fn.apply(context, args) //只执行一部分方法,这些方法是在某个时间段内执行一次
     start = curr
 }else{
 //让方法在脱离事件后也能执行一次
     timeout = setTimeout(function(){
        fn.apply(context, args) 
     }, threshhold);
    }
  }
}
var mousemove = throttle(function(e) {
 console.log(e.pageX, e.pageY)
});

// 绑定监听
document.querySelector("#panel").addEventListener('mousemove', mousemove);

3、什么是内存泄漏,怎么导致怎么防止?⭐⭐

内存泄漏可以定义为程序不再使用或不需要的一块内存,但是由于某种原因没有被释放仍然被不必要的占有。在代码中创建对象和变量会占用内存,但是javaScript是有自己的内存回收机制,可以确定那些变量不再需要,并将其清除。但是当你的代码存在逻辑缺陷的时候,你以为你已经不需要,但是程序中还存在着引用,导致程序运行完后并没有合适的回收所占用的空间,导致内存不断的占用,运行的时间越长占用的就越多,随之出现的是,性能不佳,高延迟,频繁崩溃。‘

常见的四种内存泄漏(当然,不止这四种咯)

  • 全局变量
  • 被遗忘的定时器和回调函数
  • DOM引用
  • 闭包错误的使用

4、讲讲垃圾机制回收⭐⭐⭐

JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。

3.4、从原型原型链引导到this和继承(8道)

1、请讲述什么是原型和原型链?⭐

每一个对象都与另一个对象相关联,那个关联的对象就称为原型。
每一个对象,都有一个原型对象与之关联,这个原型对象它也是一个普通对象,这个普通对象也有自己的原型对象,这样层层递进,就形成了一个链条,这个链条就是原型链。通过原型链可以实现JS的继承,把父类的原型对象赋值给子类的原型,这样子类实例就可以访问父类原型上的方法了。

2、讲讲JS内哪些方式可以实现继承?⭐⭐

1、原型链继承

  function Parent() {
    this.name = 'parent'
    this.play = [1, 2, 3]
  }
  function Child() {
    this.type = 'child'
  }
  Child.prototype = new Parent()
  console.log(new Child())

缺陷:声明多个去继承同一个,他们会共享同一个原型,内存空间也是共享的,当一个发生变化的时候,另外一个也随之进行了变化。
2、构造函数继承(借助 call

  function Parent(){
    this.name = 'parent'
  }
  Parent.prototype.getName = function () {
    return this.name
  }
  function Child(){
    Parent.call(this)
    this.type = 'child'
  }
  let child = new Child()
  console.log(child)  // 没问题
  console.log(child.getName())  // 会报错

缺陷:从上面的结果就可以看到构造函数实现继承的优缺点,它使父类的引用属性不会被共享,优化了第一种继承方式的弊端;但是随之而来的缺点也比较明显——只能继承父类的实例属性和方法,不能继承原型属性或者方法
3、组合继承(前两种组合)

  function Parent () {
    this.name = 'parent'
    this.play = [1, 2, 3]
  }
  Parent.prototype.getName = function () {
    return this.name
  }
  function Child() {
    Parent.call(this)
    this.type = 'child'
  }
  Child.prototype = new Parent()
  Child.prototype.constructor = Child
  var s1 = new Child()
  var s2 = new Child()
  s1.play.push(4)
  console.log(s1.play, s2.play)  // 不互相影响
  console.log(s1.getName()) // 正常输出'parent'
  console.log(s2.getName()) // 正常输出'parent'

4、原型式继承
5、寄生式继承
6、寄生组合式继承
7、ES6 的 extends 关键字实现逻辑

3、如何实现一个New?⭐⭐

(1) 创建一个新对象
(2) 将构造函数中的this指向该对象
(3) 执行构造函数中的代码(为这个新对象添加属性)
(4) 返回新对象
扩展代码块(手写new)

 function _new(obj, ...rest){
	 // 基于obj的原型创建一个新的对象
	 const newObj = Object.create(obj.prototype)
	 // 添加属性到新创建的newObj上, 并获取obj函数执行的结果.
	 const result = obj.apply(newObj, rest)
	 // 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
	 return typeof result === 'object' ? result : newObj
}

4、讲讲箭头函数和普通函数的区别?⭐

1、写法不同
2、箭头函数都是匿名函数,普通函数可以是别的
3、箭头函数不能被new,因为他没有自己的this
4、箭头函数本身没有this,他会捕捉上下文的this供自己使用且任何方法都改变不了其指向,如 call() , bind() , apply()

5、为什么箭头函数不能new?⭐

自身没有this

6、对this的理解,请讲述改变call apply bind的异同?⭐

this的指向是根据调用的上下文来决定的,默认指向window对象,指向window对象时可以省略不写
全局环境下

  • 指向window

局部环境下

  • 在全局作用域下直接调用函数,this指向window
  • 对象函数调用,哪个对象调用就指向哪个对象
  • 使用 new 实例化对象,在构造函数中的this指向实例化对象。
  • 使用call或apply改变this的指向
  • 箭头函数本身没有this

7、如何去实现一个call?⭐⭐⭐

Function.prototype.call = function () {
  const [ctx, ...args] = arguments;
  ctx.fn = this || window;
  const result = ctx.fn(...args);
  delete ctx.fn;
  return result;
}

8、如何去实现一个apply?⭐⭐⭐

Function.prototype.apply = function () {
  const [ctx, args] = arguments; // args区别
  ctx.fn = this || window;
  const result = ctx.fn(...args);
  delete ctx.fn;
  return result;
}

3.5、从数组操作方向引申

1、讲讲一些数组操作方式⭐

push、pop、shift、unshift、sort等等
参考文献:https://www.w3school.com.cn/jsref/jsref_obj_array.asp

2、数组方法 forEach 和 map 的区别 ?⭐

foreach参考文献:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
map参考文献:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map

3、Set 和 Map 的原理 ?⭐⭐

4、如何实现数组去重?⭐⭐

5、如何实现数组排序?⭐⭐⭐

6、给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。例:输入nums [2,7,9,11] target 11 ,输出[0,2]⭐⭐⭐

7、如何实现数组扁平化?⭐⭐⭐

8、forEach里面的async await有什么问题吗⭐⭐

9、什么是高阶函数?⭐⭐

10、什么是函数柯里化?⭐⭐

3.6

你可能感兴趣的:(前端面试总结,面试题,原生js,javascript,前端,开发语言)