面试宝典

104 阅读15分钟

js基础 整理知识 简易版 介绍:面试题内容没变,可根据大白话去讲述

备注: 整理的不是很详细

Two-JavaScript基础

1 JavaScript的基本类型有哪些?引用类型有哪些?null和undefined的区别?(必会)

数据类型 基本数据类型: Number.stirng,Boolean,null,underfiend, 引用数据类型:Function,object,Array

区别: undefined:变量声明但未初始化时的值 null:准备用来保存对象,还没有真正保存对象的值。从逻辑角度,null表示一个空对象指针

2 如何判断JavaScript的数据类型?

判断方法 typeof typeof可以用来区分除了null类型以外的原始数据类型,对象类型的可以从普通对象里面识别出函数: typeof undefiend //"undefined" typeof null // "object" typeof 1 //"number"

问题一:typeof不能识别null,如何识别null?

解答: 如果需要判断是否为null,可以直接使用=== 全等运算符来判断(或者使用下面的 object.prototype。toString方法)

问题二 typeof 作用于未定义的变量,会报错吗?

解答:不好报错,返回undefined

问题三 typeofNumber(1)的返回值是什么?

解答:'Number'.numberhe1stirng 作为普通函数调佣的时候,把参数转换为相应的原始数据类型,也就是类似于一 个强制类型转换的操作,而不是默认当做构造函数

问题四 typeofnewNumbeer的返回值是什么?

答案:‘object’

chantGpt解答: typeof 是 JavaScript 中的一个操作符,用于返回一个值的数据类型。typeof newNumber 的返回值将取决于 newNumber 变量的类型。

如果 newNumber 是一个数字类型的变量,那么 typeof newNumber 将返回 "number"; 如果 newNumber 是一个字符串类型的变量,那么 typeof newNumber 将返回 "string"; 如果 newNumber 是一个布尔类型的变量,那么 typeof newNumber 将返回 "boolean"; 如果 newNumber 是一个数组类型的变量,那么 typeof newNumber 将返回 "object"; 如果 newNumber 是一个对象类型的变量,那么 typeof newNumber 将返回 "object"; 如果 newNumber 是一个 null 类型的变量,那么 typeof newNumber 将返回 "object"; 如果 newNumber 是一个未定义的变量,那么 typeof newNumber 将返回 "undefined"。

请注意,typeof 操作符返回的是一个字符串,表示被操作值的数据类型。

3=== 和 == 区别

区别: JavaScript中,=== 和 == 是两个用于比较相等性的操作符,它们之间有一些区别。

  1. ===(严格相等):=== 用于比较两个值的类型和值是否完全相等,如果两个值的类型和值都相等,返回 true,否则返回 false。严格相等比较不会进行类型转换。
  2. ==(相等):== 用于比较两个值的值是否相等,如果两个值相等,返回 true,否则返回 false。相等比较会进行类型转换,尝试将两个值转换为相同的类型再进行比较。

区别如下:

  • 当比较两个不同类型的值时,=== 会直接返回 false,而 == 会先进行类型转换后再进行比较。
  • 对于字符串和数字比较,=== 在类型和值都相等的情况下才返回 true,而 == 可能会进行类型转换后再比较。
  • 对于 null 和 undefined 的比较,=== 在两者都为 null 或 undefined 时返回 true,而 == 会将它们视为相等。
  • 对于对象比较,无论使用 === 还是 ==,只有当两个对象引用同一个对象(即指向同一个内存地址)时才返回 true

通常情况下,推荐使用 === 进行相等性比较,因为它不会进行隐式的类型转换,更符合直觉和预期。而 == 的使用往往需要更小心,需要注意进行类型转换后的结果,以避免出现意外的比较结果。

4 null、undefined的区别(了解)

区别 null表示一个对象被定义了,值为"空值" undefined表示不存在这个值 typeoffundefined //undefined undefined: 是一个表示"无"的原始值或者说表示"缺少值",就是此处应该有一个值,但还没有定义,当尝试读取时会返回undefined 例如变量被声明了,但没有赋值时,就等于undefined

typeofnull// "object" null: 是一个对象(空对象,没有任何属性和方法) 例如作为函数的参数,表示该函数的参数不是对象; 注意: 在验证null时,一定要使用 === ,因为 === 无法分别null 和undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义。

典型用法是: 1 变量被声明了,但没有赋值时,就等于undefined 2 调用函数时,应该提供的参数没有提供,该参数等于 undefined 3 对象没有赋值的属性,该属性的值为undefined 4 函数没有返回值时,默认返回的值为undefined null表示"没有对象",即该处不应该有值,典型用法是: 4.1 作为函数的参数,表示该函数的参数不是对象 4.2 作为对象原型链的终点

