2024年Web前端最全一些JavaScript的基础题(1),2024年最新京东最新Web前端面试真题解析

最后

小编的一位同事在校期间连续三年参加ACM-ICPC竞赛。从参赛开始,原计划每天刷一道算法题,实际上每天有时候不止一题,一年最终完成了 600+:

凭借三年刷题经验,他在校招中很快拿到了各大公司的offer。

入职前,他把他的刷题经验总结成1121页PDF书籍,作为礼物赠送给他的学弟学妹,希望同学们都能在最短时间内掌握校招常见的算法及解题思路。

整本书,我仔细看了一遍,作者非常细心地将常见核心算法题和汇总题拆分为4个章节。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

而对于有时间的同学,作者还给出了他结合众多数据结构算法书籍,挑选出的一千多道题的解题思路和方法,以供有需要的同学慢慢研究。

//使用reduce的方式

function arrFlat(arr) {

return arr.reduce((pre, cur) => {

return pre.concat(Array.isArray(cur) ? arrFlat(cur) : cur)

}, [])

}

//使用递归加循环的方式

function arrFlat(arr) {

let result = []

arr.map((item, index) => {

if (Array.isArray(item)) {

result = result.concat(arrFlat(item))

} else {

result.push(item)

}

})

return result

}

//将数组先变成字符串,再复原 toString()

//这种方法存在缺陷,就是数组中元素都是Number或者String类型的才能展开

function arrFlat(arr) {

return arr.toString().split(‘,’).map(item=> +item)

}

数组去重

定义去重数据

let arr = [1, 1, “1”, “1”, null, null, undefined, undefined, /a/, /a/, NaN, NaN, {}, {}, [], []]

我们先看下几种不能去掉重复的引用数据类型的写法

// 使用 Set

let res = […new Set(arr)]

console.log(res)

这种方法虽然很简洁,但是使用该种方法我们可以看到里面的引用数据类型并没有能成功去重,只能去除基本数据类型

//使用filter

let res = arr.filter((item, index) => {

return arr.indexOf(item) === index

})

console.log(res)

//使用reduce

let res = arr.reduce((pre, cur) => {

return pre.includes(cur) ? pre : […pre, cur]

}, [])

console.log(res)

使用该两种方法也和上面的方法一样,不能去掉引用数据类型。

我们再来看一下如何去除引用类型的重复值

利用对象的hasOwnProperty方法进行判断对象上是否含有该属性,如果含有则过滤掉,不含有则返回新数组中

let obj = {}

let res = arr.filter(item => {

if (obj.hasOwnProperty(typeof item + item)) {

return false

} else {

obj[typeof item + item] = true

return true

}

})

console.log(res)

这次可以看到成功的将引用数据类型也去掉了。

除了以上这几种方法,还有一些循环遍历的方法也是类似的

类数组变成数组

类数组是具有length属性,但不具有数组原型上的方法。 比如说arguments,DOM操作返回的结果就是类数组。那么如何将类数组变成数组呢

  • Array.from(document.querySelectorAll('div'))

  • Array.prototype.slice.call(document.querySelectorAll('div'))

  • [...document.querySelectorAll('div')]

数据类型检测

typeof 1 // number

typeof ‘1’ // string

typeof undefined // undefined

typeof true // boolean

typeof Symbol() // symbol

上面的几种类型都能正确的检测,但是引用数据类型除了函数都会显示为object,而且对于 typeof null 也是 object 这是历史遗留下的bug,因为怕影响到一些现有的web项目,所以一直没有修复这个bug。

当检测引用数据类型的时候,用instanceof比较好,它会基于原型链进行查询,如果查询结果在原型链中,就会返回true。

Object.prototype.toString.call(检测数据类型最佳方案)

调用Object原型上的toString()方法,并且通过call改变this指向。返回字符串 ,我们看看八种数据类型分别返回的结果

function checkType(param) {

return Object.prototype.toString.call(param)

}

