html设计题手写,前端面试手写题有备无患

前言

手写其实在前端的面试过程中必不可少,因为手写是考验你了解某一原理的最可观的体现。下面我汇总了一些,我在面试复习过程中遇到的手写题。我将实现思路写出来与大家共享,而实现只是一个参考,有兴趣的可以点击参考答案,有问题请指正。

原理实现

实现一个new操作符

首先我们需要了解new操作符干了哪些事情

new操作符返回的是一个对象。

对象的原型,指向的是构造函数的原型

如果构造函数有return的话,需要对return的进行判断,如果是对象,那么用函数return的,如果不是对象,那么直接返回新创建的对象

参考答案

function myNew(fn) {

let obj = Object.create(fn.prototype);

let res = fn.call(obj);

return res instanceof Object ? res : obj;

}

复制代码

实现一个instanceof操作符

首先我们需要知道instanceof是通过原型链来进行判断的

参考答案

instanceof操作符是判断原型链来生效的,所以只要你将左边的_proto_和右边的prototype做对比

复制代码function myInstance(left, right) {

// 当left是基础类型的时候直接返回false

if(typeof left !== 'object' || left === null) return false;

let proto = Object.getPrototypeOf(left);

while(true) {

if(proto === null) return false;

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

proto = Object.getPrototypeOf(proto);

}

}

复制代码

实现一个apply

实现apply主要是要注意以下几个方面:

1、参数不能取第一个,因为第一个是上下文。

2、赋值到对象上的方法需要进行删除,不能影响原对象。

3、对于上下文为undefined的情况需要把上下文指向window对象

参考答案

Function.prototype.myApply = function(context, [...args]) {

// 先判断context是否为空,如果为空则指向window对象

context = context || window;

context.fn = this;

context.fn(...args);

delete context.fn;

}

复制代码

实现一个call

call的实现和apply的实现类似,但是参数处理层面会略有不同。

参考答案

Function.prototype.myCall = function(context) {

// 先判断context是否为空,如果为空则指向window对象

context = context || window;

let args = [...arguments].slice(1);

context.fn = this;

context.fn(args);

delete context.fn;

}

复制代码

实现一个bind

bind的实现需要注意的是函数柯里化的情况。

参考答案

Function.prototype.myBind = function(context) {

const self = this;

let args = [...arguments].slice(1);

return function() {

// 考虑函数柯里化的情况

let newArgs = [...arguments];

this.apply(context, newArgs.concat(args))

}

}

复制代码

实现一个promise

promise的实现有必要着重看一下,我自己划分几个步骤来看:

1、先处理同步的逻辑,比如状态值变化之后,将不在发生改变,以及有哪些属性值等。

2、再处理异步逻辑,用回调的形式,以及存放的列表。

3、再处理链式调用的逻辑,链式调用比较复杂,多注意thenable的对象。

4、在处理promise的其他方法。

最后我在这里给大家推荐一篇文章,写的很不错!!promise文章

参考答案

class Promise(exector) {

constructor() {

this.value = undefined;

this.reason = '';

this.state = 'pending';

this.onResolveList = [];

this.onRejectList = [];

const resolve = (value) => {

if(this.state === 'fulfilled') {

this.value = value

this.state = 'fulfilled';

this.onResolveList.forEach((fn) => {

fn();

})

}

};

const reject = (reason) => {

if(this.state === 'rejected') {

this.reason = reason

this.state = 'rejected';

this.onRejectList.forEach((fn) => {

fn();

})

}

}

try {

exector(resolve, reject);

} catch(err) {

reject(err)

}

}

then(onFulfilled, onRejected) {

const promise2 = new Promise((reslove, reject) => {

if(this.state === 'fulfilled') {

let x = onFulfilled(this.value);

resolvePromise(promise2, x, reslove, reject);

}

if(this.state === 'rejected') {

onRejected(this.reason);

resolvePromise(promise2, x, reslove, reject);

}

if(this.state === 'pending') {

onResolveList.push(() => {

let x = onFulfilled(this.value);

resolvePromise(promise2, x, reslove, reject);

});

onRejectList.push(() => {

let x = onRejected(this.reason);

resolvePromise(promise2, x, reslove, reject);

});

}

});

return promise2;

}

race(promises) {

return new Promise((resolve, reject) => {

for(let i = 0; i < promises.length; i++) {

promises[i].then(resolve, reject);

}

})

}

all(promises) {

let arr = [];

let i = 0;

function processData(index, data) {

arr[index] = data;

i++;

if(i === promises.length) {

resolve(data);

}

}

return new Promise((resolve, reject) => {

for(let i = 0; i < promises.length; i++) {

promises[i].then((val) => {

processData(i, val);

}, reject)

}

})

}

resolve(val) {

return new Promise((resovle, reject) => {

resovle(val);

})

}

reject(val) {

return new Promise((resovle, reject) => {

reject(val);

})

}

}

// 这边要处理的情况有以下几种:1、循环引用, 2、thenable对象,3、promise对象