5、 JavaScript中什么情况下会返回undefined值?(必会)

1 访问声明,但是没有初始化的变量 var aaa: console.log(aaa); //undefined

2 访问不存在的属性 var aaa = {} ; console.log(aaa.c)

3 访问的函数的参数没有被显示的传递值 fuction(b){ console.log(b) // undefined }

4 访问任何被设置为 undefined 值的变量 var aaa = undefined; cosole.log(aaa); //undefined

5 没有定义 return 的函数隐式返回 function aaa() { return; } console.log(aaa()); //undefined

6 函数return 没有显式的返回任何内容 fuction(){ return; } console.log(aaa()); //undefined

6 多维数据扁平几种方法(必会)

1 数组字符串化

let arr= [222,333.444].[55,66,77]
arr='';
arr = arr.split(',');
console.log(arr); //["222","333","444","55","66","77"]

2 递归

fucnction reduce Dimension(arr){
let ret = [];
let to Arr = function(arr){
   arr.forEach(function(item){
   item instanceofArray?toArr(item);
   });
   }
   toArr(arr);
   returnret;
}

3 Array.prototype.flat()

var arr1 = [1,2,[3,4]];
arr1 = flat();
// [1,2,3,4]
var arr2 = [1.2.[3,4],[5,6]];
arr2.flat()
// [1,2,3,4,[5,6]]
var arr3 = [1,2[3,4,[5,6]];
arr3.flat(2);
//[1,2,3,4,5,6]
//使用Infinity作为深度,展开任意深度的嵌套数组
arr3.flat(Infinity);
//[1,2,3,4,5,6]

4 使用stack无限反嵌套多层嵌套数组

var arr1 = [1,2,3[1,2,3,4[2,3,4]];
function flatten(input) {
   const stack = [...input];
   constres = [];
   while(stack.length){
   // 使用pop从stack中取出并移除值
   const next = stack.pop();
   // 使用push送回内层数组中的元素,不会感动原始输入 originalinput
   stack.push(...next)
   }else{
   res.push(next)
   }
   // 使用reverse恢复原数组的顺序
   returnres.reverse();
   }
   flatten(arr1); // [1,2,3,1,2,3,4,2,3,4]

5.使用reduce.concat和递归无限反嵌套多层嵌套的数组

   var arr1=[1,2,3[1,2,3,4,[2,3,4]]];
   
   fucnction flattenDeep(arr1){
      return arr1.reduce((acc,val) => Array.isArray(val) ? acc.concat(flattenDeep(val)): acc.concat(val),[]);
      }
      flattenDeep(arr1);
      // [1,2,3,1,2,3,4,               2,3,4]

7 怎么判断两个对象相等?(必会)

Es6中有一个方法判断两个对象是否相等,这个方法判断是两个对象引用地址是否一致

let obj - {
   a:1
}
let obj2 = 1
  a:1
  }
console.log(Object.is(obj1,obj2)) // false
let obj3 = obj1
console.log(object.is(obj,obj3) // false

当需求是比较两个对象内容是否一致时就没用了 想要比较两个对象内容是否一致,思路是遍历对象的所有键名键值是否一致; 1 判断两个对象是否指向同一内存 2 使用 Object.getOwnPropertyNames获取对象所有键名数组 3 判断两个对象的键名数组是否相等 4 遍历键名,判断键值是否相等

let Obj11 = {
a:1,
b:{
c:2
}
}

let obj2 ={
b:{
c:3
},
a:1
}
function isObject ValueEqual(a,b){
// 判断两个对象是否指向同一内存,指向同一内存返回true
if(a== b) return  true
// 获取两个对象键值数组
let a props=object.get(OwnPropertyNames(a))
let b Props=object.getOwnPropertyNames(b)
// 判断两个对象键值数组长度是否一致,不一致返回false
if(aProps.length! = bProps.length)returnfalse
// 遍历对象的键值
for(let propina)
// 判断a的键值,在b中是否存在,不存在,返回false
if(b.hasOwnProperty(prop)){
// 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回
false if(typeof a[prop] === 'object'){
if(b.hasOwnproperty(prop)){
 // 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回
 false if(typeof a [prop] === 'object'){
    if (!isObject Value Equal(a[prop],b[prop]) return flase
    }else if {
    (a[prop]! ==b[prop]){
    return false
    }
    }else{
    return false
    }
    }
    return false
    }
    }
    console.log(isObject Value Equal(obj1,obj2)) // false

8 什么类数组(伪数组),如何将其转换为真实的数组?(必会)

伪数组

  1. 具有length属性
  2. 按索引方式存储数据
  3. 不具有数组的push.pop 等方法 伪数组(类数组):无法直接调佣数组方法或期望 length属性有什么特殊的行为,不具有数组的 Push.pop等方法,但仍可以对真正数据遍历方法来遍历它们,典型的是函数 doucument,childnodes之类的,它们返回的nodeList对象都属于伪数组

伪数组 - 真实数组

  1. 使用Array.from() ---Es6
 [].slice.call(eleArr)或则Array.prototype.slice.call(eleArr) 示例:
let else Arr = doucument.querySelectorAll('li');
Array.from(elsArr).forEach(function(item){
alert(item)
})
let ele Arr = doucument.querySlectorAll('li'):
[].slice.call(eleArr).forEach(function(item){
alert(item);
})

9 如何遍历对象的属性?(必会)

如何遍历对象的属性。

在JavaScript中,可以使用循环来遍历对象的属性。有几种方法可以实现:

1. for...in循环:使用for...in循环可以遍历对象的所有可枚举属性。

```
for (let key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(key + ": " + obj[key]);
  }
}

javascript复制代码
```

在循环体中,可以通过obj[key]来访问属性的值。

2. Object.keys()方法:此方法返回一个由对象的可枚举属性组成的数组。可以使用forEach()或for...of循环遍历这个数组。

```
Object.keys(obj).forEach(key => {
  console.log(key + ": " + obj[key]);
});

// 或

for (let key of Object.keys(obj)) {
  console.log(key + ": " + obj[key]);
}

javascript复制代码
```

3. Object.entries()方法:此方法返回一个由对象的可枚举属性键值对组成的数组。同样,可以使用forEach()或for...of循环遍历这个数组。

```
Object.entries(obj).forEach(([key, value]) => {
  console.log(key + ": " + value);
});

// 或

for (let [key, value] of Object.entries(obj)) {
  console.log(key + ": " + value);
}

javascript复制代码
```

4. Object.getOwnPropertyNames()方法:这个方法返回一个由对象的所有属性组成的数组,包括不可枚举属性。可以使用forEach()或for...of循环来遍历这个数组。

```
Object.getOwnPropertyNames(obj).forEach(key => {
  console.log(key + ": " + obj[key]);
});

// 或

for (let key of Object.getOwnPropertyNames(obj)) {
  console.log(key + ": " + obj[key]);
}

javascript复制代码
```

10.JavaScript中的作用域、预解析与变量声明提升?(必会)

作用域是指变量、函数和对象的可访问范围。JavaScript有全局作用域和函数作用域。

预解析

(Hoisting)是JavaScript的一个特性,它会在代码执行之前将变量声明和函数声明提升到当前作用域的顶部。这意味着你可以在声明之前使用变量或调用函数,而不会引发错误。

变量声明提升

指的是在代码中,所有的变量声明(使用var或let关键字)在代码执行之前都会被提升到当前作用域的顶部。这意味着你可以在变量声明之前使用变量,但是变量的赋值操作并不会被提升。

例如:

console.log(a); // undefined
var a = 5;

// 上述代码在执行时实际上被预解析为:
var a;
console.log(a); // undefined
a = 5;

javascript复制代码

在预解析阶段,var a被提升到代码的顶部,但是赋值操作并没有被提升,因此在第一行打印a时,它的值为undefined

函数声明也会被预解析并提前到当前作用域的顶部,这意味着你可以在函数声明之前调用这个函数。

例如:

foo(); // "Hello, world!"

function foo() {
  console.log("Hello, world!");
}

// 上述代码在执行时实际上被预解析为:
function foo() {
  console.log("Hello, world!");
}
foo(); // "Hello, world!"

javascript复制代码

在预解析阶段,函数声明function foo()被提升到代码的顶部,因此在第一行调用foo()时,函数已经可以被调用。

需要注意的是,变量声明提升只适用于使用varlet关键字声明的变量,并不适用于使用const关键字声明的常量。而函数声明提升则适用于所有的函数声明,包括普通函数和函数表达式。

为了编写更具可读性和可维护性的代码,建议在变量使用之前先进行声明,而不是依赖于变量声明提升这种特性。

拓展作用域:

  1. 全局作用域(Global Scope):全局作用域是整个程序中都可访问的作用域。在全局作用域中声明的变量和函数可以在程序的任何地方被访问。
  2. 函数作用域(Function Scope):函数作用域是指在函数内部声明的变量和函数只能在函数内部访问。函数作用域使得变量在函数内部具有局部性,不会与其他作用域中的变量产生冲突。
  3. 块级作用域(Block Scope):块级作用域是指由花括号({})包围的代码块中声明的变量。在ES6之前,JavaScript中没有块级作用域,变量声明都是函数作用域或全局作用域。但是在ES6中引入了letconst关键字,它们允许在块级作用域中声明变量。

作用域的作用主要有以下几个方面:

  1. 避免变量冲突:作用域可以确保变量在不同的作用域中具有唯一的标识符,避免了变量冲突的问题。不同作用域中的变量可以拥有相同的名称,而不会产生冲突。
  2. 封装变量:作用域可以将变量封装在函数或块级作用域中,限制其作用范围,提高代码的可读性和可维护性。在函数作用域中声明的变量只能在函数内部访问,而在块级作用域中声明的变量只能在代码块内部访问。
  3. 作用域链与闭包:作用域链是指当访问一个变量时,JavaScript引擎会先在当前作用域查找,如果找不到,就会向上一级作用域继续查找,直到找到该变量或达到全局作用域。作用域链的机制使得内部函数可以访问外部函数中的变量,创建了闭包的特性。

11 变量提升与函数提升的区别?(必会)

变量提升和函数提升是JavaScript中的两个概念,它们在变量和函数的声明被提升到作用域顶部的方式上存在区别。

1. 变量提升

(Variable Hoisting):在JavaScript中,变量的声明会被提升到作用域的顶部,无论它们在作用域中的位置是在哪里。这意味着可以在声明之前引用变量,变量会被提升,但其值将为undefined。例如:

console.log(foo); // 输出:undefined
var foo = 'Hello';

javascript复制代码

变量提升的实际行为是将变量的声明提升到作用域顶部,但初始化的赋值并没有被提升。所以在上面的例子中,虽然变量foo被提升,但其值在初始化之前是undefined

  1. 函数提升(Function Hoisting):在JavaScript中,函数的声明也会被提升到作用域的顶部,类似于变量提升。这样我们可以在声明函数之前调用函数。例如:
foo(); // 输出:'Hello'

function foo() {
  console.log('Hello');
}

javascript复制代码

函数提升

会将整个函数声明提升到作用域的顶部,包括函数体,所以我们可以在函数声明之前调用该函数。

需要注意的是,虽然变量和函数的声明会被提升,但初始化的部分并不会被提升。因此,在使用变量或函数之前,仍然需要在代码中进行声明和赋值。

总结来说,变量提升和函数提升都是JavaScript中的特性,它们都是将声明提升到作用域的顶部。区别在于变量提升只提升变量的声明,而函数提升则将整个函数声明(包括函数体)一起提升

12 什么是作用域?(必会)

  • 作用域
  • 当代码在一个环境中执行时,会创建变量对象的一个作用域
  • 由子级作用域返回父级作用域中寻找变量,就叫做作用域链
  • 作用域链中的下一个变量对象来自包含环境,也叫外部环境,而再下一个变量对象则来自下一个包含对象,一直延续到全局执行环境。全局执行的变量对象始终都是作用域链中最后一个对象
  • 作用域链前端始终都是当前执行的代码所在环境的变量对象,如果环境是函数,则其活动对象作为变量对象

13 如何实现数组的随机排序?(必会)

实现数组的随机排序有多种方法,下面介绍三种常见的方法:

方法一:使用洗牌算法(Fisher-Yates算法)

function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

// 示例用法
const myArray = [1, 2, 3, 4, 5];
const shuffledArray = shuffleArray(myArray);
console.log(shuffledArray); // 输出:[3, 2, 4, 1, 5] 或类似的随机排序

javascript复制代码

方法二:使用sort()方法和随机数

function randomSort(a, b) {
  return Math.random() - 0.5;
}

// 示例用法
const myArray = [1, 2, 3, 4, 5];
const shuffledArray = myArray.sort(randomSort);
console.log(shuffledArray); // 输出:类似的随机排序

javascript复制代码

方法三:使用数组的map()方法和随机数

function randomSort() {
  return Math.random() - 0.5;
}

// 示例用法
const myArray = [1, 2, 3, 4, 5];
const shuffledArray = myArray.map((a) => ({sort: Math.random(), value: a}))
                           .sort((a, b) => a.sort - b.sort)
                           .map((a) => a.value);
console.log(shuffledArray); // 输出:类似的随机排序