console.log(checkType(123)) //[object Number]

console.log(checkType(“123”)) //[object String]

console.log(checkType(true)) //[object Boolean]

console.log(checkType({ a: 123 })) //[object Object]

console.log(checkType(() => {})) //[object Function]

console.log(Symbol(1)) //Symbol(1)

console.log(null) //null

console.log(undefined) //undefined

我们再对上述函数进行一下处理

function checkType(param) {

return Object.prototype.toString.call(param).slice(8, -1).toLowerCase()

}

console.log(checkType(1)) // number

Object.is和===的区别

Object.is在严格等于上的基础修复了一些特殊情况下的错误,比如NaN 不等于 NaN

function is(x, y){

if(x === y){

// 1/+0 = +Infinity 1/-0 = -Infinity 这两个是不相等的

// 当 x和y都等于0的时候,就对x/0和y/0做判断

return x !== 0 || y !== 0 || x / 0 === y / 0

}

}

和=的区别和隐式数据类型转化

===是严格相等,左右两边不仅值要相等,类型也要相等,例如'1'===1的结果是false,因为左边是string,右边是number。

==只要值相等就会返回true,而且使用==时会发生隐式类型转化, 在js中,当运算符在运算时,如果两边数据不统一,CPU就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成一样的数据类型再计算 。

  • 转成string类型:+ 字符串连接符如 1 + "1" = "11"

  • 转成number类型: ++、–(自增自减运算符) + 、-、*、/、%(加减乘除取余算术运算符) >、 <、 >=、 <=、 ==、 !=、 =、 ! (关系运算符)

let i = “1”

console.log(++i) // 2

  • 转成boolean类型 : !(逻辑非运算符取反操作),使用Boolean转化除了下面这八种情况得到false以外,其它的情况都转为true 。0、-0、NaN、undefined、null、“”(空字符串)、false、document.all()

  • 如果其中一方为Object,且另一方为String、Number或者Symbol,会将Object转换成字符串,再进行比较

例子:

//字符串连接符

console.log(1 + ‘true’)// +是字符串连接符, String(1) + ‘true’,打印出’1true’

//算术运算符

console.log(1 + true) // +是算术运算符,true被Number(true)->1,打印出2

console.log(1 + undefined) // 1 + Number(undefined) -> 1 + NaN, 打印NaN

console.log(1 + null) // 1 + Number(null) -> 1 + 0,打印出1

//关系运算符

// 一边数字一边字符串,Number(“2”)

// 2 > 5,打印false

console.log(“2” > 5)

// 两边字符串,调用"2".charCodeAt() -> 50

// “5”.charAtCode()-> 53, 打印false

console.log(“2” > “5”)

//多个字符串从左往右匹配,也是调用charCodeAt方法进行比较

//比较"a".charCodeAt() < “b”.charCodeAt(),打印false

console.log(“abc” > “b”)

// 左边第一个"a"和右边第一个"a"的unicode编码相等

// 继续比较两边第二个字符, “b” > “a”,打印true

console.log(“abc” > “aaa”)

//无视上述规则自成体系

console.log(NaN == NaN) // NaN和任何数据比较都是 false

console.log(undefined == undefined) //true

console.log(undefined === undefined) //true

console.log(undefined == null) //true

console.log(undefined === null) //false

对于复杂的数据类型,比如对象和数组

对象和数组和字符串类型比较:先使用valueOf() 取得原始值,如果原始值不是number类型,则用toString()方法转成字符串类型valueOf -> toString

//发生了a.valueOf().toString()的转化,打印true

console.log([1,2] == “1,2”)

// 发生了a.valueOf().toString()的转化,打印true

let a = {}

console.log(a == “[object Object]”)

对象转原始类型,会调用内置的[ToPrimitive]函数,对于该函数而言,其逻辑如下:

  • 如果有设置Symbol.toPrimitive()方法,会优先调用并返回数据

  • 调用valueOf(),如果转换为原始类型,则返回

  • 调用toString(),如果转换为原始类型,则返回

  • 如果没有返回原始类型,则报错

