前端js常见面试题

面试题

1.js基本类型有哪些?

  • string number boolean undefined null

2.null和undefined的区别?

  • null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的 原始值,转为数值时为NaN;

  • typeof null 返回的是object,typeof undefined返回的是undefined

3.谈谈你对NaN的理解?

Not a Number 不是一个数字,但是数值类型
+> NaN 与其他数值进行比较的结果总是不相等的,包括它自身在内

4.谈谈你对this的理解?

  • this只能在作用域内使用,在全局作用域内调用this指向全局window

  • this在局部作用域内使用,谁调用函数,this就指向谁

5.类型转换

  • 转数值 Number parseInt parseFloat

  • 转字符串 toString String

  • 转布尔 Boolean

  • '' undefined NaN 0 null 都会转换成false

6.值传递和引用传递的区别

  • 基础数据类型和复杂的数据类型在赋值的时候因为存储方式不一样,导致赋值的内容也不一样。

  • 基础的数据类型之间赋值是直接将一个变量的数据拷贝给另外一个变量,一个变量的变化不会改变另外一个变量的数据,被称为值传递的过程。

  • 复杂数据类型之间的赋值是将一个变量的地址拷贝给另外一个变量,因为地址(引用)指向的数据是同一个位置,所以一个变量变化会导致另一个也发生变化,这种赋值过程被称为引用传递。

7.谈谈你对js作用域的理解

  • 作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域

  • 我们一般将作用域分成:

    • 全局作用域

      任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问

    • 局部作用域

      • 块级作用域

        ES6引入了let和const关键字,和var关键字不同,在大括号中使用let和const声明的变量存在于块级作用域中。在大括号之外不能访问这些变量

      • 函数作用域

        函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问

8.js的内置构造函数有哪些,他们都有哪些共性

  • Array Object String Date Function RegExp Number Boolean

  • 使用的时候都要用new关键字去实例化

9.构造函数和普通函数以及箭头函数区别

  • 构造函数在定义的时候和普通函数没有区别,只是定义的时候要求名称首字母大写。

  • 箭头函数不可以当成构造函数使用,因为箭头函数的this指向问题

  • 普通函数this指向看谁在调用这个函数,如果没有找到,一般是window的隐式调用

  • 箭头函数this有两种说法:一种认为箭头函数没有this,一种认为箭头有this和外层的this一样

  • 构造函数this指向当前的实例对象

10.var let const的区别

  1. var声明的变量会挂载在window上,而let和const声明的变量不会
  2. var声明变量存在变量提升,let和const不存在变量提升
  3. let和const声明形成块作用域
  4. 同一作用域下let和const不能声明同名变量,而var可以
  5. 使用let/const声明的变量在当前作用域存在暂存死区
  6. const一旦声明必须赋值,不能使用null占位,声明后不能再修改,如果声明的是复合类型数据,可以修改其属性

11. 闭包的含义,闭包的使用场景,闭包的优缺点

  • 闭包的含义
    • 全局作用域(window)随着页面打开就会自动生成,一直到页面关闭才会被销毁
    • 局部作用域在函数执行时会生成,等函数执行完成之后就会被销毁,局部作用域销毁之后局部的变量也会被销毁。如果我们想要局部作用域函数执行之后不被销毁,我们可以在函数内部返回一个引用数据类型。闭包使用的就是返回一个函数,这样函数作用域就不会被销毁,局部变量会在内存空间内。
    • 只要我们在调用这个函数,该函数内部有一个局部变量,返回的函数内部使用着这个局部变量,此时就可以访问到局部变量。
  • 闭包的使用场景
    • 闭包一般在我们希望避免全局变量污染时会使用,我们使用一个局部变量,这样别人就无法覆盖我使用的变量
  • 闭包的优缺点
    • 优点
      1. 可以访问一个局部变量
      2. 可以延长一个局部变量的生命周期
      3. 可以避免全局变量污染问题
    • 缺点
      1. 占用内层空间 大量使用闭包会造成 栈溢出
      2. 由于闭包会一直占用内存空间,直到页面销毁,我们可以主动将已使用的闭包销毁:
        只有我们主动切断链接,将这个链接地址置为null,闭包才会被垃圾回收机制回收内存空间

12. this关键字以及修改this指向

  • this指向问题
    1. 普通函数内部的this看调用的对象,有人调用,this就指向调用者,如果没有找到谁在调用默认全局在调用,this指向window。
    2. 箭头函数内部的this有两种说法:一种认为箭头函数没有this,一种认为箭头有this和外层的this一样。总之箭头函数的this指向看它定义位置作用域的this
    3. 构造函数内部的this指向当前创建出来的实例
  • 修改this的指向
    1. call/apply/bind都可以修改this的指向
    2. call和apply在改变函数this指向的同时会调用这个函数,如果要传参,call放在第二个参数后面,apply第二个参数里面传一个数组
    3. bind改变函数this指向不会执行函数,会返回一个新的函数

13.new关键字做了哪些事情?

  1. new会创建一个空对象
  2. new会将构造函数内部this指向到这个创建出来的对象
  3. new会将创建对象的__proto__指向构造函数的prototype
  4. new会返回这个创建出来的对象

