jQuery源码学习-构造函数

jQuery 是一个快速、简洁的 JavaScript 框架,是继 Prototype 之后又一个优秀的 JavaScript 代码库(或 JavaScript 框架)。jQuery 设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装 JavaScript 常用的功能代码,提供一种简便的 JavaScript 设计模式,优化 HTML 文档操作、事件处理、动画设计和 Ajax 交互。 如此优秀的一个代码库,即便没有以前流行,也是有很多地方值得学习的。 本文并非讲解jquery源码,而是记录从源码学习到的思想、技术等 构造函数与继承 JavaScript是一门面向对象的编程语言,但是与传统面向对象语言不同的是,JavaScript没有提供class关键字,并且JavaScript的是基于原型继承的面向对象语言。那么,在JavaScript中编写类的过程,必定与传统的面向对象语言有所区别。 传统的类与继承 在ES6之前,都是通过创建构造函数的方式来创建一个类,比如: // Book类的原型 const BookPrototype = { getName() { return this.name }, } // Book类 function Book(name, author, content) { this.name = name this.author = author this.content = content } // Book类继承自 BookPrototype Book.prototype = BookPrototype const book = new Book('金瓶梅', '佚名', '此处省略999999999字') console.log(book.getName()) BookPrototype为Book类的原型(基类),使用new关键字创建的Book实例,都会包含BookPrototype里面的属性和方法。这里刚开始然我有些迷惑,参考经典的面向对象语言,基类应该也是一个类,这里却是一个对象字面量。首先,需要深刻明白一点: JavaScript 中,一切皆是对象。比如一个函数,我们可以给它加上任何的属性...

October 23, 2019 · 2 min · 347 words · Johnson

web-components实践

web-components是一个比较新的开发技术,但组件化思想已经实际应用了很长时间,代表性的组件话框架有vue, react等。web-components可以直接使用原生HTML和Javascript来封装通用的 web 组件,其拥有简单直接、无任何依赖等特点,是 web 前端开发的一个强有力的技术。 文章前半部分讲web-components相关接口,最后有一个进度条组件实例,点击这里可直接跳转。 Web-Components API Web-Components API有三个核心部分,分别是:Custom elements, Shadow DOM, HTML templates。 Custom elements custom elements顾名思义,允许我们自定义通用的 HTML 标签,可以是一个包含特定功能如:日历、时间显示、进度条等。 创建自定义元素 使用自定义元素,必须先使用customElements.define(name, constructor, options)方法注册元素后才能使用,参数分别为元素名称,元素构造函数,选项(可选)。自定义元素名称不能是单个字母。 生命周期 自定义元素生命周期有 4 个回调函数: connectedCallback:当 custom element 首次被插入文档 DOM 时调用。 disconnectedCallback:当 custom element 从文档 DOM 中删除时调用。 adoptedCallback:当 custom element 被移动时调用。 attributeChangedCallback:当 custom element 增加、删除、修改自身属性时调用。 Shadow DOM 通常情况下,我们封装 web 组件的时候,需要将组件内部的样式封装进去,同时也要避免被外部样式干扰。Shadow DOM是隐藏在一个 DOM 节点里面的节点树,主要特点就是于外部 DOM 树隔离,使组件维护成本大大降低。Shadow DOM并不是新鲜玩意儿,而是浏览器内部 API 实现,我们常用的一些标签如带控制按钮的video标签,其内部就是一个Shadow DOM树。 创建 Shadow DOM Shadow DOM有几个核心概念: Shadow host: 一个常规 DOM 节点,Shadow DOM 会被添加到这个节点上。 Shadow tree: Shadow DOM 内部的 DOM 树。 shadow root: Shadow tree的根节点。 Shadow root: Shadow tree 的根节点。 Shadow boundary: Shadow DOM 结束的地方,也是常规 DOM 开始的地方。 创建Shadow DOM需要用到attachShadow({ mode: 'open' })方法,参数中mode的取值有两个:open, closed,用来配置是否可以从外部获取shadow tree。该函数返回一个Shadow Root。...

October 3, 2019 · 2 min · 340 words · Johnson

Apollo-Client 文件上传

