JavaScript面试准备(一)

274 阅读7分钟

前言

继续整理八股文——JS篇,实习上岸!!!

JavaScript篇

1. JS的数据类型有哪些?

undefined、null、boolean、string、number、object、symbol、bigint(ES10)

基本数据类型:undefiend, null, boolean, string, number, symbol, bigint

引用数据类型:Object, Function, Array, Date, RegExp

2. undefined和null有什么区别

undefined代表未定义,null代表控对象

当变量声明了没有被赋值就会返回undefined

null主要用于给对象作初始化

3. var、let、const的区别

  • 块级作用域
function a() {
  var b;
}
console.log(b) // Uncaught ReferenceError: b is not defined
function a() {
  var b;
  console.log(b)
}
a() // undefined

If 也存在块级作用域,在if外部使用变量不会报错

console.log(b)  // 不输出
if(false) {
  var b
  console.log(b)  // undefined
}
  • 变量提升

变量只能在声明之后使用,否则就会报错

console.log(a)  // Uncaught ReferenceError: a is not defined
console.log(a)  // undefined
var a
  • 暂时性死区

不声明变量之前,该变量不可用

总结

区别varletconst
块级作用域
变量提升
全局添加属性
重复声明
暂时性死区
初始值
指针指向

4. const属性可以修改吗

可以修改。

对于引用类型的数据来说,const保存的是变量的内存地址。

而我们修改const指向的对象中的属性,是可以修改的。

5. 数据检测方法

  • typeof
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
  • instanceof
  • constructor
  • Object.prototype.toString.call()

后面三种常用于判断所有的引用数据类型

6. ==, ===, Object.is() 有什么区别

== 只判断值是否相同。如果两边的类型不同,会强制转换后再比较

=== 先判断类型是否相同,再判断值是否相同,全满足才为 true

Object.is() 弥补全等操作符的不准确运算。在判断 +0 和 -0 为false,两个NaN为true

7. 作用域和作用域链

作用域

作用域是变量(变量作用域又称上下文)和函数生效的区域或集合。决定了代码区块中变量和其他资源的可见性。

分类

  • 全局作用域

不再函数中或是大括号中声明的变量。全局作用域是在window的对象上

var greeting = 'hello'
function greet() {
  console.log(greeting)
}
greet() // hello
  • 函数作用域

函数作用域也叫局部作用域。如果一个变量在函数内部声明,这个变量只能在函数内部访问。

function greet() {
  var greeting = 'hello'
  console.log(greeting)
}
greet() // hello
console.log(greeting) // 报错: Uncaught ReferenceError: greeting is not defined
  • 块级作用域

let和const声明的变量存在块级作用域,在括号之外不能访问这些变量

{
  let greeting = 'hello'
  var lang = 'en'
  console.log(greeting) // hello
}
console.log(lang) // en
console.log(greeting) // 报错: Uncaught ReferenceError: greeting is not defined

词法作用域

词法作用域,又叫静态作用域,变量被创建时就确定好了,而非执行阶段确定的。

var a = 2
function foo() {
  console.log(a)
}
function bar() {
  var a = 3
  foo()
  function log() {
    console.log(a)
  }
  log() // 3
}
​
bar() // 2
foo() // 2

因为foo和bar处于相同的层级,按声明顺序判断

作用域链

当我们使用一个变量,会在当前作用域寻找该变量,如果找不到,就会到他的上层作用域寻找,以此类推,知道全局作用域。如果到全局作用域也没有找到就会报错 xxx is not defined

// 最外层作用域(全局作用域 window)
var a = '1'
function b() {
  // 中间层作用域 (函数b的作用域)
  var c = '2'
  function d() {
    // 最内层的作用域 (函数d的作用域)
    var f = '3'
    // 在函数d的作用域使用变量a, c
    console.log(c) // 此层没有c - 去上一层找 - 找到了,输出
    console.log(a) // 此层没有a - 去上一层找 - 没有 - 再去上一层找 - 找到了
  }
  d()
  // 在函数b的作用域使用变量f
  console.log(f) // 此层没有f - 去上一层找 - 没有,报错
}
b()

