关于javascript:JS知识点梳理之作用域作用域链柯里化闭包

一、作用域与作用域链作用域是指 js 变量应用时所存在的一个区域,分为全局作用域(window)和部分作用域(function、setTimeout...等都会产生部分作用域)。当部分作用域变量名与全局作用域变量名反复时,局部变量会笼罩全局变量。 在部分作用域应用变量时,如果在本人作用域找不到对应变量,则会往上一级作用域查找,直到全局作用域,如果全局作用域无此变量则会报 undefined。相同,全局作用域中无奈应用部分作用域中的变量。 window.a = 1function(){ // 输入 1,尽管部分没有 a 变量,然而 全局中有。 console.log(a) var b = 2}// 报错,全局中无奈应用局部变量。console.log(b)下面这种一层层向外查问变量的过程叫做查问作用域链。而这种一层层部分作用域直到全局作用域的构造被称为作用域链。 // 全局作用域,申明了一个全局变量 avar a = 100// 函数会生成部分作用域function acs(){ // 在此部分作用域中申明一个局部变量 b var b = 50 // 输入:100, 50 console.log(a, b) // 执行过程:在此作用域查找变量 a, // 找不到-->往上一级作用域找-->在全局找到,应用全局作用域中的a // 在此作用域查找变量 b,查找到了,应用此局部变量的 b}()// 输入:b is not definedconsole.log(a, b)二、闭包(Closure)1. 闭包是什么?闭包是指在函数内部调用函数外部的局部变量,且在调用后局部变量不会被浏览器立刻回收,会始终存在的一种公有变量。再简略点说就是函数返回函数。 红宝书中的刻画:闭包是指有权拜访另一个函数作用域中的变量的函数。 其实闭包就是返回一个函数,且这个函数对局部变量存在援用造成的蕴含关系就是闭包。 其实就是创立一个不会被 GC 回收的局部变量。也正因如此,闭包才会有内存透露的危险,须要在每次应用完后立即革除。 闭包的造成:以后环境中存在指向父级作用域的援用。 2. 闭包的写法// 应用自执行函数造成闭包var add = function(){ let sum = 0 return function operation(){ return sum = sum ? sum + 1 : 1 }}()// 输入:1add()// 输入:2add()// 输入:3add()// 输入:4add()// 革除闭包,删除公有变量add = null// 输入:nullconsole.log(add)// 输入:add is not functionadd()3. 闭包的作用应用闭包的目标――暗藏变量,间接拜访一个变量,在定义函数的词法作用域外,调用函数。 ...

October 25, 2022 · 2 min · jiezi

关于javascript:JS词法环境和执行上下文

前言JavaScript是一门解释性动静语言,但同时它也是一门充斥神秘感的语言。如果要成为一名优良的JS开发者,那么对JavaScript程序的外部执行原理要有所理解。 本文以最新的ECMA标准中的第八章节为根底,理清JavaScript的词法环境和执行上下文的相干内容。这是了解JavaScript其余概念(let/const暂时性死区、变量晋升、闭包等)的根底。 本文参考的是最新公布的第十代ECMA-262规范,即ES2019ES2019与ES6在词法环境和执行上下文的内容上是近似的,ES2019在细节上做了局部补充,因而本文间接采纳ES2019的规范。你也能够比照两个版本的规范的差别。执行上下文(Execution Context)执行上下文是用来跟踪记录代码运行时环境的抽象概念。每一次代码运行都至多会生成一个执行上下文。代码都是在执行上下文中运行的。 你能够将代码运行与执行上下文的关系类比为过程与内存的关系,在代码运行过程中的变量环境信息都放在执行上下文中,当代码运行完结,执行上下文也会销毁。在执行上下文中记录了代码执行过程中的状态信息,依据不同运行场景,执行上下文会细分为如下几种类型: 全局执行上下文:当运行代码是处于全局作用域内,则会生成全局执行上下文,这也是程序最根底的执行上下文。函数执行上下文:当调用函数时,都会为函数调用创立一个新的执行上下文。eval执行上下文:eval函数执行时,会生成专属它的上下文,因eval很少应用,故不作探讨。执行栈有了执行上下文,就要有正当治理它的工具。而执行栈(Execution Context Stack)是用来治理执行期间创立的所有执行上下文的数据结构,它是一个LIFO(后进先出)的栈,它也是咱们熟知的JS程序运行过程中的调用栈。程序开始运行时,会先创立一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创立一个新的函数执行上下文并压入栈内。 咱们从一小段代码来看下执行栈的工作过程: <script> console.log('script') function foo(){ function bar(){ console.log('bar', isNaN(undefined)) } bar() console.log('foo') } foo()</script>当这段JS程序开始运行时,它会创立一个全局执行上下文GlobalContext,其中会初始化一些全局对象或全局函数,如代码中的console,undefined,isNaN。将全局执行上下文压入执行栈,通常JS引擎都有一个指针running指向栈顶元素: JS引擎会将全局范畴内申明的函数(foo)初始化在全局上下文中,之后开始一行行的执行代码,运行到console就在running指向的上下文中的词法环境中找到全局对象console并调用log函数。 PS:当然,当调用log函数时,也是要新建函数上下文并压栈到调用栈中的。这里为了简略流程,疏忽了log上下文的创立过程。运行到foo()时,辨认为函数调用,此时创立一个新的执行上下文FooContext并入栈,将FooContext内词法环境的outer援用指向全局执行上下文的词法环境,挪动running指针指向这个新的上下文: 在实现FooContext创立后,进入到FooContext中继续执行代码,运行到bar()时,同理仍须要新建一个执行上下文BarContext,此时BarContext内词法环境的outer援用会指向FooContext的词法环境: 持续运行bar函数,因为函数上下文内有outer援用实现层层递进援用,因而在bar函数内仍能够获取到console对象并调用log。 之后,实现bar和foo函数调用,会顺次将上下文出栈,直至全局上下文出栈,程序完结运行。 执行上下文的创立执行上下文创立会做两件事件: 创立词法环境LexicalEnvironment;创立变量环境VariableEnvironment;因而一个执行上下文在概念上应该是这样子的: ExecutionContext = { LexicalEnvironment = <ref. to LexicalEnvironment in memory>, VariableEnvironment = <ref. to VariableEnvironment in memory>,}在全局执行上下文中,this指向全局对象,window in browser / global in nodejs。 词法环境(LexicalEnvironment)词法环境是ECMA中的一个标准类型 —— 基于代码词法嵌套构造用来记录标识符和具体变量或函数的关联。简略来说,词法环境就是建设了标识符——变量的映射表。这里的标识符指的是变量名称或函数名,而变量则是理论变量原始值或者对象/函数的援用地址。 在LexicalEnvironment中由两个局部形成: 环境记录EnvironmentRecord:寄存变量和函数申明的中央;外层援用outer:提供了拜访父词法环境的援用,可能为null;this绑定ThisBinding:确定以后环境中this的指向,this binding存储在EnvironmentRecord中;词法环境的类型 全局环境(GlobalEnvironment):在JavaScript代码运行伊始,宿主(浏览器、NodeJs等)会当时初始化全局环境,在全局环境的EnvironmentRecord中会绑定内置的全局对象(Infinity等)或全局函数(eval、parseInt等),其余申明的全局变量或函数也会存储在全局词法环境中。全局环境的outer援用为null。这里提及的全局对象就有咱们相熟的所有内置对象,如Math、Object、Array等构造函数,以及Infinity等全局变量。全局函数则蕴含了eval、parseInt等函数。模块环境(ModuleEnvironment):你若写过NodeJs程序就会很相熟这个环境,在模块环境中你能够读取到export、module等变量,这些变量都是记录在模块环境的ER中。模块环境的outer援用指向全局环境。函数环境(FunctionEnvironment):每一次调用函数时都会产生函数环境,在函数环境中会波及this的绑定或super的调用。在ER中也会记录该函数的length和arguments属性。函数环境的outer援用指向调起该函数的父环境。在函数体内申明的变量或函数则记录在函数环境中。参考视频解说:进入学习环境记录ER 代码中申明的变量和函数都会寄存在EnvironmentRecord中期待执行时拜访。环境记录EnvironmentRecord也有两个不同类型,别离为declarative和object。declarative是较为常见的类型,通常函数申明、变量申明都会生成这种类型的ER。object类型能够由with语句触发的,而with应用场景很少,个别开发者很少用到。 如果你在函数体中遇到诸如var const let class module import 函数申明,那么环境记录就是declarative类型的。 ...

October 25, 2022 · 2 min · jiezi

关于javascript:几个常见的js手写题你能写出来几道

实现 new 过程:要点: 函数第一个参数是构造函数实例的__proto__指向构造函数的原型属性prototype函数残余参数要挂载到一个实例对象上构造函数有返回值时,就返回这个返回值const createObj = function () { let obj = {} let Constructor = [].shift.call(arguments) // 1 obj.__proto__ = Constructor.prototype // 2 let ret = Constructor.apply(obj, arguments) // 3 return typeof ret === 'object' ? ret: obj // 4}// 应用const Fun = function (name) { this.name = name}Fun.prototype.getName = function() { alert(this.name)}let fun = createObj(Fun, 'gim')fun.getName() // gim值得注意的是,es6的class必须用new调用,否则会报错,如下: class Fun { constructor(name) { this.name = name } getName() { alert(this.name) }}let fun = createObj(Fun, 'gim')fun.getName() // Uncaught TypeError: Class constructor Fun cannot be invoked without 'new'手写 call、apply 及 bind 函数共同点: ...

October 25, 2022 · 4 min · jiezi

关于javascript:记录面试前端遇到的手写题

滚动加载原理就是监听页面滚动事件,剖析clientHeight、scrollTop、scrollHeight三者的属性关系。 window.addEventListener('scroll', function() { const clientHeight = document.documentElement.clientHeight; const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; if (clientHeight + scrollTop >= scrollHeight) { // 检测到滚动至页面底部,进行后续操作 // ... }}, false);将虚构 Dom 转化为实在 Dom{ tag: 'DIV', attrs:{ id:'app' }, children: [ { tag: 'SPAN', children: [ { tag: 'A', children: [] } ] }, { tag: 'SPAN', children: [ { tag: 'A', children: [] }, { tag: 'A', children: [] } ] } ]}把下面虚构Dom转化成下方实在Dom<div id="app"> <span> <a></a> </span> <span> <a></a> <a></a> </span></div>实现 ...

October 25, 2022 · 7 min · jiezi

关于javascript:实现Promise的原型方法前端面试能力提升

说起Promise大家应该都耳熟能详,咱们明天来看下Promise的相干办法有如下:原型办法:then、catch、finally 静态方法:resolve、reject、race、all、allSettled、any 手写实现办法如下:实现resolve办法promise.resolve('123')本质上就是new Promise(resolve=>resolve('123')})Promise.resolve(value) 将给定的一个值转为Promise对象。 如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then" 办法),返回的promise会“追随”这个thenable的对象,采纳它的最终状态;否则返回的promise将以此值实现,即以此值执行resolve()办法 (状态为fulfilled)。 class MyPromise { static PENDING = 'pending' static FULFILLED = 'fulfilled' static REJECTED = 'rejected' constructor(executor) { this.PromiseState = MyPromise.PENDING this.PromiseResult = null this.fulfilledCallBacks = [] this.rejectedCallBacks = [] try { executor(this.resolve.bind(this), this.reject.bind(this)) } catch (error) { this.reject(error) } } resolve(result) { if ((this.PromiseState = MyPromise.PENDING)) { setTimeout(() => { this.PromiseState = MyPromise.FULFILLED this.PromiseResult = result for (const callBack of this.fulfilledCallBacks) { callBack(result) } }) } } reject(reason) { if ((this.PromiseState = MyPromise.PENDING)) { setTimeout(() => { this.PromiseState = MyPromise.REJECTED this.PromiseResult = reason for (const callBack of this.rejectedCallBacks) { callBack(reason) } }) } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val) => val onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err } return new MyPromise((resolve, reject) => { if (this.PromiseState === MyPromise.PENDING) { this.fulfilledCallBacks.push(() => { setTimeout(() => { let x = onFulfilled(this.PromiseResult) x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) }) }) this.rejectedCallBacks.push(() => { setTimeout(() => { let x = onRejected(this.PromiseResult) x instanceof MyPromise ? x.then(resolve, reject) : reject(x) }) }) } else if (this.PromiseState === MyPromise.FULFILLED) { try { setTimeout(() => { let x = onFulfilled(this.PromiseResult) x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) }) } catch (error) { reject(error) } } else { try { setTimeout(() => { let x = onRejected(this.PromiseResult) x instanceof MyPromise ? x.then(resolve, reject) : reject(x) }) } catch (error) { reject(error) } } }) } //value 要解析为 Promise 对象的值 static resolve(value) { //如果是 if (value instanceof MyPromise) { return value } else if (value && typeof value === 'object' && 'then' in value) { return new MyPromise((resolve, reject) => { value.then(resolve, reject) }) } return new MyPromise((resolve) => { resolve(value) }) } } const promise1 = MyPromise.resolve(123) promise1.then((value) => { console.log(value) // expected output: 123 }) // Resolve一个thenable对象 var p1 = MyPromise.resolve({ then: function (onFulfill) { onFulfill('Resolving') }, }) console.log(p1 instanceof MyPromise) // true, 这是一个Promise对象 setTimeout(() => { console.log('p1 :>> ', p1) }, 1000) p1.then( function (v) { console.log(v) // 输入"Resolving!" }, function (e) { // 不会被调用 } ) // Thenable在callback之前抛出异样 // MyPromise rejects var thenable = { then: function (resolve) { throw new TypeError('Throwing') resolve('Resolving') }, } var p2 = MyPromise.resolve(thenable) p2.then( function (v) { // 不会被调用 }, function (e) { console.log(e) // TypeError: Throwing } ) ...

October 25, 2022 · 15 min · jiezi

关于javascript:使用js高效地将扁平的数据转换成树形结构对象形式

function buildtree(list){ let temp={}; let tree={}; for(let i in list){ temp[list[i].id]=list[i]; } for(let i in temp){ // 如果有父级id if(temp[i].parent_id){ if(!temp[temp[i].parent_id].children){ temp[temp[i].parent_id].children=new Object(); } // 将父节点与子节点连接起来 temp[temp[i].parent_id].children[temp[i].id]=temp[i]; }else{ tree[temp[i].id]=temp[i]; } } return tree;}

October 25, 2022 · 1 min · jiezi

关于javascript:解决项目中两个值Number后一直相等的问题

有任何问题都能够留言征询。 问题切换实例名称的下拉选项后,应该会更新缓存中的实例名称的值。 但发现不管怎么切换,实例名称的值都不会扭转。 页面页面是上面这样的。 切换以后实例的下拉选项,会更新缓存中的inst和instName的值。 文章不易,请关注公众号 毛毛虫的小小蜡笔。 代码单从代码逻辑来看,是看不出什么问题的。 因为代码逻辑还是比较简单清晰,这里没有什么简单的判断。 奇怪的问题是,每次切换后,都能进入if条件外面,但instName和inst的值始终没更新。 详情 请查看:毛毛虫的小小蜡笔 有任何问题可加群征询:

October 25, 2022 · 1 min · jiezi

关于javascript:从输入URL到渲染的完整过程

CDN缓存DNSTCP三次握手、四次挥手浏览器渲染过程输出URL到页面渲染过程的一些优化上面我将“从输出URL到渲染的全过程”大略的形容进去,再对其过程加以解释,理解过程中能够做哪些优化。文章内容有点长,须要有足够的急躁看完哟!!上面我要开始啦! 1、URL解析 2、DNS解析 3、建设TCP链接 4、客户端发送申请 5、服务器解决和响应申请 6、浏览器解析并渲染响应内容 7、TCP四次挥手断开连接 一、URL解析地址解析和编码咱们输出URL后,浏览器会解析输出的字符串,判断是URL还是搜寻关键字,如果是URL就开始编码。 一般来说URL只能应用英文字母、阿拉伯数字和某些标点符号,不能应用其余文字和符号,所以,如果URL中有文字就必须编码后应用。然而URL编码很凌乱,不同的操作系统、浏览器、网页字符集,会导致不同的编码后果。所以咱们须要应用JavaScript先对URL编码,而后提交给服务器,不给浏览器插手的机会。咱们通常会应用encodeURI()函数或者encodeURIComponent()函数来编码URL HSTSHSTS(HTTP Strict TransportSecurity)是一种新的Web平安协定,HSTS的作用是强制客户端应用HTTPS与服务器创立连贯。比方你在地址栏输出http://xxx/,浏览器会主动将http转写成https,而后间接向 https://xxx/ 发送申请。 缓存查看浏览器在发送申请之前先查看有没有缓存,过程如下: 浏览器会先去查看强缓存(Expires和cache-control)判断是否过期,如果强缓存失效,间接从缓存中读取资源;若不失效则进行协商缓存(Last-Modified / If-Modified-Since和Etag/If-None-Match),协商缓存由服务器决定是否应用缓存,若协商缓存生效,那么代表该申请的缓存生效,返回200,并从新返回资源和缓存标识,再次存入浏览器缓存中;失效则返回304,并从缓存中读取资源。(协商缓存之前要通过DNS域名解析,之后建设TCP链接) 那么浏览器缓存的地位在哪呢? Service Worker:浏览器独立线程进行缓存Memory Cache:内存缓存Disk Cache:硬盘缓存Push Cache:推送缓存(HTTP/2中的)留神:输出网址之后,会查找内存缓存,没有再找硬盘,都没有就产生网络申请。一般刷新(F5):因为TAB没有敞开,所以内存缓存可用,如果匹配上会被优先应用,其次是磁盘缓存强制刷新(Ctrl+F5):浏览器不应用缓存,因而发送的申请头均带有Cache-control:no-cache,服务器间接返回200和最新内容。 二、进行DNS解析DNS(1)、DNS:把域名和ip地址互相映射分布式数据库,让用户能更不便的拜访互联网,DNS协定运行在UDP协定之上 (2)、DNS解析:通过域名最终失去对应ip地址的过程。 (3)、DNS缓存:浏览器,操作系统,路由器,本地DNS,根域名服务器都会对DNS后果作出肯定的缓存 DNS解析过程(1)、首先搜寻浏览器本身的DNS缓存,有缓存间接返回; (2)、浏览器本身DNS不存在,浏览器就会调用一个相似gethostbyname的库函数,此函数会先去检测本地hosts文件,查看是否有对应ip。 (3)、如果本地hosts文件不存在映射关系,就会查问路由缓存,路由缓存不存在就去查找本地DNS服务器(个别TCP/IP参数里会设首选DNS服务器,通常是8.8.8.8)(客户端到本地DNS服务器是递归过程) (4)、如果本地DNS服务器还没找到就会向根服务器发出请求。(DNS服务器之间是迭代过程) 具体过程: 本地DNS服务器代咱们的浏览器发动迭代DNS解析申请,首先它会找根域的DNS的IP地址(寰球13台哟,惋惜中国没有!)。找到根域的DNS地址,就会向其发动申请(请问www.baidu.com这个域名的IP地址是多少呀?);根域发现这是一个顶级域com域的一个域名,于是通知本地DNS服务器我不晓得这个域名的IP地址,然而我晓得com域的IP地址,你去找它去吧;于是本地DNS服务器就失去了com域的IP地址,又向com域的IP地址发动了申请(请问www.baidu.com这个域名的IP地址是多少呀?),于是com域服务器通知本地DNS服务器我不晓得www.baidu.com这个域名的IP地址,然而我晓得baidu.com这个域的DNS地址,你去找它去;于是本地DNS服务器又向baidu.com这个域名的DNS地址(这个个别就是由域名注册商提供的,像万网,新网等)发动申请(请问www.baidu.com这个域名的IP地址是多少?),这个时候baidu.com域的DNS服务器一查,呀!果然在我这耶,于是就把找到的后果发送给本地DNS服务器;这个时候本地DNS服务器就拿到了www.baidu.com这个域名对应的IP地址。DNS优化DNS也是开销,通常浏览器查找一个给定域名的IP地址要花费20~120毫秒,在实现域名解析之前,浏览器不能从服务器加载到任何货色。那么如何缩小域名解析工夫,放慢页面加载速度呢? (1)、缩小DNS申请次数 (2)、DNS预获取,DOM还没开始,浏览器预解析地址,把解析好的地址放在本地缓存外面,DOM树生成完,要加载图片类的发现DNS曾经解析好了,再发送申请。<link rel='dns-prefetch'href='//dfns.tanx.com'> (次要对图片资源) (3)、DNS 查问的过程经验了很多的步骤,如果每次都如此,会消耗太多的工夫、资源。所以咱们应该尽早的返回实在的IP地址:(缩小查问过程,也就是DNS缓存。浏览器获取到IP地址后,个别都会缓存到浏览器的缓存中,本地的DNS缓存服务器,也能够去记录。另外,每天几亿网名的拜访需要,一秒钟几千万的申请域名服务器如何满足?就是DNS负载平衡。通常咱们的网站利用各种云服务,或者各种服务商提供相似的服务,由他们去帮咱们解决这些问题。 DNS零碎依据每台机器的负载量,地理位置的限度(长距离的传输效率)等等,去提供高效疾速的 DNS 解析服务。 (4)、当客户端DNS缓存(浏览器和操作系统)缓存为空时,DNS查找的数量与要加载的Web页面中惟一主机名的数量雷同,包含页面URL、脚本、样式表、图片、Flash对象等的主机名。缩小主机名的数量就能够缩小DNS查找的数量; (5)、缩小惟一主机名的数量会潜在缩小页面中并行下载的数量(HTTP1.1标准倡议从每个主机名并行下载两个组件,但实际上能够多个);然而缩小主机名和并行下载的计划会产生矛盾,须要大家本人衡量。倡议将组件放到至多两个但不多于4个主机名下,缩小DNS查找的同时也容许高度并行下载。DNS解析后会把域名的解析权交给cname()指向的内容散发(CDN)专用的DNS服务器。CDN专用的DNS服务器把CDN的全局负载平衡设施的ip地址返回给用户。 CDN举个例子:以前坐火车买票,都要到火车站买,所有人都去火车站买票,火车站售票厅的压力可想而知有多大。起初火车票代售点呈现了,散布在各个城市,城镇,咱们只须要去间隔咱们最近的火车票售卖点买票就能够了。卖火车票的代理售票点(缓存服务器),为买票者提供了不便,帮忙他们在最近的中央(最近的CDN节点),用最短的工夫(最短的申请工夫)买到票(拿到资源)。加重了售票大厅的压力(起到分流作用,加重服务器负载压力) CDN缓存:在浏览器本地缓存生效后,浏览器会像CDN边缘节点发动申请,相似浏览器缓存,CDN边缘节点也存在一套缓存机制, CDN边缘节点缓存策略因服务商不同而不同,通过http响应头中的cache-control:max-age字段设置CDN边缘节点数据缓存工夫。当浏览器向CDN节点申请数据时,CDN节点会判断缓存数据是否过期,若缓存数据过期,CDN会向服务器收回回源申请,从服务器拉取最新数据,更新本地缓存,并将最新数据返回给客户端,CDN服务商个别会提供基于文件后缀,目录多个维度来指定CDN缓存工夫,为用户提供更精细化的缓存治理。CDN工作形式:(1)、当你点击网站页面的url时,通过本DNS解析,DNS解析后会把域名的解析权交给cname()指向的内容散发专用的DNS服务器。内容散发专用的DNS服务器把内容散发的全局负载平衡(GSLB)设施的ip地址返回给用户。 (2)、当你向CDN的全局负载平衡设施的ip地址发动url拜访申请,CDN的全局负载平衡设施会为你抉择一台适合的缓存服务器提供服务。 抉择的根据:用户的ip地址,判断哪台服务器间隔用户最近,依据用户申请的url中携带的内容名称判断哪台服务器上有用户要的数据,查问各个服务器以后负载状况,判断哪台服务器有服务能力。调配:基于这些条件综合剖析后,区域负载平衡设施会向全局负载平衡设施申请返回一台缓存服务器的IP地址。全局负载平衡设施返回服务器IP地址,用户向缓存服务器发动申请,缓存服务器响应用户申请,将用户所需内容传送到用户终端,如果这台缓存服务器没有用户想要的内容,而区域平衡设施仍然将它调配给了用户,那么这台服务器就要向它的上一级缓存服务器申请内容,直至追溯到网站的源服务器将内容拉到本地。域名解析服务器依据用户ip地址,把域名解析成相应节点的缓存服务器ip地址,实现用户就近拜访,应用CDN服务的网站,只有将其域名解析权交给CDN的全局负载平衡设施,将须要散发的内容注入到CDN就能够实现内容减速了。参考:前端进阶面试题具体解答CDN劣势:(1)、CDN节点解决了跨运营商和跨地区拜访的问题,拜访延时大大降低; (2)、大部分申请在CDN边缘节点实现,CDN起到分流作用,加重了源服务器的负载。CDN劣势(1)、当网站更新时,如果CDN节点上数据没有及时更新,即使用户在浏览器应用 Ctrl +F5 的形式使浏览器端的缓存生效,也会因为CDN边缘节点没有同步最新数据而导致用户拜访异样。 (2)、CDN不同的缓存工夫会对“回源率”产生间接的影响: 如果缓存工夫短,CDN边缘节点的内容常常生效,导致频繁回源。不仅减少服务器压力,也减少了用户拜访工夫。如果缓存工夫长,数据更新了,边缘节点的内容都还没更新,开发者对特定的工作做特定的数据缓存工夫治理。CDN刷新缓存CDN边缘节点对开发者是通明的,相比于浏览器Ctrl+F5的强制刷新来使浏览器本地缓存生效,开发者能够通过CDN服务商提供的“刷新缓存”接口来达到清理CDN边缘节点缓存的目标。这样开发者在更新数据后,能够应用“刷新缓存”性能来强制CDN节点上的数据缓存过期,保障客户端在拜访时,拉取到最新的数据。 |CDN优化(1)、前端须要被减速的文件大抵包含: js、css、图片、视频、和页面等文件。页面文件有动静和动态之分。这些文件和页面(比方html)最大的区别是:这些文件都是动态的,改变比拟小,这类动态文件适宜做CDN减速。咱们把这些动态文件通过CDN散发到世界各地的节点,用户能够在间隔最近的边缘节点拿到须要的内容,从而晋升内容下载速度放慢网页关上速度。页面分为动静页面和动态页面,动静页面不适宜做CDN缓存,因为页面是动静的话,内容的有效期就比拟沉闷。边缘节点的数据常常生效要回源,造成源服务器压力。(2)、缩小资源申请的等待时间 不同浏览器的并发数量不一样:IE11 、IE10 、chrome、Firefox 的并发连接数是 6个,IE9是10个。如果页面动态资源(图片等)过多(大于6个)会存在资源申请期待的状况。目前现实状况是大多用户带宽越来越大,然而咱们的动态资源并非那么大,很多文件都是几k或者几十k,6个文件加起来都小于带宽。这样就导致了资源的节约。 解决方案是:用多个不同IP的服务器来存储这些文件,并在页面中通过绝对路径的形式援用(要求同一IP的文件不超过6个)。这样就能够尽可能的缩小资源申请期待的状况。至此,你曾经获取到缓存服务器的IP地址,并且筹备向这个IP地址发送申请了。 三、建设TCP连贯TCP(1)、TCP是一种面向连贯的,牢靠的,基于字节流的传输层通信协议。 (2)、建设TCP连贯须要进行三次握手。过程如下: TCP握手过程(1)、客户端发送带有SYN标识(SYN=1,seq=x)的申请报文段,而后进入SYN_SEND状态,期待服务端确认; (2)、服务端接管到客户端SYN报文段后,须要发送ACK信息对这个SYN进行确认,同时还要发送本人的SYN信息(SYN=1,ACK=1,seq=y,ack=x+1)服务端把这些信息放在一个报文段中((SYN+ACK报文段),一并发给客户端,此时客户端进入SYN_RECV状态; (3)、客户端接管到服务端的SYN+ACK报文段后会向服务端发送ACK(ACK=1,seq=x+,ack=y+1)确认报文段,这个报文段发送后,客户端和服务端都进入ESTABLISHED状态,实现三次握手。为什么TCP建设肯定要三次呢?两次不行吗?起因: 单方要明确对方接管能力都是失常的,(客户端发之后,服务端能够确定客户端发送能力失常,服务端发送给客户端,客户端能够确定服务端的接管和发送能力失常,最初客户端发送确认,来确定客户端的接管能力。为了避免已生效的连贯申请报文段忽然又传送到了服务端,因此产生谬误”。四、客户端发送申请TCP三次握手建设连贯胜利后,客户端依照指定的格局开始向服务端发送HTTP申请。 ...

October 25, 2022 · 1 min · jiezi

关于javascript:社招前端必会面试题附答案

箭头函数的this指向哪⾥?箭头函数不同于传统JavaScript中的函数,箭头函数并没有属于⾃⼰的this,它所谓的this是捕捉其所在高低⽂的 this 值,作为⾃⼰的 this 值,并且因为没有属于⾃⼰的this,所以是不会被new调⽤的,这个所谓的this也不会被扭转。 能够⽤Babel了解⼀下箭头函数: // ES6 const obj = { getArrow() { return () => { console.log(this === obj); }; } }转化后: // ES5,由 Babel 转译var obj = { getArrow: function getArrow() { var _this = this; return function () { console.log(_this === obj); }; } };数组去重ES5 实现: function unique(arr) { var res = arr.filter(function(item, index, array) { return array.indexOf(item) === index }) return res}ES6 实现: var unique = arr => [...new Set(arr)]事件流传机制(事件流)冒泡和捕捉 ...

October 25, 2022 · 4 min · jiezi

关于javascript:从输入URL到渲染的过程中到底发生了什么

浏览器有一个重要的安全策略,称之为「同源策略」 其中,源=协定+主机+端口,**两个源雷同,称之为同源,两个源不同,称之为跨源或跨域 同源策略是指,若页面的源和页面运行过程中加载的源不统一时,出于平安思考,浏览器会对跨域的资源拜访进行一些限度 同源策略对 ajax 的跨域限度的最为凶狠,默认状况下,它不容许 ajax 拜访跨域资源 所以,咱们通常所说的跨域问题,就是同源策略对 ajax 产生的影响 有多种形式解决跨域问题,常见的有: 代理,罕用CORS,罕用JSONP无论应用哪一种形式,都是要让浏览器晓得,我这次跨域申请的是本人人,就不要拦挡了。 跨域解决办法1-代理对于前端开发而言,大部分的跨域问题,都是通过代理解决的 代理实用的场景是:生产环境不产生跨域,但开发环境产生跨域 因而,只须要在开发环境应用代理解决跨域即可,这种代理又称之为开发代理 在理论开发中,只须要对开发服务器稍加配置即可实现 // vue 的开发服务器代理配置// vue.config.jsmodule.exports = { devServer: { // 配置开发服务器 proxy: { // 配置代理 "/api": { // 若申请门路以 /api 结尾 target: "http://dev.taobao.com", // 将其转发到 http://dev.taobao.com }, }, },};跨域解决办法2-JSONP在CORS呈现之前,人们想了一种微妙的方法来实现跨域,这就是JSONP。 要实现JSONP,须要浏览器和服务器来一个浑然一体的绝妙配合。 JSONP的做法是:当须要跨域申请时,不应用AJAX,转而生成一个script元素去申请服务器,因为浏览器并不阻止script元素的申请,这样申请能够达到服务器。服务器拿到申请后,响应一段JS代码,这段代码实际上是一个函数调用,调用的是客户端事后生成好的函数,并把浏览器须要的数据作为参数传递到函数中,从而间接的把数据传递给客户端 JSONP有着显著的毛病,即其只能反对GET申请 跨域解决办法3-CORS概述CORS是基于http1.1的一种跨域解决方案,它的全称是Cross-Origin Resource Sharing,跨域资源共享。 它的总体思路是:如果浏览器要跨域拜访服务器的资源,须要取得服务器的容许 而要晓得,一个申请能够附带很多信息,从而会对服务器造成不同水平的影响 比方有的申请只是获取一些新闻,有的申请会改变服务器的数据 针对不同的申请,CORS 规定了三种不同的交互模式,别离是: 简略申请须要预检的申请附带身份凭证的申请这三种模式从上到下层层递进,申请能够做的事越来越多,要求也越来越严格。 上面别离阐明三种申请模式的具体标准。 简略申请当浏览器端运行了一段 ajax 代码(无论是应用 XMLHttpRequest 还是 fetch api),浏览器会首先判断它属于哪一种申请模式 ...

October 25, 2022 · 2 min · jiezi

关于javascript:javascript编程单线程之同步模式

javascript编程单线程之同步模式支流的js 环境都是单线程吗模式执行js 代码, js采纳为单线程的起因与最开始设计初衷无关,最早是运行在浏览器端的脚本语言,目标是为了实现页面上的动静交互,实现页面交互的外围就是dom操作,这也就决定了js必须应用单线程的模式来解决,不然就会造成重大的线程同步问题。如果js多个线程同时批改dom元素,此时浏览器就无奈明确以那个线程的后果为准,为了防止这种线程同步问题,所以从一开始js就被设置成了单线程模式工作。这里所说的单线程指的是javascript执行环境中负责执行代码的线程只有一个。 能够设想成只有一个人来执行工作,一个人一次只能执行一个工作,如果有多个工作就须要排队顺次去实现。这种模式最大的长处是更平安更简略,毛病也很显著,遇到某一个特地耗时的工作前面的工作就须要等这个工作的完结,这也就导致整个程序的执行的迁延,呈现假死的状况。 长处:更平安、更简略耗时工作会呈现程序假死的状况 为了终局耗时工作的问题,javscript 把 工作的执行分了两种模式,别离是 同步模式(Synchoronous)异步模式(Asynchronous) 同步模式Synchoronous 代码顺次执行,后一个工作要期待前一个工作执行实现,同步执行比较简单,代码的执行程序就是代码的程序。单线程大部分都是同步模式。 Console 是输入的打印,Call stack 是执行栈 开始执行 js 会把咱们的整体的代码加载进来并放到一个匿名函数外面执行,而后逐行开始执行, 第一行 执行会把 console.log('global begin') 压入调用栈中,控制台打印global begin 执行完结,弹出调用栈 接下来是两个函数的申明,函数的申明不会产生调用所以接着往下执行 接下来是一个foo 函数的调用,会把 foo压入调用栈,foo 函数打印了一个音讯,接着执行了 bar 函数,bar 函数也会被放入执行栈中,bar函数执行过程中又打印了一次,bar执行结束弹出调用栈,紧接着 foo 函数也执行完结,弹出调用栈 最初打印了一个音讯,也是一样的压栈,整体代码执行完,执行栈就会被清空掉 这种排队执行的机制下某行代码执行工夫过长,前面的工作就会被提早。咱们把这种提早称为阻塞,这种阻塞对于用户而言会有页面卡顿或者叫卡死,所以须要异步模式来解决程序中无奈防止的耗时操作,比方ajax操作,或者nodejs中的大文件读写 原文地址:https://kspf.xyz/archives/18更多内容微信公众号搜寻充饥的泡饭小程序搜一搜开水泡饭的博客

October 24, 2022 · 1 min · jiezi

关于javascript:JS小记-比较数值大小

一、 简略循环算法代码如下: const numbers = [5, 6, 2, 3, 7];let max = -Infinity;for (let i = 0; i < numbers.length; i++) { if (numbers[i] > max) max = numbers[i];}Infinity概述全局属性 Infinity 是一个数值,示意无穷大。阐明 Infinity 的初始值是 Number.POSITIVE_INFINITY。Infinity(正无穷大)大于任何值。在 ECMAScript 5 的标准中, Infinity 是只读的。示例 console.log(Infinity ); /* Infinity */console.log(Infinity + 1 ); /* Infinity */console.log(Math.pow(10, 1000)); /* Infinity */console.log(Math.log(0) ); /* -Infinity */console.log(1 / Infinity ); /* 0 */二、 Math.max()1. 概述Math.max() 函数返回作为输出参数的最大数字。 2. 参数value1, value2, … , valueN0 个或多个数字,将在其中抉择,并返回最大的值。 ...

October 24, 2022 · 3 min · jiezi

关于javascript:vue组件通信bus的使用

bus的应用作用:进行组件通信1.utils文件夹下新建bus.jsimport Vue from 'vue'const bus = new Vue()export default bus2.传数据的页面引入 bus.jsimport Bus from "../../门路"Bus.$emit("tranfTimeData", this.sceneData);//this.sceneData是要传递的数据3.接收数据的页面引入 bus.jsimport Bus from "../../门路"Bus.$on("tranfTimeData", (sceneTimeData) => {     // this.sceneTimeData = sceneTimeData; //场景增加工夫规定      console.log("接管的数据:", sceneTimeData);    }); 全局应用时,main.js中const bus = new Vue()Vue.prototype.$bus = bus;

October 24, 2022 · 1 min · jiezi

关于javascript:ajax-xhr-level2新特性-json等众多内容

1. 明天的内容其实挺多的,咱们缓缓来说。首先第一个是xhr的根本应用,什么是xhr? XMLHTTPRequest是浏览器提供的js对象,能够申请服务器上的数据资源,包含咱们后面始终用的jq外面的三个申请资源的办法都是基于xhr来封装的。 那么首先咱们看到xhr的get申请怎么来实现 首先要创立xhr实例通过new来实现 而后调用open函数,外面值为申请形式以及url 第三步调用send函数 第四步监听onreadyStateChange事件在这个事件外面要留神一下申请状态和服务器响应状态的固定写法,还有服务器响应回的数据 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script> // 1.创立xhr对象 var xhr = new XMLHttpRequest() // 2.调用open函数 决定申请形式和url xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks') // 3.调用send函数 xhr.send() // 4.监听事件 xhr.onreadystatechange = function() { // 4.1留神申请状态和服务器响应状态固定写法 if (xhr.readyState ==4 && xhr.status == 200) { // 4.2获取相应的数据 console.log(xhr.response); } } </script></body></html>1咱们看到在监听申请状态变动事件里有一个readystate的属性这个属性示意ajax以后申请所处的状态,其中0示意xhr对象以创立,但申请未调用open。1示意已调用open函数。2示意已调用send函数。3示意数据接管中。最初4示意所有申请实现 那么xhr带参的get申请怎么来实现的呢?只须要在open函数的url外面接一个查问字符串即可 xhr.open('get', 'http://www.ssfddaf.com/api?na...') 那么什么是查问字符串? 在url开端加上?参数=值多个参数间用&来连贯这就是查问字符串,无论是jQuery的三个申请形式还是xhr指定的参数其本质都是在url后带查问字符串 这里还要理解一个点url编码与解码 url中只容许出线英文不容许中文等其余语种,所以他就会把除英文外其余语种转换为%结尾带两个字符的款式来代替 编码encodeURI('中文;) 解码decodeURI(%的字符)三组%示意一个中文 2. 接下来咱们看到xhr怎么发动post申请 第一步创建对象 第二部open函数把申请形式改为post 第三步设置content-type 这是一个固定写法 ...

October 24, 2022 · 8 min · jiezi

关于javascript:使用-Canvas-API-模拟一个彩色时钟

介绍<canvas> 最早由 Apple 引入 WebKit,用于 Mac OS X 的 Dashboard,随后被各个浏览器实现。现在,所有支流的浏览器都反对它。Canvas API 提供了一个通过JavaScript 和 HTML的 <canvas> 元素来绘制图形的形式。它能够用于动画、游戏画面、数据可视化、图片编辑以及实时视频解决等方面。Canvas 适宜绘制大数据量图形元素的图表(如热力求、天文坐标系或平行坐标系上的大规模线图或散点图等),也适宜实现某些视觉特效。它还能可能以 png、jpg 或 webp 格局保留图像。Canvas 提供了弱小的 Web 绘图能力,所以咱们要学会应用它。 成果: 创立一个画布默认状况下 <canvas> 元素没有边框和内容。默认大小为 300px × 150px(宽 × 高),咱们能够应用 width 和 height 属性指定。 <canvas id="canvas"></canvas>获取画布和半径为了在 <canvas> 上绘制图形,咱们须要应用一个 JavaScript 上下文对象,它能动态创建图像。这里建设了一个 CanvasRenderingContext2D 二维渲染上下文。 const ctx = canvas.getContext('2d');let radius = canvas.height / 2;绘制圆周和时钟核心beginPath() 用来起始一条门路,或重置以后门路。arc() 用于创立圆形或弧形。fill() 用来填充图形。stroke() 用来绘制门路。 function drawFace(context, radius) {context.beginPath();context.arc(0, 0, radius, 0, 2 * Math.PI);context.fillStyle = 'white';context.fill();// 重置门路 画时钟核心圆点context.beginPath();context.arc(0, 0, radius * 0.06, 0, 2 * Math.PI);context.fillStyle = 'green';context.fill();}绘制表盘数字rotate() 用来旋转以后绘图。360 度角等于 Math.PI * 2,Math.PI / 6 就是 30 度角。translate() 用来将原点挪动到新地位。fillText() 用来绘制文本。 ...

October 24, 2022 · 2 min · jiezi

关于javascript:Vuejs-组件编码规范

Vue.js 组件编码标准指标本标准提供了一种对立的编码标准来编写 Vue.js 代码。这使得代码具备如下的个性: 其它开发者或是团队成员更容易浏览和了解。IDEs 更容易了解代码,从而提供高亮、格式化等辅助性能更容易应用现有的工具更容易实现缓存以及代码包的分拆目录基于模块开发vue 组件命名组件表达式简单化组件 props 原子化验证组件的 props将 this 赋值给 component 变量组件结构化组件事件命名防止 this.$parent审慎应用 this.$refs应用组件名作为款式作用域空间提供组件 API 文档提供组件 demo对组件文件进行代码校验只在须要时创立组件<!-- * 应用 *.vue 扩展名 --><!-- * 为你的我的项目增加徽章标识 -->基于模块开发始终基于模块的形式来构建你的 app,每一个子模块只做一件事件。 Vue.js 的设计初衷就是帮忙开发者更好的开发界面模块。一个模块是应用程序中独立的一个局部。 怎么做?每一个 Vue 组件(等同于模块)首先)必须专一于解决一个繁多的问题,独立的、可复用的、渺小的 和 可测试的。 如果你的组件做了太多的事或是变得臃肿,请将其拆分成更小的组件并放弃繁多的准则。一般来说,尽量保障每一个文件的代码行数不要超过 100 行。也请保障组件可独立的运行。比拟好的做法是减少一个独自的 demo 示例。 Vue 组件命名组件的命名需听从以下准则: 有意义的: 不过于具体,也不过于形象简短: 2 到 3 个单词具备可读性: 以便于沟通交流同时还须要留神: 必须合乎自定义元素标准: 应用连字符分隔单词,切勿应用保留字。app- 前缀作为命名空间: 如果十分通用的话可应用一个单词来命名,这样能够不便于其它我的项目里复用。为什么?组件是通过组件名来调用的。所以组件名必须简短、富裕含意并且具备可读性。如何做?<!-- 举荐 --><app-header></app-header><user-list></user-list><range-slider></range-slider><!-- 防止 --><btn-group></btn-group> <!-- 尽管简短然而可读性差. 应用 `button-group` 代替 --><ui-slider></ui-slider> <!-- ui 前缀太过于宽泛,在这里意义不明确 --><slider></slider> <!-- 与自定义元素标准不兼容 -->组件表达式简单化Vue.js 的表达式是 100% 的 Javascript 表达式。这使得其功能性很弱小,但也带来潜在的复杂性。因而,你应该尽量放弃表达式的简单化。 ...

October 24, 2022 · 4 min · jiezi

关于javascript:假如面试官要你手写一个promise

promise在开发中,常常须要用到promise,promise具备很多个性,这一次将对promise个性进行总结,并从零写一个promise。 步骤一Promise特点 1,创立时须要传递一个函数,否则会报错2,会给传入的函数设置两个回调函数3,刚创立的Promise对象状态是pendingclass MyPromise { constructor(handle) { // 3,刚创立的Promise对象状态是pending this.status = "pending"; // 1,创立时须要传递一个函数,否则会报错 if (!this._isFunction(handle)) { throw new Error("请传入一个函数"); } // 2,会给传入的函数设置两个回调函数 handle(this._resolve.bind(this), this._reject.bind(this)) } _resolve() { } _reject() { } _isFunction(fn) { return typeof fn === "function"; }}步骤二Promise特点 4,状态一旦产生扭转就不可再次扭转5,能够通过then来监听状态的扭转 5.1,如果创立监听时,状态曾经扭转,立刻执行监听回调5.2,如果创立监听时,状态未扭转,会等状态扭转后执行5.3,同一promise对象能够增加多个then监听,状态扭转时依照注册程序顺次执行// 定义常量保留对象的状态const PENDING = "pending";const FULFILLED = "fulfilled";const REJECTED = "rejected";class MyPromise { constructor(handle) { // 3,刚创立的Promise对象状态是pending this.status = PENDING; // 胜利回调的值 this.value = undefined; // 失败回调的值 this.reason = undefined; // 注册的胜利回调 this.onResolvedCallbacks = []; // 注册的失败回调 this.onRejectedCallbacks = []; // 1,创立时须要传递一个函数,否则会报错 if (!this._isFunction(handle)) { throw new Error("请传入一个函数"); } // 2,会给传入的函数设置两个回调函数 handle(this._resolve.bind(this), this._reject.bind(this)) } _resolve(value) { // 4,状态一旦产生扭转就不可再次扭转 if (this.status === PENDING) { this.status = FULFILLED; this.value = value; // 5.3,同一promise对象能够增加多个then监听,状态扭转时依照注册程序顺次执行 this.onResolvedCallbacks.forEach(fn => fn(this.value)); } } _reject(reason) { // 4,状态一旦产生扭转就不可再次扭转 if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // 5.3,同一promise对象能够增加多个then监听,状态扭转时依照注册程序顺次执行 this.onRejectedCallbacks.forEach(fn => fn(this.reason)); } } then(onResolved, onRejected) { // 判断有没有传入胜利的回调 if (this._isFunction(onResolved)) { // 5.1,如果创立监听时,状态曾经扭转,立刻执行监听回调 if (this.status === FULFILLED) { onResolved(this.value); } } // 判断有没有传入失败的回调 if (this._isFunction(onRejected)) { // 5.1,如果创立监听时,状态曾经扭转,立刻执行监听回调 if (this.status === REJECTED) { onRejected(this.reason); } } // 5.2,如果创立监听时,状态未扭转,会等状态扭转后执行 if (this.status === PENDING) { if (this._isFunction(onResolved)) { this.onResolvedCallbacks.push(onResolved); } if (this._isFunction(onRejected)) { this.onRejectedCallbacks.push(onRejected); } } } _isFunction(fn) { return typeof fn === "function"; }}参考 前端手写面试题具体解答 ...

October 24, 2022 · 3 min · jiezi

关于javascript:js手写前端需要掌握的点

throttle(节流)高频工夫触发,但n秒内只会执行一次,所以节流会浓缩函数的执行频率。 const throttle = (fn, time) => { let flag = true; return function() { if (!flag) return; flag = false; setTimeout(() => { fn.apply(this, arguments); flag = true; }, time); }}节流常利用于鼠标一直点击触发、监听滚动事件。 手写 new 操作符在调用 new 的过程中会产生以上四件事件: (1)首先创立了一个新的空对象 (2)设置原型,将对象的原型设置为函数的 prototype 对象。 (3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象增加属性) (4)判断函数的返回值类型,如果是值类型,返回创立的对象。如果是援用类型,就返回这个援用类型的对象。 function objectFactory() { let newObject = null; let constructor = Array.prototype.shift.call(arguments); let result = null; // 判断参数是否是一个函数 if (typeof constructor !== "function") { console.error("type error"); return; } // 新建一个空对象,对象的原型为构造函数的 prototype 对象 newObject = Object.create(constructor.prototype); // 将 this 指向新建对象,并执行函数 result = constructor.apply(newObject, arguments); // 判断返回对象 let flag = result && (typeof result === "object" || typeof result === "function"); // 判断返回后果 return flag ? result : newObject;}// 应用办法objectFactory(构造函数, 初始化参数);实现数组元素求和arr=[1,2,3,4,5,6,7,8,9,10],求和let arr=[1,2,3,4,5,6,7,8,9,10]let sum = arr.reduce( (total,i) => total += i,0);console.log(sum);arr=[1,2,3,[[4,5],6],7,8,9],求和var = arr=[1,2,3,[[4,5],6],7,8,9]let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);console.log(arr);递归实现: ...

October 24, 2022 · 22 min · jiezi

关于javascript:写个JS深拷贝面试备用

深拷贝浅拷贝和赋值的原理及实现分析在工作中咱们常常会用到深拷贝与浅拷贝,然而你有没有去剖析什么场景下应用它,为什么须要应用呢,深浅拷贝有何异同呢,什么是深拷贝呢,如何实现呢,你会有这些问题吗,明天就为大家总结一下吧。 栈内存与堆内存区别浅拷贝---拷贝的是一个对象的指针,而不是复制对象自身,拷贝进去的对象共用一个指针,其中一个扭转了值,其余的也会同时扭转。深拷贝---拷贝进去一个新的对象,开拓一块新的空间,拷贝前后的对象互相独立,相互不会扭转,领有不同的指针。简略的总结下,假如有个A,咱们拷贝了一个为B,就是批改A或者B的时候看看另一个会不会也变动,如果扭转A的值B也变了那么就是浅拷贝,如果扭转A之后B的值没有发生变化就是深拷贝,当然这是根底了解,上面咱们一起来剖析下吧。 赋值/** demo1根本数据类型 */let a = 1;let b = a;b = 10;console.log(a,b)// 1 10/** demo2援用数据类型 */let a = { name: '小九', age: 23, favorite: ['吃饭','睡觉','打豆豆']}let b = a;a.name = '小七'a.age = 18a.favorite = ['下班','上班','加班']console.log(a,b)/** { name: '小七', age: 18, favorite: [ '下班', '上班', '加班' ] } { name: '小七', age: 18, favorite: [ '下班', '上班', '加班' ] }*/通过看下面的例子能够看出通过赋值去拿到新的值,赋值对于根本数据来说就是在栈中新开了一个变量,相当于是两个独立的栈内存,所以互相不会影响,然而对于援用数据类型,他只是复制了一份a在栈内存的指针,所以两个指针指向了同一个堆内存的空间,通过任何一个指针扭转值都会影响其余的,通过这样的赋值能够产生多个指针,然而堆内存的空间始终只有一个,这就是赋值产生的问题,咱们在开发中当然不心愿扭转B而影响了A,所以这个时候就须要用到浅拷贝和深拷贝了。 针对根本数据类型,轻易赋值都不会相互影响针对援用数据类型,赋值就会呈现咱们不想看到的,改变一方单方都变动。浅拷贝Object.assign()/** Object.assign */let A = { name: '小九', age: 23, sex: '男'}let B = Object.assign( {}, A);B.name = '小七'B.sex = '女'B.age = 18console.log(A,B)/** { name: '小九', age: 23, sex: '男' } { name: '小七', age: 18, sex: '女' } */首先实现浅拷贝的第一个办法是通过 Object.assign()这个 办法,Object.assign() 办法用于将所有可枚举属性的值从一个或多个源对象复制到指标对象。它将返回指标对象。 ...

October 24, 2022 · 6 min · jiezi

关于javascript:三次握手与四次挥的问题怎么回答

在面试中,三次握手和四次挥手能够说是问的最频繁的一个知识点了,我置信大家也都看过很多对于三次握手与四次挥手的文章,明天的这篇文章,重点是围绕着面试,咱们应该把握哪些比拟重要的点,哪些是比拟被面试官给问到的,我感觉如果你能把我上面列举的一些点都记住、了解,我想就差不多了。 三次握手因为在面试中,三次握手是被问的最频繁的面试题,所以本次咱们从面试的角度来解说三次握手当面试官问你为什么须要有三次握手、三次握手的作用、讲讲三次三次握手的时候,我想很多人会这样答复: 首先很多人会先讲下握手的过程: 1、第一次握手:客户端给服务器发送一个 SYN 报文。 2、第二次握手:服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。 3、第三次握手:客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。 4、服务器收到 ACK 报文之后,三次握手建设实现。 作用是为了确认单方的接管与发送能力是否失常。 这里我顺便解释一下为啥只有三次握手能力确认单方的承受与发送能力是否失常,而两次却不能够: 第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接管能力是失常的。 第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接管、发送能力,客户端的接管、发送能力是失常的。不过此时服务器并不能确认客户端的接管能力是否失常。 第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接管、发送能力失常,服务器本人的发送、接管能力也失常。 因而,须要三次握手能力确认单方的接管与发送能力是否失常。 这样答复其实也是能够的,但我感觉,这个过程的咱们应该要形容的更具体一点,因为三次握手的过程中,单方是由很多状态的扭转的,而这些状态,也是面试官可能会问的点。所以我感觉在答复三次握手的时候,咱们应该要形容的具体一点,而且形容的具体一点意味着能够扯久一点。加分的形容我感觉应该是这样: 刚开始客户端处于 closed 的状态,服务端处于 listen 状态。而后 1、第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 SN(c)。此时客户端处于 SYN_Send 状态。 2、第二次握手:服务器收到客户端的 SYN 报文之后,会以本人的 SYN 报文作为应答,并且也是指定了本人的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,示意本人曾经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。 3、第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,示意曾经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。 4、服务器收到 ACK 报文之后,也处于 establised 状态,此时,单方以建设起了链接。 ...

October 24, 2022 · 2 min · jiezi

关于javascript:前端经典面试题合集

事件循环 默认代码从上到下执行,执行环境通过script来执行(宏工作)在代码执行过程中,调用定时器 promise click事件...不会立刻执行,须要期待以后代码全副执行结束给异步办法划分队列,别离寄存到微工作(立刻寄存)和宏工作(工夫到了或事件产生了才寄存)到队列中script执行结束后,会清空所有的微工作微工作执行结束后,会渲染页面(不是每次都调用)再去宏工作队列中看有没有达到工夫的,拿进去其中一个执行执行结束后,依照上述步骤不停的循环例子 主动执行的状况 会输入 listener1 listener2 task1 task2 如果手动点击click 会一个宏工作取出来一个个执行,先执行click的宏工作,取出微工作去执行。会输入 listener1 task1 listener2 task2 console.log(1)async function asyncFunc(){ console.log(2) // await xx ==> promise.resolve(()=>{console.log(3)}).then() // console.log(3) 放到promise.resolve或立刻执行 await console.log(3) // 相当于把console.log(4)放到了then promise.resolve(()=>{console.log(3)}).then(()=>{ // console.log(4) // }) // 微工作谁先注册谁先执行 console.log(4)}setTimeout(()=>{console.log(5)})const promise = new Promise((resolve,reject)=>{ console.log(6) resolve(7)})promise.then(d=>{console.log(d)})asyncFunc()console.log(8)// 输入 1 6 2 3 8 7 4 51. 浏览器事件循环 涉及面试题:异步代码执行程序?解释一下什么是 Event Loop ? JavaScript的单线程,与它的用处无关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很简单的同步问题。比方,假设JavaScript同时有两个线程,一个线程在某个DOM节点上增加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了防止复杂性,从一诞生,JavaScript就是单线程,这曾经成了这门语言的外围特色,未来也不会扭转 js代码执行过程中会有很多工作,这些工作总的分成两类: 同步工作异步工作当咱们关上网站时,网页的渲染过程就是一大堆同步工作,比方页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的工作,就是异步工作。,咱们用导图来阐明: 咱们解释一下这张图: 同步和异步工作别离进入不同的执行"场合",同步的进入主线程,异步的进入Event Table并注册函数。当指定的事件实现时,Event Table会将这个函数移入Event Queue。主线程内的工作执行结束为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会一直反复,也就是常说的Event Loop(事件循环)。那主线程执行栈何时为空呢?js引擎存在monitoring process过程,会继续一直的查看主线程执行栈是否为空,一旦为空,就会去Event Queue那里查看是否有期待被调用的函数以上就是js运行的整体流程 ...

October 24, 2022 · 11 min · jiezi

关于javascript:从URL输入到页面展现到底发生什么

从开发&运维角度方面来看,总体来说分为以下几个过程: DNS 解析:将域名解析成 IP 地址TCP 连贯:TCP 三次握手发送 HTTP 申请服务器解决申请并返回 HTTP 报文浏览器解析渲染页面断开连接:TCP 四次挥手一、什么是URL?URL(Uniform Resource Locator),对立资源定位符,用于定位互联网上资源,俗称网址。 scheme: // host.domain:port / path / filename ? abc = 123 # 456789 scheme - 定义因特网服务的类型。常见的协定有 http、https、ftp、file, 其中最常见的类型是 http,而 https 则是进行加密的网络传输。host - 定义域主机(http 的默认主机是 www)domain - 定义因特网域名,比方 baidu.comport - 定义主机上的端口号(http 的默认端口号是 80)path - 定义服务器上的门路(如果省略,则文档必须位于网站的根目录中)。filename - 定义文档/资源的名称query - 即查问参数fragment - 即 # 后的hash值,个别用来定位到某个地位二、DNS域名解析在浏览器输出网址后,首先要通过域名解析,因为浏览器并不能间接通过域名找到对应的服务器,而是要通过 IP 地址。 IP 地址IP 地址是指互联网协议地址,是 IP Address 的缩写。IP 地址是 IP 协定提供的一种对立的地址格局,它为互联网上的每一个网络和每一台主机调配一个逻辑地址,以此来屏蔽物理地址的差别。什么是域名解析DNS 协定提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。DNS 是一个网络服务器,咱们的域名解析简略来说就是在 DNS 上记录一条信息记录。浏览器如何通过域名去查问 URL 对应的 IP 呢?DNS域名解析分为递归查问和迭代查问两种形式,现个别为迭代查问。 ...

October 24, 2022 · 2 min · jiezi

关于javascript:JavaScript函数式编程之函子

函子(Functor)函子是一个非凡的容器,通过一个一般对象来实现,该对象具备map办法,map办法能够运行一个函数对值进行解决(变形关系),容器蕴含值和值变形关系(这个变形关系就是函数)。函数式编程中解决副作用的存在 函数式编程的运算不间接操作值,,而是由函子实现函子就是一个实现了map契约的对象咱们能够把函子设想成一个盒子,盒子外面封装了一个值想要解决盒子中的值,咱们须要给盒子的map办法传递一个解决值的函数(纯函数),由这个函数来对值进行解决最终map办法返回一个蕴含新值所在的盒子(函子)依据函子的定义咱们创立一个函子 // functor 函子class Container { constructor (value) { // 函子外部保留这个值。下划线是不想内部拜访 this._value = value } // map 办法接管一个解决值的函数 map (fn) { return new Container(fn(this._value)) }}此时就曾经创立了一个函子然而这是面向对象的形式来创立的,换成用函数式编程来写一个函子 class Container { constructor (value) { this._value = value } map (fn) { return Container.of(fn(this._value)) } static of (value) { return new Container(value) }}let x = Container.of(5).map(x => x + 1).map(x => x - 1)然而这个函子还是存在一些问题,比方空值的时候就会报错, 会让咱们的函子变的不纯,咱们须要去拦挡空值谬误,咱们创立一个办法去判断是否为空值,如果是管制咱们间接返回一个空值的函子,如果有值再去解决,这个时候就须要应用MayBe函子 let x = Container.of(null).map(x => x + 1).map(x => x - 1)MayBe 函子咱们在编程的过程中可能会遇到很多谬误,须要对这些谬误做相应的解决,MayBe函子的作用就是能够对外部的空值状况做解决(管制副作用在容许的范畴) ...

October 23, 2022 · 3 min · jiezi

关于javascript:ajax-接口表单模板引擎

1. 明天持续ajax的一个学习,首先明确一个观点,接口,什么是接口,当应用ajax申请数据时,被申请的url就叫做数据接口也就是接口,留神每个接口必须有申请形式,这里有一个接口的测试工具,postman自称是寰球最快的,反正应用起来没多大故障,应用这个软件的时候有一个留神点就是在post申请的时候,在body外面输出参数要抉择x-www-form-这个选项才行,而后是接口文档,咱们如果要调用接口,那必定是要参照接口文档的,外面的蕴含这个接口的所有信息,个别一个接口文档大抵分为以下五个内容: ①接口名称:可能一眼看出这个接口是个什么类型的接口 ②url:这个不必多说,接口的调用地址 ③调用形式:会给你阐明这个接口要用get还是post ④参数格局:接口需传递的参数,每个参数必须蕴含参数名称、参数类型以及参数阐明 ⑤响应格局:接口返回值的形容,个别蕴含数据名称类型阐明 2. 而后持续看到一个广泛利用,表单,咱们说的form表单个别是拿来收集数据的,而后再form外面有一些属性比方 action示意向何处发送表单数据,如果未指定就是以后页面 target是在何处关上这个action,就跟a标签的一样 method是发送action的形式能够为get或者post默认是get,get适宜用来提交一些简略数据的,post适宜提交简单数据,咱们在我的项目中用到post居多 enctype是规定发送表单数据前如何对数据进行编码,个别默认是后面提到的x-www-form-urlencoded,然而这里要留神下如果说是蕴含文件上传的表单的话,这里的值要改为multipart/form-data 持续看到表单的同步提交,就是当你一点击提交,页面就会产生跳转到action的地址下来,这样的用户体验极差,而且页面之前的数据和状态都会失落,怎么来解决这一景象,咱们只须要让表单控件负责采集数据,ajax来负责提交即可。 如果要用到ajax跟表单的一个配合,首先要晓得一个事件也就是submit提交事件,而后在这外面组织表单默认行为,因为每次一点提交就会刷新页面,而后通过一个函数可疾速获取到表单的数据,。serialize()应用这个有个前提就是必须为表单外面每个元素增加name属性就像面这样 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <form action="/login"> <input type="text" name="uname"> <input type="password" name="password"> <input type="submit" value="提交"> </form> <script src="../day01/lib/jquery.js"></script> <script> $('form').on('submit', e => { console.log(11); e.preventDefault() // 这里闹了个大乌龙,刚开始我是用的$(this)我就说为什么始终获取不到数据思来想去也就是这里的问题应该,起初才晓得我这里明明用的是 // 箭头函数啊,箭头函数的this是谁啊能乱用吗? var str = $('form').serialize() console.log(str); }) </script></body></html>案例:评论列表,页面还是才去bootstrap,vscode可装置bs3实现疾速bootstrap编程,这个案例就是通过接口先获取评论列表的数据,渲染到html上,而后通过接口发表评论 . <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="../day01/lib/bootstrap.css"> <script src="../day01/lib/jquery.js"></script></head><body style="padding: 15px;"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">发表评论</h3> </div> <div class="panel-body"> <form> <div>评论人</div> <input type="text" class="form-control" name="username"> <div>内容</div> <textarea class="form-control" name="content"></textarea> <button type="submit" class="btn btn-primary">发表评论</button> </form> </div> </div> <ul class="list-group"> </ul> <script> // 1.获取评论列表数据 function getComment() { $.ajax({ type : 'get', url : 'http://www.liulongbin.top:3006/api/cmtlist', success : res => { console.log(res); if (res.status !== 200) { return alert('获取评论列表失败') } else { $('.list-group').empty() $.each(res.data, (i, item) => { $('.list-group').append(`<li class="list-group-item"> <span class="badge" style="background-color: #f0ad4e;">评论工夫:${item.time}</span> <span class="badge" style="background-color: #5bc0de;">评论人:${item.username}</span> ${item.content} </li>`) }) } } }) } getComment() // 2.发表评论 $('form').on('submit', function(e) { e.preventDefault() var str = $('form').serialize() // console.log(str); $.post('http://www.liulongbin.top:3006/api/addcmt', str,res => { if (res.status !== 201) { return alert('发表评论失败') } else { getComment() // 2.1 提交胜利后应该将表单的内容清空,这里有个快捷办法,间接将form用她的reset办法,然而要先将jq转为原生对象 $('form')[0].reset() } }) }) </script></body></html>3. ...

October 23, 2022 · 3 min · jiezi

关于javascript:JS-如何实现上次操作未完成之前禁止新的操作的逻辑

置信很多人都遇到过相似的场景:某一个按钮是用来发送申请的,并且须要一段时间来解决。然而用户往往会在解决期间无意或无心地点击屡次,因而咱们心愿在上一次收回的申请处理完毕之前,不再收回新的申请。1.初步解决计划:特事特办“特事特办”的意思,就是每次遇到这样的场景,都特意写一段逻辑来解决: document.addEventListener('click', (() => { let lock = false; return () => { if(lock) return; lock = true; console.log('clicked'); // 为了不便测试就应用延时了 setTimeout(() => { lock = false; }, Math.random() * 4e3) }})());2. 基于约定回调的条件式回调函数下面的写法其实也不麻烦,然而这种条件限度能不能像曾经被面试问烂了的“节流”和“防抖”那样,用一个函数把它包裹起来就能够达成成果呢?问题的要害其实在于:防抖和节流须要思考的执行条件是工夫,这个条件对于所有函数而言都是一个“独特的语言”,因而才单方能够做到那样的“默契”。而要在这种场景里实现同样的成果,单方须要刻意的约定:例如被条件执行的函数额定承受一个函数,用于在适合的机会解除条件限度。 function conditioned(callback:(release:Function,...args:any[]) => any){ let lock = false; return function(...args:any[]){ if(lock) return; lock = true; callback.call(this, () => { lock = false; }, ...args); }} 为了不便形容这种约定对callback 的要求,这里应用 TS 而不是 JS。应用的时候: button.addEventListener('click', conditioned((release) => { return () => { console.log('clicked'); setTimeout(() => { release(); // 开释 lock }, Math.random() * 4e3); }}));3. 基于 Promise 的条件式回调函数如果说有什么办法能比回调函数更“优雅”、更“通用”一些,答案显然是Promise。因为下面的写法对原来的回调函数的参数进行了改写,遇到一个喜好 Ctrl + C 的初学者的话,他会纳闷复制过去的函数为什么就不工作了。 ...

October 23, 2022 · 2 min · jiezi

关于javascript:helmicro教学视频1发布和使用远程js库

hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块计划 ,欢送关注与理解 hel-micro 教学视频来了hel-micro 教学视频来了,为了帮忙更多小伙伴疾速上手sdk化模块联邦 hel-micro ,会继续推出一系列教学视频,期待大家观看后可能学到有用的常识并提出相干改良意见,本期为第一期视频教程。 公布和应用近程js库本期主讲内容为:学会基于hel-micro 公布和应用近程js库 理解更多欢送入群理解更多,因为微信探讨群号 200 人已满,需加作者微信号或 qq 群号,再邀请你如hel-micro探讨群(加号时记得备注 hel 哦)

October 22, 2022 · 1 min · jiezi

关于javascript:工作问题如何给已有项目增加主题换色功能

一.前言 之前我的项目须要为其减少主题换色性能,接手这个需要后,就开始剖析我的项目的代码内容。我把他们次要分成两个大类,一是elementUI下的组件,二是自定义模板或者组件。这两类都须要对其款式进行切换来达到换色成果。为了做到尽可能的优雅,最好是只扭转一处中央,也不必反复写一样的款式,就能够实现换色。我这种菜鸡真是花了不少工夫在下面。 二.方案设计计划1.link标签动静引入或应用不同类名切换不同主题 设计几套不同主题款式的文件,在须要的时候,创立link标签动静加载到head标签中,或者是动静扭转link标签的href属性。或者在须要切换主题的时候根标签切换对应的类名来切换不同的款式。 然而这样子的操作的话,如果须要更改某一样式,所有主题都须要更改,麻烦且容易出错,这能够说是最笨的办法。 计划2.应用css变量+类名切换 这是计划1的改良计划。大体思路跟计划1类似,仍然是提前将款式文件载入,切换时将指定的根元素类名更换。不过这里绝对灵便的是,默认在根作用域下定义好CSS变量,只须要在不同的主题下更改CSS变量对应的取值即可。 这样子操作的益处就是通过CSS变量,能够达到只改一次就能全副更改的成果。 计划3.应用CSS变量+动静setProperty 计划3较于前两种会更加灵便,不过视状况而定,这个计划实用于由用户依据色彩面板自行设定各种色彩主题,这种是主题色彩不确定的状况,而前两种计划更实用于定义预设的几种主题。 三.计划实操 综合思考之后,决定应用第三种计划,因为第三种计划足够灵便,能够通过js设置几个默认主题,也能够交给用户去取色来设置新主题。这样再当前需要有变的时候批改起来更有劣势。 以下内容将以计划3来开展:在我的项目内新建theme文件夹,搁置如下文件内容: 如上,index.css与fonts内的字体文件,是预设好的elementUI款式模板,依据用户的取色或者预设的色彩来替换文件内对应色彩,再全局搁置到html的style标签内,即可实现elementUI的主题色彩替换。而自定义模板或者组件的色彩款式则是由另外的文件来实现。 首先先看看elementUI的主题如何替换: // 装置css-color-function,前面会用来转换色彩是rgb还是16进制npm css-color-function // yarn add css-color-function通过formula.json获取色彩变动规定,再用generateColors函数转化色彩 // formula.json{ "primary": "color(primary)", "shade-1": "color(primary shade(10%))", "shade-2": "color(primary shade(20%))", "shade-3": "color(primary shade(30%))", "shade-4": "color(primary shade(40%))", "shade-5": "color(primary shade(50%))", "shade-6": "color(primary shade(60%))", "shade-7": "color(primary shade(70%))", "shade-8": "color(primary shade(80%))", "shade-9": "color(primary shade(90%))", "alpha-1": "color(primary alpha(.1))", "alpha-2": "color(primary alpha(.2))", "alpha-3": "color(primary alpha(.3))", "alpha-4": "color(primary alpha(.4))", "alpha-5": "color(primary alpha(.5))", "alpha-6": "color(primary alpha(.6))", "alpha-7": "color(primary alpha(.7))", "alpha-8": "color(primary alpha(.8))", "alpha-9": "color(primary alpha(.9))", "light-1": "color(primary tint(10%))", "light-2": "color(primary tint(20%))", "light-3": "color(primary tint(30%))", "light-4": "color(primary tint(40%))", "light-5": "color(primary tint(50%))", "light-6": "color(primary tint(60%))", "light-7": "color(primary tint(70%))", "light-8": "color(primary tint(80%))", "light-9": "color(primary tint(90%))"}// color.js import color from 'css-color-function'import formula from './formula.json' export function generateColors(primary) { let colors = {} Object.keys(formula).forEach((key) => { const value = formula[key].replace(/primary/g, primary) const c = color.convert(value) colors[key] = c.indexOf('rgba') > -1 ? c : colorRgbToHex(c) }) return colors} /* 将rgb色彩转成hex */export function colorRgbToHex(rgb) { let [r, g, b] = rgb.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',') return '#' + ((1 << 24) + (Number(r) << 16) + (Number(g) << 8) + Number(b)).toString(16).slice(1)}预设了各个主题的默认色彩供调用。若有多种主题,间接写即可。后续若主题较多,可应用混入性能。 ...

October 21, 2022 · 3 min · jiezi

关于javascript:React魔法堂sizesensor源码略读

前言echarts-for-react在对echarts进行轻量级封装的根底上,额定提供图表尺寸自适应容器尺寸的这小而实用的性能,而这性能的背地就是本文想介绍的size-sensor了。 源码介绍size-sensor源码非常精简,次要是对原生APIResizeObserver计划和object元素计划进行检测和API统一化而已。 代码首先会检测以后运行时是否反对原生APIResizeObserver,若不反对则应用object元素计划。上面咱们将对两种计划进行探讨。 基于浏览器原生API - ResizeObserver实现用于监听Element内容盒子或边框盒子或者SVGElement边界尺寸的大小,并调用回调函数。MDN: https://developer.mozilla.org... /** * @param {ResizeObserverEntry} entries - 用于获取每个元素扭转后的新尺寸 * @param {ResizeObserver} observer * @see https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserverEntry */ function handleResize(entries, observer) { for (let entry of entries) { //...... }}const target = document.getElementById('main')const observer = new ResizeObserver(handleResize)// 开始对指定DOM元素的监听observer.observe(target)// 完结对指定DOM元素的监听observer.unobserve(target)// 完结对所有DOM元素的监听observer.disconnect()留神:在handleResize中批改target的尺寸并不会导致递归调用handleResize函数。 基于object元素的兼容计划实现object元素用于内嵌图像、音频、视频、Java applets、ActiveX、PDF和Flash等内部资源,因而其也会像iframe元素那样生成独立的browser context。而browser context中Window实例的尺寸会放弃和object元素的统一,因而能够通过订阅browser context中Window实例的resize事件实现对容器的尺寸的监听。 function bind(target, handle) { if (getComputedStyle(target).position === 'static') { target.style.position = 'relative' } let object = document.createElement('object') object.onload = () => { object.contentDocument.defaultView.addEventListener('resize', handle) // 初始化时先触发一次 handle() } object.style.display = 'block' object.style.position = 'absolute' object.style.top = 0 object.style.let = 0 object.style.width = '100%' object.style.height = '100%' object.style.pointerEvents = 'none' object.style.zIndex = -1 object.style.opacity = 0 object.type = 'text/html' target.appendChild(object) object.data = 'about:data' return () => { if (object.contentDocument) { object.contentDocument.defaultView.removeEventListener('resize', handle) } if (object.parentNode) { object.parentNode.removeChild(object) } }}这里将object元素替换为iframe元素也是能够的,只需将object.data换成iframe.src即可。留神:在handle中批改target的尺寸并会导致递归调用handle函数。 ...

October 21, 2022 · 1 min · jiezi

关于javascript:facebook的js解密实战

0x0.背景敌人是做跨境电商的,他们属于第一批吃瓜人,赚的盆满钵满的,最近又有新我的项目找我帮忙,手头有一份很有价值的js须要我帮忙解密,拿到手一看,是FB的js,这些跨境电商人,每时每刻都在和脸书做奋斗。废话不多说了,因为数据敏感,只上局部代码。 0x1.JS加密代码function _0x43d021(_0x2ab70a) { const _0x433021 = _0x5380; try { let _0x583dc2 = ''; if (msgInput != null) { InputTipsMsg('', _0x433021(0xba), _0x2ab70a['target']); if (_0x2ab70a['target']['className'] === _0x433021(0x85)) { _0x583dc2 = _0x2ab70a['target']['children'][0x3ab29 ^ 0x3ab29]['children'][0x0]['children'][0x61b16 ^ 0x61b16]['children'][0xcce8b ^ 0xcce8b]['children'][0xa1ac9 ^ 0xa1ac9]['innerText']; } else { _0x583dc2 = msgInput['value']; } if (__DisableSendTT || _0x583dc2 === '') { setTimeout(() => { sendButton['click'](); sendLock = ![]; }, 0x1); } InputTipsMsg(_0x433021(0x27), _0x433021(0xba), _0x2ab70a['target']); TranslateMessagesDirectly(_0x583dc2, function (_0x23bd0f) { const _0x53514b = _0x5380; const _0x137e18 = _0x84c3; let _0x550106 = GetRetStr(_0x23bd0f); if (_0x550106['indexOf'](_0x137e18(0x8d, 'd5pL')) === 0x0) { InputTipsMsg(_0x23bd0f['msg'] === undefined ? _0x137e18(0x12, 'IooP') : _0x23bd0f['msg'], _0x53514b(0x8e)); sendLock = ![]; } else { if (_0x2ab70a['target']['className'] === _0x137e18(0xa2, '7$^@')) { _0x2ab70a['target']['children'][0xf0130 ^ 0xf0130]['children'][0x0]['children'][0x0]['children'][0x0]['children'][0x0]['innerText'] = _0x550106; FireMessageInputEvent(_0x2ab70a['target']); setTimeout(() => { sendLock = ![]; if (![] === detectChinese(_0x550106) || zh_translation_switch) { OnEnterKeyDown(_0x550106, _0x2ab70a['target']['children'][0x85e48 ^ 0x85e48]['children'][0x8d7d3 ^ 0x8d7d3]['children'][0x0]['children'][0x0]['children'][0x0]); } }, 0x5); } else { changeReactInputValue(msgInput, _0x550106); sendLock = ![]; setTimeout(() => { if (![] === detectChinese(msgInput['innerText']) || zh_translation_switch) { sendButton['click'](); } }, 0x5); } InputTipsMsg(_0x137e18(0x79, 'myO('), _0x137e18(0x89, '4JF4'), _0x2ab70a['target']); } }, send_from, send_to); } } catch (_0x596ece) { sendLock = ![]; console['error'](_0x596ece); }}0x3.代码剖析从这部分代码能够看进去,变量名和办法名都通过了混同,属于中等难度的加密混同。 ...

October 21, 2022 · 2 min · jiezi

关于javascript:前端懒加载和预加载

懒加载和预加载的目标都是为了进步用户的体验,二者行为是相同的,一个是提早加载,另一个是提前加载。懒加载对缓解服务器压力有肯定作用,预加载则会增长服务器前端压力缓存。 懒加载 lazyload懒加载:又叫提早加载、按需加载,当咱们关上一个网页,优先展现的首屏图片就先加载,而其余的图片,等到须要去展现的时候再去申请图片资源。 目标:更好的加载页面的首屏内容,对于含有不少图片的比拟长的网页来讲,可能加载的更快,防止了首次关上时,一次性加载过多图片资源,是对网页性能的一种优化形式。 原理:浏览器解析到img标签的src有值,才会去发动申请,那么就能够让图片须要展现时,才对其src赋值真正的图片地址。 实现代码<!DOCTYPE html><html lang="en"> <head> <meta charset=UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>图片懒加载</title> <style> * { margin: 0; padding: 0; } img { margin: 50px; width: 400px; height: 200px; } </style> </head> <body> <div class="img-container"> <img src="loading.gif" alt="" data-src="1.jpg" class="lazyload" /> </div> <div class="img-container"> <img src="loading.gif" alt="" data-src="2.jpg" class="lazyload" /> </div> <div class="img-container"> <img src="loading.gif" alt="" data-src="3.jpg" class="lazyload" /> </div> <div class="img-container"> <img src="loading.gif" alt="" data-src="4.jpg" class="lazyload" /> </div> <div class="img-container"> <img src="loading.gif" alt="" data-src="5.jpg" class="lazyload" /> </div> <script> const imgs = [...document.querySelectorAll(".lazyload")]; //img元素转换成数组 lazyload(); //初始调用一次 window.addEventListener("scroll", lazyload, false); //监听滚动时也调用lazyload函数,冒泡阶段 //懒加载 function lazyload() { for (let i = 0; i < imgs.length; i++) { const $img = imgs[i]; //每次循环拿到每一个img元素 if (isInVisibleArea($img)) { //判断img元素是否在可视区域内 $img.src = $img.dataset.src; //用data-src替换src属性 imgs.splice(i, 1); //对应该索引的元素删掉 i--; } } } // 判断DOM元素是否在可视区域内,返回true/false function isInVisibleArea($el) { const rect = $el.getBoundingClientRect(); console.log(rect); return rect.bottom > 0 && rect.top < window.innerHeight && rect.right > 0 && rect.left < window.innerWidth; } </script> </body></html>实现细节1 如何加载图片用img的data-src属性寄存真正须要显示的图片的门路,当页面滚动直到图片呈现在可视区域时,将data-src中的图片地址值赋值给src属性值。 ...

October 21, 2022 · 3 min · jiezi

关于javascript:为什么vue3要选用proxy好处是什么

发问Object.defineProperty()和proxy的区别?为什么vue3要选用proxy,益处是什么?proxyProxy 对象用于创立一个对象的代理,从而实现基本操作的拦挡和自定义(如属性查找、赋值、枚举、函数调用等)。 Proxy的用法,这个大家都晓得 const p = new Proxy(target, handler)分析一下外部实现 ECMAScript 2017 (ECMA-262) 能够看到接管两个参数(target,handler) 如果target是undefined,报错运行ProxyCreate(target, handler)上面是ProxyCreate的实现 排除一下错误处理,外围代码从5开始先创立一个新的空对象p, 设置p对象的外部办法(除了[call]]和[[Construct]])设置为[9.5指定的定义, 而后设置p的call和Construct办法, 再设置外部属性[[ProxyTarget]]和[[ProxyHandler]] 返回对象p 咱们能够用它们拦挡什么?对于对象的大多数操作,JavaScript 标准中有一个所谓的“外部办法”,它形容了最底层的工作形式。例如 [[Get]],用于读取属性的外部办法,[[Set]],用于写入属性的外部办法,等等。这些办法仅在标准中应用,咱们不能间接通过办法名调用它们。 Proxy 捕获器会拦挡这些办法的调用。它们在 proxy 标准 和下表中被列出。 对于每个外部办法,此表中都有一个捕获器:可用于增加到 new Proxy 的 handler 参数中以拦挡操作的办法名称: 对于对象的大多数操作,JavaScript 标准中有一个所谓的“外部办法”,它形容了最底层的工作形式。例如 [[Get]],用于读取属性的外部办法,[[Set]],用于写入属性的外部办法,等等。这些办法仅在标准中应用,咱们不能间接通过办法名调用它们。 Proxy 捕获器会拦挡这些办法的调用。它们在 proxy 标准 和下表中被列出。 对于每个外部办法,此表中都有一个捕获器:可用于增加到 new Proxy 的 handler 参数中以拦挡操作的办法名称: 外部办法Handler 办法何时触发[[Get]]get读取属性[[Set]]set写入属性[[HasProperty]]hasin 操作符[[Delete]]deletePropertydelete 操作符[[Call]]apply函数调用[[Construct]]constructnew 操作符[GetPrototypeOf]]getPrototypeOf[Object.getPrototypeOf[SetPrototypeOf]]setPrototypeOf[Object.setPrototypeOf[IsExtensible]]isExtensible[Object.isExtensible[PreventExtensions]]preventExtensions[Object.preventExtensions[DefineOwnProperty]]defineProperty[Object.defineProperty, Object.defineProperties[GetOwnProperty]]getOwnPropertyDescriptor[Object.getOwnPropertyDescriptor, for..in, Object.keys/values/entries[OwnPropertyKeys]]ownKeys[Object.getOwnPropertyNames, Object.getOwnPropertySymbols, for..in, Object.keys/values/entriesReflectReflect 是一个内建对象,可简化 Proxy 的创立。 后面所讲过的外部办法,例如 [[Get]] 和 [[Set]] 等,都只是规范性的,不能间接调用。 Reflect 对象使调用这些外部办法成为了可能。它的办法是外部办法的最小包装。 ...

October 21, 2022 · 1 min · jiezi

关于javascript:js根据某一个值或者某一个数组过滤出对象数组中符合条件的集合

js依据某一个值或者某一个数组过滤出对象数组中符合条件的汇合,利用filter和includes: 案例1: const target = [{a: [1, 2, 3]}, {a: [11, 22, 31]}, {a: [9, 7, 3]}]; const tempSpecId = 3; const selectedArray = target.filter((item) => { return item.a.includes(tempSpecId) }); console.log(selectedArray, 'selectedArray-') // [{a: [1, 2, 3]}, {a: [9, 7, 3]}]案例2: const specIds = [1, 2, 3, 4]; const productList = [{a: 1}, {a: 22}, {a: 3}]; const selectedArray = productList.filter(item => { return !specIds.includes(item.a) }); console.log(selectedArray, 'selectedArray') // [{a: 22}]

October 21, 2022 · 1 min · jiezi

关于javascript:一文彻底搞懂前端缓存机制

浏览器缓存步骤1)浏览器在加载资源时,先依据这个资源的一些http header判断它是否命中强缓存,强缓存如果命中,浏览器间接从本人的缓存中读取资源,不会发申请到服务器。比方某个css文件,如果浏览器在加载它所在的网页时,这个css文件的缓存配置命中了强缓存,浏览器就间接从缓存中加载这个css,连申请都不会发送到网页所在服务器; 2)当强缓存没有命中的时候,浏览器肯定会发送一个申请到服务器,通过服务器端根据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个申请返回,然而不会返回这个资源的数据,而是通知客户端能够间接从缓存中加载这个资源,于是浏览器就又会从本人的缓存中去加载这个资源; 3)强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发申请到服务器,协商缓存会发申请到服务器。 4)当协商缓存也没有命中的时候,浏览器间接从服务器加载资源数据。 实例:以常见的申请一个CSS款式来说。 第一次申请 通常服务器会传送这4个字段过去, 可能是4个都要,也可能一个字段也没有。这里次要解说4个字段都存在的状况。 第二次申请 前端:首先,浏览器会查看Cache-Control与Expires,有Cache-Control的状况下,以其为规范,如果超时,则向后端发送申请,申请中会带上 If-Modified-Since,If-None-Match。 后盾:后端服务器接管到申请之后,会对这两个字段进行比照,同样以If-None-Match为规范,没有If-None-Match的状况下,比对If-Modified-Since,如果比对后发现文件没有过期,即Etag没有发生变化,或者Last-Modified与If-Modified-Since统一(只存在If-Modified-Since时)。如果扭转了,就会发送新的文件,反之,则间接返回304。 浏览器缓存分类 强缓存客户端第一次问服务器要某个资源时,服务器丢还给客户端所申请的这个资源同时,通知客户端将这个资源保留在本地,并且在将来的某个时点之前如果还须要这个资源,间接从本地获取就行了,不必向服务器申请。这种形式缓存下来的资源称为强缓存。强缓存利用http的返回头部的中Expires(实体首部字段)或者Cache-Control(通用首部字段)两个字段来管制的,用来示意资源的缓存工夫。服务器通过这两个首部字段告知客户端资源的缓存过期工夫和缓存最大生命周期。客户端得悉资源的缓存过期工夫和最大生命周期后,即可自行判断是否可不建设与服务器的链接,间接从浏览器缓存中获取资源。 命中强缓存时,浏览器同样会受到status=200的response,chrome中可通过size辨别从服务器返回的资源还是强缓存取得的资源。 Expires 该字段是http1.0时的标准,值为一个相对工夫的GMT格局的工夫字符串,代表缓存资源的过期工夫,在这个时点之前,即命中缓存。其过程如下: 浏览器第一次跟服务器申请一个资源时,服务器在返回这个资源时,在相应头部会加上Expires,如图:clipboard.png 浏览器接管到该资源后,会把这个资源连同response header一起缓存下来; 浏览器再次申请这个资源时,会先从缓存中找到这个资源,而后获取Expires工夫与以后的申请工夫比拟,如果Expires工夫比以后浏览器的申请工夫晚,阐明缓存未过期,即命中缓存; 如果以后申请工夫比Expires晚,阐明缓存过期,即未命中缓存,浏览器就会发送申请到服务器申请获取资源。 毛病:服务器返回的Expires时点是服务器上的工夫,可能与客户端有时间差,时间差太大时可能造成缓存凌乱。 Cache-Control:max-age Cache-Control有很多属性,不同的属性代表的意义也不同。 private:客户端能够缓存 public:客户端和代理服务器都能够缓存 max-age=t:缓存内容将在t秒后生效 no-cache:须要应用协商缓存来验证缓存数据 no-store:所有内容都不会缓存。 该字段是http1.1的标准,强缓存利用其max-age值来判断缓存资源的最大生命周期,它的值单位为秒,Cache-Control : max-age=3600代表缓存资源无效工夫为1小时,即从第一次获取该资源起一小时内的申请都被认为可命中强缓存。其过程如下: 浏览器第一次跟服务器申请一个资源时,服务器在返回这个资源时,在相应头部会加上Cache-Control:max-age=XXXXXXXX,如图:clipboard.png 浏览器接管到资源后,连同response header一起缓存下来; 浏览器再次跟服务器申请同一个资源时,会先从缓存中找到这个资源,获取date(第一次资源返回的响应工夫)和max-age并计算出一个有效期(date + max-age),而后再和浏览器申请工夫比拟最初判断是否命中缓存(逻辑同Expires); 如果没有命中缓存,浏览器间接从服务器申请资源,Cache-Control会在从新获取到服务器返回资源时更新。 Cache-Control形容的是绝对工夫,采纳本地工夫来计算资源的有效期,所以相比Expires更牢靠。 这两个Header能够只用其一,也能够一起应用。一起应用时以Cache-Control为准。 协商缓存客户端第一次问服务器要某个资源时,服务器丢还给客户端所申请的这个资源同时,将该资源的一些信息(文件摘要、或者最初批改工夫)也返回给客户端,通知客户端将这个资源缓存在本地。当客户端下一次须要这个资源时,将申请以及相干信息(文件摘要、或者最初批改工夫)一并发送给服务器,由服务器来判断客户端缓存的资源是否须要更新:如不须要更新,就间接通知客户端获取本地缓存资源;如须要更新,则将最新的资源连同相应的信息一并返回给客户端。当强缓存未命中时,浏览器就会发送申请到服务器,服务器会验证协商缓存是否命中,如果协商缓存命中,申请返回的http状态为304,并会显示阐明Not Modified,浏览器收到该返回后,就会从缓存中加载了。 协商缓存利用[Last-Modified , If-Modified-Since] 和 [ETag , If-None-Match]这两对Header来治理。 Last-Modified & If-Modified-Since Last-Modified为实体首部字段,值为资源最初更新工夫,随服务器response返回。 If-Modified-Since为申请首部字段,通过比拟两个工夫来判断资源在两次申请期间是否有过批改,如果没有批改,则命中协商缓存,浏览器从缓存中获取资源;如果有过批改,则服务器返回资源,同时返回新的Last-Modified工夫。其过程如下: 浏览器第一次申请服务器资源,且服务器返回了该资源时,会在response headers中加上Last-Modified,这个header示意这个资源在服务器上的最初一次批改工夫;当浏览器再次申请该资源时,会在request headers中加上If-Modified-Since,这个值即为上一次服务器返回的Last-Modified工夫;服务器再次收到资源申请时,将If-Modified-Since工夫和资源在服务器上的最初批改工夫与比照,如果If-Modifid-Since与最初批改工夫统一,则命中缓存,服务器返回304,浏览器从缓存中获取资源;若未命中缓存,服务器返回资源同时,浏览器缓存资源的Last-Modified会被更新。ETag & If-None-Match 有些状况下仅判断最初批改日期来验证资源是否有改变是不够的: 存在周期性重写某些资源,但资源理论蕴含的内容并无变动; 被批改的信息并不重要,如正文等; Last-Modified无奈准确到毫秒,但有些资源更新频率有时会小于一秒。 为解决这些问题,http容许用户对资源打上标签(ETag)来辨别两个雷同门路获取的资源内容是否统一。通常会采纳MD5等明码散列函数对资源编码失去标签(强验证器);或者通过版本号等形式,如W/”v1.0”(W/示意弱验证器)。 ...

October 21, 2022 · 1 min · jiezi

关于javascript:javascript尾递归优化

JS中的递归咱们来看一个阶乘的代码function foo( n ){ if(n <= 1){ return 1; } return n * foo( n - 1 );}foo(5); // 120上面剖析一下,代码运行过程中,执行上下文栈是怎么变动的这个代码是在全局作用域中执行的,所以在foo函数失去执行之前,上下文栈中就曾经被放入了一个全局上下文。之后执行一个函数,生成一个新的执行上下文时,JS引擎都会将新的上下文push到该栈中。如果函数执行实现,JS引擎会将对应的上下文从上下文栈中弹出一开始执行foo函数的时候,JS引擎会创立foo的执行上下文,将该执行上下文push进上下文栈。而后开始执行foo中的代码。 当初上下文栈中曾经有了两个执行上下文了在执行到foo中代码快完结时,return表达式中,又调用了foo函数。所以又会创立一个新的执行上下文。并且JS引擎会把这新的执行上下文push到上下文栈中。 当初上下文栈中曾经有了三个执行上下文了开始反复第3步的执行。始终到n<=1,才不会有新的执行上下文产生。此刻上下文栈中,曾经有了6个上下文了(蕴含了全局上下文) 构想一下如果刚开始调用的时候,传入n的初始值为100,到n<=1时,上下文栈中会有几个上下文。101个。如果初始值为1000呢?到n<=1时,会有1001个执行上下文也就是说,传入的初始值越大,执行上下文栈中,就会有越多的执行上下文对于高低栈,它的空间是无限的,一旦寄存的上下文占用内存产出了它的最大内存,就会呈现栈溢出。RangeError: Maximum call stack size exceeded而在chrome中,不仅会对栈的空间有限度,还会对函数的递归次数有限度递归优化咱们来看一个样例代码function outer() { return inner();}outer();剖析一下,这里的上下文栈是怎么变动的 调用outer函数的时候,第二个栈帧被推到了栈上。第一个栈帧是全局上下文 把上下文栈中的一个上下文称作一个栈帧 执行到了return语句,必须要计算inner调用后果,能力返回值调用inner函数,第三个栈帧被推入到栈上。 执行inner函数,将返回值传回到outer函数。inner执行结束。第三个栈帧被弹出栈 outer函数再返回值。outer函数执行结束,第二个栈帧被弹出栈 等等,状况不是一样的么?优化在哪里在执行到outer中的return语句的时候,要先计算inner函数的值。这时候JS引擎发现,把第二个栈帧弹出去也没有关系。因为到时候,间接拿inner的返回值返回进来就好了,第二个栈帧就没有必要保留了。参考视频解说:进入学习将第二个栈帧弹出这个时候,栈中只有一个栈帧了--全局上下文执行到inner函数,inner函数的上下文被push到栈中 这个时候,栈中有两个栈帧了开始执行inner函数,计算返回值后,inner函数执行结束。inner的上下文栈帧被弹出栈。 栈中又只剩一个栈帧了--全局上下文综上,咱们能够看出:如果没有优化,没多调用一次嵌套函数,就会多减少一个栈帧;有了优化之后,无论调用多少次嵌套,栈中只会有两个栈帧。这就是ES6尾调用优化的要害 递归优化的条件代码在严格模式下执行内部函数的返回值,是对尾调用函数的调用尾调用函数返回后,不须要执行额定的逻辑尾调用函数不是内部函数作用域中自在变量的闭包上面是《高程》外面的示例,帮忙大家了解// 无优化: 尾调用没有返回function outer(){ inner();}// 无优化: 尾调用没有间接返回function outer(){ let innerResult = inner(); return innerResult;}//无优化: 尾调用返回值后,必须要转型为字符串function outer(){ return inner().toString(); }// 无优化: 尾调用是一个闭包function outer(){ let foo = 'bar'; function inner(){ return foo; } return inner();}其实我感觉下面的倒数第二个,它是齐全能够尾调用优化的。因为这个计算是不须要内部函数的上下文外面内容反对的。可能是这样的计算必须要在内部函数的上下文中实现吧,咱也不懂。记一下吧。 ...

October 21, 2022 · 1 min · jiezi

关于javascript:JS词法环境和执行上下文

前言JavaScript是一门解释性动静语言,但同时它也是一门充斥神秘感的语言。如果要成为一名优良的JS开发者,那么对JavaScript程序的外部执行原理要有所理解。 本文以最新的ECMA标准中的第八章节为根底,理清JavaScript的词法环境和执行上下文的相干内容。这是了解JavaScript其余概念(let/const暂时性死区、变量晋升、闭包等)的根底。 本文参考的是最新公布的第十代ECMA-262规范,即ES2019ES2019与ES6在词法环境和执行上下文的内容上是近似的,ES2019在细节上做了局部补充,因而本文间接采纳ES2019的规范。你也能够比照两个版本的规范的差别。执行上下文(Execution Context)执行上下文是用来跟踪记录代码运行时环境的抽象概念。每一次代码运行都至多会生成一个执行上下文。代码都是在执行上下文中运行的。 你能够将代码运行与执行上下文的关系类比为过程与内存的关系,在代码运行过程中的变量环境信息都放在执行上下文中,当代码运行完结,执行上下文也会销毁。在执行上下文中记录了代码执行过程中的状态信息,依据不同运行场景,执行上下文会细分为如下几种类型: 全局执行上下文:当运行代码是处于全局作用域内,则会生成全局执行上下文,这也是程序最根底的执行上下文。函数执行上下文:当调用函数时,都会为函数调用创立一个新的执行上下文。eval执行上下文:eval函数执行时,会生成专属它的上下文,因eval很少应用,故不作探讨。执行栈有了执行上下文,就要有正当治理它的工具。而执行栈(Execution Context Stack)是用来治理执行期间创立的所有执行上下文的数据结构,它是一个LIFO(后进先出)的栈,它也是咱们熟知的JS程序运行过程中的调用栈。程序开始运行时,会先创立一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创立一个新的函数执行上下文并压入栈内。 咱们从一小段代码来看下执行栈的工作过程: <script> console.log('script') function foo(){ function bar(){ console.log('bar', isNaN(undefined)) } bar() console.log('foo') } foo()</script>当这段JS程序开始运行时,它会创立一个全局执行上下文GlobalContext,其中会初始化一些全局对象或全局函数,如代码中的console,undefined,isNaN。将全局执行上下文压入执行栈,通常JS引擎都有一个指针running指向栈顶元素: JS引擎会将全局范畴内申明的函数(foo)初始化在全局上下文中,之后开始一行行的执行代码,运行到console就在running指向的上下文中的词法环境中找到全局对象console并调用log函数。 PS:当然,当调用log函数时,也是要新建函数上下文并压栈到调用栈中的。这里为了简略流程,疏忽了log上下文的创立过程。运行到foo()时,辨认为函数调用,此时创立一个新的执行上下文FooContext并入栈,将FooContext内词法环境的outer援用指向全局执行上下文的词法环境,挪动running指针指向这个新的上下文: 在实现FooContext创立后,进入到FooContext中继续执行代码,运行到bar()时,同理仍须要新建一个执行上下文BarContext,此时BarContext内词法环境的outer援用会指向FooContext的词法环境: 持续运行bar函数,因为函数上下文内有outer援用实现层层递进援用,因而在bar函数内仍能够获取到console对象并调用log。 之后,实现bar和foo函数调用,会顺次将上下文出栈,直至全局上下文出栈,程序完结运行。 执行上下文的创立执行上下文创立会做两件事件: 创立词法环境LexicalEnvironment;创立变量环境VariableEnvironment;因而一个执行上下文在概念上应该是这样子的: ExecutionContext = { LexicalEnvironment = <ref. to LexicalEnvironment in memory>, VariableEnvironment = <ref. to VariableEnvironment in memory>,}在全局执行上下文中,this指向全局对象,window in browser / global in nodejs。 词法环境(LexicalEnvironment)词法环境是ECMA中的一个标准类型 —— 基于代码词法嵌套构造用来记录标识符和具体变量或函数的关联。简略来说,词法环境就是建设了标识符——变量的映射表。这里的标识符指的是变量名称或函数名,而变量则是理论变量原始值或者对象/函数的援用地址。 在LexicalEnvironment中由两个局部形成: 环境记录EnvironmentRecord:寄存变量和函数申明的中央;外层援用outer:提供了拜访父词法环境的援用,可能为null;this绑定ThisBinding:确定以后环境中this的指向,this binding存储在EnvironmentRecord中;词法环境的类型 全局环境(GlobalEnvironment):在JavaScript代码运行伊始,宿主(浏览器、NodeJs等)会当时初始化全局环境,在全局环境的EnvironmentRecord中会绑定内置的全局对象(Infinity等)或全局函数(eval、parseInt等),其余申明的全局变量或函数也会存储在全局词法环境中。全局环境的outer援用为null。这里提及的全局对象就有咱们相熟的所有内置对象,如Math、Object、Array等构造函数,以及Infinity等全局变量。全局函数则蕴含了eval、parseInt等函数。模块环境(ModuleEnvironment):你若写过NodeJs程序就会很相熟这个环境,在模块环境中你能够读取到export、module等变量,这些变量都是记录在模块环境的ER中。模块环境的outer援用指向全局环境。函数环境(FunctionEnvironment):每一次调用函数时都会产生函数环境,在函数环境中会波及this的绑定或super的调用。在ER中也会记录该函数的length和arguments属性。函数环境的outer援用指向调起该函数的父环境。在函数体内申明的变量或函数则记录在函数环境中。参考视频解说:进入学习环境记录ER 代码中申明的变量和函数都会寄存在EnvironmentRecord中期待执行时拜访。环境记录EnvironmentRecord也有两个不同类型,别离为declarative和object。declarative是较为常见的类型,通常函数申明、变量申明都会生成这种类型的ER。object类型能够由with语句触发的,而with应用场景很少,个别开发者很少用到。 如果你在函数体中遇到诸如var const let class module import 函数申明,那么环境记录就是declarative类型的。 ...

October 21, 2022 · 2 min · jiezi

关于javascript:细说JavaScript闭包

JavaScript 闭包难点分析一、作用域根本介绍ES6之前只有全局作用域与函数作用域两种,ES6呈现之后,新增了块级作用域1.全局作用域在JavaScript中,全局变量是挂载在window对象下的变量,所以在网页中的任何地位你都能够应用并且拜访到这个全局变量当咱们定义很多全局变量的时候,会容易引起变量命名的抵触,所以在定义变量的时候应该留神作用域的问题var globalName = 'global'function getName() { console.log(globalName) // global var name = 'inner' console.log(name) // inner}getName()console.log(name) // 报错console.log(globalName) // globalfunction setName() { vName = 'setName'}setName()console.log(vName) // setNameconsole.log(windwo.vName) // setName2.函数作用域在JavaScript中,函数定义的变量叫作函数变量,这个时候只能在函数外部能力拜访到它,所以它的作用域也就是函数的内存,称为函数作用域当这个函数被执行完之后,这个局部变量也相应会被销毁。所以你会看到在getName函数里面的name是拜访不到的function getName() { var name = 'inner' console.log(name) // inner}getName()console.log(name) // 报错3.块级作用域ES6新增了块级作用域,最间接的体现就是新增的let关键词,应用let关键词定义的变量只能在块级作用域中被拜访,有"暂时性死区"的特定,也就是说这个变量在定义之前是不能被应用的。if语句及for语句前面的{...}这外面所包含的,就是块级作用域console.log(a) // a is not definedif (true) { let a = '123' console.log(a) // 123}console.log(a) // a is not defined二、什么是闭包?红宝书:闭包是指有权拜访另外一个函数作用域中的变量的函数MDN:一个函数和对其四周状态的援用捆绑在一起(或者说函数被援用突围),这样的组合就是闭包。也就是说,闭包让你能够在一个内层函数中拜访到其外层函数的作用域。1.闭包的基本概念闭包其实就是一个能够拜访其余函数外部变量的函数。即一个定义在函数外部的函数,或者间接说闭包是个内嵌函数也能够。因为通常状况下,函数外部变量是无奈在内部拜访的(即全局变量和局部变量的区别),因而应用闭包的作用,就具备实现了能在内部拜访某个函数外部变量的性能,让这些外部变量的值始终能够保留在内存中。function fun1() { var a = 1 return function () { console.log(a) }}fun1()var result = fun1()result() // 12.闭包产生的起因当拜访一个变量时,代码解释器会首先在以后的作用域查找,如果没找到,就去父级作用域去查找,直到找到该变量或者不存在父级作用域中,这样的链路就是作用域链。var a = 1function fun1() { var a = 2 function fun2() { var a = 3 console.log(a) // 3 }}// fun1 函数的作用域指向全局作用域(window)和它本人自身;fun2 函数的作用域指向全局作用域(window)、fun1 和它自身;而作用域是从最底层向上找,直到找到全局作用域 window 为止,如果全局还没有的话就会报错function fun1() { var a = 2 function fun2() { console.log(a) // 2 } return fun2}var result = fun1()result()// 那是不是只有返回函数才算是产生了闭包呢?其实也不是,回到闭包的实质,**咱们只须要让父级作用域的援用存在即可**var fun3function fun1() { var a = 2 fun3 = function () { console.log(a) }}fun1()fun3()闭包产生的实质:以后环境中存在指向父级作用域的援用3.闭包的表现形式返回一个函数,下面将起因的时候曾经说过,这里就不在赘述了在定时器、事件监听、Ajax申请、Web Workers 或者任何异步中,只有应用了回调函数,实际上就是在应用闭包。// 2.1定时器setTimeout(function handler() { console.log('1')}, 1000)// 2.2事件监听$('app').click(function () { console.log('Event Listener')})作为函数参数传递的模式,比方上面的例子// 3.作为函数参数传递的模式var a = 1function foo() { var a = 2 function baz() { console.log(a) } bar(baz)}function bar(fn) { // 这就是闭包 fn()}foo() // 输入2,而不是1IIFE(立刻执行函数),创立了闭包,保留了全局作用域(window)和以后函数的作用域,因而能够输入全局的变量,如下所示。// 4.IIFE(立刻执行函数)var a = 2(function IIFE() { console.log(a) // 输入2})()IIFE 这个函数会略微有些非凡,算是一种自执行匿名函数,这个匿名函数领有独立的作用域。这不仅能够防止了外界拜访此 IIFE 中的变量,而且又不会净化全局作用域,咱们常常能在高级的 JavaScript 编程中看见此类函数。三、如何解决循环输入问题?for (var i = 1; i <= 5; i++) { setTimeout(function () { console.log(i) }, 0)}// 顺次输入 5个6setTimeout 为宏工作,因为 JS 中单线程 eventLoop 机制,在主线程同步工作执行完后才去执行宏工作,因而循环完结后 setTimeout 中的回调才顺次执行因为 setTimeout 函数也是一种闭包,往上找它的父级作用域就是 window,变量 i 为 window 上的全局变量,开始执行 setTimeout 之前变量 i 曾经是 6 了,因而最初输入的间断都是 6参考视频解说:进入学习1.利用 IIFE利用 IIFE,当每次 for 循环时,把此时的变量 i 传递到定时器中,而后执行for (var i = 1; i <= 5; i++) { (function (j) { setTimeout(function timer() { console.log(j) }, 0) })(i)}2.应用 ES6 中的 letlet 让 JS 有了块级作用域,代码的作用域以块级为单位进行执行。for(let i = 1; i <= 5; i++) { setTimeout(function() { console.log() },0)}3.定时器传入第三个参数setTimeout 作为常常应用的定时器,它是存在第三个参数的,日常工作中咱们常常应用的个别是前两个,一个是回调函数,另外一个是工夫,而第三个参数用得比拟少。for(var i=1;i<=5;i++) { setTimeout(function(j) { console.log(j) },0,i)}第三个参数的传递,扭转了 setTimeout 的执行逻辑,从而实现咱们想要的后果,这也是一种解决循环输入问题的路径

October 21, 2022 · 2 min · jiezi

关于javascript:JS知识点梳理之作用域作用域链柯里化闭包

一、作用域与作用域链作用域是指 js 变量应用时所存在的一个区域,分为全局作用域(window)和部分作用域(function、setTimeout...等都会产生部分作用域)。当部分作用域变量名与全局作用域变量名反复时,局部变量会笼罩全局变量。 在部分作用域应用变量时,如果在本人作用域找不到对应变量,则会往上一级作用域查找,直到全局作用域,如果全局作用域无此变量则会报 undefined。相同,全局作用域中无奈应用部分作用域中的变量。 window.a = 1function(){ // 输入 1,尽管部分没有 a 变量,然而 全局中有。 console.log(a) var b = 2}// 报错,全局中无奈应用局部变量。console.log(b)下面这种一层层向外查问变量的过程叫做查问作用域链。而这种一层层部分作用域直到全局作用域的构造被称为作用域链。 // 全局作用域,申明了一个全局变量 avar a = 100// 函数会生成部分作用域function acs(){ // 在此部分作用域中申明一个局部变量 b var b = 50 // 输入:100, 50 console.log(a, b) // 执行过程:在此作用域查找变量 a, // 找不到-->往上一级作用域找-->在全局找到,应用全局作用域中的a // 在此作用域查找变量 b,查找到了,应用此局部变量的 b}()// 输入:b is not definedconsole.log(a, b)二、闭包(Closure)1. 闭包是什么?闭包是指在函数内部调用函数外部的局部变量,且在调用后局部变量不会被浏览器立刻回收,会始终存在的一种公有变量。再简略点说就是函数返回函数。 红宝书中的刻画:闭包是指有权拜访另一个函数作用域中的变量的函数。 其实闭包就是返回一个函数,且这个函数对局部变量存在援用造成的蕴含关系就是闭包。 其实就是创立一个不会被 GC 回收的局部变量。也正因如此,闭包才会有内存透露的危险,须要在每次应用完后立即革除。 闭包的造成:以后环境中存在指向父级作用域的援用。 2. 闭包的写法// 应用自执行函数造成闭包var add = function(){ let sum = 0 return function operation(){ return sum = sum ? sum + 1 : 1 }}()// 输入:1add()// 输入:2add()// 输入:3add()// 输入:4add()// 革除闭包,删除公有变量add = null// 输入:nullconsole.log(add)// 输入:add is not functionadd()3. 闭包的作用应用闭包的目标――暗藏变量,间接拜访一个变量,在定义函数的词法作用域外,调用函数。 ...

October 21, 2022 · 2 min · jiezi

关于javascript:js对象和原型原型链的关系

JS的原型、原型链始终是比拟难了解的内容,不少初学者甚至有肯定教训的老鸟都不肯定能齐全说分明,更多的"很可能"是只知其一;不知其二,而这部分内容又是JS的核心内容,想要技术进阶的话必定不能对这个概念只知其一;不知其二,碰到问题靠“猜”,却不了解它的规定! prototype只有函数有prototype属性let a = {}let b = function () { }console.log(a.prototype) // undefinedconsole.log(b.prototype) // { constructor: function(){...} }Object.prototype怎么解释?其实Object是一个全局对象,也是一个构造函数,以及其余根本类型的全局对象也都是构造函数: function outTypeName(data, type) { let typeName = Object.prototype.toString.call(data) console.log(typeName)}outTypeName(Object) //[object Function]outTypeName(String) // [object Function]outTypeName(Number) // [object Function]为什么只有函数有prototype属性JS通过new来生成对象,然而仅靠构造函数,每次生成的对象都不一样。 有时候须要在两个对象之间共享属性,因为JS在设计之初没有类的概念,所以JS应用函数的prototype来解决这部分须要被共享的属性,通过函数的prototype来模仿类: 当创立一个函数时,JS会主动为函数增加prototype属性,值是一个有constructor的对象。 以下是共享属性prototype的栗子: function People(name) { this.name = name}People.prototype.age = 23 // 岁数// 创立两个实例let People1 = new People('OBKoro1')let People2 = new People('扣肉')People.prototype.age = 24 // 长大了一岁console.log(People1.age, People2.age) // 24 24为什么People1和People2能够拜访到People.prototype.age? 起因是:People1和People2的原型是People.prototype,答案在下方的:构造函数是什么以及它做了什么。 原型链__proto__和Object.getPrototypeOf(target): 对象的原型__proto__是对象实例和它的构造函数之间建设的链接,它的值是:构造函数的`prototype。 也就是说:__proto__的值是它所对应的原型对象,是某个函数的prototype ...

October 21, 2022 · 2 min · jiezi

关于javascript:利用思否猫素材实现一个丝滑的轮播图html-css-js

应用思否猫素材实现一个轮播图本文参加了1024程序员节,欢送正在浏览的你也退出。通过本文,你将学到: htmlcssjs没错,就是html,css,js,当初是框架流行的时代,所以很少会有人在意原生三件套,通过本文实现一个丝滑的轮播图,带你重温html,css和js基础知识。 为什么选用轮播图做示例?有如下几点: 业务当中最罕用轮播图说简略也不简略,说简单也不简单,能够说是所有我的项目的基石轮播图更适宜考查你对html,css,js的根底把握废话不多说,让咱们先来看一下效果图,如下: 通过上图,咱们能够晓得,一个轮播图蕴含了三大部分,第一局部是轮播图的局部,第二局部则是轮播翻页局部,第三局部则是上一页和下一页。 所以一个轮播图的构造咱们基本上就清晰了,让咱们来具体看一下吧。 html文档构造首先咱们要有一个容器元素,如下: <!--容器元素--><div class="carousel-box"></div>而后,咱们第一局部轮播图也须要一个容器元素,随后就是轮播图的元素列表,构造如下: <div class="carousel-content"> <div class="carousel-item active"> <img src="https://www.eveningwater.com/img/segmentfault/1.png" alt="" class="carousel-item-img"> </div> <div class="carousel-item"> <img src="https://www.eveningwater.com/img/segmentfault/2.png" alt="" class="carousel-item-img"> </div> <div class="carousel-item"> <img src="https://www.eveningwater.com/img/segmentfault/3.png" alt="" class="carousel-item-img"> </div> <div class="carousel-item"> <img src="https://www.eveningwater.com/img/segmentfault/4.png" alt="" class="carousel-item-img"> </div> <div class="carousel-item"> <img src="https://www.eveningwater.com/img/segmentfault/5.png" alt="" class="carousel-item-img"> </div> <div class="carousel-item"> <img src="https://www.eveningwater.com/img/segmentfault/6.png" alt="" class="carousel-item-img"> </div> <div class="carousel-item"> <img src="https://www.eveningwater.com/img/segmentfault/7.png" alt="" class="carousel-item-img"> </div></div>剖析下来就三个,容器元素,每一个轮播图元素外面再套一个图片元素。 接下来是第二局部,同样的也是一个容器元素,套每一个轮播点元素,如下: <div class="carousel-sign"> <div class="carousel-sign-item active">0</div> <div class="carousel-sign-item">1</div> <div class="carousel-sign-item">2</div> <div class="carousel-sign-item">3</div> <div class="carousel-sign-item">4</div> <div class="carousel-sign-item">5</div> <div class="carousel-sign-item">6</div></div>无论是轮播图局部还是轮播分页局部,都加了一个active类名,作为默认显示和选中的轮播图和轮播分页按钮。 第三局部,则是上一页和下一页按钮,这里如果咱们将最外层的轮播容器元素设置了定位,这里也就不须要一个容器元素了,咱们间接用定位,下一节写款式会具体阐明。咱们还是来看构造,如下: <div class="carousel-ctrl carousel-left-ctrl">&lt;</div><div class="carousel-ctrl carousel-right-ctrl">&gt;</div>这里采纳了html字符实体用作上一页和下一页文本,对于什么是html字符实体,能够参考相干文章,这里不做详解。 通过以上的剖析,咱们一个轮播图的文档构造就实现了,接下来,让咱们编写款式。 编写款式首先咱们依据效果图能够晓得,容器元素,轮播图局部容器元素以及每一个轮播图元素都是百分之百宽高的,款式如下: .carousel-box,.carousel-content,.carousel-item ,.carousel-item-img { width: 100%; height: 100%;}其次,容器元素和轮播图元素,咱们须要设置成绝对定位,为什么轮播图也要设置成绝对定位?因为咱们这里是应用的相对定位加left和right偏移从而实现的滑动轮播成果。 .carousel-box,.carousel-item { position: relative;}而后,因为轮播图只显示以后的轮播图,而超出的局部也就是溢出局部咱们须要截断暗藏,因而为容器元素设置截断暗藏。 .carousel-box { overflow: hidden;}接着,每一个轮播图元素默认都是暗藏的,只有加了active类名,才显示。 .carousel-item { display: none;}.carousel-item.active { display: block; left: 0;}再而后别离是向左还是向右,这里咱们是通过增加类名的形式来实现滑动,所以咱们在这里额定为轮播元素减少了left和right类名,如下: ...

October 21, 2022 · 4 min · jiezi

关于javascript:Dvajs-快速上手指南

先说些废话最近在开发React技术栈的我的项目产品,对于数据状态的治理应用了Dva.js,作为一个资深的ow玩家,我看到这个名字第一反馈就是————这不是ow里的一个女英雄吗?仔细阅读了官网文档之后,发现开发者还真是因为这个角色取得灵感,来命名这个数据状态治理插件,果然开发大佬都是工作和休闲两不误~ 学过React的同学都晓得它的技术栈十分多且杂,所以每当你应用React的时候都须要引入很多的模块,那么Dva就是把这些用到的模块集成在一起,比方一些须要引入的依赖react-saga/react-loger、必写的ReactDOM.render、provider、connect包裹等都省去不写,造成肯定的架构标准,大大提高咱们的开发效率 明天,就来写一份文档,帮忙后续应用Dva的开发者更好得在理论我的项目中(PS:须要是以UMI为根底框架,纯Dva来构建我的项目能够间接看文章结尾的参考文档列表)上手应用 什么是DvaDva首先是一个基于redux和redux-saga的数据流计划,而后为了简化开发体验,Dva还额定内置了react-router和fetch,所以也能够了解为一个轻量级的利用框架。 在我目前的我的项目中,更多是应用数据状态治理的性能,他在我司的fish框架中做了内嵌,在支流的React开发框架UMI中也做了内嵌适配,应用起来十分不便疾速。 Dva设计的目标就是简化元素,升高难度,让你不必管他怎么实现的,咱们依照默认的这个规定去写就能够 数据流向数据的扭转产生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会扭转数据的时候能够通过dispatch发动一个action,如果是同步行为会间接通过reducers扭转states,如果是异步行为(副作用)会先触发effects而后流向reducers最终扭转states 分层开发无论是Vue还是React开发,理论的大型利用肯定有严格的分层开发标准,确保后续开发的可维护性,次要的分层构造有以下几点: Page 负责与用户间接打交道:渲染页面,承受用户的操作输出,侧重于展现型交互性逻辑,这里须要理解无状态组件Model 负责解决业务逻辑,为 Page 做数据、状态的读写、变换、暂存等,Dva中model就是做了这一层的操作Service 负责与 HTTP 接口对接,进行纯正的数据读写根底概念namespace model的命名空间,同时也是他在全局state上的属性只能用字符串,不反对通过.的形式创立多层命名空间,相当于这个model的key在组件外面,通过connect这个key将想要引入的model退出 import { connect } from 'dva';export default connect(({ namespaceValue }) => ({ ...namespaceValue }))(DvaCompoent);state 示意model的状态数据操作的时候每次都要当作不可变数据immutable data来看待,保障每次都是全新对象,没有援用关系reducer 必须是纯函数,有固定输入输出,次要目标是批改本身state承受两个参数:之前曾经累积运算的后果和以后要被累积的值,返回的是一个新的累积后果,该函数把一个汇合归并成一个单值须要留神的是同样的输出必然失去同样的输入,它们不应该产生任何副作用effect。并且,每一次的计算都应该应用immutable dataeffect 次要用于异步申请,接口调用之类的effect被称为副作用,在咱们的利用中,最常见的就是异步操作它来自于函数编程的概念,之所以叫副作用是因为它使得咱们的函数变得不纯,同样的输出不肯定取得同样的输入subscription subscription语义是订阅,用于订阅一个数据源,而后依据条件dispatch须要的action数据源能够是以后的工夫、服务器的websocket连贯、keyboard输出、geolocation变动、history路由变动等等外部定义的函数都会被被执行,执行之后作为监听来处理事务dispatch dispatch是一个用于触发action的函数,action是扭转state的惟一路径,然而它只形容了一个行为,而dipatch能够看作是触发这个行为的形式,reducer则是形容如何扭转数据的在Dva中,connect model的组件通过props能够拜访到dispatch,能够调用model中的reducer或者effects import { connect } from 'dva';const testCom = props => {const { dispatch } = props;const changeValue = (id, val) => { // 调用reducer,个别是同步批改state中的值 dispatch({ type: 'dva/save', payload: { param: val }, }); // 调用effect,个别是发送后盾申请 dispatch({ type: 'dva/queryValue', payload: { id: id }, }); };return( <div>'hello world'</div>)}export default connect(({ dva }) => ({ ...dva }))(testCom);Model中的Effects函数解析须要留神的是:Effects外面的函数都是Generator函数 ...

October 20, 2022 · 3 min · jiezi

关于javascript:JavaScript中对象的属性名

JavaScript标识符什么是一个非法的JavaScript标识符,规定如下: 只能蕴含 英文字母、数字、下划线"-"或美元符号"$"。不能以数字结尾。对象的属性名的命名规定对象的属性名能够是任意字符串,包含空串。但如果对象的属性名既不是非法的javascript标识符、也不是数字,那么它必须用单引号或双引号包裹。举例: // 能够var person = {name: 'Sherwei'};// 能够(但不倡议这么写)var person = {5: '不太失常的属性'};// 不能够var person = {!: '不失常的属性'};// 能够(但不倡议这么写)var person = {"!": '不失常的属性'};如果对象的属性名不是非法的javascript标识符,那么不能用点语法.来拜访该属性,必须用方括号+引号[""]的形式来拜访。举例: var person = { name: "Sherwei", 7: "不太失常的属性", "!": "不失常的属性"};// 2种形式均可console.log(person.name);console.log(person["name"]);// 不能够console.log(person.7);// 不能够console.log(person[7]);// 能够console.log(person["7"]);// 不能够console.log(person.!);// 能够console.log(person["!"]);就算以数字作为对象的属性名,那么实质上也还是字符串。举例如下: // 不能够,因为7和字符串"7"其实是一样的名称,都算作字符串,所以重名了var person = { 7: "不太失常的属性", "7": "更不失常的属性"};

October 20, 2022 · 1 min · jiezi

关于javascript:Day98100PostCSS-plugin-postcssflexbugsfixes-requires-PostCSS8

(一)需要react的我的项目,在装置胜利后,页面报错。找教程没有找到适合的。 PostCSS plugin postcss-flexbugs-fixes requires PostCSS 8.(二)剖析须要降级PostCSS的版本到8 (三)解决# 卸载npm uninstall tailwindcss postcss autoprefixer# 重新安装npm install [email protected]:@tailwindcss/postcss7-compat [email protected]^8 [email protected]^9运行起来失效了。 参考链接https://www.tailwindcss.cn/do...

October 20, 2022 · 1 min · jiezi

关于javascript:VS-Code-For-Web-深入浅出-进程间通信篇

在上一篇中,咱们一起剖析了 VS Code 整体的代码架构,理解了 VS Code 是由前后端拆散的形式开发的。且无论前端是基于 electron 还是 web,后端是本地还是云端,其调用形式并无不同。 这样的架构下,前后端的通信形式是如何实现的呢?本篇咱们将一起来探索 VS Code For Web 的过程间通信形式。 过程通信与调用形式过程间通信协议对于多过程架构的我的项目,过程之间的通信会通过过程间调用 (Inter Process Calling, IPC)。VSCode 中本人设计了专门的 IPC 模块来实现通信。代码位于 src/vs/base/parts/ipc。 export const enum RequestType { Promise = 100, PromiseCancel = 101, EventListen = 102, EventDispose = 103}从 enum type 能够看出,VSCode 的 IPC 模块同时反对两种调用形式,一种是基于 Promise 的调用实现, 另一种是通过 Event Emitter/Listener 的那一套事件监听机制来实现。 以事件监听机制为例,VSCode 中采纳 vscode-jsonrpc 这个包来封装实现,调用形式如下: import * as cp from 'child_process';import * as rpc from 'vscode-jsonrpc/node';let childProcess = cp.spawn(...);// Use stdin and stdout for communication:let connection = rpc.createMessageConnection( new rpc.StreamMessageReader(childProcess.stdout), new rpc.StreamMessageWriter(childProcess.stdin));let notification = new rpc.NotificationType<string, void>('testNotification');connection.listen();connection.sendNotification(notification, 'Hello World');服务端调用也采纳相似的包装: ...

October 20, 2022 · 2 min · jiezi

关于javascript:从零开始实现一个Promise

1.Promise产生背景及标准家喻户晓,Promise是ES6引入的新个性,旨在解决回调天堂。上面是一个简略的例子:管制接口调用程序: apiA-->apiB-->apiC。简单的业务,开发人员会裂开。后生在此向老前辈致敬。 // 回调天堂apiA({ handleSuccess(resA){ apiB({ handleSuccess(resB){ apiC({ handleSuccess(resC){ } }) } }) }})因而Promise/A+标准应运而生,ES6的Promise就是遵循标准开发进去的。 2. 同步Promise浏览标准可得上面几点根本要求: Promise存在三个状态:pending(期待态)、fulfilled(胜利态)、rejected(失败态)pending为初始态,并能够转化为fulfilled和rejected胜利时,不可转为其余状态,且必须有一个不可扭转的值(value)失败时,不可转为其余状态,且必须有一个不可扭转的起因(reason)new Promise(executor=(resolve,reject)=>{resolve(value)}),resolve(value)将状态置为 fulfillednew Promise(executor=(resolve,reject)=>{reject(reson)}),reject(reson)将状态置为 rejected若是executor运行异样执行reject()thenable:then(onFulfilled, onRejected) onFulfilled:status为fulfilled,执行onFulfilled,传入valueonRejected:status为rejected,执行onRejected,传入reason// 1.Promise存在三个状态:pending(期待态)、fulfilled(胜利态)、rejected(失败态)const STATUS_PENDING = 'pending'const STATUS_FULFILLED = 'fulfilled'const STATUS_REJECTED = 'rejected'class myPromise { constructor(executor) { // pending为初始态,并能够转化为fulfilled和rejected this.status = STATUS_PENDING this.value = '' // 3 this.reason = '' // 4 let resolve = value => { // 5. if (this.status === STATUS_PENDING) { this.status = STATUS_FULFILLED this.value = value } } let reject = reason => { //6. if (this.status === STATUS_PENDING) { this.status = STATUS_REJECTED this.reason = reason } } // 7. try { executor(resolve, reject); } catch (err) { reject(err); } } // 8. then(onFulfilled = () => {}, onRejected = () => {}) { // 8.1 if (this.status === STATUS_FULFILLED) { onFulfilled(this.value) } // 8.2 if (this.status === STATUS_REJECTED) { onRejected(this.reason) } }}new myPromise(resolve => { console.log('before resolve') resolve(1)}).then(res => { console.log(res)})new myPromise((resolve, reject) => { console.log('before reject') reject('reject error')}).then(res => { console.log(res)}, error => { console.log(error)})3. 异步Promisenew myPromise(resolve => { console.log('before resolve') setTimeout(()=>{ resolve(1) },1000)}).then(res => { console.log(res)})promise的状态只能在resolve或者reject的时候扭转,同步代码执行到then回调的时候promise的状态还是pending,明细不合乎咱们的冀望。 ...

October 19, 2022 · 5 min · jiezi

关于javascript:社招前端经典手写面试题合集

实现千位分隔符// 保留三位小数parseToMoney(1234.56); // return '1,234.56'parseToMoney(123456789); // return '123,456,789'parseToMoney(1087654.321); // return '1,087,654.321'function parseToMoney(num) { num = parseFloat(num.toFixed(3)); let [integer, decimal] = String.prototype.split.call(num, '.'); integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,'); return integer + '.' + (decimal ? decimal : '');}正则表达式(使用了正则的前向申明和反前向申明): function parseToMoney(str){ // 仅仅对地位进行匹配 let re = /(?=(?!\b)(\d{3})+$)/g; return str.replace(re,','); }实现filter办法Array.prototype.myFilter=function(callback, context=window){ let len = this.length newArr = [], i=0 for(; i < len; i++){ if(callback.apply(context, [this[i], i , this])){ newArr.push(this[i]); } } return newArr;}实现节流函数(throttle)节流函数原理:指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的工夫才会执行回调函数。总结起来就是: 事件,依照一段时间的距离来进行触发 。 ...

October 19, 2022 · 17 min · jiezi

关于javascript:从零手写reactrouter

蛮多同学可能会感觉react-router很简单, 说用都还没用明确, 还从0实现一个react-router, 其实router并不简单哈, 甚至说你看了这篇博客当前, 你都会感觉router的外围原理也就那么回事至于react-router帮忙咱们实现了什么货色我就不过多论述了, 这个间接移步官网文档, 咱们上面间接聊实现 另外: react-router源码有依赖两个库path-to-regexp和history, 所以我这里也就间接引入这两个库了,尽管上面我都会讲到根本应用, 然而同学有工夫的话还是能够浏览以下官网文档 还有一个须要留神的点是: 上面我书写的router原理都是应用hooks + 函数组件来书写的, 而官网是应用类组件书写的, 所以如果你对hooks还不是很明确的话, 得去补一下这方面的常识, 为什么要抉择hooks, 因为当初绝大多数大厂在react上根本都在鼎力举荐应用hook, 所以咱们得跟上时代不是, 而且我着重和大家聊的也是原理, 而不是跟官网截然不同的源码, 如果要1比1的复刻源码不带本人的了解的话, 那你去看官网的源码就行了, 何必看这篇博文了 在本栏博客中, 咱们会聊聊以下内容: 封装本人的生成match对象办法history库的应用Router和BrowserRouter的实现Route组件的实现Switch和Redirect的实现withRouter的实现Link和NavLink实现聚合api封装本人的生成match对象办法在封装之前, 我想跟大家先分享path-to-regexp这个库 为什么要先聊这个库哈, 次要起因是因为react-router中用到了这个库, 我看了一下其实咱们也没必要本人再去实现一个这个库(为什么没必要呢,倒并不是因为react-router没有实现咱们就不实现, 而是因为这个库实现的性能非常简单, 然而细节十分繁琐, 有十分多的因素须要去思考到我感觉没必要), 这个库做的事件非常简单: 将一个字符串变成一个正则表达式咱们晓得, react-router的大抵原理就是依据门路的不同从而渲染不同的页面, 那么这个过程其实也就是门路A匹配页面B的过程, 所以咱们之前会写这样的代码 <Route path="/news/:id" component={News} /> // 如果门路匹配上了/news/:id这样的门路, 则渲染News组件那么react-router他是怎么去判断浏览器地址栏的门路和这个Route组件中的path属性匹配上的? path填写的如果是/news/:id这样的门路, 那么/news/123 /news/321这种都可能被react-router匹配上 咱们可能想到的办法是不是大略能够如下: 将所有的path属性全副转换为正则表达式(比方/news/:id转换为/^\/news(?:\/([^\/#\?]+?))[\/#\?]?$/i), 而后将地址栏的path值取出来跟该正则表达式进行匹配, 匹配上了就要渲染相应的路由, 匹配不上就渲染其余的逻辑path-to-regexp就是做这个事件的, 他把咱们给他的门路字符串转换为正则表达式, 供咱们匹配 装置: yarn add path-to-regexp -S// 咱们能够来轻易试试这个库import { pathToRegexp } from "path-to-regexp";const keys = [];// pathToRegexp(path, keys?, options?)// path: 就是咱们要匹配的门路规定// keys: 如果你传递了, 当他匹配上当前, 会把绝对应的参数key传递到keys数组中// options: 给path门路规定的一些附加规定, 比方sensitive大小写敏感之类的const result = pathToRegexp("/news/:id", keys);console.log("result", result);console.log(result.exec("/news/123")); // 输入 ["/news/123", "123", index: 0, input: "/news/123", groups: undefined]console.log(result.exec("/news/details/123")); // 输入nullconsole.log(keys); // 输入一个数组, 数组的有一个对象{modifier: " name: "id", pattern: "[^\/#\?]+?", prefix: "/", suffix: ""}当然, 这个库还有很多玩法, 他也不是专门为react-router实现的, 只是刚好被react-router拿过去用了, 对这个库有趣味的同学能够去看看他的文档 ...

October 19, 2022 · 8 min · jiezi

关于javascript:js异步编程面试题你能答上来几道

在上一节中咱们理解了常见的es6语法的一些知识点。这一章节咱们将会学习异步编程这一块内容,鉴于异步编程是js中至关重要的内容,所以咱们将会用三个章节来学习异步编程波及到的重点和难点,同时这一块内容也是面试常考范畴。 并发(concurrency)和并行(parallelism)的区别面试题 并发和并行的区别? 异步和这一大节的知识点其实并不是一个概念,然而这个两个名词的确是很多人混同的知识点,其实混同的起因可能只是两个名词在中文的类似,在英文上来说齐全是不同的单词。 并发是宏观概念,我别离有工作A和工作B,在一段时间内通过工作间的切换实现了这两个工作,这种状况就能够成为并发。 并行是宏观概念,假如cpu中存在两个外围,那么我就能够同时实现工作A,B。同时实现多个工作的状况就能够称之为并行。 回调函数(callback)面试题: 什么是回调函数?回调函数有什么毛病?如何解决回调天堂问题? 回调函数应该是大家常常应用到的,以下代码是回调函数的例子: ajax(url,()=>{ //解决逻辑})然而回调函数有个致命的弱点,就是容易写出回调天堂,假如多个申请存在依赖性,你可能就会写出如下代码: ajax(url,()=>{ ajax(url,()=>{})})以上代码看起来不利于浏览和保护,当然你可能会说解决这个问题还不简略,把函数离开来写不就得了 function firstAjax(){ ajax(url1,()=>{ secondAjax() })}function second(){ ajax(url2,()=>{ })}ajax(url,()=>{ firstAjax()})以上代码看上去有利于浏览了,然而还是没有解决基本问题 回调天堂得基本问题是: 嵌套函数存在耦合性,一旦有改变,就会牵一发而动全身嵌套函数一多就很难处理错误当然,回调函数还存在着别的毛病,比方不能应用try catch捕捉谬误,不能间接return。 Generator面试题:你了解的generator是什么? Generator算是es6中难了解的概念之一了,Generator最大的特点就是能够管制函数的执行。在这一大节中咱们不会讲什么是Generator,而把重点放在Generator的一些容易困惑的中央。 function *foo(){ let y = 2*(yield(x+1)) let z = yield(y/3) return (x+y+z)}let it = foo(5)console.log(it.next())console.log(it.next(12))console.log(it.next(13))你兴许会纳闷为什么会产生与你料想不同的值,接下来就让我为你逐行代码剖析起因 首先 Generator 函数调用和一般函数不同,它会返回一个迭代器当执行第一次 next 时,传参会被疏忽,并且函数暂停在 yield (x + 1) 处,所以返回 5 + 1 = 6当执行第二次 next 时,传入的参数等于上一个 yield 的返回值,如果你不传参,yield 永远返回 undefined。此时 let y = 2 12,所以第二个 yield 等于 2 12 / 3 = 8当执行第三次 next 时,传入的参数会传递给 z,所以 z = 13, x = 5, y = 24,相加等于 42Generator 函数个别见到的不多,其实也于他有点绕有关系,并且个别会配合 co 库去应用。当然,咱们能够通过 Generator 函数解决回调天堂的问题,能够把之前的回调天堂例子改写为如下代码: ...

October 19, 2022 · 3 min · jiezi

关于javascript:前端关于面试你可能需要收集的面试题

组件之间的传值有几种形式1、父传子2、子传父3、eventbus4、ref/$refs5、$parent/$children6、$attrs/$listeners7、依赖注入(provide/inject)对this对象的了解this 是执行上下文中的一个属性,它指向最初一次调用这个办法的对象。在理论开发中,this 的指向能够通过四种调用模式来判断。 第一种是函数调用模式,当一个函数不是一个对象的属性时,间接作为函数来调用时,this 指向全局对象。第二种是办法调用模式,如果一个函数作为一个对象的办法来调用时,this 指向这个对象。第三种是结构器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。第四种是 apply 、 call 和 bind 调用模式,这三个办法都能够显示的指定调用函数的 this 指向。其中 apply 办法接管两个参数:一个是 this 绑定的对象,一个是参数数组。call 办法接管的参数,第一个是 this 绑定的对象,前面的其余参数是传入函数执行的参数。也就是说,在应用 call() 办法时,传递给函数的参数必须一一列举进去。bind 办法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了应用 new 时会被扭转,其余状况下都不会扭转。这四种形式,应用结构器调用模式的优先级最高,而后是 apply、call 和 bind 调用模式,而后是办法调用模式,而后是函数调用模式。 说一下HTTP 3.0HTTP/3基于UDP协定实现了相似于TCP的多路复用数据流、传输可靠性等性能,这套性能被称为QUIC协定。 流量管制、传输可靠性性能:QUIC在UDP的根底上减少了一层来保障数据传输可靠性,它提供了数据包重传、拥塞管制、以及其余一些TCP中的个性。集成TLS加密性能:目前QUIC应用TLS1.3,缩小了握手所破费的RTT数。多路复用:同一物理连贯上能够有多个独立的逻辑数据流,实现了数据流的独自传输,解决了TCP的队头阻塞问题。疾速握手:因为基于UDP,能够实现应用0 ~ 1个RTT来建设连贯。判断数组的形式有哪些通过Object.prototype.toString.call()做判断Object.prototype.toString.call(obj).slice(8,-1) === 'Array';通过原型链做判断obj.__proto__ === Array.prototype;通过ES6的Array.isArray()做判断Array.isArrray(obj);通过instanceof做判断obj instanceof Array通过Array.prototype.isPrototypeOfArray.prototype.isPrototypeOf(obj)template预编译是什么对于 Vue 组件来说,模板编译只会在组件实例化的时候编译一次,生成渲染函数之后在也不会进行编译。因而,编译对组件的 runtime 是一种性能损耗。 而模板编译的目标仅仅是将template转化为render function,这个过程,正好能够在我的项目构建的过程中实现,这样能够让理论组件在 runtime 时间接跳过模板渲染,进而晋升性能,这个在我的项目构建的编译template的过程,就是预编译。闭包闭包其实就是一个能够拜访其余函数外部变量的函数。创立闭包的最常见的形式就是在一个函数内创立另一个函数,创立的函数能够 拜访到以后函数的局部变量。 因为通常状况下,函数外部变量是无奈在内部拜访的(即全局变量和局部变量的区别),因而应用闭包的作用,就具备实现了能在内部拜访某个函数外部变量的性能,让这些外部变量的值始终能够保留在内存中。上面咱们通过代码先来看一个简略的例子 function fun1() { var a = 1; return function(){ console.log(a); };}fun1();var result = fun1();result(); // 1// 联合闭包的概念,咱们把这段代码放到控制台执行一下,就能够发现最初输入的后果是 1(即 a 变量的值)。那么能够很分明地发现,a 变量作为一个 fun1 函数的外部变量,失常状况下作为函数内的局部变量,是无奈被内部拜访到的。然而通过闭包,咱们最初还是能够拿到 a 变量的值闭包有两个罕用的用处 ...

October 19, 2022 · 22 min · jiezi

关于javascript:promise执行顺序面试题令我头秃你能作对几道

阐明最近在温习 Promise 的常识,所以就做了一些题,这里挑出几道题,大家一起看看吧。 题目一const promise = new Promise((resolve, reject) => { console.log(1); resolve(); console.log(2);})promise.then(() => { console.log(3);})console.log(4);解析首先 Promise 新建后立刻执行,所以会先输入 1,2,而 Promise.then() 外部的代码在 当次 事件循环的 结尾 立即执行 ,所以会持续输入4,最初输入3。 答案1243题目二const promise = new Promise((resolve, reject) => { resolve('success1'); reject('error'); resolve('success2');});promise.then((res) => { console.log('then:', res);}).catch((err) => { console.log('catch:', err);})解析resolve 函数将 Promise 对象的状态从“未实现”变为“胜利”(即从 pending 变为 resolved),在异步操作胜利时调用,并将异步操作的后果,作为参数传递进来; reject 函数将 Promise 对象的状态从“未实现”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的谬误,作为参数传递进来。 而一旦状态扭转,就不会再变。所以 代码中的reject('error'); 不会有作用。 Promise 只能 resolve 一次,剩下的调用都会被疏忽。所以 第二次的 resolve('success2'); 也不会有作用。 ...

October 19, 2022 · 4 min · jiezi

关于javascript:记一次奇葩的js解密经历

前言家里亲戚是老师,他们最近在搞培训流动,须要去看完指定的培训视频。于是找到我,让我帮忙做一个自动化培训的程序,心愿通过程序实现登录,批量导入账号主动学习等性能。 指标网站aHR0cHM6Ly9pcHgueWFueGl1LmNvbS8= 0x1.开始剖析关上的页面,间接就是显示登录页。 咱们间接随便输个手机号和明码,开始抓登录接口~ https://ipx-api.yanxiu.com/user-hub/security/loginAuthorization: Basic c2FucmVuLXRlYWNoZXItcGM6MjZlZEV0WmlObTJ3NzZwbFhTsrxUserInfo: distinctId=183ea7dcf872ef-060972e1be9a65-26021c51-2073600-183ea7dcf88347X-DT-accessTokenX-DT-clientId: ums-teacher-pcX-DT-Passport初步判断申请头中这五个参数有可能是加密参数。 间接用F12开发工具去搜Authorization var s = { Authorization: "Basic c2FucmVuLXRlYWNoZXItcGM6MjZlZEV0WmlObTJ3NzZwbFhT", srxUserInfo: i.a.get("srxUserInfo") || "distinctId=" + d()}比拟震惊啊·····Authorization如同是硬编码在前端js中的,这网站前端用的vue。 srxUserInfo 通过剖析,是存在localStorage中的,在登录申请的时候,该参数曾经生成了,只是从localStorage中取出而已,那么它是什么时候存进去的呢? 通过我的剖析,distinctId的生成规定在这,第一次生成会存到localStorage中,后边每次申请就从localStorage中取 var n = function() { for (var e = 1 * new Date, t = 0; e == 1 * new Date; ) t++; return e.toString(16) + t.toString(16)}function generate() { var e = String(screen.height * screen.width); e = e && /\d{5,}/.test(e) ? e.toString(16) : String(31242 * Math.random()).replace(".", "").slice(0, 8); var t = n() + "-" + Math.random().toString(16).replace(".", "") + "-" + function(e) { var t, a, n = navigator.userAgent, i = [], o = 0; function r(e, t) { var a, n = 0; for (a = 0; a < t.length; a++) n |= i[a] << 8 * a; return e ^ n } for (t = 0; t < n.length; t++) a = n.charCodeAt(t), i.unshift(255 & a), i.length >= 4 && (o = r(o, i), i = []); return i.length > 0 && (o = r(o, i)), o.toString(16) }() + "-" + e + "-" + n(); return t || (String(Math.random()) + String(Math.random()) + String(Math.random())).slice(2, 15)}console.log(generate());开始测试登录申请这样,须要的参数咱们就都筹备齐全了,拿出我的神器ApiPost,如下 ...

October 18, 2022 · 1 min · jiezi

关于javascript:前端首屏渲染时间的极致优化

咱们晓得,用户体验是 Web 产品最为重要的局部。尽可能减少首屏加载工夫,更为流畅地展现用户所需要的内容,会是用户是否留存的关键因素。 而随着古代 Web 业务可供用户的交互行为越来越多,前端我的项目的复杂度越来越高,每个页面的渲染工夫也必然越来越长,这就导致了用户的体验不佳,用户的操作变慢。 为此,前端工程师们在首屏申请的各个阶段中继续钻研,一直探索如何将首次页面渲染的工夫缩小到更小,力求提供更为优良的产品体验。 CSR(Client Side Render) 浏览器渲染是最简略,最合乎 Web 利用设计思路的渲染形式。 所谓浏览器渲染,就是将利用所需的页面展现、前端逻辑、接口申请全都在用户的浏览器中执行。它很好的实现了前后端的解耦,让前端开发更为独立,也让后盾实现更为简略。 同时,为了缓解用户的期待焦虑,咱们能够用 loading 态,或者骨架屏,进一步晋升异步申请接口时的用户体验。 不过,随着业务复杂程度进步,浏览器渲染的开销也会变大,咱们无法控制用户侧应用的机器性能,很多时候,用户应用的机器性能甚至不足以满足利用的需要,造成卡顿,甚至解体,这一点在挪动端上尤甚。 而浏览器渲染因为前端的动态性过高,也会带来 SEO 不佳的问题。 SSR(Server Side Render) 服务端渲染的呈现工夫实际上是要比浏览器渲染要更早的。在 Web 利用倒退的晚期,所有的 ASP、JSP 等模板引擎构建的前端页面实际上就是服务端渲染的后果。而此时的服务端渲染无奈进行前后端职责的解耦,因而逐渐被浏览器渲染淘汰。 但在解决首屏体验的问题上,服务端渲染有着独到的劣势。它能提前再服务端中实现页面模板的数据填充,从而一次性返回残缺的首屏内容,从而面对 SEO 的爬取时能获取到更多无效的要害信息。 此外,因为其能疾速直出首页的实在数据,体验往往比 loading 态更佳,在 TTI 的体现上更为杰出。 然而,服务端渲染也有其本身的局限性。因为从实质上来说,SSR 服务无奈齐全与前端页面解耦开来。因而市面上较齐备的 SSR 解决方案都只解决首屏的服务端渲染,并采纳同构的形式,减少一层 node 中间层的形式来解决前端与 SSR 服务的更新同步问题,并与后端开发我的项目解耦。 但这无疑减少了我的项目的复杂度,并且随着业务的复杂程度变高,服务端渲染往往须要调起多个接口去申请数据并填充页面,这样可能会导致在 TTFB 上有肯定劣势。 当然,最重要的是,服务端渲染对于服务器的负载要求是很高的。 上图是援用的字节的某我的项目的 SSR 服务的单机 QPS 承载体现。咱们能够看出,对于一个高访问量的网页利用来说,提供一个较为简单的 SSR 服务的老本是相当高的,须要破费大量的金钱来堆机器。 因而,从降本增效的角度思考,咱们须要评估 SSR 带来的 ROI 是否合乎预期。 NSR(Native Side Render)在挪动互联网的浪潮下,挪动端机能飞速晋升,那么 Web 利用是否能搭上这一班车,将 Native 的性能利用起来,晋升页面渲染性能呢?答案是必定的,这就须要介绍到 NSR 了。 ...

October 18, 2022 · 1 min · jiezi

关于javascript:从零到一手写迷你版Vue

Vue响应式设计思路Vue响应式次要蕴含: 数据响应式监听数据变动,并在视图中更新Vue2应用Object.defineProperty实现数据劫持Vu3应用Proxy实现数据劫持模板引擎提供形容视图的模板语法插值表达式{{}}指令 v-bind, v-on, v-model, v-for,v-if渲染将模板转换为html解析模板,生成vdom,把vdom渲染为一般dom数据响应式原理 数据变动时能自动更新视图,就是数据响应式Vue2应用Object.defineProperty实现数据变动的检测 原理解析new Vue()⾸先执⾏初始化,对data执⾏响应化解决,这个过程发⽣在Observer中同时对模板执⾏编译,找到其中动静绑定的数据,从data中获取并初始化视图,这个过程发⽣在 Compile中同时定义⼀个更新函数和Watcher实例,未来对应数据变动时,Watcher会调⽤更新函数因为data的某个key在⼀个视图中可能呈现屡次,所以每个key都须要⼀个管家Dep来治理多个 Watcher未来data中数据⼀旦发⽣变动,会⾸先找到对应的Dep,告诉所有Watcher执⾏更新函数 一些要害类阐明CVue:自定义Vue类 Observer:执⾏数据响应化(分辨数据是对象还是数组) Compile:编译模板,初始化视图,收集依赖(更新函数、 watcher创立) Watcher:执⾏更新函数(更新dom) Dep:治理多个Watcher实例,批量更新 波及要害办法阐明observe: 遍历vm.data的所有属性,对其所有属性做响应式,会做繁难判断,创立Observer实例进行真正响应式解决 html页面<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>cvue</title> <script src="./cvue.js"></script></head><body> <div id="app"> <p>{{ count }}</p> </div> <script> const app = new CVue({ el: '#app', data: { count: 0 } }) setInterval(() => { app.count +=1 }, 1000); </script></body></html>CVue创立根本CVue构造函数:执⾏初始化,对data执⾏响应化解决// 自定义Vue类class CVue { constructor(options) { this.$options = options this.$data = options.data // 响应化解决 observe(this.$data) }}// 数据响应式, 批改对象的getter,setterfunction defineReactive(obj, key, val) { // 递归解决,解决val是嵌套对象状况 observe(val) Object.defineProperty(obj, key, { get() { return val }, set(newVal) { if(val !== newVal) { console.log(`set ${key}:${newVal}, old is ${val}`) val = newVal // 持续进行响应式解决,解决newVal是对象状况 observe(val) } } })}// 遍历obj,对其所有属性做响应式function observe(obj) { // 只解决对象类型的 if(typeof obj !== 'object' || obj == null) { return } // 实例化Observe实例 new Observe(obj)}// 依据传入value的类型做相应的响应式解决class Observe { constructor(obj) { if(Array.isArray(obj)) { // TODO } else { // 对象 this.walk(obj) } } walk(obj) { // 遍历obj所有属性,调用defineReactive进行响应化 Object.keys(obj).forEach(key => defineReactive(obj, key, obj[key])) }}为vm.$data做代理不便实例上设置和获取数据 ...

October 18, 2022 · 5 min · jiezi

关于javascript:一步步实现ReactHooks核心原理

React Hooks曾经推出一段时间,大家应该比拟相熟,或者多多少少在我的项目中用过。写这篇文章简略剖析一下Hooks的原理,并带大家实现一个简易版的Hooks。 这篇写的比拟细,相干的知识点都会解释,给大家刷新一下记忆。 HooksHooks是React 16.8推出的新性能。以这种更简略的形式进行逻辑复用。之前函数组件被认为是无状态的。然而通过Hooks,函数组件也能够有状态,以及类组件的生命周期办法。 useState用法示例: import React, { useState } from 'react';function Example() { // count是组件的状态 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}闭包开始之前,咱们来简略回顾一下闭包的概念,因为Hooks的实现是高度依赖闭包的。 闭包(Closure),Kyle Simpson在《你不晓得的Javascript》中总结闭包是: Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.闭包就是,函数能够拜访到它所在的词法作用域,即便是在定义以外的地位调用。 闭包的一个重要利用就是,实现外部变量/公有数据。 var counter = 0;// 给计数器加1function add() { counter += 1;}// 调用 add() 3次add(); // 1add(); // 2counter = 1000;add(); // 1003这里因为counter不是外部变量,所以谁都能批改它的值。咱们不想让人随便批改counter怎么办?这时候就能够用闭包: ...

October 18, 2022 · 4 min · jiezi

关于javascript:美团前端手写面试题总结

Promise// 模仿实现Promise// Promise利用三大伎俩解决回调天堂:// 1. 回调函数提早绑定// 2. 返回值穿透// 3. 谬误冒泡// 定义三种状态const PENDING = 'PENDING'; // 进行中const FULFILLED = 'FULFILLED'; // 已胜利const REJECTED = 'REJECTED'; // 已失败class Promise { constructor(exector) { // 初始化状态 this.status = PENDING; // 将胜利、失败后果放在this上,便于then、catch拜访 this.value = undefined; this.reason = undefined; // 胜利态回调函数队列 this.onFulfilledCallbacks = []; // 失败态回调函数队列 this.onRejectedCallbacks = []; const resolve = value => { // 只有进行中状态能力更改状态 if (this.status === PENDING) { this.status = FULFILLED; this.value = value; // 胜利态函数顺次执行 this.onFulfilledCallbacks.forEach(fn => fn(this.value)); } } const reject = reason => { // 只有进行中状态能力更改状态 if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // 失败态函数顺次执行 this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } try { // 立刻执行executor // 把外部的resolve和reject传入executor,用户可调用resolve和reject exector(resolve, reject); } catch(e) { // executor执行出错,将谬误内容reject抛出去 reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected : reason => { throw new Error(reason instanceof Error ? reason.message : reason) } // 保留this const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { // try捕捉谬误 try { // 模仿微工作 setTimeout(() => { const result = onFulfilled(self.value); // 分两种状况: // 1. 回调函数返回值是Promise,执行then操作 // 2. 如果不是Promise,调用新Promise的resolve函数 result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { // 以下同理 try { setTimeout(() => { const result = onRejected(self.reason); // 不同点:此时是reject result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }); } catch(e) { reject(e); } } else if (self.status === REJECTED) { try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } } }); } catch(onRejected) { return this.then(null, onRejected); } static resolve(value) { if (value instanceof Promise) { // 如果是Promise实例,间接返回 return value; } else { // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED return new Promise((resolve, reject) => resolve(value)); } } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promiseArr) { const len = promiseArr.length; const values = new Array(len); // 记录曾经胜利执行的promise个数 let count = 0; return new Promise((resolve, reject) => { for (let i = 0; i < len; i++) { // Promise.resolve()解决,确保每一个都是promise实例 Promise.resolve(promiseArr[i]).then( val => { values[i] = val; count++; // 如果全副执行完,返回promise的状态就能够扭转了 if (count === len) resolve(values); }, err => reject(err), ); } }) } static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) }}查找字符串中呈现最多的字符和个数例: abbcccddddd -> 字符最多的是d,呈现了5次 ...

October 18, 2022 · 7 min · jiezi

关于javascript:一步步实现ReactHooks核心原理

React Hooks曾经推出一段时间,大家应该比拟相熟,或者多多少少在我的项目中用过。写这篇文章简略剖析一下Hooks的原理,并带大家实现一个简易版的Hooks。 这篇写的比拟细,相干的知识点都会解释,给大家刷新一下记忆。 HooksHooks是React 16.8推出的新性能。以这种更简略的形式进行逻辑复用。之前函数组件被认为是无状态的。然而通过Hooks,函数组件也能够有状态,以及类组件的生命周期办法。 useState用法示例: import React, { useState } from 'react';function Example() { // count是组件的状态 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}闭包开始之前,咱们来简略回顾一下闭包的概念,因为Hooks的实现是高度依赖闭包的。 闭包(Closure),Kyle Simpson在《你不晓得的Javascript》中总结闭包是: Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.闭包就是,函数能够拜访到它所在的词法作用域,即便是在定义以外的地位调用。 闭包的一个重要利用就是,实现外部变量/公有数据。 var counter = 0;// 给计数器加1function add() { counter += 1;}// 调用 add() 3次add(); // 1add(); // 2counter = 1000;add(); // 1003这里因为counter不是外部变量,所以谁都能批改它的值。咱们不想让人随便批改counter怎么办?这时候就能够用闭包: ...

October 18, 2022 · 4 min · jiezi

关于javascript:JS模块化CJSAMDCMDES6前端面试知识点查漏补缺

本文从以工夫为轴从以下几个方面进行总结JS模块化。从无模块化 => IIFE => CJS => AMD => CMD => ES6 => webpack这几个阶段进行剖析。历史幼年期:无模块化形式须要在页面中加载不同的js,用于动画,组件,格式化多种js文件被分在了不同的文件中不同的文件被同一个模板所援用<script src="jquery.js"></script><script src="main.js"></script><script src="dep1.js"></script>此处写法文件拆分是最根底的模块化(第一步) * 面试中的诘问script标签的参数:async & defer <script src="jquery.js" async></script>总结三种加载一般加载:解析到立刻阻塞,立即下载执行以后scriptdefer加载:解析到标签开始异步加载,在后盾下载加载js,解析实现之后才会去加载执行js中的内容,不阻塞渲染async加载:(立刻执行)解析到标签开始异步加载,下载实现后开始执行并阻塞渲染,执行实现后持续渲染 兼容性:> IE9问题可能被疏导到 => 1. 浏览器的渲染原理 2.同步异步原理 3.模块化加载原理呈现的问题净化全局作用域成长期(模块化前夜) - IIFE(语法测的优化) 作用域的把控let count = 0;const increase = () => ++count;const reset = () => { count = 0;}利用函数的块级作用域 (() => { let count = 0; ...})//最根底的局部实现一个最简略的模块const iifeModule = (() => { let count = 0; const increase = () => ++count; const reset = () => { count = 0; } console.log(count); increase();})();诘问:独立模块自身的额定依赖如何优化优化1:依赖其余模块的传参型const iifeModule = ((dependencyModule1,dependencyModule2) => { let count = 0; const increase = () => ++count; const reset = () => { count = 0; } console.log(count); increase(); ...//能够解决依赖中的办法})(dependencyModule1,dependencyModule2)面试1:理解jquery或者其余很多开源框架的模块加载计划将自身的办法裸露进来 ...

October 18, 2022 · 2 min · jiezi

关于javascript:来自大厂-10-前端面试题附答案整理版

如何提⾼webpack的打包速度?(1)优化 Loader对于 Loader 来说,影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST,而后对 AST 持续进行转变最初再生成新的代码,我的项目越大,转换代码越多,效率就越低。当然了,这是能够优化的。 首先咱们优化 Loader 的文件搜寻范畴 module.exports = { module: { rules: [ { // js 文件才应用 babel test: /\.js$/, loader: 'babel-loader', // 只在 src 文件夹下查找 include: [resolve('src')], // 不会去查找的门路 exclude: /node_modules/ } ] }}对于 Babel 来说,心愿只作用在 JS 代码上的,而后 node_modules 中应用的代码都是编译过的,所以齐全没有必要再去解决一遍。 当然这样做还不够,还能够将 Babel 编译过的文件缓存起来,下次只须要编译更改过的代码文件即可,这样能够大幅度放慢打包工夫 loader: 'babel-loader?cacheDirectory=true'(2)HappyPack受限于 Node 是单线程运行的,所以 Webpack 在打包的过程中也是单线程的,特地是在执行 Loader 的时候,长时间编译的工作很多,这样就会导致期待的状况。 HappyPack 能够将 Loader 的同步执行转换为并行的,这样就能充分利用系统资源来放慢打包效率了 module: { loaders: [ { test: /\.js$/, include: [resolve('src')], exclude: /node_modules/, // id 前面的内容对应上面 loader: 'happypack/loader?id=happybabel' } ]},plugins: [ new HappyPack({ id: 'happybabel', loaders: ['babel-loader?cacheDirectory'], // 开启 4 个线程 threads: 4 })](3)DllPluginDllPlugin 能够将特定的类库提前打包而后引入。这种形式能够极大的缩小打包类库的次数,只有当类库更新版本才有须要从新打包,并且也实现了将公共代码抽离成独自文件的优化计划。DllPlugin的应用办法如下: ...

October 18, 2022 · 10 min · jiezi

关于javascript:js事件循环与macromicro任务队列前端面试进阶

背景一天惬意的下午。敌人给我分享了一道头条面试题,如下: async function async1(){ console.log('async1 start') await async2() console.log('async1 end')}async function async2(){ console.log('async2')}console.log('script start')setTimeout(function(){ console.log('setTimeout') },0) async1();new Promise(function(resolve){ console.log('promise1') resolve();}).then(function(){ console.log('promise2')})console.log('script end')这个题目次要是考查对同步工作、异步工作:setTimeout、promise、async/await的执行程序的了解水平。(倡议大家也本人先做一下o) 过后因为我对async、await理解的不是很分明,答案错的千奇百怪 :(),就不记录了,而后我就去看文章理了理思路。当初写在上面以供日后参考。 js事件轮询的一些概念这里首先须要明确几个概念:同步工作、异步工作、工作队列、microtask、macrotask 同步工作指的是,在主线程上排队执行的工作,只有前一个工作执行结束,能力执行后一个工作; 异步工作指的是,不进入主线程、而进入"工作队列"(task queue)的工作,期待同步工作执行结束之后,轮询执行异步工作队列中的工作 macrotask 即宏工作,宏工作队列等同于咱们常说的工作队列,macrotask是由宿主环境散发的异步工作,事件轮询的时候总是一个一个工作队列去查看执行的,"工作队列"是一个先进先出的数据结构,排在后面的事件,优先被主线程读取。 microtask 即微工作,是由js引擎散发的工作,总是增加到当前任务队列开端执行。另外在解决microtask期间,如果有新增加的microtasks,也会被增加到队列的开端并执行。留神与setTimeout(fn,0)的区别: setTimeOut(fn(),0)指定某个工作在主线程最早可得的闲暇工夫执行,也就是说,尽可能早得执行。它在"工作队列"的尾部增加一个事件,因而要等到同步工作和"工作队列"现有的事件都解决完,才会失去执行。 总结一下: task queue、microtask、macrotask An event loop has one or more task queues.(task queue is macrotask queue)Each event loop has a microtask queue.task queue = macrotask queue != microtask queuea task may be pushed into macrotask queue,or microtask queuewhen a task is pushed into a queue(micro/macro),we mean preparing work is finished,so the task can be executed now.所以咱们能够失去js执行程序是: ...

October 18, 2022 · 2 min · jiezi

关于javascript:如何利用js加密防止xss注入

简介xss又名跨站脚本攻打,攻击者能够利用网站的xss破绽执行一些脚本代码,达到本人的目标。以上比拟闻名的xss攻打事件就有hellosamy,这个作者利用了微博的xss破绽,刷屏关注本人。 业务场景最无效的xss还是属于存储型的,通过提交表单的模式,将本人的xss代码提交到数据库,若指标服务没有本义和屏蔽关键词,有相干的破绽,将会在显示到页面的时候执行咱们的xss代码,达到反客为主的目标。 像存储型的xss针对的次要是后端逻辑,看后端逻辑是否有本义和关键词过滤。 还有其余两种类型的xss 别离是反射性和DOM型,这两种类型的XSS攻打,都是攻击者通过剖析你的前端js代码来进行XSS攻打试探调试的,这时候咱们如果想要避免攻击者剖析咱们的代码,就须要用到js加密了,至于如何加密js,这就要交给业余的工具来做了。 如何加密JS达到爱护咱们前端代码的目标目前国内js加密做的最好的就是 jsjiami.com 了。先关上这个工具站,而后如图所示默认配置加密就行了。 结语通过加密后的JS代码能够说曾经齐全看不出本来的代码模样了,我钻研过,加密后的执行效率齐全没有影响,执行后果也统一,能够说十分好用了。这个工具站除了加密还能够一键解密一些其余不厉害的小加密算法,也能够分割客服人工解密,能够说没有不能解密的代码。

October 17, 2022 · 1 min · jiezi

关于javascript:前端食堂技术周刊第-55-期Rollup-v300Volar-10-NikaTypeScript-十年

美味值: 口味:桂花秋梨 食堂技术周刊仓库地址:https://github.com/Geekhyt/weekly本期摘要Rollup v3.0.0Volar 1.0 NikaTypeScript 十年,不忘初心2022 Web 网络年鉴Resumable vs. HydrationJavaScript 框架的新浪潮用 Sandpack 打造世界级 Playground大家好,我是童欧巴。欢送来到本期的前端食堂技术周刊,咱们先来看下上周的技术资讯。 技术资讯1.Rollup v3.0.0Rollup 公布 v3.0.0,带来了大量更新。其中 Breaking Change 包含最低反对 Node 14.18.0、浏览器构建拆成独自的包 @rollup/browse、Node 构建应用 node: 前缀导入内置模块、移除一些以前被废除的性能,应用时提醒正告等。还有包含 Options 配置、插件 API、以及一系列的新个性。 2.Volar 1.0 NikaVolar 公布 1.0,代号 Nika,此次更新次要改良了 UX/DX、性能、包体积、Plugin API v1,将外围代码重构使其与框架无关。 3.TypeScript 十年,不忘初心祝 TypeScript 10 周年生日快乐 。 上面咱们来看技术材料。 技术材料1.2022 Web 网络年鉴2022 Web 网络年鉴,共计 23 个小章节,蕴含页面内容、用户体验、内容公布和内容散发等方面,从这些调研数据中能够窥探出各个技术的实在现状。 2.Resumable vs. Hydration本文介绍了 Qwik 框架可恢复性的实现原理以及与惯例补水相比具备的劣势。 3.JavaScript 框架的新浪潮新浪潮这个词往年特地风行,又是一篇“新浪潮”的好文,本文把从古至今各个时代 JavaScript 的典型框架和遇到的痛点问题进行梳理,从而还原出一篇极简的 JavaScript 框架演进史。 4.用 Sandpack 打造世界级 PlaygroundCodeSandbox 开源了 Sandpack,本文教你应用 Sandpack 打造出一个功能齐全的 Playground。 ...

October 17, 2022 · 1 min · jiezi

关于javascript:彻底搞懂JS原型与原型链

说到JavaScript的原型和原型链,相干文章已有不少,然而大都艰涩难懂。本文将换一个角度登程,先了解原型和原型链是什么,有什么作用,再去剖析那些令人头疼的关系。 一、援用类型皆为对象原型和原型链都是来源于对象而服务于对象的概念,所以咱们要先明确一点: JavaScript中所有援用类型都是对象,对象就是属性的汇合。 Array类型、Function类型、Object类型、Date类型、RegExp类型等都是援用类型。 也就是说 数组是对象、函数是对象、正则是对象、对象还是对象。 二、原型和原型链是什么下面咱们说到对象就是属性(property)的汇合,有人可能要问不是还有办法吗?其实办法也是一种属性,因为它也是键值对的表现形式,具体见下图。 能够看到obj上的确多了一个sayHello的属性,值为一个函数,然而问题来了,obj下面并没有hasOwnProperty这个办法,为什么咱们能够调用呢?这就引出了 原型。 每一个对象从被创立开始就和另一个对象关联,从另一个对象上继承其属性,这个另一个对象就是 原型。 当拜访一个对象的属性时,先在对象的自身找,找不到就去对象的原型上找,如果还是找不到,就去对象的原型(原型也是对象,也有它本人的原型)的原型上找,如此持续,直到找到为止,或者查找到最顶层的原型对象中也没有找到,就完结查找,返回undefined。 这条由对象及其原型组成的链就叫做原型链。 当初咱们曾经初步了解了原型和原型链,到当初大家明确为什么数组都能够应用push、slice等办法,函数能够应用call、bind等办法了吧,因为在它们的原型链上找到了对应的办法。 OK,总结一下: 原型存在的意义就是组成原型链:援用类型皆对象,每个对象都有原型,原型也是对象,也有它本人的原型,一层一层,组成原型链。原型链存在的意义就是继承:拜访对象属性时,在对象自身找不到,就在原型链上一层一层找。说白了就是一个对象能够拜访其余对象的属性。继承存在的意义就是属性共享:益处有二:一是代码重用,字面意思;二是可扩大,不同对象可能继承雷同的属性,也能够定义只属于本人的属性。三、创建对象对象的创立形式次要有两种,一种是new操作符后跟函数调用,另一种是字面量表示法。 目前咱们当初能够了解为:所有对象都是由new操作符后跟函数调用来创立的,字面量表示法只是语法糖(即实质也是new,性能不变,应用更简洁)。 // new操作符后跟函数调用let obj = new Object()let arr = new Array()// 字面量表示法let obj = { a: 1}// 等同于let obj = new Object()obj.a = 1let arr = [1,2]// 等同于let arr = new Array()arr[0] = 1arr[1] = 2Object、Array等称为构造函数,不要怕这个概念,构造函数和一般函数并没有什么不同,只是因为这些函数常被用来跟在new前面创建对象。new前面调用一个空函数也会返回一个对象,任何一个函数都能够当做构造函数。 所以构造函数更正当的了解应该是函数的结构调用。 Number、String、Boolean、Array、Object、Function、Date、RegExp、Error这些都是函数,而且是原生构造函数,在运行时会主动呈现在执行环境中。 构造函数是为了创立特定类型的对象,这些通过同一构造函数创立的对象有雷同原型,共享某些办法。举个例子,所有的数组都能够调用push办法,因为它们有雷同原型。 咱们来本人实现一个构造函数: // 常规,构造函数应以大写字母结尾function Person(name) { // 函数内this指向结构的对象 // 结构一个name属性 this.name = name // 结构一个sayName办法 this.sayName = function() { console.log(this.name) }}// 应用自定义构造函数Person创建对象let person = new Person('logan')person.sayName() // 输入:logan总结一下:构造函数用来创建对象,同一构造函数创立的对象,其原型雷同。 ...

October 17, 2022 · 3 min · jiezi

关于javascript:细说Js中的this

为什么应用this先看个例子: function identity() { return this.name.toUpperCase();}function speak() { return "Hello, i'm " + identity.call(this);}var me = { name: 'rod chen'}var you = { name: "others in Aug"}console.log(identity.call(me)); //ROD CHENconsole.log(identity.call(you)); //OTHERS IN AUGconsole.log(speak.call(me)); //Hello, i'm ROD CHEN console.log(speak.call(you)); //Hello, i'm OTHERS IN AUG输入的后果很显著,对于call的用法后面文章有提到,第一个参数就是传入到函数里的this的值。这段代码能够在不同的上下文对象( me 和 you )中重复使用函数 identify() 和 speak() ,如果咱们不实用this的话,那就须要identity和speak显示传入一个上下文对象,就像上面的形式 function identity(context) { return context.name.toUpperCase();}function speak(context) { return "Hello, i'm " + identity(context);}var me = { name: 'rod chen'}var you = { name: "others in Aug"}console.log(identity(me));console.log(identity(you));console.log(speak(me));console.log(speak(you));总结: this 提供了一种更优雅的形式来隐式“传递”一个对象援用,因而能够将API设计得更加简洁并且易于复用。随着应用模式越来越简单,显式传递上下文对象会让代码变得越来越凌乱,应用 this 则不会这样 ...

October 17, 2022 · 6 min · jiezi

关于javascript:细说js变量作用域和垃圾回收

根本类型和援用类型在 JavaScript 中,数据类型可分为根本类型和援用类型, 根本类型有六种:Null,Undefined,String,Boolean,Number,Symbol; 而援用类型就是传说中的 Object 了。 其中根本类型是按值传递,而援用类型的值是按援用拜访的,所以在操作对象时,实际上是在操作对象的援用而不是理论的对象 ( ps:在为对象增加属性时,操作的是理论的对象 )。 对于根本类型和援用类型的不同,大略有以下几点: 1、援用类型是动静的属性,而根本类型不是。 对于援用类型,咱们能够为其增加、删除属性和办法,但不能给根本类型的值增加属性: // 根本类型var name = 'Fly_001';name.age = 22;alert(name.age); // undefined;// 援用类型var person = new Object();person.name = 'Fly_001';alert(person.name); // 'Fly_001';2、复制的形式不同。 如果从一个变量向另一个变量复制根本类型的值,会将值复制到为新变量调配的地位上: var num1 = 5;var num2 = num1;当应用 num1 的值来初始化 num2 时,num2 中也保留了值5,但该值只是 num1 中 5 的一个正本,两个变量不会相互影响。 当从一个变量向另一个变量复制援用类型的值时,传递的是一个指针,其指向存储在堆中的一个对象,在复制完结后,两个变量实际上将援用同一个对象,扭转其中一个变量就会影响另一个变量: var obj1 = new Object();var obj2 = obj1;obj1.name = 'Fly_001';alert(obj2.name); // 'Fly_001';3、传递参数的特点。 这是一个容易困惑的点 。 ECMAScript 中所有函数的参数都是按值传递的。也就是说,把函数内部的值复制给函数外部的参数,就和把值从一个变量复制到另一个变量一样。根本类型值的传递如同根本类型变量的复制一样,而援用类型的传递,则如同援用类型变量的复制一样,这一点的确会引起很多小伙伴的争议,欢送探讨~ 在向参数传递根本类型的值时,被传递的值会被复制给一个局部变量( 即 arguments 对象中的一个元素 )。在向参数传递援用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因而该局部变量的变动会反映到函数的内部:function addTen(num) { num += 10; return num;}var count = 20;var result = addTen(count);alert(count); // 20,木有变动;alert(result); // 30function setNmae(obj) { obj.name = 'Fly_001';}var person = new Object();setName(person);alert(person.name); // 'Fly_001';在下面代码中咱们创立了一个对象,并将其保留在了变量 person 中。而后,这个对象被传递到 setName () 函数中就被复制给了 obj,在这个函数外部,obj 和 person 援用的是同一个对象。 ...

October 17, 2022 · 3 min · jiezi

关于javascript:js常用方法梳理

1、将下标转为中文零一二三... export function transfromNumber(number){ const INDEX_MAP = ['零','一'.....] if(!number) return if(number === 10) return INDEX_MAP[number] return [...number.toString()].reduce( (pre, cur) => pre + INDEX_MAP[cur] , '' )}2、判断整数的不同办法 /* 1. 增加一个是数字的判断 */function isInteger(obj) { return typeof obj === 'number' && obj%1 === 0}/* 2. 应用Math.round、Math.ceil、Math.floor判断 整数取整后还是等于本人。利用这个个性来判断是否是整数*/function isInteger(obj) { return Math.floor(obj) === obj}/* 3. 通过位运算符*/function isInteger(obj) { return (obj | 0) === obj}/* 4.ES6提供了Number.isInteger */3、dom节点平滑滚动到可视区域,顶部,底部 原生的scrollTo办法没有动画,相似于锚点跳转,比拟僵硬,能够通过这个办法会自带平滑的适度成果 function scrollTo(element) { element.scrollIntoView({ behavior: "smooth", block: "start" }) // 顶部 element.scrollIntoView({ behavior: "smooth", block: "end" }) // 底部 element.scrollIntoView({ behavior: "smooth"}) // 可视区域}4、获取随机色彩 ...

October 17, 2022 · 2 min · jiezi

关于javascript:图扑案例合集丨用赛博朋克语言诠释数字孪生

前言霓虹之下的机械义肢,孤单黑客的代码沉睡,隐匿于雨夜的叛变灵魂…这些具备举世无双锐利感的画面,让“赛博朋克”这个起源自80年代的文化命题曾经从小众文化火出圈,浸透至支流社会跃升为审美时尚。 图扑软件也同样在践行这种赛博朋克美学。并通过自主研发的 HT for Web 产品,数字孪生智能工厂、智慧城市、智慧航空、智慧电力、智慧园区、智慧楼宇等,将工业互联网的赛博语言娓娓道来。 智慧城市赛博朋克格调的色彩个别以粉色、青色、蓝色以及绿色为主,并通过高饱和冷暖色的强烈比照,给人强烈的审美感受。图扑软件在智慧城市 3D 可视化场景中退出赛博朋克的元素,将朋克色调融入可视化大屏,虚拟现实等将来科技的视觉元素,带来不一样的视觉冲击感。 赛博朋克炫酷霓虹城市设计师充分利用图扑软件的辅助劣势,并以 “HIGHTOPO” 为设计对象,设计生成 3D 赛博朋克城市夜景。Demo 选用暗黑、紫红、荧光蓝等色彩碰撞,描绘出极具将来感的集体理想城市。 案例会重点突出基础设施信息化、经营治理精细化、性能服务便当等方面。在触屏设施上可进行单指旋转、双指缩放、三指平移操作查看,无需为跨平台的不同模式而担心。 5G 智慧城市图扑软件 5G 智慧城市可视化零碎同样采纳赛博朋克格调元素设计。动静光圈成果炫酷且科技感十足,可帮忙咱们疾速聚焦眼帘,找到我的项目重点内容。   赛博朋克智能微电网图扑软件利用自主研发引擎 HT for Web 将 Web 智慧“双碳”微电网场景进行数字孪生,无效实现源网荷储一体化管控。整体场景采纳了轻量化建模的形式,重点围绕智慧园区电网联通中的源、网、荷、储四方面的设施和修建进行建模还原,为用户带来“赛博朋克”的视觉体验。 公开综合管廊图扑软件利用三维可视化和虚构仿真技术,对公开综合管廊的物理因素和信息系统进行高精度模仿重现,围绕感知、传输、数据、平台与利用以及展示等层面的关键技术钻研及其利用。画面低亮度、高饱和、强比照,色调以青色和紫色为主,打造色调魅惑的场景,让画面更具备科技城市的外延。   政务管理中心政务数据管理核心以陆家嘴街道为背景,基于 Web 端的 UI/2D/3D、GIS、BIM 及 VR/AR 等可视化工具及利用,构建具备翻新科技与文化创意的政务管理系统。修建呈现出紫色、粉红色的搭配,通过冷色之间的互相搭配,让设计更有科技感,充斥奇幻色调。 智慧钢铁间断铸造作业是将钢液转变为钢胚的过程。图扑软件依靠 HT 引擎将连铸生产过程及设施数字孪生,实现 1:1 场景还原的连铸生产零碎。钢坯的加热状态选用赛博朋克格调展示,帮忙业务人员冲破加热钢坯测温技术的局限,为实时出钢提供根据。 军工坦克图扑软件搭建的智慧坦克治理场景,整体采纳轻量化建模模式,在确保高精度可视化渲染的同时,也为增加了赛博朋克元素的视觉体验。   智慧火电厂图扑软件可视化平台将火电厂、设施机器、人员等信息集成展示,打造全面感知、全面笼罩、全面可视、全面可控的智慧工厂管理系统。场景内冷色调的黑、灰、蓝、青为主,让人产生不同于传统设计的离奇体验。 SMT IOT 实时状态2019 年图扑软件助力武汉联想打造全新的 SMT 贴片机生产线 3D 可视化仿真运维零碎。SMT 运维零碎基于图扑可视化技术,可对产线各种根底信息进行疾速建档、数字化实时监测,实现对贴片产线的全生命周期和精细化治理。赛博朋克设计格调合乎业务需要。 赛博朋克汽车设计师以纵深的平面街道为主体设计:在流光溢彩的霓虹灯、高耸入云的摩天楼下,将来感十足的场景映入眼帘,尽显将来驾驶魅力。远中远景层次分明,让画面在视觉上更加趋于丰满。奔跑的数字孪生赛博朋克汽车说明城市凋敝外衣之下的无序和动荡。   反对遵循企业业务数据的实时动态变化进行二维、三维柱状图形的实时涨跌出现,反对自定义格调和菜单工具条等内容。 赛博朋克飞机驾驶舱以蓝、紫、青等冷色调为主的霓虹视觉色系成为少数人对赛博朋克的共识,图扑软件采纳高级渲染引擎 HT 加持,让客户转瞬之间即置身于布满霓虹灯般的灿烂赛博朋克世界,展示将来工业艺术的独特美感。 ...

October 17, 2022 · 1 min · jiezi

关于javascript:思否猫拼接怪编辑器

前言1024程序员节马上就要来了,咱们借着这个机会实现一个社区的吉祥物思否猫的编辑器,临时起名【思否猫拼接怪】,惟一的遗憾就是素材不是准确到胳膊腿的 demoPC在线体验 效果图 实现思路左侧面板提供一些思否猫的整机(耳朵、鼻子等等)[正在抠图中]用户能够在左侧面板点击整机,而后整机会呈现在右侧的编辑区域编辑区域用户能够对整机进行地位、大小、图层的调整,而后设计出本人青睐的思否猫筹备工作用到的拖拽库moveable导出图片应用html2canvas零碎架子间接应用了ant design pro的根底架子外围代码具体的实现细节比方页面布局等就不在这里进行论述了,咱们只列举下外围的一些相干代码 左侧面板图片上传并转为base64 const getBase64 = (img: RcFile, callback: (url: string) => void) => { const reader = new FileReader(); reader.addEventListener('load', () => callback(reader.result as string)); reader.readAsDataURL(img);};节点拖拽这个性能依赖了moveable这个插件 <Moveable target={target} container={null} flushSync={flushSync} edge={false} draggable={true} throttleDrag={0} snappable={true} snapCenter={true} origin={false} resizable={true} throttleResize={0} rotatable={true} rotationAtCorner={false} scrollable={true} pinchable={true}/>图层治理图层用了简略的实现形式,用css的zindex来粗犷的进行实现导出图片 const clipImgBase64: any = canvas.toDataURL(); // 生成图片urlconst link: any = document.createElement('a');link.href = clipImgBase64; //下载链接link.setAttribute('download', new Date().toLocaleString() + 'sf_截图.png');link.style.display = 'none'; //a标签暗藏document.body.appendChild(link);link.click(); // 点击下载document.body.removeChild(link); // 移除a标签导出gif正在开发,应用gif.js进行渲染导出本文参加了1024程序员节,欢送正在浏览的你也退出。交换群 ...

October 17, 2022 · 1 min · jiezi

关于javascript:VS-Code-For-Web-深入浅出-导读篇

下一代 IDE 的状态到底是什么呢?VS Code For Web 试图答复这个问题。 背景家喻户晓,VS Code 是以后工业界最优良的代码编辑器之一。它由《设计模式》的作者 Erich Gamma 领导开发,因而,它的设计架构在很多中央非常精妙,近些年曾经成为了各家竞相模拟学习的对象。 只管 VSCode 作为桌面端利用非常优良,但因为它基于 Electron,相当于在 Chromium 外套了一层壳,使得它的打包体积十分大,且无奈间接装置在云端环境中,这就导致了它的应用场景十分无限。 2019 年,微软在 PyCon 2019 大会上公布了 VSCode Remote 扩大,反对了近程开发,这个扩大的呈现,使得 VSCode 成为了一款真正的跨平台 IDE,不再局限于本地开发,能够通过 SSH 连贯到近程服务器,而后在近程服务器上开发。 2020 年,Github Codespaces 又将 Web IDE 的停顿向前迈进了一步,它容许用户拜访 Github 上托管的所有我的项目来间接进行编辑与提交,而无需本人搭建服务器,实现了一个全托管的近程开发解决方案。但微软过后并未将其 Server 局部开源,由 Github 集中经营。 而在最近的 2022 年 7 月,微软公开并公布了 private preview 版本的 VS Code Server。这是一个可私有化的,能够在近程开发机器上运行的独立服务端。与 Github Codespaces 雷同,它容许用户间接通过 URL,在浏览器中平安地链接到近程开发机,且同时反对了 HTTP 与 Web Socket 协定。在此过程中不须要进行任何后期的 SSH 或 HTTPS 设置。 ...

October 17, 2022 · 2 min · jiezi

关于javascript:图扑案例合集丨用赛博朋克语言诠释数字孪生

前言霓虹之下的机械义肢,孤单黑客的代码沉睡,隐匿于雨夜的叛变灵魂…这些具备举世无双锐利感的画面,让“赛博朋克”这个起源自80年代的文化命题曾经从小众文化火出圈,浸透至支流社会跃升为审美时尚。 图扑软件也同样在践行这种赛博朋克美学。并通过自主研发的 HT for Web 产品,数字孪生智能工厂、智慧城市、智慧航空、智慧电力、智慧园区、智慧楼宇等,将工业互联网的赛博语言娓娓道来。 智慧城市赛博朋克格调的色彩个别以粉色、青色、蓝色以及绿色为主,并通过高饱和冷暖色的强烈比照,给人强烈的审美感受。图扑软件在智慧城市 3D 可视化场景中退出赛博朋克的元素,将朋克色调融入可视化大屏,虚拟现实等将来科技的视觉元素,带来不一样的视觉冲击感。 赛博朋克炫酷霓虹城市设计师充分利用图扑软件的辅助劣势,并以 “HIGHTOPO” 为设计对象,设计生成 3D 赛博朋克城市夜景。Demo 选用暗黑、紫红、荧光蓝等色彩碰撞,描绘出极具将来感的集体理想城市。 案例会重点突出基础设施信息化、经营治理精细化、性能服务便当等方面。在触屏设施上可进行单指旋转、双指缩放、三指平移操作查看,无需为跨平台的不同模式而担心。 5G 智慧城市图扑软件 5G 智慧城市可视化零碎同样采纳赛博朋克格调元素设计。动静光圈成果炫酷且科技感十足,可帮忙咱们疾速聚焦眼帘,找到我的项目重点内容。   赛博朋克智能微电网图扑软件利用自主研发引擎 HT for Web 将 Web 智慧“双碳”微电网场景进行数字孪生,无效实现源网荷储一体化管控。整体场景采纳了轻量化建模的形式,重点围绕智慧园区电网联通中的源、网、荷、储四方面的设施和修建进行建模还原,为用户带来“赛博朋克”的视觉体验。 公开综合管廊图扑软件利用三维可视化和虚构仿真技术,对公开综合管廊的物理因素和信息系统进行高精度模仿重现,围绕感知、传输、数据、平台与利用以及展示等层面的关键技术钻研及其利用。画面低亮度、高饱和、强比照,色调以青色和紫色为主,打造色调魅惑的场景,让画面更具备科技城市的外延。   政务管理中心政务数据管理核心以陆家嘴街道为背景,基于 Web 端的 UI/2D/3D、GIS、BIM 及 VR/AR 等可视化工具及利用,构建具备翻新科技与文化创意的政务管理系统。修建呈现出紫色、粉红色的搭配,通过冷色之间的互相搭配,让设计更有科技感,充斥奇幻色调。 智慧钢铁间断铸造作业是将钢液转变为钢胚的过程。图扑软件依靠 HT 引擎将连铸生产过程及设施数字孪生,实现 1:1 场景还原的连铸生产零碎。钢坯的加热状态选用赛博朋克格调展示,帮忙业务人员冲破加热钢坯测温技术的局限,为实时出钢提供根据。 智慧火电厂图扑软件可视化平台将火电厂、设施机器、人员等信息集成展示,打造全面感知、全面笼罩、全面可视、全面可控的智慧工厂管理系统。场景内冷色调的黑、灰、蓝、青为主,让人产生不同于传统设计的离奇体验。 SMT IOT 实时状态2019 年图扑软件助力武汉联想打造全新的 SMT 贴片机生产线 3D 可视化仿真运维零碎。SMT 运维零碎基于图扑可视化技术,可对产线各种根底信息进行疾速建档、数字化实时监测,实现对贴片产线的全生命周期和精细化治理。赛博朋克设计格调合乎业务需要。 赛博朋克汽车设计师以纵深的平面街道为主体设计:在流光溢彩的霓虹灯、高耸入云的摩天楼下,将来感十足的场景映入眼帘,尽显将来驾驶魅力。远中远景层次分明,让画面在视觉上更加趋于丰满。奔跑的数字孪生赛博朋克汽车说明城市凋敝外衣之下的无序和动荡。   反对遵循企业业务数据的实时动态变化进行二维、三维柱状图形的实时涨跌出现,反对自定义格调和菜单工具条等内容。 赛博朋克飞机驾驶舱以蓝、紫、青等冷色调为主的霓虹视觉色系成为少数人对赛博朋克的共识,图扑软件采纳高级渲染引擎 HT 加持,让客户转瞬之间即置身于布满霓虹灯般的灿烂赛博朋克世界,展示将来工业艺术的独特美感。   ...

October 17, 2022 · 1 min · jiezi

关于javascript:JS继承有哪些你能否手写其中一两种呢

引言JS系列暂定 27 篇,从根底,到原型,到异步,到设计模式,到架构模式等, 本篇是 JS系列中第 3 篇,文章主讲 JS 继承,包含原型链继承、构造函数继承、组合继承、寄生组合继承、原型式继承、 ES6 继承,以及 多继承与 new 。 ES5 继承先定义一个父类 function SuperType () { // 属性 this.name = 'SuperType';}// 原型办法SuperType.prototype.sayName = function() { return this.name;};一、 原型链继承根本思维将父类的实例作为子类的原型 // 父类function SuperType () { this.name = 'SuperType'; // 父类属性}SuperType.prototype.sayName = function () { // 父类原型办法 return this.name;};// 子类function SubType () { this.subName = "SubType"; // 子类属性};SubType.prototype = new SuperType(); // 重写原型对象,代之以一个新类型的实例// 这里实例化一个 SuperType 时, 实际上执行了两步// 1,新创建的对象复制了父类构造函数内的所有属性及办法// 2,并将原型 __proto__ 指向了父类的原型对象SubType.prototype.saySubName = function () { // 子类原型办法 return this.subName;}// 子类实例let instance = new SubType();// instanceof 通过判断对象的 prototype 链来确定对象是否是某个类的实例instance instanceof SubType; // trueinstance instanceof SuperType; // true// 留神SubType instanceof SuperType; // falseSubType.prototype instanceof SuperType ; // true ...

October 17, 2022 · 4 min · jiezi

关于javascript:前端必会手写题总结

实现数组的乱序输入次要的实现思路就是: 取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行替换。第二次取出数据数组第二个元素,随机产生一个除了索引为1的之外的索引值,并将第二个元素与该索引值对应的元素进行替换依照下面的法则执行,直到遍历实现var arr = [1,2,3,4,5,6,7,8,9,10];for (var i = 0; i < arr.length; i++) { const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i; [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];}console.log(arr)还有一办法就是倒序遍历: var arr = [1,2,3,4,5,6,7,8,9,10];let length = arr.length, randomIndex, temp; while (length) { randomIndex = Math.floor(Math.random() * length--); temp = arr[length]; arr[length] = arr[randomIndex]; arr[randomIndex] = temp; }console.log(arr)Array.prototype.filter()Array.prototype.filter = function(callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not undefined'); } if (typeof callback !== 'function') { throw new TypeError(callback + 'is not a function'); } const res = []; // 让O成为回调函数的对象传递(强制转换对象) const O = Object(this); // >>>0 保障len为number,且为正整数 const len = O.length >>> 0; for (let i = 0; i < len; i++) { // 查看i是否在O的属性(会查看原型链) if (i in O) { // 回调函数调用传参 if (callback.call(thisArg, O[i], i, O)) { res.push(O[i]); } } } return res;}模仿newnew操作符做了这些事: ...

October 17, 2022 · 10 min · jiezi

关于javascript:js函数柯里化面试手写版

概念用我本人的话来总结一下,函数柯里化的意思就是你能够一次传很多参数给curry函数,也能够分屡次传递,curry函数每次都会返回一个函数去解决剩下的参数,始终到返回最初的后果。 实例这里还是举几个例子来阐明一下: 柯里化求和函数 // 一般形式 var add1 = function(a, b, c){ return a + b + c; } // 柯里化 var add2 = function(a) { return function(b) { return function(c) { return a + b + c; } } }这里每次传入参数都会返回一个新的函数,这样始终执行到最初一次返回a+b+c的值。然而这种实现还是有问题的,这里只有三个参数,如果哪天产品经理通知咱们须要改成100次?咱们就从新写100次?这很显著不合乎开闭准则,所以咱们须要对函数进行一次批改。 var add = function() { var _args = []; return function() { if(arguments.length === 0) { return _args.reduce(function(a, b) { return a + b; }) } [].push.apply(_args, arguments); return arguments.callee; }}var sum = add();sum(100, 200)(300);sum(400);sum(); // 1000咱们通过判断下一次是否传进来参数来决定函数是否运行,如果持续传进了参数,那咱们持续把参数都保存起来,等运行的时候全副一次性运行,这样咱们就初步实现了一个柯里化的函数。 ...

October 17, 2022 · 3 min · jiezi

关于javascript:精读Headless-组件用法与原理

Headless 组件即无 UI 组件,框架仅提供逻辑,UI 交给业务实现。这样带来的益处是业务有极大的 UI 自定义空间,而对框架来说,只思考逻辑能够让本人更轻松的笼罩更多场景,满足更多开发者不同的诉求。 咱们以 headlessui-tabs 为例看看它的用法,并读一读 源码。 概述headless tabs 最简略的用法如下: import { Tab } from "@headlessui/react";function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> );}以上代码没有做任何逻辑定制,只用 Tab 及其提供的标签把 tabs 的构造形容进去,此时框架能提供最根底的 tabs 切换个性,即依照程序,点击 Tab 时切换内容到对应的 Tab.Panel。 此时没有任何额定的 UI 款式,甚至连 Tab 选中态都没有,如果须要进一步定制,须要用框架提供的 RenderProps 能力拿到状态后做业务层的定制,比方选中态: <Tab as={Fragment}> {({ selected }) => ( <button className={selected ? "bg-blue-500 text-white" : "bg-white text-black"} > Tab 1 </button> )}</Tab>要实现选中态就要自定义 UI,如果应用 RenderProps 拓展,那么 Tab 就不应该提供任何 UI,所以 as={Fragment} 就示意该节点作为一个逻辑节点而非 UI 节点(不产生 dom 节点)。 ...

October 17, 2022 · 3 min · jiezi

关于javascript:22道JavaScript输出顺序问题你能做出几道

前言最近在筹备面试题,console的输入程序之前始终迷迷糊糊。 必备常识JS是单线程的单线程是 JavaScript 外围特色之一。这意味着,在 JS 中所有工作都须要排队执行,前一个工作完结,才会执行后一个工作。所以这就造成了一个问题:如果前一个工作耗时很长,后一个工作就不得不始终等着后面的工作执行完能力执行。比方咱们向服务器申请一段数据,因为网络问题,可能须要期待 60 秒左右能力胜利返回数据,此时只能期待申请实现,JS 能力去解决前面的代码。 同步工作和异步工作为了解决JS单线程带来的问题,JavaScript 就将所有工作分成了同步工作和异步工作。 同步工作(Synchronous)同步工作指的是以后一个(如果有)工作执行结束,接下来能够立刻执行的工作。这些工作将在主线程上顺次排队执行。也就是说排排队 //for(){} 和 console.log() 将会顺次执行,最终输入 0 1 2 3 4 done。for (let i = 0; i < 5; i++) {console.log(i)}console.log('done')异步工作(Asynchronous)异步工作绝对于同步工作,指的是不须要进入主线程排队执行,而是进入超车道、并车道。也就是工作队列中,造成一系列的工作。这些工作只有当被告诉能够执行的时候,该工作才会从新进入主线程执行。 //上面的 then() 办法须要期待 Promise 被 resolve() 之后能力执行,它是一个异步工作。最终输入 1 3 2。console.log(1)Promise.resolve().then(() => { console.log(2)})console.log(3)具体来说就是,所有同步工作会在主线程上顺次排队执行,造成一个执行栈(Execution ContextStack)。主线程之外,还存在一个工作队列。当异步工作有了运行后果,会在工作队列之中搁置对应的事件。当执行栈中的所有同步工作执行结束,工作队列里的异步工作就会进入执行栈,而后持续顺次执行。 异步工作(工作队列)能够分为 macrotasks(taskQueue):宏工作 task,也是咱们常说的工作队列 macrotasks 的划分:(留神先后顺序!) (1)setTimeout(提早调用)(2)setInterval(间歇调用)(3)setImmediate(Node 的立刻调用)(4)requestAnimationFrame(高频的 RAF)(5)I/O(I/O 操作)(6)UI rendering(UI 渲染)(7) 包裹在一个 script 标签中的 js 代码也是一个 Macrotasks留神: (1)每一个 macrotask 的回调函数要放在下一车的结尾去执行! (2)只有 setImmediate 可能确保在下一轮事件循环立刻失去解决 microtasks:微工作(也称 job)调度在以后脚本执行完结后,立刻执行的工作,以防止付出额定一个 task 的费用。 ...

October 17, 2022 · 6 min · jiezi

关于javascript:腾讯前端高频面试题合集

实现 JSONP 跨域JSONP 外围原理:script 标签不受同源策略束缚,所以能够用来进行跨域申请,长处是兼容性好,然而只能用于 GET 申请; 实现: const jsonp = (url, params, callbackName) => { const generateUrl = () => { let dataSrc = ""; for(let key in params) { if(params.hasOwnProperty(key)) { dataSrc += `${key}=${params[key]}&` } } dataSrc += `callback=${callbackName}`; return `${url}?${dataSrc}`; } return new Promise((resolve, reject) => { const scriptEle = document.createElement('script'); scriptEle.src = generateUrl(); document.body.appendChild(scriptEle); window[callbackName] = data => { resolve(data); document.removeChild(scriptEle); } });}Unicode、UTF-8、UTF-16、UTF-32的区别?(1)Unicode在说Unicode之前须要先理解一下ASCII码:ASCII 码(American Standard Code for Information Interchange)称为美国规范信息替换码。 ...

October 17, 2022 · 9 min · jiezi

关于javascript:80的前端开发都答不上来的js异步面试题

最近面试中碰到了一道对于JS执行程序的题目,题目比拟根底,然而如果对于JS不熟的话,还是容易答不上来。再次记录和剖析此次面试题,心愿对大家有所帮忙。 async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } async function async2() { console.log("async2"); } console.log("js start"); setTimeout(function () { console.log("timeout"); }, 0); async1(); new Promise(function (resolve) { console.log("promise"); resolve(); }).then(function () { console.log("then"); }); console.log("js end");话不多说,先上后果 // 控制台输入后果 "js start" "async1 start" "async2" "promise" "js end" "async1 end" "then" "timeout"宏工作 微工作如果看官是个老手的话,看到下面的输入后果,必定一脸懵逼的,然而没关系,看完这篇文章您就懂了。想齐全明确下面这道题目,还须要理解JS的两个概念,没错,就是宏工作和微工作。 首先看官必定晓得JS是单线程,实现异步的办法就是定时器和es6+呈现的promise/async等,那么当初问题来了,既然es6呈现的新的异步形式,那么和之前的定时器相比,那个异步先执行呢? 宏工作(macro)task,能够了解为每段代码都是一个宏工作,没错JS的主程序也是宏工作。同时两个定时器异步的局部也是宏工作。 微工作microtask,能够了解是在以后 task 执行完结后立刻执行的工作。也就是在主程序执行实现之后立刻执行的局部。es6+呈现的promise,async都是微工作。在这里要记住一句话,微工作的优先级是高于宏工作的。 程序执行程序1、主程序 因为js是单线程的,同一时间只能有一段代码在执行,所以首先执行的就是JS的主程序。之前说主程序是宏工作,微工作优先级又比宏工作高,那为什么还先执行主程序这个宏工作呢? 这是因为:没有主程序去构建微工作,微工作又怎么会呈现呢,没有微工作的呈现,当然就去找到主程序这个宏工作了,所以优先级的说法没有谬误。 2、查看是否有异步工作 当上一个工作执行实现之后,程序会去检索是否有微工作,须要执行,如果有,就会先执行微工作。没有微工作但有宏工作,执行宏工作。没有工作,代码不在执行。 3、微工作 微工作代码执行,和失常的JS代码执行没有区别,从上往下编译执行!!!执行实现之后,会跳回到第二步。 4、宏工作 ...

October 17, 2022 · 1 min · jiezi

关于javascript:ajax-初步介绍

进入ajax了,想要进入vue还有一个前提就是要把ajax给相熟一下,看一看客户端与服务器之间是怎么一个通信的过程,第一天次要是先理解了一下ajax的一些介绍,ajax嘛,在进入之前,必定是要理解一下客户端与服务器之间的一个通信过程,其实不论是申请还是发送都遵循的一个准则,即申请、解决、响应,如何来申请服务器上的数据,须要用到XMLHttpRequest对象,也就是申明一个对象实例var xhrObj = new XMLHttpRequest()。 咱们通常所说的资源申请形式次要是分为两种,一种是get申请向服务器所求资源,一种是post向服务器发送资源。 持续看到什么事ajax?能够简略了解为用到了xhr,那他就是ajax,那么为什么要学习ajax?因为它能够轻松实现网页与服务器之间的数据交互,次要利用在如检测用户名是否被占用、加载搜寻提醒列表、依据页码刷新表格数据等。 咱们这部分先以jQuery为例对ajax做一些操作,因为浏览器提供的xhr用法比较复杂,就先用jq来实现,jq外面次要是就是分为三种$.get() $.post() $.ajax() 这三种,其中前两种的用法是(url,【data】,【callback】),url使咱们要申请的资源地址,data是咱们的参数,callback是申请胜利后的回调函数,他们两个能够带参申请也能够不带参申请,而后$.ajax()它是既能够实现get也能够实现post, ({type : ‘get or post’, url :‘’, data : {} , success :}) type就是申请形式,url申请地址,data参数,success是执行胜利的回调 上面就是一些jq别离利用ajax的get以及post申请的用法 1. 不带参数的申请 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><button> 发动不带参数的申请</button><body> <script src="./lib/jquery.js"></script> <script> $('button').on('click', () => $.get('http://www.liulongbin.top:3006/api/getbooks', res => console.log(res))) </script></body></html>2. 带参数的申请 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><button> 发动带参数的申请</button><body> <script src="./lib/jquery.js"></script> <script> $('button').click(() => { $.get('http://www.liulongbin.top:3006/api/getbooks',{id : 2},res => console.log(res)) }) </script></body></html>3. ...

October 16, 2022 · 4 min · jiezi

关于javascript:qiankun子应用图片资源加载404-解决方案

计划一:配置webpack url-loader小文件转为base64大文件配置publicPath 为子利用地址毛病: 打包前须要辨别不同环境,配置publicPath 计划二:资源援用地址依据环境env写死主利用为window绑定环境变量, window._env子利用依据window._env,设置援用地址 <div class="down-image" :style="{backgroundImage:`url(${domain}/static/img/img_man.png)`}"></div>计划三:子利用地址加'/xxx'前缀,主利用nginx将'/xxx'代理到子利用地址

October 16, 2022 · 1 min · jiezi

关于javascript:微信小程序调用上一个页面的方法

最近开发的一个性能:列表外面对商品有选中和勾销选中的性能,在详情页面也要有选中和勾销选中该商品的性能: // 获取以后页面js外面的pages里的所有信息。 var pages = getCurrentPages(); // 是获取上一个页面的js外面的pages的所有信息。 -2 是上一个页面,-3是上上个页面以此类推。 var beforePage = pages[pages.length - 2]; // 传递的参数 let current = { currentTarget: { dataset: { index: this.data.index, disabled: this.data.disabled } } } // 调取上一个页面的办法 beforePage.selectItem(current); // 也能够间接扭转上一个页面的数据 beforePage.setData({ text: '测试扭转列表页面的数据' })

October 16, 2022 · 1 min · jiezi

关于javascript:js将对象数组按照某一个属性分类成数组集合

js将对象数组依照某一个属性分类成数组汇合: var objOld = [ { type: 1, name: 1 }, { type: 1, name: 2 }, { type: 1, name: 2 }, { type: 2, name: 11 }, { type: 3, name: 7 }, { type: 3, name: 2 }, ]; // 办法封装 function objectBasis(obj) { var objNew = {}; obj.map((item) => { // 判断是否有这个属性,没有就新建一个数组,有的话就往那个新数组外面放值 if (!objNew[item.type]) { objNew[item.type] = []; } objNew[item.type].push(item) }); return objNew }; console.log(objectBasis(objOld))打印值: ...

October 16, 2022 · 1 min · jiezi

关于javascript:JS模块化历史进程

模块化——DOM脚本和语言的分水岭脚本还是语言? 当 Brendan Eich 设计 JavaScript 的第一个版本时,他可能不晓得他的我的项目在过来 20 年将如何倒退。目前,该语言标准曾经有六个次要版本,其改良工作仍在持续。 诚实说,JavaScript 素来都不是完满的编程语言。 JS 的弱点之一是模块化,更分明地说,它的缺席。的确,当您仅将脚本语言用于页面上飘落的雪花动画或表单验证时,当所有都能够在同一个全局范畴内生存和交互时,为什么您须要关怀代码和依赖项的隔离? 随着工夫的推移,JavaScript 曾经转变为一种通用语言,因为它开始用于在各种环境(浏览器、挪动设施、服务器、物联网)中构建简单的应用程序。通过全局范畴交互程序组件的旧办法变得不牢靠,因为越来越多的代码往往会使您的应用程序过于软弱。这就是为什么为了简化创立 JavaScript 应用程序而创立了各种模块化实现。 两个外围问题 命名抵触从它呈现的那一刻起,JavaScript 就应用全局对象窗口作为没有应用 var 关键字定义的所有变量的存储。 在晚期,它十分不便,因为 JavaScript 代码偏向于解决不须要很多代码行的小工作。 然而当应用程序的代码库变得很大时,该语言的这一个性开始因为名称抵触而导致重大的谬误。代码共享JavaScript 构建大型应用程序的另一个不便之处是须要在最常见的 ES5 浏览器环境中应用 script 标签显式指定插件脚本。如果您关怀应用程序的源代码应该是可保护的,那么您须要将其拆分为独立的局部。 因而,文件的数量可能十分大。 对于大量文件,手动管制脚本(即通过脚本标签将脚本搁置在页面上)变得十分繁琐,因为首先您必须记住在页面中搁置必要的脚本,其次要放弃正确的程序 脚本标签,以便解决文件之间的所有依赖关系。JS模块化过程概述间接定义依赖 (1999): 因为过后 js 文件非常简单,模块化形式非常简单粗犷 —— 通过全局办法定义、援用模块。这种定义形式与当初的 commonjs 十分神似,区别是 commonjs 以文件作为模块,而这种办法能够在任何文件中定义模块,模块不与文件关联。 闭包模块化模式 (2003): 用闭包形式解决了变量净化问题,闭包内返回模块对象,只需对外裸露一个全局变量。 模版依赖定义 (2006): 这时候开始风行后端模版语法,通过后端语法聚合 js 文件,从而实现依赖加载,但挂在可维护性上。 正文依赖定义 (2006): 简直和模版依赖定义同时呈现,与 1999 年计划不同的,不仅仅是模块定义形式,而是终于以文件为单位定义模块了,通过 lazyjs 加载文件,同时读取文件正文,持续递归加载剩下的文件。 内部依赖定义 (2007): 这种定义形式在 cocos2d-js 开发中广泛应用,其核心思想是将依赖抽出独自文件定义,这种形式不利于项目管理,毕竟依赖抽到代码之外,我是不是得中间找呢?所以才有通过 webwpack 打包为一个文件的形式暴力替换为 commonjs 的形式呈现。 Sandbox模式 (2009): 这种模块化形式很简略,暴力,将所有模块塞到一个 sanbox 变量中,硬伤是无奈解决明明抵触问题,毕竟都塞到一个 sandbox 对象里,而 Sandbox 对象也须要定义在全局,存在被笼罩的危险。模块化须要保障全局变量尽量洁净,目前为止的模块化计划都没有很好的做到这一点。 ...

October 16, 2022 · 1 min · jiezi

关于javascript:vue指令总结一次帮你学会使用

指令vue 内置了一些指令,也提供了自定义指令的接口。 指令的作用:可把一些可复用的逻辑封装成指令,以实现逻辑复用。 指令依照应用范畴看,分为全局指令和部分指令(在某个组件外部应用的)。 指令和组件一样,具备一些在特定期间执行的函数,就是通过它们定义指令的。 应用形式有一个v-test指令。 <template> <div v-test:disabled.foo="'directive'"> <h1>{{ msg }}</h1> </div></template>:之后的是指令参数,相似v-on:keyup中的 keyup。.之后的时指令修饰符,foo 是修饰符。 参数只能有一个,修饰符可有多个。v-test:disabled:boo.foo.zoo="msg" { arg: "disabled:boo" modifiers: {foo: true, zoo: true}}心愿不同状况下绑定不同的参数,可应用动静参数。 v-test:[argu].foo.zoo="msg" 参数必须在修饰符之前。指令的等号前面是指令表达式,其值对应binding对象的 value 属性。 binding 一个对象:{ arg: "disabled", expression: "msg", modifiers: {foo: true}, name: "test", value: "你好",}vue2 指令定义形式以插件的模式定义一个全局v-click-outside: export const clickOutside = (Vue, options) => { Vue.directive('clickOutside', { inserted(el, binding, vnode) { const { value } = binding // const _this = vnode.context // NOTE 技巧:处理函数挂载在元素上,不便解绑时移出事件监听 el.onClick = ({ target }) => { if (el.contains(target)) { // 点击外部 console.log('clickInside') } else { // 点击内部 console.log('clickOutside') value && value() } } document.addEventListener('click', el.onClick, false) }, unbind(el, binding, vnode) { document.removeEventListener('click', el.onClick, false) }, })}注册插件: ...

October 16, 2022 · 3 min · jiezi

关于javascript:JavaScript-数组对象-新增对象到数组-根据key判断没则添加有则替换

/* initialArr: 原数组 obj: 筹备放入数组的对象 pro: 比照的参数(肯定要传字符串) */ function formateArrObjData(initialArr, obj, pro) { let index = initialArr.findIndex((val) => val[pro] === obj[pro]) // 如果有就替换 没有就增加 if (initialArr.findIndex((val) => val[pro] === obj[pro]) !== -1) { initialArr.splice(index, 1, obj); } else { initialArr.push(obj); } return initialArr } var initialArr = [ {a: 1, b: 1}, {a: 2, b: 2} ] let newObj = formateArrObjData(initialArr, {a: 4, b: 44}, 'a') console.log(newObj, 'newObj')我也是在网上找的前辈的办法,我记录下来就当是笔记了。 ...

October 15, 2022 · 1 min · jiezi

关于javascript:js新旧对象数组对比如果两个数组有相等的值id则把新数组对应的id的某一项过滤出来

// 上一次的数据,旧数组 var oldArray = [ { id: '1', name: 'zhangsan', age: '15' }, { id: '2', name: 'lisi', age: '16' }, { id: '3', name: 'ani', age: '17' }, ] // 刚刚返回的新数组 var newArray = [ { id: '1', name: 'zhangsan', age: '100' }, { id: '2', name: 'lisi' }, ] // 将旧数组过滤出符合条件的数据id汇合 const idsArray = oldArray.filter((item) => { return item.id !== '1' }).map((item) => { return item.id }); console.log(idsArray, 'idsArray'); // ['2', '3'] 'idsArray' const productList = newArray.filter((item) => { return idsArray.includes(item.id) }) console.log(productList, 'productList') // [{id: '2', name: 'lisi'}] 'productList'

October 15, 2022 · 1 min · jiezi

关于javascript:81高德地图范围绘制区域遮掩蒙版遮罩

参考:高德地图-2D地图下区域遮掩(只显示固定区域里的内容) 其中:地图范畴绘制:工具 实现代码: <!DOCTYPE html><html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width" /> <style type="text/css">#container,html,body{ height: 100%; margin:0;}</style> <title>清华大学</title> <script src="http://cdn.highcharts.com.cn/highcharts/highcharts.js"></script> <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" /> <script language="javascript" src="https://webapi.amap.com/maps?v=1.4.15&amp;key=你的高德key&amp;plugin=Map3D,AMap.Scale,AMap.ControlBar,AMap.PolyEditor"></script> <script src="https://a.amap.com/jsapi_demos/static/demo-center/js/demoutils.js"></script> <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" /> <style> html, body, #container { height: 100%; width: 100%; } .content-window-card { position: relative; box-shadow: none; bottom: 0; left: 0; width: auto; padding: 0; } .content-window-card p { height: 2rem; } .custom-info { border: solid 1px silver; } div.info-top { position: relative; background: none repeat scroll 0 0 #F9F9F9; border-bottom: 1px solid #CCC; border-radius: 5px 5px 0 0; } div.info-top div { display: inline-block; color: #333333; font-size: 14px; font-weight: bold; line-height: 31px; padding: 0 10px; } div.info-top img { position: absolute; top: 10px; right: 10px; transition-duration: 0.25s; } div.info-top img:hover { box-shadow: 0px 0px 5px #000; } div.info-middle { font-size: 12px; padding: 10px 6px; line-height: 20px; } div.info-bottom { height: 0px; width: 100%; clear: both; text-align: center; } div.info-bottom img { position: relative; z-index: 104; } span { margin-left: 5px; font-size: 11px; } .info-middle img { float: left; margin-right: 6px; } </style> <script language="javascript">function mapInit() { var options = { areas:[{ //围栏1 //visible:false,//是否可见 rejectTexture:true,//是否屏蔽自定义地图的纹理 path: [[[116.316092,39.992203], [116.314933,39.999798], [116.31519,40.004598], [116.31562,40.005255], [116.316414,40.00583], [116.318216,40.006258], [116.318967,40.006652], [116.318946,40.008214], [116.3185,40.012725], [116.326804,40.013267], [116.328349,40.013251], [116.328972,40.013152], [116.330903,40.013218], [116.331632,40.012955], [116.335688,40.009356], [116.336675,40.007729], [116.336932,40.00689], [116.336954,40.006841], [116.337126,40.001368], [116.337705,39.992869], [116.325367,39.992409]]] }] }; var areapath1 = [ [116.326348,40.013238], [116.326182,40.012671], [116.325538,40.011849], [116.32483,40.01111], [116.324246,40.011258], [116.323833,40.011295], [116.323607,40.011278], [116.323141,40.011221], [116.322508,40.011056], [116.322384,40.011122], [116.322298,40.010929], [116.3224,40.010358], [116.322567,40.010222], [116.322899,40.010321], [116.323478,40.010386], [116.323849,40.010411], [116.323838,40.009215], [116.323865,40.007753], [116.323951,40.005982], [116.324814,40.006035], [116.32637,40.006117], [116.327539,40.006195], [116.327641,40.005086], [116.327668,40.004634], [116.330506,40.004757], [116.330393,40.006643], [116.330345,40.007995], [116.330318,40.009084], [116.333784,40.009228], [116.335125,40.009289], [116.33543,40.009355], [116.335607,40.009396] ]; var areapath2 = [ [116.315051,39.99988], [116.316022,39.999925], [116.316049,39.99983], [116.316226,39.999748], [116.318302,39.999863], [116.321858,40.000032], [116.324052,40.000825], [116.3249,40.000899], [116.325898,40.000997], [116.327958,40.001014], [116.328151,39.999575], [116.328473,39.997381], [116.328623,39.996255], [116.328956,39.99268] ]; var outer = [ new AMap.LngLat(-360,90,true), new AMap.LngLat(-360,-90,true), new AMap.LngLat(360,-90,true), new AMap.LngLat(360,90,true), ]; var pathArray = [ outer ]; pathArray.push.apply(pathArray,options.areas[0].path) var map = new AMap.Map("container", { resizeEnable: true, //是否监控地图容器尺寸变动 rotateEnable:true,//地图是否能够旋转 //pitchEnable:true,//是否3D地图 zoom:15, zooms:[14,20], //pitch:80,//歪斜角度 showIndoorMap: false, // 暗藏地图自带的室内地图图层 buildingAnimation:true,//楼块呈现是否带动画 expandZoomRange:true, //viewMode:'3D',//开启3D视图,默认为敞开 buildingAnimation:true,//楼块呈现是否带动画 mapStyle:'amap://styles/normal', center: [116.327164, 40.003479], //初始地图中心点 features:['bg','point','road'], layers:[ new AMap.TileLayer({}) ] }); map.addControl(new AMap.ControlBar({ showZoomBar:true, showControlButton:true, position:{ right:'10px', top:'10px' } })) new AMap.Polygon({ bubble:true, fillOpacity:0.6, strokeWeight:2, strokeColor: '#00eeff', fillColor: '#71B3ff', path:pathArray, map:map }) var polyline = new AMap.Polyline({ path: areapath1, isOutline: true, outlineColor: '#ffeeff', borderWeight: 3, strokeColor: "#3366FF", strokeOpacity: 1, strokeWeight: 2, // 折线款式还反对 'dashed' strokeStyle: "solid", // strokeStyle是dashed时无效 strokeDasharray: [10, 5], lineJoin: 'round', lineCap: 'round', zIndex: 50, }) var polyline2 = new AMap.Polyline({ path: areapath2, isOutline: true, outlineColor: '#ffeeff', borderWeight: 3, strokeColor: "#3366FF", strokeOpacity: 1, strokeWeight: 2, // 折线款式还反对 'dashed' strokeStyle: "solid", // strokeStyle是dashed时无效 strokeDasharray: [10, 5], lineJoin: 'round', lineCap: 'round', zIndex: 50, }) polyline.setMap(map) polyline2.setMap(map) //实例化信息窗体var title = 'xxx<span style="font-size:11px;color:#F00;">停车场</span>',content = [];content.push("停放车辆:300/320");content.push("长期停车:100/120");content.push("月卡停车:200/200");content.push("<a href='https://ditu.amap.com/detail/B000A8URXB?citycode=110105'>详细信息</a>");var infoWindow = new AMap.InfoWindow({ isCustom: true, //应用自定义窗体 content: createInfoWindow(title, content.join("<br/>")), offset: new AMap.Pixel(16, -45)});//构建自定义信息窗体function createInfoWindow(title, content) { var info = document.createElement("div"); info.className = "custom-info input-card content-window-card"; //能够通过上面的形式批改自定义窗体的宽高 //info.style.width = "400px"; // 定义顶部题目 var top = document.createElement("div"); var titleD = document.createElement("div"); var closeX = document.createElement("img"); top.className = "info-top"; titleD.innerHTML = title; closeX.src = "https://webapi.amap.com/images/close2.gif"; closeX.onclick = closeInfoWindow; top.appendChild(titleD); top.appendChild(closeX); info.appendChild(top); // 定义中部内容 var middle = document.createElement("div"); middle.className = "info-middle"; middle.style.backgroundColor = 'white'; middle.innerHTML = content; info.appendChild(middle); // 定义底部内容 var bottom = document.createElement("div"); bottom.className = "info-bottom"; bottom.style.position = 'relative'; bottom.style.top = '0px'; bottom.style.margin = '0 auto'; var sharp = document.createElement("img"); sharp.src = "https://webapi.amap.com/images/sharp.png"; bottom.appendChild(sharp); info.appendChild(bottom); return info;}//敞开信息窗体function closeInfoWindow() { map.clearInfoWindow();} addMarker();//增加marker标记function addMarker() { var carparkIcon = new AMap.Icon({ // 图标尺寸 size: new AMap.Size(35, 35), // 图标的取图地址 image: '停车场.png', // 图标所用图片大小 imageSize: new AMap.Size(35, 35), }); var CameraIcon = new AMap.Icon({ // 图标尺寸 size: new AMap.Size(35, 35), // 图标的取图地址 image: '摄像头.png', // 图标所用图片大小 imageSize: new AMap.Size(40, 40), }); var marker = new AMap.Marker({ map: map, position: [116.3246,40.01113], icon: carparkIcon }); var marker2 = new AMap.Marker({ map: map, position: [116.333515,39.99886], icon: carparkIcon }); var CameraMark = new AMap.Marker({ map: map, position: [116.327968,40.001001], icon: CameraIcon }); //鼠标点击marker弹出自定义的信息窗体 AMap.event.addListener(marker, 'click', function () { infoWindow.open(map, marker.getPosition()); }); AMap.event.addListener(marker2, 'click', function () { infoWindow.open(map, marker2.getPosition()); });} }</script> </head> <body onload="mapInit();"> <div id="container"></div> </body></html>

October 15, 2022 · 4 min · jiezi

关于javascript:如何使用-JavaScript-代码连接部署在-SAP-ABAP-服务器上的-OData-服务

本教程有一位读者通过《一套适宜 SAP UI5 开发人员循序渐进的学习教程》读者意见反馈和下一步写作计划表给我反馈: 本教程目前的步骤,都是生产本地 Mock Server 提供的假的 OData 服务,或者是 northwind 这种用于教学目标的 OData 服务,还没有真正生产过 SAP 零碎上的 OData 服务。本步骤咱们就来补救这个知识点的空缺。 首先咱们得有一个可能失常运行的,部署在 SAP ABAP 零碎上的 OData 服务。 笔者会花工夫在网络上搜寻 SAP 是否提供了此类用于教学目标的,可能 通过公网拜访,并且反对创立性能 的 OData 服务。 与此同时,大家也能够利用本人 ABAP 零碎上现成的 OData 服务。当然,也能够用笔者另一份教程 SAP OData 开发实战教程 - 从入门到进步 介绍的步骤,一步步在本人的 ABAP 零碎上开发一个图书治理的 OData 服务,而后用来依照笔者这份 SAP UI5 教程,应用 SAP UI5 利用来生产这些 ABAP OData 服务。 咱们本着先易后难的准则,先不急于应用 SAP UI5 利用的 OData API 进行生产,而是用简略的 JavaScript 原生 API fetch ,来尝试读取 SAP ABAP OData 服务,看此过程中会遇到什么问题。 ...

October 15, 2022 · 1 min · jiezi

关于javascript:关于-Angular-HTTP-Interceptor-中-Request-和-Response-的-immutable-特性

只管拦截器可能批改申请和响应,但 HttpRequest 和 HttpResponse 实例属性为 readonly,这意味着其具备 immutability 个性。 这种个性是 Angular 框架无意为之的设计:应用程序可能会在一个 HTTP 申请胜利实现之前,多次重试申请。换言之,这意味着 Interceptor chain 能够屡次重新处理(re-process)雷同的申请。 如果拦截器能够批改原始申请对象,则重试操作将从批改后的申请开始,而不是从原始申请开始,这会给应用程序的解决引入极大的不确定性。 因而,Angular Interceptor 解决上下文中的 HTTP 申请和响应的 immutability 个性,确保拦截器在每次尝试中解决的是雷同的申请。 TypeScript 阻止开发人员设置 HttpRequest 对象中具备 readonly 的属性,看个具体的例子: // Typescript disallows the following assignment because req.url is readonlyreq.url = req.url.replace('http://', 'https://');如果应用程序里必须更改 HTTP Request,请先克隆它并批改克隆,而后再将其传递给 next.handle()。 能够在一个步骤中克隆和批改申请,如以下示例所示: // clone request and replace 'http://' with 'https://' at the same timeconst secureReq = req.clone({ url: req.url.replace('http://', 'https://')});// send the cloned, "secure" request to the next handler.return next.handle(secureReq);上面是 SAP Spartacus Interceptor 中应用 clone 办法去批改一个 HTTP Request 的具体例子: ...

October 15, 2022 · 1 min · jiezi

关于javascript:React魔法堂echartsforreact源码略读

前言在以后工业4.0和智能制作的产业降级浪潮当中,智慧大屏无疑是展现企业IT成绩的最无效形式之一。然而其背地怎么能短少ECharts的身影呢?对于React利用而言,间接应用ECharts并不是最高效且优雅的形式,而echarts-for-react则是针对React利用对ECharts进行轻量封装和加强的工具库。 echarts-for-react的源码十分精简,本文将针对次要逻辑剖析介绍。 从与原生初始化比照开始原生ECharts中咱们会通过如下代码初始化图表实例 <div id="container" style="width: 100px; height: 100px"></div><script> const chart = echarts.init(document.getElementById('container'))</script>那么生成的HTML Element构造为 <div id="container" style="width: 100px; height: 100px" _echarts_instance="....."> <div style="width: 100px; height: 100px;position: relative;"> <canvas width="100" height="100"></canvas> </div></div>其中第二层的div和canvas的宽高默认为容器div#container的宽高,咱们能够通过init入参指定两者宽度。 const chart = echarts.init( document.getElementById('container'), null, { width: 300, // 可显式指定实例宽度,单位为像素。如果传入值为null/undefined/'auto',则示意主动取 dom(实例容器)的宽度 height: 300 // 可显式指定实例高度,单位为像素。如果传入值为null/undefined/'auto',则示意主动取 dom(实例容器)的高度 })留神:若此时容器div#container尺寸发生变化,第二层div和canvas尺寸并不会自适应,须要咱们手工调用chart.resize()触发。 而通过echarts-for-react上述步骤将被简化为如下,并且生成雷同的HTML Element构造: import ReactECharts from 'echarts-for-react'function Demo() { return ( <ReactECharts style={{width: 100, height: 100}} // 设置容器的宽高 autoResize={true} // 默认为true,主动监测容器尺寸的变动,并调用`chart.resize()` /> )}陷阱-默认值height为300px因为ReactECharts的style默认内置height: 300,源码如下: // src/core.tsxrender(): JSX.Element { const { style, className = '' } = this.props const newStyle = { height: 300, ...style } return ( <div ref={(e: HTMLElement) => { this.ele = e }} style={newStyle} className={`echarts-for-react ${className}`} /> )}因而通过className的形式设置容器高度时必须应用!important ...

October 14, 2022 · 1 min · jiezi

关于javascript:优雅的单行js代码

1.单行代码实现数组求和const sum=(arr)=>[...new set(arr)]console.log(sum[1,23,56,89]);

October 14, 2022 · 1 min · jiezi

关于javascript:写个JS深拷贝面试备用

深拷贝浅拷贝和赋值的原理及实现分析在工作中咱们常常会用到深拷贝与浅拷贝,然而你有没有去剖析什么场景下应用它,为什么须要应用呢,深浅拷贝有何异同呢,什么是深拷贝呢,如何实现呢,你会有这些问题吗,明天就为大家总结一下吧。 栈内存与堆内存区别浅拷贝---拷贝的是一个对象的指针,而不是复制对象自身,拷贝进去的对象共用一个指针,其中一个扭转了值,其余的也会同时扭转。深拷贝---拷贝进去一个新的对象,开拓一块新的空间,拷贝前后的对象互相独立,相互不会扭转,领有不同的指针。简略的总结下,假如有个A,咱们拷贝了一个为B,就是批改A或者B的时候看看另一个会不会也变动,如果扭转A的值B也变了那么就是浅拷贝,如果扭转A之后B的值没有发生变化就是深拷贝,当然这是根底了解,上面咱们一起来剖析下吧。 赋值/** demo1根本数据类型 */let a = 1;let b = a;b = 10;console.log(a,b)// 1 10/** demo2援用数据类型 */let a = { name: '小九', age: 23, favorite: ['吃饭','睡觉','打豆豆']}let b = a;a.name = '小七'a.age = 18a.favorite = ['下班','上班','加班']console.log(a,b)/** { name: '小七', age: 18, favorite: [ '下班', '上班', '加班' ] } { name: '小七', age: 18, favorite: [ '下班', '上班', '加班' ] }*/通过看下面的例子能够看出通过赋值去拿到新的值,赋值对于根本数据来说就是在栈中新开了一个变量,相当于是两个独立的栈内存,所以互相不会影响,然而对于援用数据类型,他只是复制了一份a在栈内存的指针,所以两个指针指向了同一个堆内存的空间,通过任何一个指针扭转值都会影响其余的,通过这样的赋值能够产生多个指针,然而堆内存的空间始终只有一个,这就是赋值产生的问题,咱们在开发中当然不心愿扭转B而影响了A,所以这个时候就须要用到浅拷贝和深拷贝了。 针对根本数据类型,轻易赋值都不会相互影响针对援用数据类型,赋值就会呈现咱们不想看到的,改变一方单方都变动。浅拷贝Object.assign()/** Object.assign */let A = { name: '小九', age: 23, sex: '男'}let B = Object.assign( {}, A);B.name = '小七'B.sex = '女'B.age = 18console.log(A,B)/** { name: '小九', age: 23, sex: '男' } { name: '小七', age: 18, sex: '女' } */首先实现浅拷贝的第一个办法是通过 Object.assign()这个 办法,Object.assign() 办法用于将所有可枚举属性的值从一个或多个源对象复制到指标对象。它将返回指标对象。 ...

October 14, 2022 · 6 min · jiezi

关于javascript:手撕常见JS面试题

高阶函数实现AOP(面向切面编程) Function.prototype.before = function (beforefn) { let _self = this; // 缓存原函数的援用 returnfunction () { // 代理函数 beforefn.apply(this, arguments); // 执行前置函数 return _self.apply(this, arguments); // 执行原函数 } } Function.prototype.after = function (afterfn) { let _self = this; returnfunction () { letset = _self.apply(this, arguments); afterfn.apply(this, arguments); returnset; } } let func = () => console.log('func'); func = func.before(() => { console.log('===before==='); }).after(() => { console.log('===after==='); }); func();输入后果: ===before===func===after=== 当咱们 new 一个类的时候 都产生了什么/** * new2 new关键字的代码实现演示 * @param {function} func 被new的类 (构造函数) */function new2(func) { // 创立了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象 let o = Object.create(func.prototype); // (在构造函数中this指向以后实例)让这个类作为一般函数值行 并且外面this为实例对象 let k = func.call(o); // 最初再将实例对象返回 如果你在类中显示指定返回值k, // 留神如果返回的是援用类型则将默认返回的实例对象o代替掉 return typeof k === 'object' ? k : o;}// 试验functionM() { // 行将被new的类 this.name = 'liwenli';}let m = new2(M); // 等价于 new M 这里只是模仿console.log(m instanceof M); // instanceof 检测实例console.log(m instanceof Object);console.log(m.__proto__.constructor === M);Object.create 兼容实现let obj1 = {id: 1}; Object._create = (o) => { let Fn = function() {}; // 长期的构造函数 Fn.prototype = o; return new Fn; } let obj2 = Object._create(obj1); console.log(obj2.__proto__ === obj1); // true console.log(obj2.id); // 1 // 原生的Object.create let obj3 = Object.create(obj1); console.log(obj3.__proto__ === obj1); // true console.log(obj3.id); // 1currying 函数柯理化curry 柯理化的实现(递归调用 + valueOf)知识点:valueOf 浏览器环境下 当咱们以log(fn)这种模式取值时,会隐式调用fn本身的valueOf 所以失去的是valueOf的返回值functionfn() {};fn.valueOf = () => console.log('valueof');console.log(fn); // valueofconst mul = x => { const result = y => mul(x * y); // 递归调用mul result.valueOf = () => x; return result;}console.log(mul(2)(3)); // 6// 在下面mul每执行一次,就会返回一个valueOf被改写后的新函数result 并且result执行会在外面调用mul(x * y)// 在result函数的valueOf里保留着 由上一次x * y 传进来的后果x, 也就是上一次x*y 会作为这一次的输入 仍然叫x// 第一次mul(2) 此时 x为2 return resultresult 为 result = y => mul(2 * y); // 第二次 mul(2)(3) 等价于 第一个mul返回的result(3), result执行 => mul(2 * 3) 再次调用mul 将2*3 = 6 的后果作为mul参数// 最初mul(6) x = 6 在返回一个新函数result 此时result的valueOf = () => 6// log(mul(2)(3)) 相当于log的最初返回的result 隐式调用valueOf 返回 6curry 将多参数函数转换为接管繁多参数的函数function fe(a, b, c) { return a + b + c;}function curry(fe) { let args = []; // 参数汇合 let len = args.length; returnfunctionbar() { args = [...args, ...arguments]; // 收集参数 if (args.length >= fe.length) { return fe.apply(this, args); } return bar; }}console.log(curry(fe)(1)(2)(3)); // 6currying 局部求值 // currying 函数柯理化 let currying = function(fn) { let args = []; returnfunctionfe() { if (arguments.length === 0) { return fn.apply(this, args); } [].push.apply(args, arguments); return fe; } } let count = currying(function (...rest) { return rest.reduce((prev, cur) => prev + cur, 0); }); console.log(count(100)(200)(10)()); // 310收集参数 提早执行 达到指定次数才执行 // 参数收集 指定次数后执行 function fn(...rest) {console.log(rest);}; function after(fn, time = 1) { let params = []; returnfunction(...rest) { params = [...params, ...rest]; if (--time === 0) { fn.apply(this, params); } } } let newFn = after(fn, 3); // 执行3次 外部fn才会执行 newFn(2); newFn(3); newFn(4);函数节流throttle 策略的电梯。保障如果电梯第一个人进来后,50毫秒后准时运送一次,不期待。如果没有人,则待机。let throttle = (fn, delay = 50) => { // 节流 管制执行间隔时间 避免频繁触发 scroll resize mousemove let stattime = 0; returnfunction (...args) { let curTime = new Date(); if (curTime - stattime >= delay) { fn.apply(this, args); stattime = curTime; } } }防抖动debounce 策略的电梯。如果电梯里有人进来,期待50毫秒。如果又人进来,50毫秒期待从新计时,直到50毫秒超时,开始运送。参考 前端手写面试题具体解答let debounce = (fn, time = 50) => { // 防抖动 管制闲暇工夫 用户输出频繁 let timer; returnfunction (...args) { let that = this; clearTimeout(timer); timer = setTimeout(fn.bind(that, ...args), time); } }深度拷贝兼容写法(不包含原型属性)function deepCopy(obj) { if (typeof obj !== 'object') return obj; if (typeof window !== 'undefined' && window.JSON) { // 浏览器环境下 并反对window.JSON 则应用 JSON return JSON.parse(JSON.stringify(obj)); } else { let newObj = obj.constructor === Array ? [] : {}; for(let key in obj) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } return newObj; }}let obj = {a: 1, b: [12]};let newObj = deepCopy(obj);newObj.b[1] = 100;console.log(obj);console.log(newObj);Function的bind实现Function.prototype._bind = function(context) { let func = this; let params = [].slice.call(arguments, 1); returnfunction() { params = params.concat([].slice.call(arguments, 0)); func.apply(context, params); }}let obj = {id: 24}function fn1(a) { console.log(this, arguments);}let foo = fn1._bind(obj, obj.id);函数组合串联compose(koa reduce中间件)// 组合串联let fn1 = (a) => a + 1;let fn2 = (b) => b + 2;let fn3 = (c) => c + 3;let funs = [fn1, fn2, fn3];let compose = (func) => { return arg => func.reduceRight((composed, fn) => fn(composed), arg);}console.log(compose(funs)(100)); // 相当于fn1(fn2(fn3(100)))co函数function* fn(a) { a = yield a; let b = yield 2; let c = yield 3; return a + b + c;}function co(fn, ...args) { let g = fn(...args); return new Promise((resolve, reject) => { function next(lastValue) { let { value, done } = g.next(lastValue); if (done) { resolve(value); } else { if (value instanceof Promise) { value.then(next, (val) => reject(val)); } else { next(value) } } } next(); });}co(fn, 100).then(value => { console.log(value); // 105});如何被动停止Promise调用链const p1 = new Promise((resolve, reject) => { setTimeout(() => { // 异步操作 resolve('start') }, 1000);});p1.then((result) => { console.log('a', result); return Promise.reject('中断后续调用'); // 此时rejected的状态将间接跳到catch里,剩下的调用不会再持续}).then(result => { console.log('b', result);}).then(result => { console.log('c', result);}).catch(err => { console.log(err);});// a start// 中断后续调用bluebird.promisify实现(将异步回调函数api 转换为promise模式)// promisify.jsmodule.exports = { promisify(fn){ returnfunction () { var args = Array.from(arguments); return new Promise(function (resolve, reject) { fn.apply(null, args.concat(function (err) { if (err) { reject(err); } else { resolve(arguments[1]) } })); }) } }}// main.jsconst fs = require('fs');const {promisify} = require('./promisify.js');const readFile = promisify('fs.readFile'); // 转换异步读取// 异步文件 由回调函数模式变成promise模式readFile('./1.txt', 'utf8').then(data => { console.log(data);}).catch(err => { console.log(err);});window.requestAnimationFrame兼容性解决window._requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); };})();字符串是否合乎回文规定let str = 'My age is 0, 0 si ega ym.';办法一function palindrome(params) { params = params.replace(/[\W\s_]/ig, ''); return params.toLowerCase() === params.split('').reverse().join('').toLowerCase();}console.log(palindrome(str));办法二function palindrome(params) { params = params.replace(/[\W\s_]/ig, '').toLowerCase(); for (var i = 0, j = params.length-1; i<j; i++, j--) { if (params[i] !== params[j]) { returnfalse; } } returntrue;}解构// 将 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4}const targetArray = [1, [2, 3], 4];const formater = "[a, [b], c]";const destructuringArray = (values, keys) => { try { const obj = {}; if (typeof keys === 'string') { keys = JSON.parse(keys.replace(/\w+/g, '"$&"')); } const iterate = (values, keys) => keys.forEach((key, i) => { if(Array.isArray(key)) iterate(values[i], key) else obj[key] = values[i] }) iterate(values, keys) return obj; } catch (e) { console.error(e.message); }}数组展平将[[1, 2], 3, [[[4], 5]]] 展平为 [1, 2, 3, 4, 5]let arr = [[1, 2], 3, [[[4], 5]]]; // 数组展平function flatten(arr) { return [].concat( ...arr.map(x => Array.isArray(x) ? flatten(x) : x) )}二分查找const arr = [1, 2, 3, 4, 5, 6, 7, 8];// 二分查找 递归 由两头开始往两边查找 前提是有序的数组 返回对应的索引地位function binarySearch1(arr, dest, start = 0, end = data.length) { if (start > end) { return -1 } let midIndex = Math.floor((start + end) / 2); // 两头地位索引 let mid = arr[midIndex]; // 两头值 if (mid == dest) { return midIndex; } if (dest < mid) { // 要找的比两头值小 就从两头往结尾找 始终到0 return binarySearch1(arr, dest, 0, midIndex - 1); } if (dest > mid) { // 要找的比两头值大 就从两头往后找 始终到end完结 return binarySearch1(arr, dest, midIndex + 1, end); } return -1; // 找不到返回-1}console.log(binarySearch1(arr, 7, 3, 6)); // 6// 非递归 arr前提有序数组 (从小到大)返回对应的索引地位 function binarySearch2(arr, dest) { let max = arr.length - 1; let min = 0; while (min <= max) { let mid = Math.floor((max + min) / 2); // mid两头地位索引 if (dest < arr[mid]) { // 如果要找的这项比两头项还要小 阐明应该在mid两头地位后面 批改最大边界值max=mid-1 max = mid - 1; } elseif (dest > arr[mid]) { // 如果要找的这项比两头项还要大 阐明应该在mid两头地位的前面 批改最小边界值min=mid+1 min = mid + 1; } else { return mid; } } return -1; // 找不到返回-1}console.log(binarySearch2(arr, 3)); // 2找出数组中反复呈现过的元素// 例如:[1,2,4,4,3,3,1,5,3]// 输入:[1,3,4]let arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];// 办法一function repeat1(arr){ var result = [], map = {}; arr.map(function(num){ if(map[num] === 1) result.push(num); // 等于1阐明之前呈现过一次 这次反复呈现了 map[num] = (map[num] || 0) + 1; // 奥妙之处 开始第一次呈现无值 记为 0 + 1 = 1 下一次从1开始累加 }); return result;}console.log(repeat1(arr));// 办法二function repeat(arr) { let result = arr.filter((x, i, self) => { return self.indexOf(x) === i && self.lastIndexOf(x) !== i }); // return result;}console.log(repeat(arr));数组中依照数字反复呈现的次数进行排序// 如果次数雷同 则依照值排序 比方 2, 2, 2和 1, 1, 1 应排序为 [1, 1, 1, 2, 2, 2]// 比方 [1,2,1,2,1,3,4,5,4,5,5,2,2] => [3, 4, 4, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2]let arr = [9, 7, 7, 1, 2, 1, 2, 1, 3, 4, 5, 4, 5, 5, 2, 2];function sortArray(arr) { let obj = {}; let newArr = []; for(let i = 0; i < arr.length; i++) { let cur = arr[i]; if(obj[cur]){ obj[cur].push(cur); continue; } obj[cur] = [cur]; } for(let k in obj) { if(obj.hasOwnProperty(k)) { newArr.push(obj[k]) } } newArr.sort((a, b) => { if(a.length === b.length){ return a[0] - b[0]; } return a.length - b.length; }); newArr = newArr.reduce((prev, cur) => prev.concat(cur)); return newArr; } console.log(sortArray(arr)); // [ 3, 9, 4, 4, 7, 7, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2 ]不必循环,创立一个长度为 100 的数组,并且每个元素的值等于它的下标。// 办法一 递归写法function createArray(len, arr = []) { if (len > 0) { arr[--len] = len; createArray(len, arr); } return arr;}console.log(createArray(100)); // 办法二// 上面评论中@MaDivH 提供的实现办法 长度为 100 的数组 Array(100).fill().map((_,i)=>i+1);// 办法三[...Array(100).keys()]依据关键词找出 所在对象idvar docs = [ { id: 1, words: ['hello', "world"] }, { id: 2, words: ['hello', "hihi"] }, { id: 3, words: ['haha', "hello"] }, { id: 4, words: ['world', "nihao"] }];findDocList(docs, ['hello']) // 文档id1,文档id2,文档id3findDocList(docs, ['hello', 'world']) // 文档id1function findDocList(docs, word = []) { if (word.constructor !== Array) return; let ids = []; for (let i = 0; i < docs.length; i++) { let {id, words} = docs[i]; let flag = word.every((item) => { return words.indexOf(item) > -1; }); flag && ids.push(id); } return ids;}findDocList(docs, ['hello', 'world']);getElementsByClassName 兼容写法function getByClass(cName) { if ('getElementsByClassName'in this) { return this.getElementsByClassName(cName); } cName = cName.replace(/(^\s+|\s+$)/g, '').split(/\s+/g); let eles = this.getElementsByTagName('*'); for (let i = 0; i < cName.length; i++) { let reg = new RegExp(`(^| )${cName[i]}( |$)`); let temp = []; for (let j = 0; j < eles.length; j++) { let cur = eles[j]; let {className} = cur; if (reg.test(className)) { temp.push(cur); } } eles = temp; } return eles; } console.log(content.getByClass('c1 c2 '));插入排序插入排序 从后往前比拟 直到碰到比以后项 还要小的前一项时 将这一项插入到前一项的前面function insertSort(arr) { let len = arr.length; let preIndex, current; for (let i = 1; i < len; i++) { preIndex = i - 1; current = arr[i]; // 以后项 while (preIndex >= 0 && arr[preIndex] > current) { arr[preIndex + 1] = arr[preIndex]; // 如果前一项大于以后项 则把前一项往后挪一位 preIndex-- // 用以后项持续和后面值进行比拟 } arr[preIndex + 1] = current; // 如果前一项小于以后项则 循环完结 则将以后项放到 前一项的前面 } return arr;}抉择排序抉择排序 每次拿以后项与前面其余项进行比拟 失去最小值的索引地位 而后把最小值和以后项替换地位function selectSort(arr) { let len = arr.length; let temp = null; let minIndex = null; for (let i = 0; i < len - 1; i++) { // 把以后值的索引作为最小值的索引一次去比拟 minIndex = i; // 假如以后项索引 为最小值索引 for (let j = i + 1; j < len; j++) { // 以后项前面向一次比小 if (arr[j] < arr[minIndex]) { // 比假如的值还要小 则保留最小值索引 minIndex = j; // 找到最小值的索引地位 } } // 将以后值和比拟出的最小值替换地位 if (i !== minIndex) { temp = arr[i] arr[i] = arr[minIndex]; arr[minIndex] = temp; } } return arr;}冒泡排序冒泡排序 相邻两项进行比拟 如果以后值大于后一项 则替换地位function bubleSort(arr) { let length = arr.length; let temp = null; for (let i = 0; i < length - 1; i++) { // 管制轮数 let flag = false; // 以后这轮是否替换过标识 for (let l = 0; l < length - i - 1; l++) { // 管制每轮比拟次数 if (arr[l] > arr[l + 1]) { temp = arr[l]; arr[l] = arr[l + 1]; arr[l + 1] = temp; flag = true; // 如果产生过替换flag则为true } } if (!flag) { // 优化 如果从头到尾比拟一轮后 flag仍然为false阐明 曾经排好序了 没必要在继续下去 temp = null; return arr; } }}疾速排序(递归)function quickSort(arr) { if (arr.length <= 1) return arr; let midIndex = Math.floor(arr.length / 2); let midNum = arr.splice(midIndex, 1)[0]; let left = []; let right = []; for(let i = 0; i < arr.length; i++) { let cur = arr[i]; if (cur <= midNum) { left.push(cur); } else { right.push(cur); } } return quickSort(left).concat(midNum, quickSort(right));}let arr = [2, 4, 12, 9, 22, 10, 18, 6];quickSort(arr);数组去重几种办法const arr = [1, 2, 1, 2, 3, 4, 2, 1, 3];// 1 ES6let newArr = [...new Set(arr)];// 2const arr = [1, 2, 1, 2, 3, 4, 'l', 2, 1, 3, 'l'];const newArr = arr.filter(function(ele, index, array) { return index === array.indexOf(ele)});console.log(newArr); // [ 1, 2, 3, 4, 'l' ]// 3Array.prototype.unique2 = function() { let newArr = []; let len = this.length; for(let i = 0; i < len; i++) { let cur = this[i]; if(newArr.indexOf(cur) === -1) { newArr[newArr.length] = cur; } } return newArr;}console.log(arr.unique1());// 4Array.prototype.unique3 = function() { let newArr = this.slice(0); let len = this.length; let obj = {}; for(let i = 0; i < len; i++) { let cur = newArr[i]; if(obj[cur]) { newArr[i] = newArr[newArr.length - 1]; newArr.length--; i--; continue; } obj[cur] = cur; } return newArr;}console.log(arr.unique3());// 5Array.prototype.unique4 = function() { let json = {}, newArr = [], len = this.length; for(var i = 0; i < len; i++) { let cur = this[i]; if (typeof json[cur] == "undefined") { json[cur] = true; newArr.push(cur) } } return newArr;}console.log(arr.unique4());千分符办法一// 解决数字let str1 = 2123456789;let str2 = 2123456789.12;console.log(str1.toLocaleString()); // 2,123,456,789console.log(str2.toLocaleString()); // 2,123,456,789.12办法二 // 解决字符串 let str1 = '2123456789'; let str2 = '2123456789.12'; // 利用正向预查 匹配 结尾一个数字\d 前面匹配这个数字前面必须是三个数字为一组为结尾或小数为结尾 function thousandth(str) { let reg = /\d(?=(?:\d{3})+(?:\.\d+|$))/g; return str.replace(reg, '$&,'); } console.log(thousandth(str1)); // 2,123,456,789 console.log(thousandth(str2)); // 2,123,456,789.12在一个数组中 如a、b两项, 要保障a和b两项的差 与 a和b两项索引的差 的相加后的后果max 是数组中其余两项max 中的最大值 找出符合条件两项a, b的值 (不能够排序 或扭转数组地位) 如:let max = (a - b) + (a的索引- b的索引);求a b ...

October 14, 2022 · 12 min · jiezi

关于javascript:从零开始实现一个Promise

1.Promise产生背景及标准家喻户晓,Promise是ES6引入的新个性,旨在解决回调天堂。上面是一个简略的例子:管制接口调用程序: apiA-->apiB-->apiC。简单的业务,开发人员会裂开。后生在此向老前辈致敬。 // 回调天堂apiA({ handleSuccess(resA){ apiB({ handleSuccess(resB){ apiC({ handleSuccess(resC){ } }) } }) }})因而Promise/A+标准应运而生,ES6的Promise就是遵循标准开发进去的。 2. 同步Promise浏览标准可得上面几点根本要求: Promise存在三个状态:pending(期待态)、fulfilled(胜利态)、rejected(失败态)pending为初始态,并能够转化为fulfilled和rejected胜利时,不可转为其余状态,且必须有一个不可扭转的值(value)失败时,不可转为其余状态,且必须有一个不可扭转的起因(reason)new Promise(executor=(resolve,reject)=>{resolve(value)}),resolve(value)将状态置为 fulfillednew Promise(executor=(resolve,reject)=>{reject(reson)}),reject(reson)将状态置为 rejected若是executor运行异样执行reject()thenable:then(onFulfilled, onRejected) onFulfilled:status为fulfilled,执行onFulfilled,传入valueonRejected:status为rejected,执行onRejected,传入reason// 1.Promise存在三个状态:pending(期待态)、fulfilled(胜利态)、rejected(失败态)const STATUS_PENDING = 'pending'const STATUS_FULFILLED = 'fulfilled'const STATUS_REJECTED = 'rejected'class myPromise { constructor(executor) { // pending为初始态,并能够转化为fulfilled和rejected this.status = STATUS_PENDING this.value = '' // 3 this.reason = '' // 4 let resolve = value => { // 5. if (this.status === STATUS_PENDING) { this.status = STATUS_FULFILLED this.value = value } } let reject = reason => { //6. if (this.status === STATUS_PENDING) { this.status = STATUS_REJECTED this.reason = reason } } // 7. try { executor(resolve, reject); } catch (err) { reject(err); } } // 8. then(onFulfilled = () => {}, onRejected = () => {}) { // 8.1 if (this.status === STATUS_FULFILLED) { onFulfilled(this.value) } // 8.2 if (this.status === STATUS_REJECTED) { onRejected(this.reason) } }}new myPromise(resolve => { console.log('before resolve') resolve(1)}).then(res => { console.log(res)})new myPromise((resolve, reject) => { console.log('before reject') reject('reject error')}).then(res => { console.log(res)}, error => { console.log(error)})3. 异步Promisenew myPromise(resolve => { console.log('before resolve') setTimeout(()=>{ resolve(1) },1000)}).then(res => { console.log(res)})promise的状态只能在resolve或者reject的时候扭转,同步代码执行到then回调的时候promise的状态还是pending,明细不合乎咱们的冀望。 ...

October 14, 2022 · 5 min · jiezi

关于javascript:sourcetree-husky-npm-command-not-found

新建/批改.huskyrc文件vim ~/.huskyrc export PATH="/usr/local/bin/:$PATH" 默认npm在/usr/local/bin下, 如果不是, 则手动查找npm所在位置, 而后再做相应的export Pathwhere npm

October 14, 2022 · 1 min · jiezi

关于javascript:利用a标签实现文件下载功能ant-design-vue可用

# 利用a标签实现文件下载性能(ant design vue可用)``````## 代码和正文``````let fileUrl = “” //所下载文件的网络地址let fileName = “” //下载胜利后保留的文件名 //创立一个a标签const link = document.createElement('a'); //设置是否在以后页面关上,target其余取值及含意:_blank:新窗口关上。_parent:在父窗口中关上链接。_self:默认,以后页面跳转。_top:在以后窗体关上链接,并替换以后的整个窗体(框架页)。link.target = '_blank' //如果该值为‘_self'该行可疏忽 //将a标签暗藏起来link.style.display = 'none'; // 给a标签设置下载的网络地址link.href = URL.createObjectURL(blob); //设置保留下来的文件名link.download = fileName + '.pdf'; //将a标签增加到文档对象中document.body.appendChild(link); //触发相应事件link.click(); //移除a标签link.remove(); //进行到这一步对于一些浏览器或者一些格局曾经胜利啦然而对于图片、文档可能它是预览状态,解决形式 参考上面办法fetch(fileUrl).then(res => res.blob()).then((blob) => {           link.href = URL.createObjectURL(blob);          link.download = fileName + '.pdf';           document.body.appendChild(link);          link.click(); ...

October 14, 2022 · 1 min · jiezi

关于javascript:NodeJS-服务-Docker-镜像极致优化指北

这段时间在开发一个腾讯文档全品类通用的 HTML 动静服务,为了不便各品类接入的生成与部署,也适应上云的趋势,思考应用 Docker 的形式来固定服务内容,对立进行制品版本的治理。本篇文章就将我在服务 Docker 化的过程中积攒起来的优化教训分享进去,供大家参考。 以一个例子结尾,大部分刚接触 Docker 的同学应该都会这样编写我的项目的 Dockerfile,如下所示: FROM node:14WORKDIR /appCOPY . .# 装置 npm 依赖RUN npm install# 裸露端口EXPOSE 8000CMD ["npm", "start"]构建,打包,上传,零打碎敲。而后看下镜像状态,卧槽,一个简略的 node web 服务体积竟然达到了惊人的 1.3 个 G,并且镜像传输与构建速度也很慢: 要是这个镜像只须要部署一个实例也就算了,然而这个服务得提供给所有开发同学进行高频集成并部署环境的(实现高频集成的计划可参见我的 上一篇文章)。首先,镜像体积过大必然会对镜像的拉取和更新速度造成影响,集成体验会变差。其次,我的项目上线后,同时在线的测试环境实例可能成千上万,这样的容器内存占用老本对于任何一个我的项目都是无奈承受的。必须找到优化的方法解决。 发现问题后,我就开始钻研 Docker 的优化计划,筹备给我的镜像动手术了。 node 我的项目生产环境优化首先开刀的是当然是前端最为相熟的畛域,对代码自身体积进行优化。之前开发我的项目时应用了 Typescript,为了图省事,我的项目间接应用 tsc 打包生成 es5 后就间接运行起来了。这里的体积问题次要有两个,一个是开发环境 ts 源码并未解决,并且用于生产环境的 js 代码也未经压缩。 另一个是援用的 node_modules 过于臃肿。依然蕴含了许多开发调试环境中的 npm 包,如 ts-node,typescript 等等。既然打包成 js 了,这些依赖天然就该去除。 一般来说,因为服务端代码不会像前端代码一样裸露进来,运行在物理机上的服务更多思考的是稳定性,也不在乎多一些体积,因而这些中央个别也不会做解决。然而 Docker 化后,因为部署规模变大,这些问题就非常明显了,在生产环境下须要优化的。 对于这两点的优化的形式其实咱们前端十分相熟了,不是本文的重点就粗略带过了。对于第一点,应用 Webpack + babel 降级并压缩 Typescript 源码,如果放心谬误排查能够加上 sourcemap,不过对于 docker 镜像来说有点多余,一会儿会说到。对于第二点,梳理 npm 包的 dependencies 与 devDependencies 依赖,去除不是必要存在于运行时的依赖,不便生产环境应用 npm install --production 装置依赖。 ...

October 14, 2022 · 2 min · jiezi

关于javascript:22道js输出顺序问题你能做出几道

前言最近在筹备面试题,console的输入程序之前始终迷迷糊糊。 必备常识JS是单线程的单线程是 JavaScript 外围特色之一。这意味着,在 JS 中所有工作都须要排队执行,前一个工作完结,才会执行后一个工作。所以这就造成了一个问题:如果前一个工作耗时很长,后一个工作就不得不始终等着后面的工作执行完能力执行。比方咱们向服务器申请一段数据,因为网络问题,可能须要期待 60 秒左右能力胜利返回数据,此时只能期待申请实现,JS 能力去解决前面的代码。 同步工作和异步工作为了解决JS单线程带来的问题,JavaScript 就将所有工作分成了同步工作和异步工作。 同步工作(Synchronous)同步工作指的是以后一个(如果有)工作执行结束,接下来能够立刻执行的工作。这些工作将在主线程上顺次排队执行。也就是说排排队 //for(){} 和 console.log() 将会顺次执行,最终输入 0 1 2 3 4 done。for (let i = 0; i < 5; i++) {console.log(i)}console.log('done')异步工作(Asynchronous)异步工作绝对于同步工作,指的是不须要进入主线程排队执行,而是进入超车道、并车道。也就是工作队列中,造成一系列的工作。这些工作只有当被告诉能够执行的时候,该工作才会从新进入主线程执行。 //上面的 then() 办法须要期待 Promise 被 resolve() 之后能力执行,它是一个异步工作。最终输入 1 3 2。console.log(1)Promise.resolve().then(() => { console.log(2)})console.log(3)具体来说就是,所有同步工作会在主线程上顺次排队执行,造成一个执行栈(Execution ContextStack)。主线程之外,还存在一个工作队列。当异步工作有了运行后果,会在工作队列之中搁置对应的事件。当执行栈中的所有同步工作执行结束,工作队列里的异步工作就会进入执行栈,而后持续顺次执行。 异步工作(工作队列)能够分为 macrotasks(taskQueue):宏工作 task,也是咱们常说的工作队列 macrotasks 的划分:(留神先后顺序!) (1)setTimeout(提早调用)(2)setInterval(间歇调用)(3)setImmediate(Node 的立刻调用)(4)requestAnimationFrame(高频的 RAF)(5)I/O(I/O 操作)(6)UI rendering(UI 渲染)(7) 包裹在一个 script 标签中的 js 代码也是一个 Macrotasks留神: (1)每一个 macrotask 的回调函数要放在下一车的结尾去执行! (2)只有 setImmediate 可能确保在下一轮事件循环立刻失去解决 microtasks:微工作(也称 job)调度在以后脚本执行完结后,立刻执行的工作,以防止付出额定一个 task 的费用。 ...

October 14, 2022 · 6 min · jiezi

关于javascript:金九银十前端面试题总结附答案

代码输入后果async function async1() { console.log("async1 start"); await async2(); console.log("async1 end");}async function async2() { console.log("async2");}async1();console.log('start')输入后果如下: async1 startasync2startasync1 end代码的执行过程如下: 首先执行函数中的同步代码async1 start,之后遇到了await,它会阻塞async1前面代码的执行,因而会先去执行async2中的同步代码async2,而后跳出async1;跳出async1函数后,执行同步代码start;在一轮宏工作全副执行完之后,再来执行await前面的内容async1 end。这里能够了解为await前面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中。 介绍 Loader罕用 Loader: file-loader: 加载文件资源,如 字体 / 图片 等,具备挪动/复制/命名等性能;url-loader: 通常用于加载图片,能够将小图片间接转换为 Date Url,缩小申请;babel-loader: 加载 js / jsx 文件, 将 ES6 / ES7 代码转换成 ES5,抹平兼容性问题;ts-loader: 加载 ts / tsx 文件,编译 TypeScript;style-loader: 将 css 代码以<style>标签的模式插入到 html 中;css-loader: 剖析@import和url(),援用 css 文件与对应的资源;postcss-loader: 用于 css 的兼容性解决,具备泛滥性能,例如 增加前缀,单位转换 等;less-loader / sass-loader: css预处理器,在 css 中新增了许多语法,进步了开发效率;编写准则: 繁多准则: 每个 Loader 只做一件事;链式调用: Webpack 会按程序链式调用每个 Loader;对立准则: 遵循 Webpack制订的设计规定和构造,输出与输入均为字符串,各个 Loader 齐全独立,即插即用;响应式设计的概念及基本原理响应式网站设计(Responsive Web design)是一个网站可能兼容多个终端,而不是为每一个终端做一个特定的版本。 ...

October 14, 2022 · 4 min · jiezi

关于javascript:80的前端开发都答不上来的js异步面试题

最近面试中碰到了一道对于JS执行程序的题目,题目比拟根底,然而如果对于JS不熟的话,还是容易答不上来。再次记录和剖析此次面试题,心愿对大家有所帮忙。 async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } async function async2() { console.log("async2"); } console.log("js start"); setTimeout(function () { console.log("timeout"); }, 0); async1(); new Promise(function (resolve) { console.log("promise"); resolve(); }).then(function () { console.log("then"); }); console.log("js end");话不多说,先上后果 // 控制台输入后果 "js start" "async1 start" "async2" "promise" "js end" "async1 end" "then" "timeout"宏工作 微工作如果看官是个老手的话,看到下面的输入后果,必定一脸懵逼的,然而没关系,看完这篇文章您就懂了。想齐全明确下面这道题目,还须要理解JS的两个概念,没错,就是宏工作和微工作。 首先看官必定晓得JS是单线程,实现异步的办法就是定时器和es6+呈现的promise/async等,那么当初问题来了,既然es6呈现的新的异步形式,那么和之前的定时器相比,那个异步先执行呢? 宏工作(macro)task,能够了解为每段代码都是一个宏工作,没错JS的主程序也是宏工作。同时两个定时器异步的局部也是宏工作。 微工作microtask,能够了解是在以后 task 执行完结后立刻执行的工作。也就是在主程序执行实现之后立刻执行的局部。es6+呈现的promise,async都是微工作。在这里要记住一句话,微工作的优先级是高于宏工作的。 程序执行程序1、主程序 因为js是单线程的,同一时间只能有一段代码在执行,所以首先执行的就是JS的主程序。之前说主程序是宏工作,微工作优先级又比宏工作高,那为什么还先执行主程序这个宏工作呢? 这是因为:没有主程序去构建微工作,微工作又怎么会呈现呢,没有微工作的呈现,当然就去找到主程序这个宏工作了,所以优先级的说法没有谬误。 2、查看是否有异步工作 当上一个工作执行实现之后,程序会去检索是否有微工作,须要执行,如果有,就会先执行微工作。没有微工作但有宏工作,执行宏工作。没有工作,代码不在执行。 3、微工作 微工作代码执行,和失常的JS代码执行没有区别,从上往下编译执行!!!执行实现之后,会跳回到第二步。 4、宏工作 ...

October 14, 2022 · 1 min · jiezi

关于javascript:zsh配置spaceship-theme

zsh根底配置看这篇.Mac配置zsh 配置成果 插件装置spaceship官网 Oh-My-ZshClone this repo:git clone https://github.com/spaceship-prompt/spaceship-prompt.git "$ZSH_CUSTOM/themes/spaceship-prompt" --depth=1Symlink spaceship.zsh-theme to your oh-my-zsh custom themes directory:ln -s "$ZSH_CUSTOM/themes/spaceship-prompt/spaceship.zsh-theme" "$ZSH_CUSTOM/themes/spaceship.zsh-theme"Set ZSH_THEME="spaceship" in your vim ~/.zshrc.配置字体字体装置见上一篇文章.抉择字体: Meslo LG M DZ for Powerline 自定义git user信息展现git clone https://github.com/brucejcw/spaceship-git-user.git "$ZSH/plugins/spaceship-git-user" 批改.zshrcvim ~/.zshrc增加pluginplugins=(... spaceship-git-user)在source命令之后增加spaceship add git_user

October 13, 2022 · 1 min · jiezi

关于javascript:优雅地管理与同步个人工作环境dotfiles-manager

:::tip实现一个完满个性化规范的指标是:一个对立治理及备份所有利用配置形式。而本篇文章带来的,就是为了满足这个指标而诞生的最佳解决方案 Dotfiles manager。::: <!-- more --> 若想进步本人的开发效率,得心应手的工具是必不可少的。而各软件的配置品种繁多且各不相同,这须要咱们破费大量的工夫去学习和记忆。这给交叉应用各种工具的咱们带来了较大的记忆老本,而为了缩小这种记忆老本,咱们会想方法对立一个个性化规范,而后在每个软件中进行设置,以尽量匹配这一规范。 而实现一个完满个性化规范的指标是:一个对立治理及备份所有利用配置形式。而本篇文章带来的,就是为了满足这个指标而诞生的最佳解决方案 Dotfiles manager。 什么是dotfilesDotfiles manager,实际上就是治理dotfiles的命令管理器。而所谓dotfiles,是指文件名称以 . 为前缀的文件或文件夹的统称。对于unix-based的零碎来说,这样的文件名称在文件列表中处于不可见状态,即所谓的暗藏文件,须要通过-a的形式能力查看到。 这些文件多呈现在用户的根目录下,通常是给该零碎用户以及其所应用的软件存储一些个性化的配置,从而达到集体应用起来更贴合习惯,从而大幅晋升工作效率。如常见的terminal个性化配置.bashrc或.bash_profile, vim个性化配置.vimrc等等,都属于dotfiles的领域。 为什么须要Dotfiles manager工具的应用的准则应该是:让工具适应咱们,而不是让咱们习惯工具。因而,咱们会须要对工具进行合乎本人应用习惯的革新。小到各软件快捷键及alias的配置对立,大到设置一键执行的多利用联动的工具化脚本。当许多或罕用,或便当的配置被增加结束之后,给本人带来的效率晋升是无可比拟的。 而这样一来,软件配置将是一个高度定制化的货色,任何一个工具都须要工夫进行深度地打磨来合乎本人的应用习惯。问题还会越来越多,你会发现随着高度的定制化,只管解脱了特定软件特定操作对你的解放,但又仿佛被固定的设施深度绑定了,因为大量芜杂的配置使得你在更换新的开发环境的时候显得尤为艰巨,须要手动对每一个软件从新进行一遍设置。由此一来,配置的更新及多设施同步也是一大难题。 之前说到,一个完满的个性化规范有两点:极为不便的 这时候,就急需一个能对立治理及备份所有利用配置的形式来帮忙咱们实现这一指标。而对于反对文件或命令行配置的利用来说,这一指标的最佳解决方案就是Dotfiles manager。 有哪些软件可能被dotfiles manager反对从实践上来说,所有反对文件配置或命令行配置的利用及零碎都肯定能被Dotfiles manager反对。对于Linux来说,这简直包含所有软件。 对于我日常应用环境来说,目前须要用到dotfiles manager来治理配置的次要有如下一些性能: <img src="https://zakum-1252497671.cos.ap-guangzhou.myqcloud.com/image-20210120174747924.png" alt="image-20210120174747924" style="zoom:50%;" />

October 13, 2022 · 1 min · jiezi

关于javascript:利用-React-高阶组件实现一个面包屑导航

什么是 React 高阶组件React 高阶组件就是以高阶函数的形式包裹须要润饰的 React 组件,并返回解决实现后的 React 组件。React 高阶组件在 React 生态中应用的十分频繁,比方react-router 中的 withRouter 以及 react-redux 中 connect 等许多 API 都是以这样的形式来实现的。 <!-- more --> 应用 React 高阶组件的益处在工作中,咱们常常会有很多性能类似,组件代码反复的页面需要,通常咱们能够通过齐全复制一遍代码的形式实现性能,然而这样页面的保护可维护性就会变得极差,须要对每一个页面里的雷同组件去做更改。因而,咱们能够将其中独特的局部,比方承受雷同的查问操作后果、组件外同一的标签包裹等抽离进去,做一个独自的函数,并传入不同的业务组件作为子组件参数,而这个函数不会批改子组件,只是通过组合的形式将子组件包装在容器组件中,是一个无副作用的纯函数,从而咱们可能在不扭转这些组件逻辑的状况下将这部分代码解耦,晋升代码可维护性。 本人入手实现一个高阶组件前端我的项目里,带链接指向的面包屑导航非常罕用,但因为面包屑导航须要手动保护一个所有目录门路与目录名映射的数组,而这里所有的数据咱们都能从 react-router 的路由表中获得,因而咱们能够从这里动手,实现一个面包屑导航的高阶组件。 首先咱们看看咱们的路由表提供的数据以及指标面包屑组件所须要的数据: // 这里展现的是 react-router4 的route示例let routes = [ { breadcrumb: '一级目录', path: '/a', component: require('../a/index.js').default, items: [ { breadcrumb: '二级目录', path: '/a/b', component: require('../a/b/index.js').default, items: [ { breadcrumb: '三级目录1', path: '/a/b/c1', component: require('../a/b/c1/index.js').default, exact: true, }, { breadcrumb: '三级目录2', path: '/a/b/c2', component: require('../a/b/c2/index.js').default, exact: true, }, } ] }]// 现实中的面包屑组件// 展现格局为 a / b / c1 并都附上链接const BreadcrumbsComponent = ({ breadcrumbs }) => ( <div> {breadcrumbs.map((breadcrumb, index) => ( <span key={breadcrumb.props.path}> <link to={breadcrumb.props.path}>{breadcrumb}</link> {index < breadcrumbs.length - 1 && <i> / </i>} </span> ))} </div>);这里咱们能够看到,面包屑组件须要提供的数据一共有三种,一种是以后页面的门路,一种是面包屑所带的文字,一种是该面包屑的导航链接指向。 ...

October 13, 2022 · 3 min · jiezi