作者:火狼1
正向链接:https://juejin.im/post/5daeefc8e51d4524f007fb15
精品原生JS从初级到高级的知识点总结(二)本文
优秀原生JS从初级到高级的知识点总结(三)
优秀原生JS从初级到高级的知识点总结(四)
小编建议小伙们从第一篇开始,按照顺序来看,更清晰明了。
//父类class person {//构造函数是构造函数(皮肤,语言){this。皮肤=皮肤;this.language=语言;}say(){console.log('我是父类')} }//子类classchinesetextsperson { constructor(skin,language,positon){//console . log(this);//错误超(皮肤,语言);//super();相当于父类//console.log的构造函数(this);调用super后得到这个,它指向一个子类,相当于调用父类。原型。构造函数。把这个叫做这个。positon=positon} about me(){ console . log(` $ { this . skin } $ { this . language } $ { this . positon } `);}}//Call只能通过new的方法得到一个实例,然后调用letobj=newChinese ('red '' Chinese '' Hong Kong ')里面的方法;obj . about me();obj . say();复制代码
Fn(a,b,c,d)=fn(a)(b)(c)(d)复制代码2。代码实现3360
Const curring=fn={const len=fn。lengthreturnfunctioncurr(.args1) {if (args1。length=len) {returnfn(.args1)} return(.args2)=curr(.args1,args2)}
Obj.func (arg1,arg2)=func (obj,arg1,arg2)复制代码2。代码实现:
function . prototype . un currying=function(){ var that=this;return function(){ return function . prototype . call . apply(即,arguments);}};functions ayhi(){ return ' hello ' this . value ' '[]. slice . call(arguments);} letsayHiuncurrying=sayhi . uncurrying();console . log(sayHiuncurrying({ value : ' world ' },'哈哈哈');复制代码
functionfoo(a,b,c){ return b c;}functionfunc(a,b){returnfoo(a,b,8);}复制代码
vartest2={x:123,y:345};console.log(test2);//{x:123,y:345};console.log(test2.x);//123console.log(test2.__proto__.x);//undefinedconsole.log(test2.__proto__.x===test2.x);//false复制代码vartest1=newObject({x:123,y:345});console.log(test1);//{x:123,y:345}console.log(test1.x);//123console.log(test1.__proto__.x);//undefinedconsole.log(test1.__proto__.x===test1.x);//false复制代码new的作用:1.创了一个新对象;2.this指向构造函数;3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象
Obejct.create(obj,descriptor),obj是对象,describe描述符属性(可选)
lettest=Object.create({x:123,y:345});console.log(test);//{}console.log(test.x);//123console.log(test.__proto__.x);//123console.log(test.__proto__.x===test.x);//true复制代码1.功能:都能实现对象的声明,并能够赋值和取值2.继承性:内置方法创建的对象继承到__proto__属性上3.隐藏属性:三种声明方法会默认为内部的每个成员(属性或方法)生成一些隐藏属性,这些隐藏属性是可以读取和可配置的,属性分类见下面4.属性读取:Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptor()5.属性设置:Object.definePropertype或Object.defineProperties
1.数据属性4个特性:configurable(可配置),enumerable(可枚举),writable(可修改),value(属性值)
2.访问器属性2个特性:get(获取),set(设置)
3.内部属性由JavaScript引擎内部使用的属性;不能直接访问,但是可以通过对象内置方法间接访问,如:[[Prototype]]可以通过Object.getPrototypeOf()访问;内部属性用[[]]包围表示,是一个抽象操作,没有对应字符串类型的属性名,如[[Prototype]].
1.定义:将一个属性的所有特性编码成一个对象返回2.描述符的属性有:数据属性和访问器属性3.使用范围:作为方法Object.defineProperty,Object.getOwnPropertyDescriptor,Object.create的第二个参数,
1.访问对象存在的属性
特性名默认值value对应属性值get对应属性值setundefinedwritabletrueenumerabletrueconfigurabletrue
所以通过上面三种声明方法已存在的属性都是有这些默认描述符2.访问对象不存在的属性
特性名默认值valueundefinedgetundefinedsetundefinedwritablefalseenumerablefalseconfigurablefalse
get,set与wriable,value是互斥的,如果有交集设置会报错
1.定义属性的函数有两个:Object.defineProperty和Object.defineProperties.例如:Object.defineProperty(obj,propName,desc)
2.在引擎内部,会转换成这样的方法调用:obj.[[DefineOwnProperty]](propName,desc,true)
1.赋值运算符(=)就是在调用[[Put]].比如:obj.prop=v;
2.在引擎内部,会转换成这样的方法调用:obj.[[Put]]("prop",v,isStrictModeOn)
名称含义用法in如果指定的属性在指定的对象或其原型链中,则in运算符返回true'name'intest//truehasOwnProperty()只判断自身属性test.hasOwnProperty('name')//true.或[]对象或原型链上不存在该属性,则会返回undefinedtest.name//"lei"test["name"]//"lei"
是一种数据类型;不能new,因为Symbol是一个原始类型的值,不是对象。
Symbol(),可以传参vars1=Symbol();vars2=Symbol();s1===s2//false
//有参数的情况vars1=Symbol("foo");vars2=Symbol("foo");s1===s2//false复制代码1.不能与其他类型的值进行运算;2.作为属性名
letmySymbol=Symbol();//第一种写法vara={};a[mySymbol]='Hello!';//第二种写法vara={[mySymbol]:'Hello!'};//第三种写法vara={};Object.defineProperty(a,mySymbol,{value:'Hello!'});//以上写法都得到同样结果a[mySymbol]//"Hello!"复制代码3.作为对象属性名时,不能用点运算符,可以用[]
leta={};letname=Symbol();a.name='lili';a[name]='lucy';console.log(a.name,a[name]);复制代码4.遍历不会被for...in、for...of和Object.keys()、Object.getOwnPropertyNames()取到该属性
1.定义:在全局中搜索有没有以该参数作为名称的Symbol值,如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值2.举例:
vars1=Symbol.for('foo');vars2=Symbol.for('foo');s1===s2//true复制代码1.定义:返回一个已登记的Symbol类型值的key2.举例:
vars1=Symbol.for("foo");Symbol.keyFor(s1)//"foo"vars2=Symbol("foo");Symbol.keyFor(s2)//undefined复制代码方法特性for...in遍历对象自身的和继承的可枚举属性(不含Symbol属性)Object.keys(obj)返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)Object.getOwnPropertyNames(obj)返回一个数组,包括对象自身的所有可枚举和不可枚举属性(不含Symbol属性)Object.getOwnPropertySymbols(obj)返回一个数组,包含对象自身的所有Symbol属性Reflect.ownKeys(obj)返回一个数组,包含对象自身的所有(不枚举、可枚举和Symbol)属性Reflect.enumerate(obj)返回一个Iterator对象,遍历对象自身的和继承的所有可枚举属性(不含Symbol属性)
总结:1.只有Object.getOwnPropertySymbols(obj)和Reflect.ownKeys(obj)可以拿到Symbol属性2.只有Reflect.ownKeys(obj)可以拿到不可枚举属性
数据模型:
vartreeNodes=[{id:1,name:'1',children:[{id:11,name:'11',children:[{id:111,name:'111',children:[]},{id:112,name:'112'}]},{id:12,name:'12',children:[]}],users:[]},];复制代码递归:
varparseTreeJson=function(treeNodes){if(!treeNodes||!treeNodes.length)return;for(vari=0,len=treeNodes.length;i0){parseTreeJson(childs);}}};console.log('-------------递归实现------------------');parseTreeJson(treeNodes);复制代码 1.定义:将源对象(source)的所有可枚举属性,复制到目标对象(target)2.用法:
合并多个对象vartarget={a:1,b:1};varsource1={b:2,c:2};varsource2={c:3};Object.assign(target,source1,source2);复制代码3.注意:这个是伪深度拷贝,只能拷贝第一层
1.原理:是将对象转化为字符串,而字符串是简单数据类型
functiondeepClone(source){consttargetObj=source.constructor===Array?[]:{};//判断复制的目标是数组还是对象for(letkeysinsource){//遍历目标if(source.hasOwnProperty(keys)){if(source[keys]&&typeofsource[keys]==='object'){//如果值是对象,就递归一下targetObj[keys]=source[keys].constructor===Array?[]:{};targetObj[keys]=deepClone(source[keys]);}else{//如果不是,就直接赋值targetObj[keys]=source[keys];}}}returntargetObj;}复制代码定义:利用对象内置方法,设置属性,进而改变对象的属性值
1.ES5出来的方法;2.三个参数:对象(必填),属性值(必填),描述符(可选);3.defineProterty的描述符属性
数据属性:value,writable,configurable,enumerable访问器属性:get,set注:不能同时设置value和writable,这两对属性是互斥的复制代码4.拦截对象的两种情况:
letobj={name:'',age:'',sex:''},defaultName=["这是姓名默认值1","这是年龄默认值1","这是性别默认值1"];Object.keys(obj).forEach(key=>{Object.defineProperty(obj,key,{get(){returndefaultName;},set(value){defaultName=value;}});});console.log(obj.name);console.log(obj.age);console.log(obj.sex);obj.name="这是改变值1";console.log(obj.name);console.log(obj.age);console.log(obj.sex);letobjOne={},defaultNameOne="这是默认值2";Object.defineProperty(obj,'name',{get(){returndefaultNameOne;},set(value){defaultNameOne=value;}});console.log(objOne.name);objOne.name="这是改变值2";console.log(objOne.name);复制代码5.拦截数组变化的情况
leta={};bValue=1;Object.defineProperty(a,"b",{set:function(value){bValue=value;console.log("setted");},get:function(){returnbValue;}});a.b;//1a.b=[];//setteda.b=[1,2,3];//setteda.b[1]=10;//无输出a.b.push(4);//无输出a.b.length=5;//无输出a.b;//[1,10,3,4,undefined];复制代码结论:defineProperty无法检测数组索引赋值,改变数组长度的变化;但是通过数组方法来操作可以检测到
多级嵌套对象监听
letinfo={};functionobserve(obj){if(!obj||typeofobj!=="object"){return;}for(variinobj){definePro(obj,i,obj[i]);}}functiondefinePro(obj,key,value){observe(value);Object.defineProperty(obj,key,{get:function(){returnvalue;},set:function(newval){console.log("检测变化",newval);value=newval;}});}definePro(info,"friends",{name:"张三"});info.friends.name="李四";复制代码6.存在的问题
不能监听数组索引赋值和改变长度的变化必须深层遍历嵌套的对象,因为defineProterty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择复制代码1.ES6出来的方法,实质是对对象做了一个拦截,并提供了13个处理方法
2.两个参数:对象和行为函数
lethandler={get(target,key,receiver){console.log("get",key);returnReflect.get(target,key,receiver);},set(target,key,value,receiver){console.log("set",key,value);returnReflect.set(target,key,value,receiver);}};letproxy=newProxy(obj,handler);proxy.name="李四";proxy.age=24;复制代码涉及到多级对象或者多级数组
//传递两个参数,一个是object,一个是proxy的handler//如果是不是嵌套的object,直接加上proxy返回,如果是嵌套的object,那么进入addSubProxy进行递归。functiontoDeepProxy(object,handler){if(!isPureObject(object))addSubProxy(object,handler);returnnewProxy(object,handler);//这是一个递归函数,目的是遍历object的所有属性,如果不是pureobject,那么就继续遍历object的属性的属性,如果是pureobject那么就加上proxyfunctionaddSubProxy(object,handler){for(letpropinobject){if(typeofobject[prop]=='object'){if(!isPureObject(object[prop]))addSubProxy(object[prop],handler);object[prop]=newProxy(object[prop],handler);}}object=newProxy(object,handler)}//是不是一个pureobject,意思就是object里面没有再嵌套object了functionisPureObject(object){if(typeofobject!=='object'){returnfalse;}else{for(letpropinobject){if(typeofobject[prop]=='object'){returnfalse;}}}returntrue;}}letobject={name:{first:{four:5,second:{third:'ssss'}}},class:5,arr:[1,2,{arr1:10}],age:{age1:10}}//这是一个嵌套了对象和数组的数组letobjectArr=[{name:{first:'ss'},arr1:[1,2]},2,3,4,5,6]//这是proxy的handlerlethandler={get(target,property){console.log('get:'+property)returnReflect.get(target,property);},set(target,property,value){console.log('set:'+property+'='+value);returnReflect.set(target,property,value);}}//变成监听对象object=toDeepProxy(object,handler);objectArr=toDeepProxy(objectArr,handler);//进行一系列操作console.time('pro')objectArr.lengthobjectArr[3];objectArr[2]=10objectArr[0].name.first='ss'objectArr[0].arr1[0]object.name.first.second.third='yyyyy'object.class=6;object.name.first.fourobject.arr[2].arr1object.age.age1=20;console.timeEnd('pro')复制代码3.问题和优点reflect对象没有构造函数可以监听数组索引赋值,改变数组长度的变化,是直接监听对象的变化,不用深层遍历
本篇未完结,请见下一篇
《学习jQuery源码整体架构,打造属于自己的js类库》
《细说RTSP实现前端直播流探索记「干货」》
《一文带你搞懂前端登陆设计》
《使用Node.js将图片中的苹果变成橘子「实践」》
《基于Canvas实现的高斯模糊(上)「JS篇」》
《基于Canvas实现的高斯模糊(下)「JS篇」》
《由浅入深,66条JavaScript面试知识点(一)》
《由浅入深,66条JavaScript面试知识点(二)》
《由浅入深,66条JavaScript面试知识点(三)》
《由浅入深,66条JavaScript面试知识点(四)》
《由浅入深,66条JavaScript面试知识点(五)》
《由浅入深,66条JavaScript面试知识点(六)》
《由浅入深,66条JavaScript面试知识点(七)》
《为什么setTimeout有最小时延4ms?》
《如何处理Node.js中出现的未捕获异常?》
《Angularv10.0.0正式发布,不再支持IE9/10》
《基于Docker的SSR持续开发集成环境实践》
《细聊图解webpack指南手册》
《一文带你彻底搞懂NPM知识点「进阶篇」》
《细聊webpack性能优化面面观》
《JS实现各种日期操作方法汇总》
《「实践」细聊前端性能优化总结》
《「实践」浏览器中的画中画(Picture-in-Picture)模式及其API》
《「多图」一文带你彻底搞懂WebWorkers(上)》
《「多图」一文带你彻底搞懂WebWorkers(中)》
《深入细聊前端下载总结「干货」》
《细品西瓜播放器功能分析(上)「实践」》
《细品西瓜播放器功能分析(下)「实践」》
《细聊50道JavaScript基础面试题「附答案」》
《webpack4主流程源码解说以及动手实现一个简单的webpack(上)》
《webpack4主流程源码解说以及动手实现一个简单的webpack(下)》
《细聊前端架构师的视野》
《细聊应用场景再谈防抖和节流「进阶篇」》
《前端埋点统一接入方案实践》
《细聊微内核架构在前端的应用「干货」》
《一种高性能的Tree组件实现方案「干货」》
《进击的JAMStack》
《前后端全部用JS开发是什么体验(Hybrid+Egg.js经验分享)上》
《前后端全部用JS开发是什么体验(Hybrid+Egg.js经验分享)中》
《前后端全部用JS开发是什么体验(Hybrid+Egg.js经验分享)下》
《一文带你搞懂babel-plugin-import插件(上)「源码解析」》
《一文带你搞懂babel-plugin-import插件(下)「源码解析」》
《JavaScript常用API合集汇总「值得收藏」》
《推荐10个常用的图片处理小帮手(上)「值得收藏」》
《推荐10个常用的图片处理小帮手(下)「值得收藏」》
《JavaScript中ES6代理的实际用例》
《12个实用的前端开发技巧总结》
《一文带你搞懂搭建企业级的npm私有仓库》
《教你如何使用内联框架元素IFrames的沙箱属性提高安全性?》
《细说前端开发UI公共组件的新认识「实践」》
《细说DOMAPI中append和appendChild的三个不同点》
《细品淘系大佬讲前端新人如何上王者「干货」》
《一文带你彻底解决背景跟随弹窗滚动问题「干货」》
《推荐常用的5款代码比较工具「值得收藏」》
《Node.js实现将文字与图片合成技巧》
《爱奇艺云剪辑Web端的技术实现》
《我再也不敢说我会写前端Button组件「实践」》
《NodeXComponent-滴滴集团Node.js生态组件体系「实践」》
《NodeBuffers完整指南》
《推荐18个webpack精美插件「干货」》
《前端开发需要了解常用7种JavaScript设计模式》
《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》
《了不起的WebpackHMR学习指南(上)「含源码讲解」》
《了不起的WebpackHMR学习指南(下)「含源码讲解」》
《10个打开了我新世界大门的WebAPI(上)「实践」》
《10个打开了我新世界大门的WebAPI(中)「实践」》
《10个打开了我新世界大门的WebAPI(下)「实践」》
《「图文」ESLint在中大型团队的应用实践》
《Deno是代码的浏览器,你认同吗?》
《前端存储除了localStorage还有啥?》
《Javascript多线程编程的前世今生》
《微前端方案qiankun(实践及总结)》
《「图文」V8垃圾回收原来这么简单?》
《Webpack5模块联邦引发微前端的革命?》
《基于Web端的人脸识别身份验证「实践」》
《「前端进阶」高性能渲染十万条数据(时间分片)》
《「前端进阶」高性能渲染十万条数据(虚拟列表)》
《图解Promise实现原理(一):基础实现》
《图解Promise实现原理(二):Promise链式调用》
《图解Promise实现原理(三):Promise原型方法实现》
《图解Promise实现原理(四):Promise静态方法实现》
《实践教你从零构建前端Lint工作流「干货」》
《高性能多级多选级联组件开发「JS篇」》
《深入浅出讲解Node.jsCLI工具最佳实战》
《延迟加载图像以提高Web网站性能的五种方法「实践」》
《比较JavaScript对象的四种方式「实践」》
《使用ServiceWorker让你的Web应用如虎添翼(上)「干货」》
《使用ServiceWorker让你的Web应用如虎添翼(中)「干货」》
《使用ServiceWorker让你的Web应用如虎添翼(下)「干货」》
《前端如何一次性处理10万条数据「进阶篇」》
《推荐三款正则可视化工具「JS篇」》
《如何让用户选择是否离开当前页面?「JS篇」》
《JavaScript开发人员更喜欢Deno的五大原因》
《仅用18行JavaScript实现一个倒数计时器》
《图文细说JavaScript的运行机制》
《一个轻量级JavaScript全文搜索库,轻松实现站内离线搜索》
《推荐Web程序员常用的15个源代码编辑器》
《10个实用的JS技巧「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》
《深入JavaScript教你内存泄漏如何防范》
《手把手教你7个有趣的JavaScript项目-上「附源码」》
《手把手教你7个有趣的JavaScript项目-下「附源码」》
《JavaScript使用mediaDevicesAPI访问摄像头自拍》
《手把手教你前端代码如何做错误上报「JS篇」》
《一文让你彻底搞懂移动前端和Web前端区别在哪里》
《63个JavaScript正则大礼包「值得收藏」》
《提高你的JavaScript技能10个问答题》
《JavaScript图表库的5个首选》
《一文彻底搞懂JavaScript中Object.freeze与Object.seal的用法》
《可视化的JS:动态图演示-事件循环EventLoop的过程》
《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》
《可视化的js:动态图演示Promises&Async/Await的过程》
《原生JS封装拖动验证滑块你会吗?「实践」》
《如何实现高性能的在线PDF预览》
《细说使用字体库加密数据-仿58同城》
《Node.js要完了吗?》
《Pug3.0.0正式发布,不再支持Node.js6/8》
《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》
《JavaScript20年中文版之创立标准》
《值得收藏的前端常用60余种工具方法「JS篇」》
《箭头函数和常规函数之间的5个区别》
《通过发布/订阅的设计模式搞懂Node.js核心模块Events》
《「前端篇」不再为正则烦恼》
《「速围」Node.jsV14.3.0发布支持顶级Await和REPL增强功能》
《深入细品浏览器原理「流程图」》
《JavaScript已进入第三个时代,未来将何去何从?》
《前端上传前预览文件image、text、json、video、audio「实践」》
《深入细品EventLoop和浏览器渲染、帧动画、空闲回调的关系》
《推荐13个有用的JavaScript数组技巧「值得收藏」》
《前端必备基础知识:window.location详解》
《不要再依赖CommonJS了》
《犀牛书作者:最该忘记的JavaScript特性》
《36个工作中常用的JavaScript函数片段「值得收藏」》
《Node+H5实现大文件分片上传、断点续传》
《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》
《【实践总结】关于小程序挣脱枷锁实现批量上传》
《手把手教你前端的各种文件上传攻略和大文件断点续传》
《字节跳动面试官:请你实现一个大文件上传和断点续传》
《谈谈前端关于文件上传下载那些事【实践】》
《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》
《最全的JavaScript模块化方案和工具》
《「前端进阶」JS中的内存管理》
《JavaScript正则深入以及10个非常有意思的正则实战》
《前端面试者经常忽视的一道JavaScript面试题》
《一行JS代码实现一个简单的模板字符串替换「实践」》
《JS代码是如何被压缩的「前端高级进阶」》
《前端开发规范:命名规范、html规范、css规范、js规范》
《【规范篇】前端团队代码规范最佳实践》
《100个原生JavaScript代码片段知识点详细汇总【实践】》
《关于前端174道JavaScript知识点汇总(一)》
《关于前端174道JavaScript知识点汇总(二)》
《关于前端174道JavaScript知识点汇总(三)》
《几个非常有意思的javascript知识点总结【实践】》
《都2020年了,你还不会JavaScript装饰器?》
《JavaScript实现图片合成下载》
《70个JavaScript知识点详细总结(上)【实践】》
《70个JavaScript知识点详细总结(下)【实践】》
《开源了一个JavaScript版敏感词过滤库》
《送你43道JavaScript面试题》
《3个很棒的小众JavaScript库,你值得拥有》
《手把手教你深入巩固JavaScript知识体系【思维导图】》
《推荐7个很棒的JavaScript产品步骤引导库》
《Echa哥教你彻底弄懂JavaScript执行机制》
《一个合格的中级前端工程师需要掌握的28个JavaScript技巧》
《深入解析高频项目中运用到的知识点汇总【JS篇】》
《JavaScript工具函数大全【新】》
《从JavaScript中看设计模式(总结)》
《身份证号码的正则表达式及验证详解(JavaScript,Regex)》
《浏览器中实现JavaScript计时器的4种创新方式》
《Three.js动效方案》
《手把手教你常用的59个JS类方法》
《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》
《深入浅出讲解js深拷贝vs浅拷贝》
《手把手教你JS开发H5游戏【消灭星星】》
《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》
《手把手教你全方位解读JS中this真正含义【实践】》
《书到用时方恨少,一大波JS开发工具函数来了》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《手把手教你JS异步编程六种方案【实践】》
《让你减少加班的15条高效JS技巧知识点汇总【实践】》
《手把手教你JS开发H5游戏【黄金矿工】》
《手把手教你JS实现监控浏览器上下左右滚动》
《JS经典实例知识点整理汇总【实践】》
《2.6万字JS干货分享,带你领略前端魅力【基础篇】》
《2.6万字JS干货分享,带你领略前端魅力【实践篇】》
《简单几步让你的JS写得更漂亮》
《恭喜你获得治疗JSthis的详细药方》
《谈谈前端关于文件上传下载那些事【实践】》
《面试中教你绕过关于JavaScript作用域的5个坑》
《Jquery插件(常用的插件库)》
《【JS】如何防止重复发送ajax请求》
《JavaScript+Canvas实现自定义画板》
《Continuation在JS中的应用「前端篇」》
作者:火狼1
转发链接:https://juejin.im/post/5daeefc8e51d4524f007fb15