使用apollo-client上传文件 apollo 文件上传 依赖 apollo-client apollo-link apollo-upload-client apollo-cache-inmemory 客户端创建 apolloClient.js import { ApolloClient } from 'apollo-client' import { createUploadLink } from 'apollo-upload-client' import { ApolloLink, from } from 'apollo-link' import { InMemoryCache } from 'apollo-cache-inmemory' const authLink = new ApolloLink((operation: Operation, forward) => { operation.setContext({ headers: { Authorization: getToken(), }, }) return forward(operation) }) const uploadLink = createUploadLink({ uri: `${API_URL}`, }) export const apolloClient = new ApolloClient({ link: from([authLink, uploadLink]), // uploadLink一定要放最后 cache: new InMemoryCache(), }) 使用 uploadFile....

September 19, 2019 · 1 min · 117 words · Johnson

闭包与防抖函数

最近在开发过程中频繁用到防抖函数(提交表单、页面滚动等),比较 low 的解决方案一般都是定义一个全局变量作为控制函数执行的锁,这样的确能解决问题,但是一点都不优雅。于是仔细琢磨了一下防抖函数,其中涉及到了闭包,顺便复习一下。 什么是闭包 简单的讲闭包就是在函数里面定义的函数。在开发过程中我们经常会写。 function a() { let i = 0 console.log(i) function b() { i++ console.log(i) } return b } let fb = a() fb() // 0 // 1 上面代码中,函数 b 就是一个闭包。根据执行结果我们可以验证闭包的一个特性,即闭包中引用了父级作用域中的变量,当父级函数执行完毕之后,被引用的内部变量不会被回收。很容易理解,将上面的执行过程拆开来看: let fb = a() // a函数中声明变量i = 0,并且将i的值打印出来 // a函数中声明b函数,在b函数中将i的值增加1 // 将b函数返回,a函数执行完毕 fb() // i的值增加1,并将其打印出来 神奇的地方就在与,我们通过将 b 函数返回,实现了在 a 函数执行完毕后对其内部的局部变量进行访问和修改。因此闭包也被看作是连接一个函数内部和外部的桥梁。 防抖函数 从另一个角度思考闭包,可以看作父级函数为闭包创建了一个临时的作用域,其中的变量可以和外部隔绝。这样的话我们就可以把一些在特定位置才会使用的全局变量通过闭包的方式使用,使代码更加优雅。 以防抖函数为例,前面提到的解决方案是定义一个全局变量来控制函数的执行: let lock = false function submit() { if (!lock) { lock = true setTimeout(() => { console....

September 18, 2019 · 2 min · 253 words · Johnson

优雅的解决axios超时

在vue中经常使用axios发起网络请求,与服务器进行数据交互。在使用过程中会有许多问题存在,比如由于网络不稳定导致请求超时/失败,通常有两种解决方案,一种是提示用户重新提交请求,另一种是进行相关提示并自动重新发送请求。第二种方式用户体验明显高于第一种方式。本文就针对第二种方式设计一个解决方案。 DEMO(https://yes-lee.github.io/axios-tutorial/) 拦截器 基本所有的网络请求库都有拦截器接口,包括请求拦截器和响应拦截器。axios设置拦截器的接口为axios.interceptor.request.use(cb, errCb)和axios.intercepter.response.use(cb, errCb),拦截器可以设置多个,按照设置的顺序执行。 请求失败 请求失败无论是网络异常还是其他原因,都会走到响应拦截器里,因此对于请求结果的相关处理,重点放在响应拦截器上,而请求异常的处理则放在响应拦截器的第二个回调函数里面。 解决方案 思路很简单,在响应拦截器中拦截到异常信息,通过分析异常信息来直接从拦截器中重新发送请求。整个过程对于调用请求的业务代码中来说是毫无感知的,只需等待resolve结果即可。 实现 首先,经过测试得到请求超时的错误代码是ECONNABORTED,网络连接异常没有错误代码,可以直接通过error.message === 'Network Error'进行判断。 const httpClient = Axios.create(defaultConfig) httpClient.interceptors.response.use(null, err => { // 异常处理 const { config, code, message } = err if (code === 'ECONNABORTED' || message === 'Network Error') { // 请求超时 console.warn(`请求超时,将在${defaultConfig.retryDelay / 1000}秒后重试`) return new Promise(resolve => { setTimeout(async _ => { resolve(await httpClient.request(config)) }, defaultConfig.retryDelay) // 设置发送间隔 }) } // 可以进行相关提示等处理 return Promise.reject(err) }) 经过错误代码判断,需要进行重新发送的时候,将请求的 config 直接传入httpClient....

September 18, 2019 · 1 min · 76 words · Johnson