resolvePromise(promise2, x, reslove, reject) {

if(promise2 === x) {

reject('循环引用');

return;

}

// 防止多次调用

let called;

// 检测x的值的类型,如果不是对象或者函数,直接返回resolve

if(x !== null && (typeof x === 'object' || typeof x === 'function')) {

// 规范中then逻辑报错也会进入catch

try {

if(called) return;

let then = x.then;

if(typeof then === 'function') {

then.call(x, (y) => {

if(called) return;

called = true;

resolvePromise(promise2, y ,reslove, reject)

}, (err) => {

if(called) return;

reject(err);

called = true;

})

} else {

resolve(x);

}

} catch(err) {

if(called) return;

reject(err);

called = true;

}

} else {

resolve(x);

}

}

复制代码

实现一个寄生组合继承

寄生组合继承其实需要注意的是子构造函数constructor的指向问题。以及继承的弊病:超集会调用两次。

参考答案

function Super() {}

function Sub() {

Super.call(this)

}

Sub.prototype = new Super();

Sub.constructor = Sub;

复制代码

业务题实现

如何实现一个防抖函数

对于防抖的理解,最好结合业务场景记忆:防抖一般用于输入框场景。所以在实现层面会有以下两个方面:

1、当一定时间内事件再次触发时,定时器应该重置。

2、执行完毕后定时器重置。

参考答案

function debounce(cb, delay, ...args) {

let time = null;

return function() {

if(time) {

clearTimeout(time);

}

time = setTimeout(() => {

cb.apply(this, ...args);

clearTimeout(time);

}, delay);

}

}

复制代码

如何实现一个节流函数

对于节流函数,也需要结合场景来记忆。一般用于滚动事件中,一定时间内只会触发一次。实现层面也会有两个需要注意的点:

1、用一个锁变量来保证一定时间内只会触发一次。

2、执行完毕之后,解开锁即可

参考答案

function tr(fn, time, ...args) {

let lock = false;

return function() {

if(lock) return;

lock = true;

setTimeout(() => {

fn.apply(this, ...args);

lock = false;

}, time)

}

}

复制代码

实现一个中划线与驼峰的互相转换

这个其实主要考的是正则和replace方法。

参考答案

function camelize(str) {

return (str + '').replace(/-\D/g, function(match) {

return match.charAt(1).toUpperCase()

})

}

复制代码function hyphenate(str) {

return (str + '').replace(/[A-Z]/g, function(match) {

return '-' + match.toLowerCase();

})

}

复制代码

实现一个sleep函数

sleep函数实现的途径有很多,promise,async/await等等。我在这里就将一些最普通的。

参考答案

function sleep(time) {

return new Promise((resolve, reject) => {

setTimeout(() => {

resolve(true);

}, time)

})

}

复制代码

实现一个柯里化函数

实现柯里化其实就是把多个参数长度很分开来调用的意思,好处在于可以观测你参数调用的一个中间的过程,或者中间的变量。面试中常考的add(1, 2, 3)和add(1)(2)(3)就是这个问题

参考答案

function curry(fn) {

const finalLen = fn.length

let args = [].slice.call(this,1)

return function currying () {

args = args.concat(Array.from(arguments))

const len = args.length

return len >= fn.length ? fn.apply(this, args) : currying

}

}

复制代码

实现一个ajax

实现一个ajax其实主要是一个XMLHttpRequest对象以及其API方法的一个使用的问题。而在这里我建议尽量封装成promise的形式,方便使用。

参考答案

function ajax({url, methods, body, headers}) {

return new Promise((resolve, reject) => {

let request = new XMLHttpRequest();

request.open(url, methods);

for(let key in headers) {

let value = headers[key]

request.setRequestHeader(key, value);

}

request.onreadystatechange = () => {

if(request.readyState === 4) {

if(request.status >= '200' && request.status < 300) {

resolve(request.responeText);

} else {

reject(request)

}

}

}

request.send(body)

})

}

复制代码

实现一个深拷贝

深拷贝也是面试中的一个高频考点,一般的方法,JSON的序列化和反序列化,但是这种方法的弊病有两个:

1、undefined、null和symbol类型的值会被删除

2、碰见循环引用的时候会报错。

那么我们在实现深拷贝的时候,也要时刻关注循环引用这个问题。

下列方法中,我主要是通过数组的形式去解决循环引用的问题。那为什么要有两个数组呢?

主要是一个数组维护的是原来对象的引用,一个数组维护的是新对象的引用。

参考答案

function deepClone(obj) {

const parents = [];

const children = [];

function helper(obj) {

if(obj === null) return null;

if(typeof obj !== 'object') return obj;

let child, proto;

if(Array.isArray(obj)) {

child = [];

} else {

proto = Object.getPrototypeOf(obj);

child = Object.create(proto);

}

// 处理循环引用

let index = parents.indexOf(obj)

if(index === -1) {

parents.push(obj);

children.push(child)

} else {

return children[index];

}

// for in迭代

for(let i in obj) {

child[i] = helper(obj[i]);

}

}

}

复制代码

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[前端面试手写题有备无患]http://www.zyiz.net/tech/detail-129907.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值