内容简介:最近在温习基础知识,如:jsonp 主要是用来解决跨域问题的。应用非常广泛,关于更多的解决跨域方案请看那
最近在温习基础知识,如: jsonp
, promise
, bind
, apply
, debounce
等。那通过什么来测试练习了,就想到了「百度搜索」功能满足上面的测试。
Jsonp
jsonp 主要是用来解决跨域问题的。应用非常广泛,关于更多的解决跨域方案请看 前端面试总结之:js跨域问题
那 jsonp
的原理是什么了?
比如我们定义一个函数 foo
,然后调用它
// 定义 function foo() { console.log('foo') } // 调用 foo() 复制代码
那我们将调用 foo()
的这段代码放在一个新建的 js
文件,比如 a.js
然后通过 script
标签引入 a.js
了
// a.js foo() 复制代码
function foo() { console.log('foo') } <script src="./a.js"></srcipt> 复制代码
jsonp
原理与之类似:
我们在本地定义好一个函数,如 jsonp_1234565
,然后将这个函数名通过特定标识符如 cb=jsonp_1234565
通过 script
的 src
属性去请求一个 js
资源(一个 get
请求),即动态创建 script
标签。如: <script src="https://www.baidu.com?a=1&b=2&cb=jsonp_1234565"></script>
后台通过 cb
这个特定标识符得到前端定义的函数名为 jsonp_1234565
然后将前端真正要的的数据放在 jsonp_1234565
的参数里,并将这个函数返回给前端如: jsonp_1234565({status: 0, data: {...}})
代码如下
function jsonp({url = '', data = {}, cb='cb'} = {}) { if (!url) return // myPromise 请看下面实现,可以用成功回调的,因为学习特意用了Promise return myPromise((resolve, reject) => { const cbFn = `jsonp_${Date.now()}` // 定义函数名 data[cb] = cbFn // 将函数名放在`cb`标识符里 const oHead = document.querySelector('head') const oScript = document.create('script') const src = `${url}?${data2Url(data)}` oScript.src = src oHead.appendChild(oScript) // 将script标签插入head,以发送get请求 // 定义函数,后台返回就调用 window[cbFn] = function(res) { res ? resolve(res) : reject('error') // 如果不用Promise用回调的话只需在参数中加个success参数然后调用即可 // success && success(res) oHead.removeChild(oScript) // 请求回来之后就没用了。如果不删除,每次请求之后就会创建一个script标签,导致页面很多的script标签,所以将它删除。用完了就扔,感觉有点过河拆桥的意思 window[cbFn] = null } }) } function data2Url(data) { return Object.keys(data).reduce((acc, cur) => { acc.push(`${cur}=${data[cur]}`) return acc }, []).join('&') } // 可以先把myPromise改成原生的Promise测试百度搜索的接口 jsonp({ url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', data: { wd: 'a' }, cb: 'cb' }).then(res => { console.log(res) }) 复制代码
测试建议用 EGOIST
开源的codepan.net/ 类似于 JSBin/CodePen/JSFiddle
Promise
简易版的 Promise
实现,没有遵循 A+
规范,查看原文 JavaScript Promises - Understand JavaScript Promises by Building a Simple Promise Example
new Promise((resolve, reject) => { // 一系列操作 伪代码 if (true) { resolve(res) } else { reject(err) } }) .then(fn1) .then(fn2) ... .catch(handleError) 复制代码
大致意思就是 Promise
这个类接受一个函数作为参数,这个参数函数又接受两个函数作为参数
new Promise
这个实例有 then
和 catch
这两个方法, then
和 catch
又都接受函数作为参数,并且可以链式调用
核心思路就是定义个数组 promiseChianFn
用来装 then
的回调函数, then
一次,就往 promiseChianFn
push
一条 then
的回调函数,当在调用 resolve
函数的时候,就循环执行 promiseChianFn
的函数
class myPromise { constructor(excuteFn) { this.promiseChianFn = [] this.handleError = () => {} // mybind 请看下面实现 this._resolve = this._resolve.mybind(this) this._reject = this._reject.mybind(this) // 立即执行 excuteFn(this._resolve, this._reject) } then(fn) { this.promiseChianFn.push(fn) return this // 原生Promise返回的是一个新的Promise } catch(handleError) { this.handleError = handleError return this } _resolve(val) { try { let storeVal = val // 循环执行,并把第一个函数执行的返回值赋值给storeVal 共下个函数接收 如: /** * .then(res => { * renturn 1 * }) * .then(res => { * console.log(res) // 1 * }) * */ this.promiseChianFn.forEach(fn => { storeVal = fn(storeVal) }) } catch(err) { this.promiseChianFn = [] this._reject(err) } } _reject(err) { this.handleError(err) } } // 现在可以用myPromise 测试上面的jsonp了 复制代码
apply
call
和 apply
都是用来改变函数的 上下文里面的this
的,即改变 this
指向。
注意:上下文包含 VO(variable Object--变量对象)
, 作用域链
和 this
这三个东西,具体请看 js引擎的执行过程(一)
// 将 foo里面的上下文指向 ctx foo.call(ctx, 1,2,3) foo.apply(ctx, [1,2,3]) 复制代码
call
和 apply
的原理就是 方法借用
在知乎上面看到一篇文章的比喻 猫吃鱼,狗吃肉
那猫要吃肉,就借用狗吃肉的方法 即 狗.吃肉.call(猫)
那狗要吃鱼,就借用猫吃鱼的方法 即 猫.吃鱼.call(狗)
// fn.call(ctx) 既然是方法借用,那就给ctx添加一个该方法就可以了 Function.prototype.myapply = function(ctx, args = []) { const hash = Date.now() // 用时间戳是防止 ctx 上面的属性冲突 ctx[hash] = this // 给 ctx 添加一个方法,this 就是 fn const res = ctx[hash](...args) delete ctx[hash] // 过河拆桥 return res } // call 的话,只需将 args = [] 改为 ...args 即可 // 测试 const a = { name: 'a', getName() { console.log(this.name) } } const b = { name: 'b' } a.getName() // a a.getName.myapply(b) // b 复制代码
bind
bind 返回一个新函数,并永久改变 this 指向,返回的新函数无论之后再怎么call,apply,bind都不会改变 this 指向了,并有偏函数的效果 如果要考虑 New
的情况请参照 MDN
// fn2 = fn.bind(ctx) Function.prototype.mybind = function(ctx, ...args1) { const _this = this return function(...args2) { // 永远指向 ctx return _this.myapply(ctx, args1.concat(args2)) } } // 测试 const fn = a.getName.mybind(b) fn() // b const fn2 = fn.bind(a) fn2() // b 复制代码
debounce
「百度搜索」并没有加入 debounce
,我们可以给他加个 debounce
看下效果
debounce
(防抖) 和 throttle
(节流)主要是用来做性能优化
debounce
就像压弹簧,只要手不松开,弹簧就不会弹起来,常见应用场景就是input输入框,我们在停止输入后才去做相关操作
throttle
就像拧紧水龙头,让水龙头隔一秒钟滴一滴水,常见应用场景为页面滚动优化
function debounce(cb, delay = 300) { let timer return function(...args) { timer && clearTimeout(timer) timer = setTimeout(() => { cb && cb.apply(this, args) }, delay) } } 复制代码
接下来我们模拟「百度搜索」加上 debounce
想传 GIF 传不了, 请上codepan.net/ 测试
全部代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <input type="text"> </body> </html> 复制代码
function jsonp({url = '', data = {}, cb = 'cb'} = {}) { return new myPromise((resolve, reject) => { if (!url) return const cbFn = `jsonp_${Date.now()}` data[cb] = cbFn const oHead = document.querySelector('head') const oScript = document.createElement('script') const src = `${url}?${data2Url(data)}` oScript.src = src oHead.appendChild(oScript) window[cbFn] = function(res) { resolve(res) oHead.removeChild(oScript) window[cbFn] = null } }) } function data2Url(data) { return Object.keys(data).reduce((acc, cur) => { acc.push(`${cur}=${data[cur]}`) return acc }, []).join('&') } class myPromise { constructor(excuteFn) { this.promiseChainFn = [] this.handleError = () => {} this._resolve = this._resolve.myBind(this) this._reject = this._reject.myBind(this) excuteFn(this._resolve, this._reject) } then(cb) { this.promiseChainFn.push(cb) return this } catch(handleError) { this.handleError = handleError return this } _resolve(res) { try { let storeVal = res this.promiseChainFn.forEach(fn => { storeVal = fn(storeVal) }) } catch(e) { this.promiseChainFn = [] this._reject(e) } } _reject(err) { return this.handleError(err) } } Function.prototype.myApply = function(ctx, args = []) { const hash = Date.now() ctx[hash] = this const res = ctx[hash](...args) delete ctx[hash] return res } Function.prototype.myBind = function(ctx, ...args1) { const _this = this return function(...args2) { return _this.myApply(ctx, args1.concat(args2)) } } function debounce(cb, delay = 300) { let timer return function(...args) { timer && clearTimeout(timer) timer = setTimeout(() => { cb && cb.myApply(this, args) }, delay) } } const oInput = document.querySelector('input') oInput.oninput = debounce(handleInput, 1000) function handleInput(v) { jsonp({ url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', data: { wd: this.value}, cb: 'cb' }).then(res => { console.log(res) return 1 }).then(res => { console.log(res) return 2 }).then(res => { console.log(res) }).catch(function a(err) { console.log(err) }) } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- [译] 通过集成学习提高机器学习结果
- 通过实例学习 tcpdump 命令
- 通过 Libra 学习 Protobuf
- [译] 通过测试学习 Go 语言
- 通过迁移学习实现OCT图像识别
- 机器学习业务实践之路:如何通过机器学习算法快速解决实际业务
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Designing for Emotion
Aarron Walter / Happy Cog / 2011-10-18 / USD 18.00
Make your users fall in love with your site via the precepts packed into this brief, charming book by MailChimp user experience design lead Aarron Walter. From classic psychology to case studies, high......一起来看看 《Designing for Emotion》 这本书的介绍吧!