【面试题】你是如何让js 代码变得简洁的?,三面美团前端岗


#### 2. apply


apply和call的本质区别就是接受的参数形式不同,call接收`零散`的参数,而apply以`数组`的方式接收参数,实现思路完全一样,代码如下:



function foo(x,y){
console.log(this.a,x+y);
}

const obj={
a:1
}

Function.prototype.myApply=function(context,args){
if(typeof this !== ‘function’) return new TypeError(‘is not a function’)
const fn=Symbol(‘fn’) //尽可能降低myCall对其他的影响
context[fn]=this
contextfn
delete context[fn]
}

//验证
foo.myApply(obj,[1,2]) //1,3


#### 3. bind


bind和call,apply的区别是会`返回一个新的函数`,接收`零散`的参数  
 需要注意的是,`官方bind`的操作是这样的:


* 当new了bind返回的函数时,相当于new了foo,且new的`参数`需作为`实参`传给foo
* foo的this.a`访问不到`obj中的a



function foo(x,y,z){
this.name=‘zt’
console.log(this.a,x+y+z);
}

const obj={
a:1
}

Function.prototype.myBind=function(context,…args){

if(typeof this !== ‘function’) return new TypeError(‘is not a function’)

context=context||window

let _this=this

return function F(…arg){
//判断返回出去的F有没有被new,有就要把foo给到new出来的对象
if(this instanceof F){
return new _this(…args,…arg) //new一个foo
}
_this.apply(context,args.concat(arg)) //this是F的,_this是foo的 把foo的this指向obj用apply
}
}

//验证
const bar=foo.myBind(obj,1,2)
console.log(new bar(3)); //undefined 6 foo { name: ‘zt’ }


### 三、手撕深拷贝



### 四、手撕Promise


思路:


* 我们知道,promise是有`三种状态`的,分别是`pending`(异步操作正在进行), `fulfilled`(异步操作成功完成), `rejected`(异步操作失败)。我们可以定义一个**变量**保存promise的状态。
* resolve和reject的实现:把`状态变更`,并把resolve或reject中的值`保存`起来留给`.then使用`。
* 要保证`实例对象`能访问`.then`,必须将.then挂在`构造函数的原型上`
* .then接收`两个函数`作为参数,我们必须对所传参数进行`判断`是否为函数,当状态为fulfilled时,onFulfilled函数`触发`,并将前面resolve中的值`传给`onFulfilled函数;状态为rejected时同理。
* 当在promise里放一个`异步函数`(例:setTimeout)`包裹`resolve或reject函数时,它会被挂起,那么当执行到.then时,promise的状态`仍然是pending`,故`不能触发`.then中的回调函数。我们可以定义`两个数组`分别存放.then中的两个`回调函数`,将其分别在resolve和reject函数中`调用`,这样保证了在resolve和reject函数触发时,.then中的回调函数即能触发。


代码如下:



const PENDING = ‘pending’
const FULFILLED = ‘fullfilled’
const REJECTED = ‘rejected’

function myPromise(fn) {
this.state = PENDING
this.value = null
const that = this
that.resolvedCallbacks = []
that.rejectedCallbacks = []

function resolve(val) {
if (that.state == PENDING) {
that.state = FULFILLED
that.value = val
that.resolvedCallbacks.map((cb)=>{
cb(that.value)
})
}
}
function reject(val) {
if (that.state == PENDING) {
that.state = REJECTED
that.value = val
that.rejectedCallbacks.map((cb)=>{
cb(that.value)
})
}
}

try {
fn(resolve, reject)
} catch (error) {
reject(error)
}

}

myPromise.prototype.then = function (onFullfilled, onRejected) {
const that = this
onFullfilled = typeof onFullfilled === ‘function’ ? onFullfilled : v => v
onRejected= typeof onRejected === ‘function’ ? onRejected : r => { throw r }

if(that.state===PENDING){
that.resolvedCallbacks.push(onFullfilled)
that.resolvedCallbacks.push(onRejected)
}
if (that.state === FULFILLED) {
onFullfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}

//验证 ok ok
let p = new myPromise((resolve, reject) => {
// reject(‘fail’)
resolve(‘ok’)
})

p.then((res) => {
console.log(res,‘ok’);
}, (err) => {
console.log(err,‘fail’);
})


### 


### 六、手撕数组API


#### 1. forEach()


思路:


* forEach()用于`数组的遍历`,参数接收一个`回调函数`,回调函数中接收`三个参数`,分别代表每一项的值、下标、数组本身。
* 要保证数组能访问到我们自己手写的API,必须将其挂到`数组的原型上`。


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

//代码实现
Array.prototype.my_forEach = function (callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this)
}
}

//验证
arr.my_forEach((item, index, arr) => { //111 111
if (item.age === 18) {
item.age = 17
return
}
console.log(‘111’);
})


#### 2. map()


思路:


* map()也用于`数组的遍历`,与forEach不同的是,它会`返回一个新数组`,这个新数组是map接收的`回调函数`的`返回值`。  
 代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_map=function(callback){
const res=[]
for(let i=0;i<this.length;i++){
res.push(callback(this[i],i,this))
}
return res
}

//验证
let newarr=arr.my_map((item,index,arr)=>{
if(item.age>18){
return item
}
})
console.log(newarr);
//[
// undefined,
// { name: ‘aa’, age: 19 },
// undefined,
// { name: ‘cc’, age: 21 }
//]


#### 3. filter()


思路:


* filter()用于`筛选过滤`满足条件的元素,并`返回一个新数组`。


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_filter = function (callback) {
const res = []
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this) && res.push(this[i])
}
return res
}

