【JavaScript面试专题】经典JS面试题详解!

数据类型

基本数据类型:String,Number,Boolean,Symbol,undefined,null;

复杂数据类型:Object,Array,Function,Map等

判断数据类型方法

typeof:能够快速区分基本数据类型,但是引用类型判断都返回object(eg: console.log(typeof function(){}); // function);

instanceof:能够快速区分引用

原型、原型链

原型:实例对象上有__proto__隐式原型,构造函数上有prototype原型对象;__propto__指向构造函数的prototype。

原型链:原型与原型层层相连的过程就叫原型链。

var、let、const

  • var:声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象,可重复定义;

  • let:声明的变量,其作用域为该语句所在的代码块内,不存在变量提升;

  • const:声明的变量不允许修改;

闭包

假如一个函数能访问外部的变量,那么这个函数它就是一个闭包,而不是一定要返回一个函数;

  1. 闭包使用场景
  2. 在全局作用域中读取内部函数的变量;
  3. 防止函数内部的变量执行完成后被销毁,使其一直保存在内存中;
  4. 封装私有变量;

继承

原型链继承: 将函数prototype指向新的对象,基于原型链,既是父类的实例,也是子类的实例;缺点是无法实现多继承,所有实例都会共享父类实例的属性;

构造继承: 将函数call绑定this,可以实现多继承(call多个),解决了所有实例共享父类实例属性的问题;缺点是只能继承父类实例的属性和方法,不能继承原型上的属性和方法;

组合继承: 将上面两种组合使用,可以继承实例属性/方法,也可以继承原型属性/方法;缺点是调用了两次父类构造函数,生成了两份实例;

浅拷贝、深拷贝

浅拷贝: 只拷贝对象里面的数据,不拷贝对象里面的子对象;浅拷贝可以通过assign和三点扩展运算符方式实现

深拷贝: 克隆出一个新的对象,数据相同,但是引用地址不同;深拷贝可以通过lodash.cloneDeep实现,或者递归拷贝,或者转JSON字符串再转JSON(但是会忽略undefined,symbol,不能序列化函数和解决循环引用对象)

call、apply、bind

三个函数的作用都是将函数绑定到上下文中,用来改变函数中this的指向,借用已实现的方法,减少重复代码,节省内存。

call: 改变函数的this上下文后马上执行该函数,接收参数若干个参数列表

Function.prototype.myCall = function(context, ...args) {
    
    
  context = context || window
  let fn = Symbol()
  context[fn] = this
  let result = context[fn](...args)
  delete context[fn]
  return result

apply: 改变函数的this上下文后马上执行该函数,接收参数是数组

Function.prototype.myApply = function(context) {
    
    
  context = context || window
  let fn = Symbol()
  context[fn] = this
  let result
  if (arguments[1]) {
    
    
    result = context[fn](...arguments[1])
  } else {
    
    
    result = context[fn]()
  }
  delete context[fn]
  return result
}

bind: 返回改变了上下文后的函数,不执行该函数,接收参数若干个参数列表;

Function.prototype.myBind = function (context) {
    
    
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    
    
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
    
    
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

== 和 === 区别

==:两边值类型不同的时候,要先进行类型转换,再比较

如果类型不同,进行类型转换

判断比较的是否是 null 或者是 undefined, 如果是, 返回 true .

判断两者类型是否为 string 和 number, 如果是, 将字符串转换成 number

判断其中一方是否为 boolean, 如果是, 将 boolean 转为 number 再进行判断

判断其中一方是否为 object 且另一方为 string、number 或者 symbol , 如果是, 将 object
转为原始类型再进行判断

===:不做类型转换,类型不同的一定不等

防抖和节流

防抖: 防止函数多次调用,将多次执行变为最后一次执行;

节流:防止函数多次调用,将多次执行变为每隔一段时间执行;

宏任务和微任务

宏任务(Task):同步代码、setTimeout 回调、setInteval 回调、IO、UI 交互事件、postMessage、MessageChannel;

微任务(MicroTask):Promise 状态改变以后的回调函数(then 函数执行,如果此时状态没变,回调只会被缓存,只有当状态改变,缓存的回调函数才会被丢到任务队列)、Mutation observer
回调函数、queueMicrotask 回调函数(新增的 API);

注意: 宏任务会被丢到下一次事件循环,并且宏任务队列每次只会执行一个任务;微任务会被丢到本次事件循环,并且微任务队列每次都会执行任务直到队列为空;假如每个微任务都会产生一个微任务,那么宏任务永远都不会被执行了;

localStorage、sessionStorage、cookie、indexDB

localStorage: 一直存在,除非手动去清理,存储大小5M;

sessionStorage: tab标签页关闭就自动清理,存储大小5M;

cookie: 由服务器生成、可设置过期时间,每次携带在请求header中,存储大小4K;

indexDB: 一直存在,除非手动清理,存储大小没有限制;

模块化

AMD: 依赖前置,提前执行 (require.js),语法是define,require

CMD: 依赖就近,延迟执行 (sea.js),语法是 define,seajs.use([],cb)

CommonJS: CommonJS语法 module.exports=fn或者exports.a=1; 通过require(‘./a1’)来引入;CommonJs模块首次执行会被缓存,再次加载只返回缓存结果,require返回的值是输出值的拷贝(对于引用类型是浅拷贝)

ES6 Module: es6 module 语法是 export {…}, import …from…, export输出的是值得引用 注意:NodeJS、webpack都是基于CommonJS该规范来实现的

CommonJS和ES6 Module区别

  • Commonjs是同步导入,因用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大,后者是异步导入,因为用于浏览器端,需下载文件,如果采用同步导入对渲染会有很大影响。

  • CommonJS 在导出时都是值的拷贝,就算导出的值变了,导入的值也不会变。如果想更新,必须重新导入一回。

  • ES Module 导入导出的值指向同一个内存地址。所以,导入值也会随着导出值变化。

  • ES Module 会编译成 require/exports来执行。

猜你喜欢

转载自blog.csdn.net/weixin_48200589/article/details/126626888