内容简介:最近在温习基础知识,如: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图像识别
- 机器学习业务实践之路:如何通过机器学习算法快速解决实际业务
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据挖掘
(美)Jiawei Han、(加)Micheline Kamber、(加)Jian Pei / 范明、孟小峰 / 机械工业出版社 / 2012-8 / 79.00元
数据挖掘领域最具里程碑意义的经典著作 完整全面阐述该领域的重要知识和技术创新 这是一本数据挖掘和知识发现的优秀教材,结构合理、条理清晰。本书既保留了相当篇幅讲述数据挖掘的基本概念和方法,又增加了若干章节介绍数据挖掘领域最新的技术和发展,因此既适合初学者学习又适合专业人员和实践者参考。本书视角广阔、资料翔实、内容全面,能够为有意深入研究相关技术的读者提供足够的参考和支持。总之, 强烈推荐......一起来看看 《数据挖掘》 这本书的介绍吧!