当前位置: 首页 > news >正文

如何做高大上的网站 知乎我需要做网站

如何做高大上的网站 知乎,我需要做网站,vps做网站 推广,网站建设遇到问题解决方案前言 #x1f4eb; 大家好#xff0c;我是南木元元#xff0c;热爱技术和分享#xff0c;欢迎大家交流#xff0c;一起学习进步#xff01; #x1f345; 个人主页#xff1a;南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、…前言 大家好我是南木元元热爱技术和分享欢迎大家交流一起学习进步   个人主页南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、bind 实现call 实现apply 实现bind 3.防抖和节流 防抖 节流 4.实现instanceof 5.实现Ajax 6.深拷贝和浅拷贝 浅拷贝 深拷贝 7.函数柯里化 参数定长的柯里化 参数不定长的柯里化 8.数组扁平化 9.数组去重 10.手写类型判断函数 结语 1.实现new 1首先创建一个新的空对象。 2设置原型将对象的原型设置为函数的prototype对象。 3让函数的this指向这个对象执行构造函数的代码。 4判断函数的返回值类型如果是值类型返回创建的对象。如果是引用类型就返回这个引用类型的对象。 function myNew(constructor, ...args) {// 如果不是一个函数就报错if (typeof constructor ! function) {throw myNew function the first param must be a function;}// 基于原型链 创建一个新对象继承构造函数constructor的原型对象上的属性let newObj Object.create(constructor.prototype);// 将newObj作为this执行 constructor 传入参数let res constructor.apply(newObj, args);// 判断函数的执行结果是否是对象typeof null 也是object所以要排除nulllet isObject typeof res newObject res ! null;// 判断函数的执行结果是否是函数let isFunction typeof res function;// 如果函数的执行结果是一个对象或函数, 则返回执行的结果, 否则, 返回新创建的对象return isObject || isFunction ? res : newObj; }// 用法 function Person(name, age) {this.name name;this.age age;// 如果构造函数内部return 一个引用类型的对象则整个构造函数失效而是返回这个引用类型的对象 } Person.prototype.say function() {console.log(this.age); }; let p1 myNew(Person, poety, 18); console.log(p1.name); //poety console.log(p1); //Person {name: poety, age: 18} p1.say(); //18 测试结果  2.call、apply、bind 实现call 思路接受传入的context上下文如果不传默认为window将被调用的方法设置为上下文的属性使用上下文对象来调用这个方法删除新增属性返回结果。 //写在函数的原型上 Function.prototype.myCall function (context) {// 如果要调用的方法不是一个函数则报错if (typeof this ! function) {throw new Error(Type error);}// 判断 context 是否传入,如果没有传就设置为 windowcontext context || window;// 获取参数[...arguments]把类数组转为数组let args [...arguments].slice(1);let result null;// 将被调用的方法设置为context的属性,this即为要调用的方法context.fn this;// 执行要被调用的方法result context.fn(...args);// 删除手动增加的属性方法delete context.fn;// 将执行结果返回return result; };//测试 function f(a,b){console.log(ab)console.log(this.name) } let obj{name:1 } f.myCall(obj,1,2) // 31 测试结果 实现apply 除了传参方式是数组其它与call没区别。 Function.prototype.myApply function (context) {if (typeof this ! function) {throw new Error(Type error);}let result null;context context || window;// 与上面代码相比我们使用 Symbol 来保证属性唯一,也就是保证不会重写用户自己原来定义在 context 中的同名属性const fnSymbol Symbol();context[fnSymbol] this;// 执行要被调用的方法,处理参数和 call 有区别判断是否传了参数数组if (arguments[1]) {//传了参数数组result context[fnSymbol](...arguments[1]);} else {//没传参数数组result context[fnSymbol]();}delete context[fnSymbol];return result; };//测试 function f(a,b){console.log(a,b)console.log(this.name) } let obj{name:张三 } f.myApply(obj,[1,2]) //1 2,张三 测试结果 实现bind bind返回的是一个函数需要判断函数作为构造函数的情况当作为构造函数时this 指向实例不会被任何方式改变 this所以要忽略传入的context上下文。 bind可以分开传递参数所以需要将参数拼接。 如果绑定的是构造函数还需要继承构造函数原型上的属性和方法保证不丢失。 Function.prototype.myBind function (context) {// 判断调用对象是否为函数if (typeof this ! function) {throw new Error(Type error);}// 获取参数const args [...arguments].slice(1);const fn this; // 保存this的值代表调用bind的函数//返回一个函数此函数可以被作为构造函数调用也可以作为普通函数调用const Fn function () {// 根据调用方式传入不同绑定值// 当作为构造函数时,this 指向实例不会被任何方式改变 this要忽略传入的context上下文return fn.apply(this instanceof Fn ? this : context,// bind可以分开传递参数(如f.bind(obj, 1)(2))所以需要将参数拼接这里使用apply参数拼接成一个数组args.concat(...arguments)//当前的这个 arguments 是指 Fn 的参数也可以用剩余参数的方式);};//对于构造函数要保证原函数的原型对象上的属性不能丢失Fn.prototype Object.create(fn.prototype);return Fn; };// 1.先测试作为构造函数调用 function Person(name, age) {console.log(name);console.log(age);console.log(this); //构造函数this指向实例对象 } // 构造函数原型的方法 Person.prototype.say function () {console.log(say); }; var obj {name: cc,age: 18, }; var bindFun Person.myBind(obj, cxx); var a new bindFun(10); // cxx // 10 // Person {} a.say(); // say// 2.再测试作为普通函数调用 function normalFun(name, age) {console.log(name);console.log(age);console.log(this); // 普通函数this指向绑定bind的第一个参数 也就是例子中的obj } var obj {name: aa,age: 18, }; var bindNormalFun normalFun.myBind(obj, cc); bindNormalFun(12); // cc // 12 // { name: aa, age: 18 } 测试结果  3.防抖和节流 防抖 防抖是指在事件被触发 n 秒后再执行回调如果在这 n 秒内事件又被触发则重新计时。可以使用在一些点击请求的事件上避免因为用户的多次点击向后端发送多次请求。 //fn是需要防抖的函数,delay是等待时间 function debounce(fn, delay 500) {let timer null;// 这里返回的函数是每次用户实际调用的防抖函数return function(...args) { //...args是es6的剩余参数语法将多余的参数放入数组用来代替arguments对象// 如果已经设定过定时器了就清空上一次的定时器if(timer) {clearTimeout(timer); }// 开始一个新的定时器延迟执行用户传入的方法注定时器的返回值是一个数值作为定时器的编号可以传入clearTimeout来取消定时器timer setTimeout(() { //这里必须是箭头函数不然this指向window,要让this就指向fn的调用者fn.apply(this, args); }, delay) } } 节流 节流就是一定时间内只执行一次事件即使重复触发也只有一次生效。可以使用在监听滚动scroll事件上通过事件节流来降低事件调用的频率。 定时器版本 function throttle(fn, delay 500) {let timer null;return function(...args) {// 当前有任务了直接返回if(timer) {return;}timer setTimeout(() {fn.apply(this, args);//执行完后需重置定时器不然timer一直有值无法开启下一个定时器timer null; }, delay)} } 时间戳版本 // 节流 function throttle(fn, delay 500) {let prev Date.now();// 上一次执行该函数的时间return function(...args) {let now Date.now();//返回从UTC到当前时间的毫秒数// 如果差值大于等于设置的等待时间就执行函数if (now - prev delay) {fn.apply(this, args);prev Date.now();}}; } 详细可以去看我的这篇文章——前端性能优化之防抖节流 4.实现instanceof instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。 function myInstanceof(instance, constructor) {//如果不是对象或者是null直接返回falseif (typeof instance ! object || instance null) {return false;}// 获取对象的原型let proto Object.getPrototypeOf(instance);// 获取构造函数的 prototype 对象let prototype constructor.prototype;// 判断构造函数的 prototype对象是否在对象的原型链上while (true) {// 到达原型链终点null说明没找到if (!proto) {return false;}if (proto prototype) {return true;}// 如果没有找到就继续从其原型上找proto Object.getPrototypeOf(proto);} }//测试 let Fn function () { } let p1 new Fn() console.log(myInstanceof(p1, Fn));//true console.log(myInstanceof([], Fn));//false console.log(myInstanceof([], Array)) // true console.log(myInstanceof(function(){}, Function)) // true 测试结果  5.实现Ajax 1创建一个 XMLHttpRequest 对象。 2在这个对象上使用 open 方法创建一个 HTTP 请求参数为请求方法、请求地址、是否异步和用户的认证信息。 3通过send方法来向服务器发起请求post请求可以入参数作为发送的数据体。 4监听请求成功后的状态变化根据状态码进行相应的处理。onreadystatechange设置监听函数当对象的readyState变为4的时候代表服务器返回的数据接收完成这个时候可以通过判断请求的状态如果状态是200则代表成功404或500代表失败。 function ajax(url) {//1.创建XMLHttpRequest对象const xhr new XMLHttpRequest();//2.使用open方法创建一个GET请求xhr.open(GET,url);//xhr.open(GET,url,true);//true代表异步已完成事务的通知可供事件监听器使用;如果为falsesend() 方法直到收到答复前不会返回//3.发送请求xhr.send(); //4.监听请求成功后的状态变化(readyState改变时触发)根据状态码(0~5)进行相应的处理xhr.onreadystatechange function () {//readyState为4代表服务器返回的数据接收完成if (xhr.readyState 4) { //请求的状态为200或304代表成功if(xhr.status 200 || xhr.status 304) {//this.response代表返回的数据handle(this.response);} else {//this.statusText代表返回的文本信息console.error(this.statusText);}}}; } 使用Promise封装AJAX function ajax(url) {return new Promise((resolve, reject) {let xhr new XMLHttpRequest()xhr.open(get, url)xhr.send() xhr.onreadystatechange () {if (xhr.readyState 4) {if (xhr.status 200 || xhr.status 304) {resolve(this.response)} else {reject(new Error(this.statusText));}}}}) } //使用 let url /data.json ajax(url).then(res console.log(res)).catch(reason console.log(reason)) 6.深拷贝和浅拷贝 浅拷贝 浅拷贝是创建一个新对象这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型拷贝的就是基本类型的值如果属性是引用类型拷贝的就是内存地址 所以如果其中一个对象改变了这个地址就会影响到另一个对象。 //实现浅拷贝 function shallowCopy (obj){// 只拷贝对象基本类型或null直接返回if(typeof obj ! object || obj null) {return obj;}// 判断是新建一个数组还是对象let newObj Array.isArray(obj) ? []: {};//for…in会遍历对象的整个原型链如果只考虑对象本身的属性需要搭配hasOwnPropertyfor(let key in obj ){//hasOwnProperty判断是否是对象自身属性会忽略从原型链上继承的属性if(obj.hasOwnProperty(key)){newObj[key] obj[key];//只拷贝对象本身的属性}}return newObj; }//测试 var obj {name:张三,age:8,pal:[王五,王六,王七] } let obj2 shallowCopy(obj); obj2.name 李四 obj2.pal[0] 王麻子 console.log(obj); //{age: 8, name: 张三, pal: [王麻子, 王六, 王七]} console.log(obj2); //{age: 8, name: 李四, pal: [王麻子, 王六, 王七]} 测试结果  深拷贝 深拷贝是将一个对象从内存中完整的拷贝一份出来从堆内存中开辟一个新的区域存放新对象且修改新对象不会影响原对象。 function deepCopy (obj, map new WeakMap()){// 基本类型或null直接返回if(typeof obj ! object || obj null) {return obj;}// 判断是新建一个数组还是对象let newObj Array.isArray(obj) ? []: {};//利用map解决循环引用if (map.has(obj)) {return map.get(obj);}map.set(obj, newObj);//将当前对象作为key克隆对象作为valuefor(let key in obj ){ if(obj.hasOwnProperty(key)){newObj[key] deepCopy(obj[key], map); //递归}}return newObj }// 测试 let obj1 {name : 南木元元,arr : [1,[2,3],4], }; let obj2deepCopy(obj1) obj2.name 阿元; obj2.arr[1] [5,6,7] ; // 新对象跟原对象不共享内存console.log(obj1,obj1) // obj1 { name: 南木元元, arr: [ 1, [ 2, 3 ], 4 ] } console.log(obj2,obj2) // obj2 { name: 阿元, arr: [ 1, [ 5, 6, 7 ], 4 ] } 测试结果 7.函数柯里化 函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。 作用可以参数复用公共的参数已经通过柯里化预置了和延迟执行柯里化时只是返回一个预置参数的新函数并没有立刻执行在满足条件后才会执行。 参数定长的柯里化 思路通过函数的length属性获取函数的形参个数​​​​​形参的个数就是所需参数的个数。维护一个数组当数组的长度与函数接收参数的个数一致再执行该函数。 // 实现函数柯里化 function curry(fn) {// 返回一个新函数return function curried(...args) {if (args.length fn.length) {return fn.apply(this, args); // 如果参数够了就执行原函数返回结果} else {//返回一个新函数继续递归去进行柯里化利用闭包将当前已经传入的参数保存下来return function (...args2) {//递归调用 curried 函数return curried.apply(this, [...args, ...args2]); //新函数调用时会继续传参拼接参数};}}; }// 测试 function sum(a, b, c) {return a b c; } var curried curry(sum); console.log(curried(1, 2, 3)); //6 console.log(curried(1, 2)(3)); //6 console.log(curried(1)(2, 3)); //6 console.log(curried(1)(2)(3)); //6 测试结果 参数不定长的柯里化 题目如何实现一个方法使计算结果能够满足如下预期。 add(1, 2, 3) // 6 add(1) // 1 add(1)(2) // 3 add(1, 2)(3) // 6 add(1)(2)(3) // 6 add(1)(2)(3)(4) // 10 思路利用闭包和递归如果参数为空则判断递归结束求和返回结果。 function addCurry() {// 利用闭包的特性收集所有参数值let arr [...arguments] //返回函数return function fn() {// 如果参数为空则判断递归结束即传入一个()执行函数if(arguments.length 0) { return arr.reduce((a, b) a b) // 求和} else {arr.push(...arguments)return fn //递归}} }// 测试 console.log(addCurry(1)()); //1 console.log(addCurry(1)(2)()); //3 console.log(addCurry(1)(2)(3)()); //6 console.log(addCurry(1, 2)(3)()); //6 console.log(addCurry(1, 2, 3)()); //6 上述写法总是要以空括号()结尾于是再改进为隐式转换.toString写法原理当用 Function的值做计算的时候会调用toString做隐式转换。注意一些旧版本的浏览器隐式转换会默认执行新版本不行了。可以利用隐式转换或者alert。 function addCurry() {let arr [...arguments]// 利用闭包的特性收集所有参数值var fn function() {arr.push(...arguments);return fn;//递归};// 利用 toString 隐式转换转换的时候再返回结果fn.toString function () {return arr.reduce(function (a, b) {return a b;});}return fn; }//测试 console.log(addCurry(1)(2) 3) //true 利用隐式转换自动调用toString方法得到柯里化的结果 //alert(addCurry(1)(2)(3))//6 alert参数只能是字符串如果其他类型的值会转换成字符串会调用toString方法 console.log(addCurry(1).toString());//1 手动调用toString console.log(addCurry(1, 2)(3).toString());//6 console.log(addCurry(1, 2)(3)(4)(5).toString());//15 测试结果 8.数组扁平化 数组扁平化其实就是将多维数组转为一维数组。 1ES6中的flat const arr [1,[2,[3,[4,5]]],6] // arr.flat([depth]) flat的参数代表的是需要展开几层如果是Infinity的话就是不管嵌套几层全部都展开 console.log(arr.flat(Infinity)) //[1,2,3,4,5,6] 2递归 let arr [1, [2, [3, 4]]]; function flatten(arr) {let result [];for (let i 0; i arr.length; i) {//如果当前元素还是一个数组if (Array.isArray(arr[i])) {result result.concat(flatten(arr[i]));//递归拼接} else {result.push(arr[i]);}}return result; } console.log(flatten(arr)); // [1, 2, 3, 4] 3reduce函数迭代 从上面普通的递归函数中可以看出其实就是对数组的每一项进行处理那么其实也可以用reduce来实现数组的拼接从而简化第一种方法的代码。 let arr [1, [2, [3, 4]]]; function flatten(arr) {return arr.reduce((total, cur) {return total.concat(Array.isArray(cur) ? flatten(cur) : cur);}, []); //传递初始值空数组[]就会从数组索引为 0 的元素开始执行 } console.log(flatten(arr)); // [1, 2, 3, 4] 4split和toString 数组的toString方法可以把数组直接转换成逗号分隔的字符串。如[1, [2, [3, 4]]] 1,2,3,4 let arr [1, [2, [3, 4]]]; function flatten(arr) {//先把数组直接转换成逗号分隔的字符串然后再用 split 方法把字符串重新转换为数组return arr.toString().split(,).map(Number); } console.log(flatten(arr)); // [ 1, 2, 3, 4 ] 9.数组去重 1利用Set。new一个Set参数为需要去重的数组Set 会自动删除重复的元素再Array.from将 Set 转为数组返回。 const arr [1, 2, 3, 5, 1, 5, 9, 1, 2, 8]; console.log([...new Set(arr)]); //[ 1, 2, 3, 5, 9, 8 ] console.log(Array.from(new Set(arr))); //[ 1, 2, 3, 5, 9, 8 ] 2利用数组的filter() indexOf去重。利用filter方法返回arr.indexOf(num)等于index的值。原理就是indexOf会返回最先找到的数字的索引。 function unique(arr) {return arr.filter((item, index, array) {return array.indexOf(item) index;}); } const arr [1, 2, 3, 5, 1, 5, 9, 1, 2, 8]; console.log(unique(arr)); // [1, 2, 3, 5, 9, 8] 3利用map。新建一个数组和map如果当前值在map中没有出现过就加入数组最后返回数组。 const unique (arr) {const map new Map();const res [];for (let item of arr) {if (!map.has(item)) {map.set(item, true);res.push(item);}}return res; } const arr [1, 2, 3, 5, 1, 5, 9, 1, 2, 8]; console.log(unique(arr)); // [1, 2, 3, 5, 9, 8] 10.手写类型判断函数 思路如果是null直接返回String(null)基本类型和函数直接使用typeof其它引用类型使用Object.prototype.toString.call。 function getType(value) {// 判断数据是 null 的情况if (value null) {return String(value);}// 判断数据是基本数据类型的情况和函数的情况使用typeofif (typeof value ! object) {return typeof value;} else {// 判断数据是引用类型的情况设当前类型为datelet valueClass Object.prototype.toString.call(value); //[object Date]type valueClass.split( )[1].split(); //[ D, a, t, e, ] ] 截取类型并转换为数组type.pop(); //[ D, a, t, e ]去掉数组最后的右括号]return type.join().toLowerCase(); //[ D, a, t, e ] Date date 数组转小写字符串} }// 测试 console.info(getType(null)); // null console.info(getType(undefined)); // undefined console.info(getType(100)); // number console.info(getType(abc)); // string console.info(getType(true)); // boolean console.info(getType(Symbol())); // symbol console.info(getType({})); // object console.info(getType([])); // array console.info(getType(() {})); // function console.info(getType(new Date())); // date console.info(getType(new RegExp())); // regexp console.info(getType(new Map())); // map console.info(getType(new Set())); // set console.info(getType(new WeakMap())); // weakmap console.info(getType(new WeakSet())); // weakset console.info(getType(new Error())); // error console.info(getType(new Promise(() {}))); // promise 测试结果 结语 本文总结了前端常见的一些手写功能你是不是全都掌握了呢欢迎在评论区交流。 如果此文对你有帮助的话欢迎关注、点赞、⭐收藏、✍️评论支持一下博主~
http://www.pierceye.com/news/789280/

