【面试题】 「中高级前端面试」JavaScript手写代码无敌秘籍_js 面试题 代码 大厂(1)

复制代码


`eval` 与 `Function` 都有着动态编译js代码的作用,但是在实际的编程中并不推荐使用。


**这里是面向面试编程,写这两种就够了。至于第三,第四种,涉及到繁琐的递归和状态机相关原理,具体可以看:**



> 
> [《JSON.parse 三种实现方式》](https://bbs.csdn.net/topics/618166371)
> 
> 
> 


### 4. 实现一个`call`或 `apply`



> 
> 实现改编来源:[JavaScript深入之call和apply的模拟实现 #11](https://bbs.csdn.net/topics/618166371)
> 
> 
> 


`call`语法:



> 
> `fun.call(thisArg, arg1, arg2, ...)`,调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
> 
> 
> 


`apply`语法:



> 
> `func.apply(thisArg, [argsArray])`,调用一个函数,以及作为一个数组(或类似数组对象)提供的参数。
> 
> 
> 


#### 4.1 `Function.call`按套路实现


`call`核心:


* 将函数设为对象的属性
* 执行&删除这个函数
* 指定`this`到函数并传入给定参数执行函数
* 如果不传入参数,默认指向为 window


为啥说是套路实现呢?因为真实面试中,面试官很喜欢让你逐步地往深考虑,这时候你可以反套路他,先写个简单版的:


#### 4.1.1 简单版



var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar() // 1
复制代码


#### 4.1.2 完善版


当面试官有进一步的发问,或者此时你可以假装思考一下。然后写出以下版本:



Function.prototype.call2 = function(content = window) {
content.fn = this;
let args = […arguments].slice(1);
let result = content.fn(…args);
delete content.fn;
return result;
}
let foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, ‘black’, ‘18’) // black 18 1
复制代码


#### 4.2 `Function.apply`的模拟实现


`apply()`的实现和`call()`类似,只是参数形式不同。直接贴代码吧:



Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判断是否有第二个参数
if(arguments[1]) {
result = context.fn(…arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
复制代码


### 5. 实现一个`Function.bind()`


`bind()`方法:



> 
> 会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
> 
> 
> 


此外,`bind`实现需要考虑实例化后对原型链的影响。



Function.prototype.bind2 = function(content) {
if(typeof this != “function”) {
throw Error(“not a function”)
}
// 若没问参数类型则从这开始写
let fn = this;
let args = […arguments].slice(1);

let resFn = function() {
    return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )
}
function tmp() {}
tmp.prototype = this.prototype;
resFn.prototype = new tmp();

return resFn;

}
复制代码


### 6. 实现一个继承


**寄生组合式继承**


一般只建议写这种,因为其它方式的继承会在一次实例中调用两次父类的构造函数或有其它缺点。


核心实现是:**用一个 `F` 空的构造函数去取代执行了 `Parent` 这个构造函数。**



function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(‘parent name:’, this.name);
}
function Child(name, parentName) {
Parent.call(this, parentName);
this.name = name;
}
function create(proto) {
function F(){}
F.prototype = proto;
return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
console.log(‘child name:’, this.name);
}
Child.prototype.constructor = Child;

var parent = new Parent(‘father’);
parent.sayName(); // parent name: father

var child = new Child(‘son’, ‘father’);
复制代码


### 7. 实现一个JS函数柯里化



![](https://img-blog.csdnimg.cn/img_convert/7fbd7decb99edc1006b3d006070ae9b4.webp?x-oss-process=image/format,png)


  


**什么是柯里化?**




> 
> 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
> 
> 
> 


**函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。**


#### 7.1 通用版



function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function(){
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
}else{
return fn.apply(this,newArgs);
}
}
}

function multiFn(a, b, c) {
return a * b * c;
}

var multi = curry(multiFn);

multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);
复制代码


#### 7.2 `ES6`骚写法



const curry = (fn, arr = []) => (…args) => (
arg => arg.length === fn.length
? fn(…arg)
: curry(fn, arg)
)([…arr, …args])

let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10
复制代码


### 8. 手写一个`Promise`(中高级必考)


我们来过一遍`Promise/A+`规范:


* 三种状态`pending| fulfilled(resolved) | rejected`
* 当处于`pending`状态的时候,可以转移到`fulfilled(resolved)`或者`rejected`状态
* 当处于`fulfilled(resolved)`状态或者`rejected`状态的时候,就不可变。


1. 必须有一个`then`异步执行方法,`then`接受两个参数且必须返回一个promise:



// onFulfilled 用来接收promise成功的值
// onRejected 用来接收promise失败的原因
promise1=promise.then(onFulfilled, onRejected);
复制代码


#### 8.1 `Promise`的流程图分析



