手写代码

  • 1、手写instanceof方法
  • 2、手写new操作符
  • 3、手写Promise.all()
  • 4、手写防抖函数
  • 5、手写节流函数
  • 6、手写call、apply函数
  • 7、手写bind函数
  • 8、封装ajax
  • 9、手写深拷贝
  • 10、实现数组扁平化

1、手写instanceof方法

instanceof运算符用于判断构造函数的prototype属性是否出现在对象的原型链中的任何位置。

实现的步骤
1、首先获取对象的原型
2、然后获取构造函数的原型
3、然后一直循环判断构造函数的原型是否等于对象的原型,直到对象原型为null,因为原型链的最终为null

 <script>/* 手写 instanceof方法 */function myInstanceof(left,right){// 1、获取对象的原型var proto = Object.getPrototypeOf(left);// 2、获取构造函数的原型对象var obj = right.prototype; // 3、循环判断对象的原型是否等于构造函数的原型,直到对象原型为null,因为原型链最终为nullwhile(true){if(!proto) return false;if(obj === proto) return true;proto = Object.getPrototypeOf(proto); // 再取对象原型的原型}}console.log(myInstanceof(133,String)) // false 因为133是数字类型,如果改为字符串就对了</script>

2、手写new操作符

在调用 new 的过程中会发生以上四件事情:
(1)首先创建了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

<script>/* 手写new操作符 */function objectFunction(){// 初始化let newObj = null;// shif()方法是从数组中删除第一个元素,并返回该元素的值// constructor 获取arguments中的第一个参数let constructor = Array.prototype.shift.call(arguments)// 判断是否为构造函数if(typeof constructor !== 'function'){console.log("error!");return;}/* new操作符过程 */// 1、创建一个新对象并且把构造函数的原型赋给新对象newObj = Object.create(constructor.prototype); // 类似 newObj={};newObj.__proto__ = constructor.prototype// 2、改变构造函数的this指向,并执行函数let result = constructor.apply(newObj,arguments);// 3、根据result结果返回.如果result是个引用数据类型 则返回,如果不是则忽略返回newObjif(result &&(typeof result === 'object' || typeof result === 'function')){return result;}else{return newObj;}// 使用方法objectFunction(构造函数,初始化参数)}</script>

3、手写Promise.all()

核心思路:
1、接受一个Promise实例的数组或具有Iterator接口的对象作为参数;
2、这个方法返回一个新的promise对象;
3、遍历传入的参数,用promise.resolve()将参数“包一层”,使其变成一个promise对象;
4、参数所有回调才是成功,返回值数组与参数顺序一致;
5、参数数组其中一个失败,则触发失败的Promise错误作为Promise.all的错误

 <script>/* 手写Promise.all */var promiseAll = function (promise) {return new Promise(function (resolve, reject) {/* 判断传递过来的参数是否为数组 */if (!Array.isArray(promise)) {throw new TypeError("参数必须是数组")}let len = 0;//用来记录数组中每个promise成功状态的的长度let result = [];// 用来记录数组中每个promise的结果let arrLen = promise.length; // 记录参数数组的长度for (let i = 0; i < arrLen; i++) {/* Promise.resolve() 将现有对象转为Promise对象,使用then来执行 */Promise.resolve(promise[i]).then(value => { // 成功的回调len++;// result.push(value);result[i] = valueif (len == arrLen) {return resolve(result)}}, error => { // 失败的回调return reject("error")})}})}// 测试let p1 = new Promise(function (resolve, reject) {setTimeout(function () {resolve(1)}, 1000)})let p2 = new Promise(function (resolve, reject) {setTimeout(function () {resolve(2)}, 2000)})let p3 = new Promise(function (resolve, reject) {setTimeout(function () {resolve(3)}, 3000)})// 返回的顺序就是你传参的顺序promiseAll([p3, p1, p2]).then(res => {console.log(res) // [3, 1, 2]})</script>

4、手写防抖函数

 let input =document.querySelector('input');// 调用封装好的防抖input.oninput = debounce(function(){console.log(this.value) },500)/* 封装防抖 */// 防抖:在规定时间后才执行,如果在这段时间内有触发,是会重新计算,只执行最后一次function debounce(fn,wait){let time = null; // 初始一个定时器return function(){/* 如果发现之前有定时器,就清空 */if(time != null){clearTimeout(time); // 清除定时器}time = setTimeout(()=>{// console.log(this); // 这里的this指的是 input事件// 参数传递过来的fn this指向是window对象,我们需要让它指向input事件// 这里使用 call()改变指针指向,让fn寻找input为主人,这样input就可以使用fn函数fn.call(this) // 这里面的this指的就是 input事件},wait)}}</script>

5、手写节流函数

<style>body{height: 2000px;}
</style>
<body><script>/* 使用滚动条来触发 节流 */window.onscroll = throttle(function(){console.log("触发滚动条")},500)/* 封装节流 */// 节流:一段时间内只会执行一次,将频繁操作转换为少量操作function throttle(fn,delay){let flag = true; // 标记return function(){if(flag){// 设置一个定时器setTimeout(()=>{// 这里跟防抖是一样的,要让fn找滚动条事件为主人,改变fn的this指向fn.call(this); // 这里的this指的是 滚动条事件/* 这里为什么要设置flag=true? *//* 就好比一个接单兼职,刚开始是没有单子做,所以刚开始flag=true,表示我可以接单,一次接一个单子我要等 delay时间才能完成,这时就把牌牌flag=false,表示我还在完成单子任务,当我完成之后,我就把flag=true,继续开始接单。*/// 这样去记忆会好些flag = true;},delay)}flag = false;}}</script>

6、手写call、apply函数

手写call的实现步骤:
1、判断调用对象是否为函数
2、判断传入上下文对象是否存在,如果不存在,则设置为window
3、处理传入的参数,slice截取第一个参数后的所有参数
4、将函数作为上下文对象的一个属性
5、使用上下文对象来调用这个方法,并保存返回结果
6、删除刚才新增的属性
7、返回结果

apply函数的实现步骤与call函数的实现步骤其实差不多,只是处理传入参数那步不太同,注释我都在代码里面写的很清楚,如果还有不懂得地方,大家也可以给我留言

  <script>/* 手写call函数 */Function.prototype.myCall = function(obj) {/* 这里的this我猜应该是函数调用者 */if (typeof this != 'function') {console.error("调用者不是函数")}// 如果没有传值过来就是windowobj = obj || window; // obj 代表第一个参数let arr = [...arguments].slice(1); // 获取从第二个参数后面的数据obj.fn = this; // 给我们所在的obj函数添加的fn变量赋值this,而这个this就是我们调用myCall的函数// 执行 相当于 把arr参数传给调用myCall函数上去执行let result = obj.fn(...arr) // 扩张运算符 ... 可以把数组转为参数序列delete obj.fn; // 删除我们自己所在obj上添加的fn这个属性return result; // 返回结果}/* 手写apply函数 */// apply 与 call 不同点就是第二个参数。call 传递的是参数序列;apply传递的是一个数组 Function.prototype.myApply = function(obj){if(typeof this != 'function'){console.error("调用者不是函数")}let result = null;obj = obj || window;obj.fn = this;if(arguments[1]){result = obj.fn(...arguments[1])}else{result = obj.fn()}return result;}// 测试let dog = {name: '狗',eat(food1, food2) {console.log(this.name + '爱吃' + food1 + food2);}}let cat = {name: '猫',}dog.eat.call(cat, '鱼', '肉');     // 猫爱吃鱼肉dog.eat.myCall(cat, '鱼', '肉');   // 猫爱吃鱼肉dog.eat.myApply(cat, ['鱼', '肉']); // 猫爱吃鱼肉</script>

7、手写bind函数


function Text(a, b, c) {console.log("this--", this);console.log(a, b, c)}/* 手写bind函数 *//* 1、改变this指向2、第一个参数是 this的值,后面的参数是 函数接收的参数的值3、返回值不变*/
Function.protoype.myBind = function(){if(typeof this != "function"){console.error("调用者不是函数")}let self = this; // this指的是函数调用者 Text函数let arr = [...arguments]; // 使用扩展运算符转为数组let fn = arr.shift(1); // 获取数组 arr的第一项参数,也就是 {name:"xuxi"}; 此时的arr数组变成了 [1,2,3]return function(){return self.apply(fn.arr); // 改变this的指向}
}
let bind = Text.myBind({ name: 'xuxi' }, 1, 2, 3)bind(); //bind接受的是一个函数,所以要进行调用

8、封装ajax

/* 封装ajax */function myAjax(option) {// 1、创建 XMLHttpRequset对象let xhr = new XMLHttpRequest();option = option || {};// 初始化option.type = (option.type || 'GET').toUpperCase();option.dataType = option.dataType || 'json';let param = option.data; // 只有一个参数// 2、判断请求类型if (option.type === 'GET') {// 3、建立链接xhr.open(option.type, option.url + '?' + param, true);// 4、发送请求xhr.send(null);} else {xhr.open(option.type, option.url, true);xhr.send(param);}// 5、更加返回参数做出响应xhr.onreadystatechange = function () {let readystate = xhr.readyState;if (readystate === 4) { // 整个请求过程完毕let status = xhr.status;if (status >= 200 && status < 300) { // 200~299 成功的请求option.success && option.success(xhr.responseText, xhr.responseXML)}} else {option.fail && option.fail("error!!!")}}}// 调用myAjax({type: 'post',dataType: 'json',data: {},url: 'http://XXXX',success: function (text, xml) {//请求成功后的回调函数console.log(text)},fail: function (status) { //请求失败后的回调函数console.log(status)}})

9、手写深拷贝

    /* 手写深拷贝 */// 第一种 JSON.stringify()/* var deepCopy = function(obj){if(!obj || typeof obj != 'object') return;return JSON.parse(JSON.stringify(obj))} */// 第二种方法 _.cloneDeep/*  var _ = require('lodash')var deepCopy = function(obj){if(!obj || typeof obj != 'object') return;return _.cloneDeep(obj)} */// 第三种 递归实现var deepCopy = function(obj){if(!obj || typeof obj != 'object') return;let newObj = Array.isArray(obj)? [] :{};for(let i in obj){if(obj[i] instanceof Object){newObj[i] = deepCopy(obj[i]); // 如果对象中的属性也是对象,那么就递归循环}else{newObj[i] = obj[i]}}return newObj;}let student = {name: '小明',age: 10,grrifriend: {name: '小麻花'}}let obj = deepCopy(student);student.grrifriend.name = "小茜";console.log("student---",student);console.log("obj---",obj)</script>

10、实现数组扁平化

    /* 第一种方法 递归实现 */var flatten = function(arr){let result = [];for(let i=0; i<arr.length; i++){if(Array.isArray(arr[i])){// 如果遇到数组中的元素为数组时,则把该数组元素扁平化,与之前进行连接,并且重新赋值给reslutresult = result.concat(flatten(arr[i]));}else{result.push(arr[i]);}}return result;}/* 第二种方法 split和toString*//*   var flatten = function(arr){return arr.toString().split(",")} *//* 第三种方法 ES6中的flat*/// 语法 arr.flat([depth]) 其中depth是flat的参数,depth是可以传递数组的展开深度(默认不填,数值为1),// 即展开一层数组。如果层数不确定,参数可以传进 Infinity,代表不论多少层都要展开。/*  var flatten = function(arr){return arr.flat(Infinity)}*/let arr = [1, [2, [3, 4, 5]]];console.log(flatten(arr)); // [1,2,3,4,5]

前端面试之手写代码篇相关推荐

  1. 前端面试高频手写代码题

    前端面试高频手写代码题 一.实现一个解析URL参数的方法 方法一:String和Array的相关API 方法二: Web API 提供的 URL 方法三:正则表达式+string.replace方法 ...

  2. 前端面试:手写代码JS实现字符串反转

    前端萌新面试:手写代码JS实现字符串反转 前言 因为做前年小红书的前端校招面试题,发现出现好几道关于字符串对象和数组对象的题目,说难不难,但突然要写的话一时想不起来,这不想着做个小总结. 首先明白字符 ...

  3. 前端面试题之手写代码篇

    原文地址:前端面试题之手写代码篇 git地址:https://gitee.com/AiShiYuShiJiePingXing/lovebetterworld 点击前往GIT 一.JavaScript ...

  4. 2021-最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)---手写代码篇

    ★★★ 手写代码:实现forEach map filter reduce ★★★ 手写实现一个简易的 Vue Reactive ★★★ 手写代码,监测数组变化,并返回数组长度 ★★★ 手写原生继承,并 ...

  5. 前端面试高频手写题目

    高频手写题目 面试高频手写题目 1 实现防抖函数(debounce) 防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时 手写简化版: // func是用户传入需要防抖的函 ...

  6. 银行java面试题手写代码_面试系列——手写代码实现(一)

    前言 本文是面试系列篇的实现篇.笔者整理了面试过程中可能会遇到的手写实现,以及它的原理.这可以帮助面试者在笔试环节获得良好的加分. 其他文章系列,欢迎关注我文末的公众号 正文 apply和call a ...

  7. 前端笔试之手写代码(一)

    1. 扁平化嵌套数组/flat实现 描述:将嵌套多层的数组展开平铺成只有一层的数组. let array = [1, [1, 2, 3], [1, [2, {}]] ] handle(array) / ...

  8. python在哪里写代码比较适合-程序员面试被要求手写代码,你与顶级程序员的差别在哪?...

    原标题:程序员面试被要求手写代码,你与顶级程序员的差别在哪? 前言: Python现在非常火,语法简单而且功能强大,很多同学都想学Python! 所以小的给各位看官们准备了收藏已久的视频教程分享给大家 ...

  9. python在哪里写代码-程序员面试被要求手写代码,你与顶级程序员的差别在哪?...

    原标题:程序员面试被要求手写代码,你与顶级程序员的差别在哪? 前言: Python现在非常火,语法简单而且功能强大,很多同学都想学Python! 所以小的给各位看官们准备了收藏已久的视频教程分享给大家 ...

最新文章

  1. 【Android】Java回调原理并结合Android源码进行理解
  2. Spring注解Annotation
  3. UEditor使用报错Cannot set property 'innerHTML' of undefined
  4. vue生命周期(列表详解)
  5. andorid程序UI线程下开启子线程闪退错误解决
  6. 面向对象-多态,反射
  7. python中while语句是_如何在Python中使用while语句[适合初学者]
  8. 三星出售比亚迪1.6%股份,价值约1.5万亿韩元
  9. OpenShift 4 - 创建Service Mesh运行环境
  10. VMWare NSX安全生产和DMZ用例的详细设计指南
  11. 公众号抢号_公众号调性是什么意思?公众号排版可以塑造公众号调性吗?
  12. jQuery 提供了多种遍历 DOM 的方法。 遍历方法中最大的种类是树遍历(tree-traversal)。jQuery 提供了多种遍历 DOM 的方法。 遍历方法中最大的种类是树遍历(tree-t
  13. 电信烽火2821t虚拟服务器设置,iTV使用指南-烽火智能高清机顶盒装维手册
  14. easyexcel导出excel文件之图片导出
  15. python开平方计算(求平方根)
  16. python开三次方_用python计算三次方根
  17. 爬虫总结(二)-- scrapy
  18. 史记.(闸北)刀客列传
  19. 嵌入式系统设计与应用
  20. mysql的slow_log表_【转载】mysql 开启慢查询 清空slow_log日志或者slow_log表

热门文章

  1. android sim卡插拔广播,Android监听SIM卡插拔的方式
  2. Linux系统安全:安全技术和防火墙
  3. [latex]在箭头上写东西,写字
  4. Linux与Windows多系统引导软件GRUB4DOS下载及使用说明
  5. 最大协方差(Maximum covarivance analysis,MCA)
  6. 苹果系统 macOS Catalina 10.15.7 安装 Ubuntu 20.04 双系统 采用rEFInd引导
  7. 朴淳 思庵 欲掩其跡
  8. 苹果Mac终端上常用的命令行指令和技巧
  9. 车联万物,有Z-ONE SOA+AIoT!
  10. 【科普】千字解读自动驾驶的十个实用冷知识