web前端面试题之代码题+数据结构

本文介绍了JavaScript中的一些核心数据处理技巧,包括获取数组最大值、去重、解析URL、数组扁平化、深拷贝实现以及数据类型的判断。同时,讨论了Array的reduce方法的使用,如数组求和与计数。此外,提到了与数组相关的Map和Set数据结构的区别,以及forEach和map遍历数组的差异和时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.获取最大值

 

方法一

 方法二

const arr = [6, 4, 1, 8, 2, 11, 23];
console.log(Math.max(...arr))

1.去重

// 传统方式
function unique(arr) {
    const res = []
    arr.forEach(item => {
        if (res.indexOf(item) < 0) {
            res.push(item)
        }
    })
    return res
}

数组或者字符串去重

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

上面代码也展示了一种去除数组重复成员的方法。// 使用 Set (无序,不能重复)

// 去除数组的重复成员
[...new Set(array)]

上面的方法也可以用于,去除字符串里面的重复字符。

[...new Set('ababbc')].join('')
// "abc"

1.解析url

 2.打平数组

function flat(arr) {
    // 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]]
    const isDeep = arr.some(item => item instanceof Array)
    if (!isDeep) {
        return arr // 已经是 flatern [1, 2, 3, 4]
    }

    const res = Array.prototype.concat.apply([], arr)
    return flat(res) // 递归
}

const res = flat( [1, 2, [3, 4, [10, 20, [100, 200]]], 5] )
console.log(res)

1.手写深拷贝

/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }

    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
        // 保证 key 不是原型的属性 hasOwnProperty(propertyName)方法 是用来检测属性是否为对象的自有属性,如果是,返回true,否者false;
        if (obj.hasOwnProperty(key)) {
            // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

    // 返回结果
    return result
}

1.js如何判断值的类型

数据结构和类型

数据类型

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 七种基本数据类型:
    • 布尔值(Boolean),有 2 个值分别是:true 和 false.
    • null,一个表明 null 值的特殊关键字。JavaScript 是大小写敏感的,因此 null 与 NullNULL或变体完全不同。
    • undefined,和 null 一样是一个特殊的关键字,undefined 表示变量未赋值时的属性。
    • 数字(Number),整数或浮点数,例如: 42 或者 3.14159
    • 任意精度的整数 (BigInt) ,可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。
    • 字符串(String),字符串是一串表示文本值的字符序列,例如:"Howdy" 。
    • 代表(Symbol)( 在 ECMAScript 6 中新添加的类型).。一种实例是唯一且不可改变的数据类型。
  • 以及对象(Object)

1.几种判断数据类型方式的弊端:

typeof的弊端:只能区分基本类型,如果数据类型为 null、array、object ,使用 typeof 判断都会统一返回 ‘’object‘’ 字符串

console.log(typeof(function() {}))   //function
console.log(typeof(null))   //object
console.log(typeof([1,2,3]))   //object
console.log(typeof({a: 'b'}))   //object

null、undefined 没有 constructor 属性,不能用来判断

推荐用这种Object.prototype.toString.call() 判断数据类型

console.log(Object.prototype.toString.call(1))   //[object Number]
console.log(Object.prototype.toString.call('a'))   //[object String]
console.log(Object.prototype.toString.call(true))   //[object Boolean]
console.log(Object.prototype.toString.call(undefined))   //[object Undefined]
console.log(Object.prototype.toString.call(function() {}))   //[object Function]
console.log(Object.prototype.toString.call(null))   //[object Null]
console.log(Object.prototype.toString.call([1,2,3]))   //[object Array]
console.log(Object.prototype.toString.call({a: 'b'}))   //[object Object]
/**
 * 获取详细的数据类型
 * @param x x
 */
export function getType(x: any): string {
    const originType = Object.prototype.toString.call(x) // '[object String]'
    const spaceIndex = originType.indexOf(' ')
    const type = originType.slice(spaceIndex + 1, -1) // 'String'
    return type.toLowerCase() // 'string'
}