![](https://img-blog.csdnimg.cn/img_convert/4eaffa7cb4c66ef7d509e33f0f8602dd.webp?x-oss-process=image/format,png)


  


来回顾下`Promise`用法:




var promise = new Promise((resolve,reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
复制代码


#### 8.2 面试够用版



> 
> 来源:[实现一个完美符合Promise/A+规范的Promise](https://bbs.csdn.net/topics/618166371)
> 
> 
> 



function myPromise(constructor){
let self=this;
self.status=“pending” //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个===“pending”,保证了状态的改变是不可逆的
if(self.status===“pending”){
self.value=value;
self.status=“resolved”;
}
}
function reject(reason){
//两个===“pending”,保证了状态的改变是不可逆的
if(self.status===“pending”){
self.reason=reason;
self.status=“rejected”;
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
复制代码


同时,需要在`myPromise`的原型上定义链式调用的`then`方法:



myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case “resolved”:
onFullfilled(self.value);
break;
case “rejected”:
onRejected(self.reason);
break;
default:
}
}
复制代码


测试一下:



var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1
复制代码


#### 8.3 大厂专供版


直接贴出来吧,这个版本还算好理解



const PENDING = “pending”;
const FULFILLED = “fulfilled”;
const REJECTED = “rejected”;

function Promise(excutor) {
let that = this; // 缓存当前promise实例对象
that.status = PENDING; // 初始状态
that.value = undefined; // fulfilled状态时 返回的信息
that.reason = undefined; // rejected状态时 拒绝的原因
that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数

function resolve(value) { // value成功态时接收的终值
    if(value instanceof Promise) {
        return value.then(resolve, reject);
    }
    // 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
    setTimeout(() => {
        // 调用resolve 回调对应onFulfilled函数
        if (that.status === PENDING) {
            // 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
            that.status = FULFILLED;
            that.value = value;
            that.onFulfilledCallbacks.forEach(cb => cb(that.value));
        }
    });
}
function reject(reason) { // reason失败态时接收的拒因
    setTimeout(() => {
        // 调用reject 回调对应onRejected函数
        if (that.status === PENDING) {
            // 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
            that.status = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach(cb => cb(that.reason));
        }
    });
}

// 捕获在excutor执行器中抛出的异常
// new Promise((resolve, reject) => {
//     throw new Error('error in excutor')
// })
try {
    excutor(resolve, reject);
} catch (e) {
    reject(e);
}

}

Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
// 处理参数默认值 保证参数后续能够继续执行
onFulfilled =
typeof onFulfilled === “function” ? onFulfilled : value => value;
onRejected =
typeof onRejected === “function” ? onRejected : reason => {
throw reason;
};
if (that.status === FULFILLED) { // 成功态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch(e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
})
}

if (that.status === REJECTED) { // 失败态
    return newPromise = new Promise((resolve, reject) => {
        setTimeout(() => {
            try {
                let x = onRejected(that.reason);
                resolvePromise(newPromise, x, resolve, reject);
            } catch(e) {
                reject(e);
            }
        });
    });
}

if (that.status === PENDING) { // 等待态
    // 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
    return newPromise = new Promise((resolve, reject) => {
        that.onFulfilledCallbacks.push((value) => {
            try {
                let x = onFulfilled(value);
                resolvePromise(newPromise, x, resolve, reject);
            } catch(e) {
                reject(e);
            }
        });
        that.onRejectedCallbacks.push((reason) => {
            try {
                let x = onRejected(reason);
                resolvePromise(newPromise, x, resolve, reject);
            } catch(e) {
                reject(e);
            }
        });
    });
}

};
复制代码



![](https://img-blog.csdnimg.cn/img_convert/58cd180709503db400f6433713692c23.webp?x-oss-process=image/format,png)


  


emmm,我还是乖乖地写回进阶版吧。



### 9. 手写防抖(`Debouncing`)和节流(`Throttling`)



> 
> `scroll` 事件本身会触发页面的重新渲染,同时 `scroll` 事件的 `handler` 又会被高频度的触发, 因此事件的 `handler` 内部不应该有复杂操作,例如 `DOM` 操作就不应该放在事件处理中。 针对此类高频度触发事件问题(例如页面 `scroll` ,屏幕 `resize`,监听用户输入等),有两种常用的解决方法,防抖和节流。
> 
> 
> 


#### 9.1 防抖(`Debouncing`)实现


典型例子:限制 鼠标连击 触发。


一个比较好的解释是:



> 
> 当一次事件发生后,事件处理器要等一定阈值的时间,如果这段时间过去后 再也没有 事件发生,就处理最后一次发生的事件。假设还差 `0.01` 秒就到达指定时间,这时又来了一个事件,那么之前的等待作废,需要重新再等待指定时间。
> 
> 
> 



![](https://img-blog.csdnimg.cn/img_convert/abd3e6418eb95a9f8c20c82b87b5056d.webp?x-oss-process=image/format,png)


  


### 最后

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

又会被高频度的触发, 因此事件的 `handler` 内部不应该有复杂操作,例如 `DOM` 操作就不应该放在事件处理中。 针对此类高频度触发事件问题(例如页面 `scroll` ,屏幕 `resize`,监听用户输入等),有两种常用的解决方法,防抖和节流。
> 
> 
> 


#### 9.1 防抖(`Debouncing`)实现


典型例子:限制 鼠标连击 触发。


一个比较好的解释是:



> 
> 当一次事件发生后,事件处理器要等一定阈值的时间,如果这段时间过去后 再也没有 事件发生,就处理最后一次发生的事件。假设还差 `0.01` 秒就到达指定时间,这时又来了一个事件,那么之前的等待作废,需要重新再等待指定时间。
> 
> 
> 



![](https://img-blog.csdnimg.cn/img_convert/abd3e6418eb95a9f8c20c82b87b5056d.webp?x-oss-process=image/format,png)


  


### 最后

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

>![](https://img-blog.csdnimg.cn/img_convert/e551f51b22597a16723282d3b6c080da.webp?x-oss-process=image/format,png)
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值