8. 深拷贝和浅拷贝

区别 浅拷贝——只复制指向某个对象的指针,新旧对象共享同一内存,不会改变原数据的基本数据,但可以改变子对象。

深拷贝——创造一个一模一样的对象,新旧对象不共享内存,修改也不会改变原对象。

实现方式

  • 浅拷贝

    • Object.assign()

    如果obj只有一层时,是深拷贝

    • Array.prototype.concat()
    • Array.prototype.slice()
  • 深拷贝

    • JSON.parse( JSON.stringify( ) )
    • 递归克隆
    • 函数库 lodash.cloneDeep( obj )

9. 原型和原型链

数组和对象是没有原型的,原型是函数特有的

原型链是数组、对象、函数共有的

image.png

原型: prototype

原型链: [[Prototype]] && __proto__

原型链继承

原型链的根部都是 Object -> null

当我们给一个函数创造实例时,新创建的变量指向的原型是实例的对象

let Person = function() {}
Person.prototype.name = 'kk'
Person.prototypr.fn = function() {
  console.log(this.name)
}
ler p1 = new Person()
console.log(p1)
p1.fn() // p1自身没有fn函数,就会去原型链中去找

自身的原型怎么判断?

item.hasOwnProperty(age)

10. JS闭包

理解:函数内返回一个函数

优点

  • 延长变量的生命周期

函数内部的变量会在函数运行结束后被垃圾回收机制回收掉,闭包的目的就是使函数内部的变量不被回收

  • 创建私有环境

函数内的变量会存在函数作用域中,函数外部是无法使用的,这样就创建了一个私有环境,不会产生数据污染

  • 沟通内外部方法的桥梁

缺点

  • 会造成内存泄漏

因为使用闭包后,闭包内部的变量无法被回收,就会一直占用着内存

11. JS防抖和节流

防抖

当我们连续触发事件,一定时间内不触发则事件处理函数执行一次

节流

当我们连续触发事件,每一段时间执行一次事件处理函数

应用场景

防抖(debounce)

  • Search搜索联想
  • 触发resize事件

节流(throttle)

  • 不断点击触发
  • 监听滚动时间, 比如底部自动加载,上拉刷新

12. for in和for of的区别

for-in 是遍历索引 for-of 是遍历值

for-in

  • for-in返回来的索引的类型是string
let arr = [1, 2, 3]
for (item in arr) {
  console.log(typeof item)  // string * 3
}
  • for-in更适合遍历对象的属性,因为对象的属性是不可枚举的,无法使用for循环来遍历对象。

我们可以使用for-in和hasOwnProperty()搭配,判断某属性是不是该对象的实例属性

遍历对象的键还可以使用Object.keys(obj),生成一个对象全部属性的数组

for-of

  • for-of适合遍历数组、字符串、map、set拥有迭代器对象的集合。
  • for-of无法遍历对象Object,因为对象内没有迭代器iterator
let arr = [1,'2', function() {}, {}]
for (item of arr) {
  console.log(typeof item)  // number string function object
}

13. Map

Map是一种新的集合类型,和对象Object相似,也是键/值的存储机制

Map可以作为构造函数,用new关键字可以创建一个空映射

Map的键可以是任何类型

基本API

  • set()        添加键/值对
  • get()        查找键的值
  • has()        判断键是否存在
  • size          map的大小
  • delete()  删除键/值对
  • clear()    清除map内全部的键/值对
let m = new Map()
m.set('a', 'b')       // Map(1) { 'a' => 'b' }
m.get('a')            // b
m.has('a')            // true
console.log(m.size)   // 1
m.delete('a')         // Map(0) {}
m.clear()             // Map(0) {}

14. Set

set在很多方面是加强的map,因为大多数API和行为是共有的

set存储的格式和数组相似,但是set存储的所有数据是不重复的

基本API

  • set()          添加值

  • has()          判断值是否存在

  • size            set的大小

  • delete()    删除值

  • clear()      销毁集合实例的所有值