// // 功能测试
// console.info( getType(null) ) // 'null'
// console.info( getType(undefined) )
// console.info( getType(100) )
// console.info( getType('abc') )
// console.info( getType(true) )
// console.info( getType(Symbol()) )
// console.info( getType({}) )
// console.info( getType([]) )
// console.info( getType(() => {}) )

1.如何判断数组:

console.log([1,2,3] instanceof Array)   //true

console.log(ary.constructor == Array)   //true

console.log(Object.prototype.toString.call([1,2,3]))   //[object Array]

1.Map 和 Object的区别

1.Set 和 Array 区别

1.数组 reduce 的用法(数组求和)

        // 传统
        function sum(arr) {
            let res = 0 // 需要新定义一个变量
            arr.forEach(n => res = res + n)
            return res
        }

        // const arr = [10, 20, 30, 40, 50]
        // const res = arr.reduce((sum, curVal, index, arr) => {
        //     // console.log('reduce function ....')
        //     // console.log('sum', sum)
        //     // console.log('curVal', curVal)
        //     // console.log('index', index)
        //     // console.log('arr', arr)

        //     return sum + curVal // 返回值,会作为下一次执行时的第一个参数 sum 的值
        // }, 0)
        const res = arr.reduce((sum, curVal) => sum + curVal, 0)
        console.log('res', res)

1.数组 reduce 的用法(计数)

        const arr = [10, 20, 30, 40, 50, 10, 20, 30, 20]
        const n = 30
        const count = arr.reduce((count, val) => {
            return val === n ? count + 1 : count
        }, 0)
        console.log('count', count)

1.输出字符串

        // 输出字符串
        const arr = [
            { name: '张三', age: '20' },
            { name: '李四', age: '21' },
            { name: '小明', age: '22' },
        ]
        // const str = arr.map(item => {
        //     return `${item.name} - ${item.age}`
        // }).join('\n')
        // console.log(str)
        const str = arr.reduce((s, item) => {
            return `${s}${item.name} - ${item.age}\n`
        }, '')
        console.log(str)

---------------------------------------------------------------------------------------------------------------------------------

forEach 和 map 遍历数组的区别,谁更快

forEachmap 都是用于遍历数组的方法,但它们有一些区别。

forEach 方法是遍历数组的每一个元素,并为每个元素执行一个回调函数。它不会返回一个新数组,而是在原始数组上执行操作。因此,它通常用于执行一些副作用,如打印数组的内容或修改原始数组。

map 方法与 forEach 类似,但它会返回一个新数组,其中包含通过对每个元素应用回调函数所生成的新值。这使得 map 方法更适合将一个数组转换为另一个数组,而不是执行副作用。

在执行相同的操作时,map 方法通常比 forEach 方法更快,因为它可以使用内部优化来创建一个新数组,而 forEach 方法需要手动创建一个新数组。但是,这取决于具体的实现和操作。在一些简单的情况下,forEach 方法可能比 map 方法更快。

总之,forEachmap 都有各自的用途,需要根据具体的场景选择使用哪种方法

---------------------------------------------------------------------------------------------------------------------------------

时间复杂度

---------------------------------------------------------------------------------------------------------------------------------

O(n)

 o(n^2)

---------------------------------------------------------------------------------------------------------------------------------

时间复杂度

 

 

---------------------------------------------------------------------------------------------------------------------------------

请解释一下数据结构中的栈是什么?

栈是一种线性数据结构,它可以被视为一组元素的集合,其中元素可以通过两个主要操作进行访问:压入和弹出。这些操作可以用来添加和删除元素但是只有栈顶的元素可以被访问或删除。

栈通常被描述为一种具有后进先出(LIFO)行为的数据结构,这意味着最后被压入的元素将是第一个被弹出的元素。当元素被压入栈中时,它们被放置在栈顶,而弹出操作则从栈顶移除元素。