//验证
let newarr = arr.my_filter((item, index, arr) => {
return item.age > 18
})
console.log(newarr); [ { name: ‘aa’, age: 19 }, { name: ‘cc’, age: 21 } ]


#### 4. reduce()


思路:


* reduce()用于将数组中所有元素按指定的规则进行`归并计算`,返回一个`最终值`。
* reduce()接收`两个参数`:回调函数、初始值(可选)。
* 回调函数中接收`四个参数`:初始值 或 存储上一次回调函数的返回值、每一项的值、下标、数组本身。
* 若不提供初始值,则从`第二项开始`,并将`第一个值`作为`第一次执行的返回值`。


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_reduce = function (callback,…arg) {
let pre,start=0
if(arg.length){
pre=arg[0]
}
else{
pre=this[0]
start=1
}
for (let i = start; i < this.length; i++) {
pre=callback(pre,this[i], i, this)
}
return pre
}

//验证
const sum = arr.my_reduce((pre, current, index, arr) => {
return pre+=current.age
},0)
console.log(sum); //76


#### 5. fill()


思路:


* fill()用于`填充`一个数组的所有元素,它会`影响原数组` ,返回值为`修改后`的`原数组`。
* fill()接收`三个参数`:填充的值、起始位置(默认为0)、结束位置(默认为this.length-1)。
* 填充遵循`左闭右开`的原则
* `不提供`起始位置和结束位置时,默认填充`整个数组`。


代码实现:



Array.prototype.my_fill = function (value,start,end) {
if(!start&&start!==0){
start=0
}
end=end||this.length
for(let i=start;i<end;i++){
this[i]=value
}
return this
}

//验证
const arr=new Array(7).my_fill(‘hh’,null,3) //往数组的某个位置开始填充到哪个位置,左闭右开
console.log(arr); //[ ‘hh’, ‘hh’, ‘hh’, <4 empty items> ]


#### 6. includes()


思路:


* includes()用于判断数组中是否`包含`某个元素,返回值为`true 或 false`
* includes()提供`第二个参数`,支持从`指定位置开始`查找


代码实现:



const arr = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]

Array.prototype.my_includes = function (item,start) {
if(start<0){start+=this.length}
for (let i = start; i < this.length; i++) {
if(this[i]===item){
return true
}
}
return false
}

//验证
const flag = arr.my_includes(‘c’,3) //查找的元素,从哪个下标开始查找
console.log(flag); //false


#### 7. join()


思路:


* join()用于将数组中的`所有元素`以`指定符号`连接成一个`字符串`


代码实现:



const arr = [‘a’, ‘b’, ‘c’]

Array.prototype.my_join = function (s = ‘,’) {
let str = ‘’
for (let i = 0; i < this.length; i++) {
str += ${this[i]}${s}
}
return str.slice(0, str.length - 1)
}

//验证
const str = arr.my_join(’ ')
console.log(str); //a b c


#### 8. find()


思路:


* find()用于返回数组中`第一个满足条件`的`元素`,找不到返回`undefined`
* find()的参数为一个`回调函数`


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_find = function (callback) {
for (let i = 0; i < this.length; i++) {
if(callback(this[i], i, this)){
return this[i]
}

}
return undefined
}

//验证
let j = arr.my_find((item, index, arr) => {
return item.age > 19
})
console.log(j); //{ name: ‘cc’, age: 21 }


#### 9. findIndex()


思路:


* findIndex()用于返回数组中`第一个满足条件`的`索引`,找不到返回`-1`
* findIndex()的参数为一个`回调函数`


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_findIndex = function (callback) {
for (let i = 0; i < this.length; i++) {
if(callback(this[i], i, this)){
return i
}
}
return -1
}

let j = arr.my_findIndex((item, index, arr) => {
return item.age > 19
})
console.log(j); //3


#### 10. some()


思路:


* some()用来检测数组中的`元素`是否`满足`指定条件。
* 若`有一个`元素符合条件,则返回`true`,且后面的元素`不`会再检测。


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_some = function (callback) {
for (let i = 0; i < this.length; i++) {
if(callback(this[i], i, this)){
return true
}
}
return false
}

//验证
const flag = arr.some((item, index, arr) => {
return item.age > 20
})
console.log(flag); //true


#### 11. every()


思路:


* every() 用来检测`所有元素`是否都`符合`指定条件。
* 若`有一个`不满足条件,则返回`false`,后面的元素都`不`会再执行。


代码实现:



const arr = [
{ name: ‘zt’, age: 18 },
{ name: ‘aa’, age: 19 },
{ name: ‘bb’, age: 18 },
{ name: ‘cc’, age: 21 },
]

Array.prototype.my_every = function (callback) {
for (let i = 0; i < this.length; i++) {
if(!callback(this[i], i, this)){
return false
}
}
return true
}

//验证
const flag = arr.my_every((item, index, arr) => {
return item.age > 16
})
console.log(flag); //true


### 七、数组去重


#### 1. 双层for循环 + splice()



let arr = [1, 1, ‘1’, ‘1’, 2, 2, 2, 3, 2]
function unique(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j-- //删除后j向前走了一位,下标需要减一,避免少遍历一位
}
}
}
return arr
}

console.log(unique(arr)) //[ 1, ‘1’, 2, 3 ]




### 文末
篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

![](https://img-blog.csdnimg.cn/img_convert/04273c2f50e58b6822663c5b2f379206.png)  

![](https://img-blog.csdnimg.cn/img_convert/081b5236ebd9ff24af676bcd4a51c99e.png)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值