相关文章:

  • 教育网站制作视频代理网址ag80hncom
  • 泰兴公司做网站建设制作外贸网站公司
  • 手机wap网站大全作品提示优化要删吗
  • 郑州网站建设技术支持云南澄江县建设局网站
  • wordpress建企业网站设置网站一级域名和二级域名
  • 云南省城乡与住房建设厅网站合肥网红打卡地
  • 用dw做的企业网站宁波seo优化费用
  • 网站制作开发建网站公司 蓝纤科技
  • 怎样到国外做合法网站法网站网站建设小组实验报告
  • DNF做钓鱼网站网站建设方案书编写
  • 提高网站粘性wordpress tag中文
  • 公司已有网站 如何自己做推广wordpress的音乐插件怎么用
  • 权威网站php wordpress 等
  • 建设网站企业公司中通建设计院第四分公司网站
  • 快站免费网站建设哪家好南宁市住房建设局网站
  • 学生做的网站成品roseonly企业网站优化
  • 台前网站建设电话百度账号注册申请
  • 政和县建设局网站公告征婚网站咋做
  • 做网站需要用到的语言注册公司需要多少钱保证金
  • 如何给别人做网站赚钱互联网广告营销方案
  • 上海专业做网站公司地址软文代写
  • 家居网站源码宁波专业建设网站建站公司
  • 成都网站建设 Vr功能 卓 公司网站后台管理系统怎么用
  • 微站网建站系统南昌科技网站建设
  • 商家网站建设模板快速备份wordpress
  • 网站建设推广价格肯德基网站建设
  • 网站开发课程百度云平湖公司做网站
  • 手机网站建设品牌什么是企业云网站建设
  • 烟台网站建设推荐企汇互联见效付款58同城网站模板
  • 琪恋网站建设深圳3d网站建设