相当于一个桶

JavaScript中栈的应用示例:

  1. 函数调用栈:

当我们在JavaScript中调用一个函数时,JavaScript引擎会使用栈来跟踪函数调用的顺序。每个函数调用都会在栈中创建一个新的栈帧,用于存储函数的参数、局部变量和执行状态等。当函数执行完成后,它的栈帧将被弹出,控制权将返回到上一层调用的函数。例如:

 在上面的示例中,当我们调用main()函数时,JavaScript引擎会创建一个新的栈帧,用于存储main()函数的参数、局部变量和执行状态等。在main()函数中,我们先调用add()函数,将其返回值存储到变量z中,然后再调用multiply()函数,将其返回值存储到变量w中。最终,JavaScript引擎会将main()函数的栈帧弹出,程序结束执行。

 

 

 

---------------------------------------------------------------------------------------------------------------------------------

数据结构中的队列是什么?

数据结构中的栈是什么?队列是一种常见的数据结构,它具有“先进先出”(First In First Out,FIFO)的特性。类似于排队购买电影票或在餐厅等待服务,先到达的人先被服务,后到达的人则需要等待。在队列中,新的元素被插入到队列的末尾,而已存在于队列中的元素则被移动到更靠近队列的前端。当需要删除元素时,队列中最靠近前端的元素首先被删除,而最靠近末尾的元素则保持不变。

队列的基本操作包括入队(Enqueue),即将新元素添加到队列的末尾;出队(Dequeue),即从队列的前端删除元素并返回该元素;以及查看队列的前端元素(Front)和查看队列是否为空(IsEmpty)。队列还可以实现一些其他操作,如查找元素(Search)、计算队列中元素的数量(Size)和清空队列(Clear)等。

队列在计算机科学中有着广泛的应用,例如在操作系统中进行进程调度,以及在网络协议中进行数据包的传输和路由等。

在JavaScript中,队列也是一个常见的数据结构,可以使用数组来实现。以下是JavaScript中队列的一些常见应用:

  1. 事件队列:JavaScript在执行异步操作时,会将事件加入到事件队列中。例如,当用户点击按钮时,点击事件会被添加到事件队列中。当JavaScript引擎完成当前执行的任务时,它会查看事件队列中是否有事件,如果有则执行相应的事件处理程序。

  2. 数据缓存:队列可以用于实现缓存机制,例如,将需要处理的数据加入到队列中,然后按照先进先出的顺序逐个处理数据。

  3. 循环队列:循环队列是一种特殊的队列,它可以通过在队列末尾添加元素来实现循环。例如,在实现滚动消息时,可以使用循环队列来循环显示消息。

  4. 实现算法:队列也可以用于实现一些算法,例如广度优先搜索和二叉树的层次遍历等。

总之,队列在JavaScript中有着广泛的应用,可以帮助我们实现各种复杂的功能和算法。

 

---------------------------------------------------------------------------------------------------------------------------------

数据结构中的链表是什么?

当涉及到链表时,最常见的两种类型是单向链表和双向链表。下面是它们的图示:

单向链表:

+---+    +---+    +---+    +---+    +---+
| 1 | -> | 2 | -> | 3 | -> | 4 | -> | 5 |
+---+    +---+    +---+    +---+    +---+

双向链表:

+---+    +---+    +---+    +---+    +---+
| 1 | <-> | 2 | <-> | 3 | <-> | 4 | <-> | 5 |
+---+    +---+    +---+    +---+    +---+

在上述示例中,每个节点都由一个值和指向下一个节点的指针组成。对于双向链表,还有一个指向前一个节点的指针。链表中的第一个节点称为“头”节点,最后一个节点称为“尾”节点。

 --------------------------------------------------------------------------------------------------------------------------------

 --------------------------------------------------------------------------------------------------------------------------------

 二叉树

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值