让我们来看俩个例子👇

let obj = {

value: 3,

valueOf() {

return 4

},

toString() {

return 5

},

Symbol.toPrimitive {

return 6

},

}

console.log(obj + 1) //打印7

if(a ==1 && a == 2 && a == 3)成立

let a = {

value: 0,

valueOf() {

return ++a.value

},

}

// 每次调用这个a对象的时候都会在0的基础上加1,调用3次后就变成了3

console.log(a == 1 && a == 2 && a == 3) //true

如果是数组和对象与number类型比较,先用valueOf取得原始值,原始值不是number类型则调用toString,然后再将字符串类型用Number转成数字类型,调用顺序valueOf() -> toString() -> Number()

空数组的toString()方法会得到空字符串,而空对象的toString()方法会得到字符串[object Object]

//发生了这样的转化:Number([].valueOf().toString()),打印true

console.log([] == 0)

//逻辑非运算符优先级大于关系运算符

//空数组转布尔得到true,然后取反得到false

//false = 0 ,打印true

console.log(![] == 0)

//左边:{}.valueOf().toString()得到”[object Object]“,Number(”[object Object]“)->NaN

//右边:!{}得到false ,Number(false) -> 0

//两边不相等,打印false

console.log({} == !{})

//左边:[].valueOf().toString()得到空字符串

//右边:![] 得到false

// Number(“”) = Number(false) 两边都为0

//打印true

console.log([] == ![])

//因为引用数据类型存储在堆中的地址,左边和右边分别属于两块不同的空间

//他们地址不相同,所以两边不相等

//下面两种情况都打印false

console.log([] == [])

console.log({} == {})

记录遇到的一个另一个相关问题

下面这三个的打印结果

//typof null返回的是object

console.log(typeof null)

//从右往左看,先看右边的typeof null整体,返回object之后

//再将整体看成typeof object

//打印结果为string,原因是typeof null返回的是object字符串

console.log(typeof typeof null)

//到这里也是从右往左看,相当于typeof string

//结果打印是string

console.log(typeof typeof typeof null)

实现一个instanceof

function myInstanceof(left,right) {

if(typeof left !== ‘object’ || left === null) return false

//获取原型

let proto = Object.getPrototypeOf(left)

while(true){

//如果原型为null,则已经到了原型链顶端,判断结束

if(proto === null) return false

//左边的原型等于右边的原型,则返回结果

if(proto === right.prototype) return true

//否则就继续向上获取原型

proto = Object.getPrototypeOf(proto)

}

}

实现继承

ES5中实现继承

//实现一下继承

function Parent() {

this.name = “大人”

this.hairColor = “黑色”

}

function Child() {

Parent.call(this)

this.name = “小孩”

}

Child.prototype = Object.create(Parent.prototype)

//将丢失的构造函数给添加回来

Child.prototype.constructor = Child

let c1 = new Child()

console.log(c1.name, c1.hairColor) //小孩,黑色

console.log(Object.getPrototypeOf(c1))

console.log(c1.constructor) //Child构造函数

let p1 = new Parent()

console.log(p1.name, p1.hairColor) //大人,黑色

console.log(Object.getPrototypeOf(p1))

console.log(p1.constructor) //Parent构造函数

ES6中实现继承

// ES6的继承

Vue

  • 什么是MVVM?

  • mvvm和mvc区别?它和其它框架(jquery)的区别是什么?哪些场景适合?

  • 组件之间的传值?

  • Vue 双向绑定原理

  • 描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?

  • 虚拟 DOM 实现原理

  • Vue 中 key 值的作用?

  • Vue 的生命周期

  • Vue 组件间通信有哪些方式?

  • vue 中怎么重置 data?

  • 组件中写 name 选项有什么作用?

  • Vue 的 nextTick 的原理是什么?

  • Vuex 有哪几种属性?

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值