14.浏览器缓存的认识

  1. 缓存机制是浏览器自带的机制,浏览器会将get请求访问到的资源缓存到本地,这样做的目的是为了提高用户的访问速度和节约用户的流量。
  2. 但是缓存也会造成一些问题,有时候服务端文件发生变化名称还是之前老的名字,浏览器无法识别这个文件的变化,还会使用缓存中的文件。
  3. 为了解决这个问题我们有时候需要在请求的过程中告诉浏览器我们不使用缓存,针对于get请求只要每次携带的参数变化浏览器就不会使用缓存文件,认为你要获取新的数据。

15.跨域和跨域的解决方案

什么是跨域?

  • 因为浏览器出于安全考虑,有同源策略。也就是说,如果协议、域名或者端口有一个不同就是跨域,Ajax 请求会失败。
  • 那么是出于什么安全考虑才会引入这种机制呢? 其实主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。
  • 也就是说,没有同源策略的情况下,A 网站可以被任意其他来源的 Ajax 访问到内容。如果你当前 A 网站还存在登录态,那么对方就可以通过 Ajax 获得你的任何信息。当然跨域并不能完全阻止 CSRF。
  • 然后我们来考虑一个问题,请求跨域了,那么请求到底发出去没有? 请求必然是发出去了,但是浏览器拦截了响应。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax就不会。因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

跨域的解决方案

  1. JSONP
    • JSONP 的原理很简单,就是利用script标签没有跨域限制的漏洞。通过script标签访问需要访问的后端的地址,后端返回一个函数调用的方式,将需要发送给前端的数据当成这个函数的参数,前端只需要定义一个同名的函数接受后端返回的数据即可。
    • JSONP使用简单且兼容性不错,但是只限于get请求,无法发送post请求
    • JSONP使用还需要后端的配合
    • 此种记住已经不是ajax请求了,是script请求
  2. 配置反向代理
    • 因为同源策略是限制浏览器访问别人的服务器,我们可以绕过直接请求,先让我们浏览器请求自己的服务器,由自己的服务器请求别人的,拿到数据之后返回给我们的浏览器。这种只需要简单的配置即可(ajax请求)
  3. 后端配置CORS
    • 无需了解,一般也不建议这样配置。

16.事件委托

什么是事件委托

  • 在开发中,如果我们想要给一个元素绑定事件,需要获取到这个元素进行绑定,但如果元素是动态生成,无法获取从而无法绑定,此时我们就需要利用冒泡原理把这个事件委托给这个元素的页面存在的父元素来绑定

使用场景

  1. ajax请求到的数据动态渲染到页面
  2. js动态创建插入页面的

如何实现事件委托

  • 获取页面已存在的父元素,给这个父元素绑定同类型的事件,利用冒泡原理子元素也会触发这个事件,在通过event.target来缩小事件触发的范围

17. 原型和原型链

原型概念

  1. 每一个函数都有一个prototype属性,构造函数也有这个属性
  2. 每一个对象都有一个__proto__属性,实例化对象也有这个属性
  3. 每个实例化对象的__proto__指向构造函数的prototype

原型链概念

  1. 每一个对象访问属性和方法的时候会优先使用自己的属性和方法
  2. 自身没有这个属性和方法会去它的__proto__里面找这个属性和方法,这一层的__proto__就是它构造函数的prototype
  3. 如果这一层没有会继续往下一层__proto__里面找 下一层的__proto__就是它父类构造函数的prototype
  4. 一直找到最上层构造函数Object,它的原型指向null,所以原型链顶层是null,这是属性会出现undefined方法会报错

18.cookie的特点

  1. 存储大小有限制,一般是4KB左右,数量有限制,一般是 50 条左右
  2. 有时效性,也就是有过期时间,默认的过期是session级别
  3. 有域名限制,只能在域下面存储,无法在本地存储,当前域只能使用自己域下面的cookie,无法访问其他域的cookie
  4. 服务端和客户端公用的空间,一般存放一些前后端公用的数据

19.cookie和localStorage还有sessionStorage区别

  1. cookie一般存储比较重要的数据,前后端公用的数据,有一定的大小限制(4kb),有过期时间
  2. localStorage和sessionStorage存储的数据只能前端使用,相对于比较大(5M)
  3. localStorage存储的数据是永久的,除非用户自己手动清除缓存,否则会一直存在。sessionStorage存储的数据是会话级别的,关闭浏览器就会自动删除

20. 防抖和节流

参考博客-防抖和节流的区别

21.js实现继承的方式

  1. 借助构造函数实现继承
    • 在子类的构造函数里面调用父类的构造函数,利用call或者apply改变父类构造函数this的指向
    • 优点:
      • 子类实例可以使用父类的属性和方法
    • 缺点:
      • 子类实例无法使用父类原型上的属性和方法
  2. 原型链继承
    • 将子类构造函数的原型指向父类的某个实例
    • 优点:
      • 子类实例可以使用父类原型上的属性和方法
    • 缺点:
      • 子类实例无法使用父类的属性和方法
      • 子类实例访问constructor属性得到的不是子类的构造函数,而是父类的构造函数
  3. 组合继承
    • 借助构造函数实现继承 + 原型链继承
    • 这样子类的实例既可以使用父类的属性和方法也可以使用父类原型上的属性和方法
    • 将子类构造函数原型的constructor属性重新指向子类的构造函数
    • 缺点
      • 会调用父类构造函数两次
        • 一次是在子类的构造函数内部
        • 一次是在将子类构造函数原型指向父类实例
      • 虽然会调用两次但是没有太大影响
  4. 使用es6的class和extends关键字实现继承
上一篇:Cypress-vscode编辑器代码提示设置、自动填充代码块


下一篇:js事件