[前端原生技术]jsonp

简介: 什么是Jsonp?Jsonp的用途是什么?可以说Jsonp让我对前端的底层逻辑有了另一种思维,本篇文章是我为此作的笔记,如果文中阐述不全或不对的,多多交流。

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)

https://developer.aliyun.com/article/1440738

出自【 进步*于辰的博客


在学习了Jsoup这个知识点之后,发觉js的这一特点真的很强大——动态解释。

本文以实用性的角度对Jsonp进行阐述,故在有些方面不是很详细或“不到位”,推荐一篇博文《 jsonp原理详解——终于搞清楚jsonp是啥了》(转发)。

1、jsonp是什么?

在学习jsonp之前,我们先来了解一个概念——同源策略


同源策略”是为了保证服务器的安全性,从而对客户端请求进行一定限制的规定,规定当客户端与服务端在协议、域名(IP地址)、端口都相同时才允许访问。


若有一种不同时仍进行访问,称之为跨域访问


由于js文件的访问不受“同源策略”限制,故若将请求“模拟”成js请求(以引入js文件的形式发送请求,服务端响应js脚本),便可解决“跨域”问题,这种“模拟”或“格式约定”就是jsonp,它是一种非正式的传输协议。

示例:

<script src="http://127.0.0.1:8081/j1.js"></script>


除了js请求外,凡是具有src属性的标签同样不受“跨域”限制。

示例:

<img src="http://127.0.0.1:8081/1.jpg">
2 <iframe src="http://127.0.0.1:8081/1.html"></iframe>

2、jsonp的原理

2.1 基本思路

从以上对jsonp的简述可以得出如下结论:


jsonp的原理是由客户端发出请求,服务端响应js脚本,再对js脚本进行“动态解释”。因此,客户端的请求方法任意(前提是客户端处理服务端响应的运行环境使用的是解释)。

注意:这里说的是“请求方法”(如:、ajax),不是“请求方式”(如:get、post)。


这个结论与上面简述有所不同,上面说的是“以引入js文件 </span> 的形式发送请求(下文简称“js请求”)”,而这里却是“任意请求方法”,为什么?而且这段总结看起来云里雾里,下面我会用一个个示例逐步进行说明。。。</div><div><br /></div><div>大家注意一个关键词“<strong>动态解释</strong>”,什么意思呢?就是当服务端响应后,客户端对响应体进行解释,将其加入到当前上下文中,并同时执行。(这就是为何服务端响应必须是js脚本的原因)</div><div>可能不太好理解,举几个例子。</div><div><br /></div><div>客户端仍是:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22typescript%22%2C%22code%22%3A%22%3Cscript%20src%3D%5C%22http%3A%2F%2F127.0.0.1%3A8081%2Fj1.js%5C%22%3E%3C%2Fscript%3E%22%2C%22id%22%3A%22iqQjS%22%7D"></div><div><br /></div><div>1、若服务端中<code>j1.js</code>的代码是:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22typescript%22%2C%22code%22%3A%22console.log(%5C%221'm%20js%E8%84%9A%E6%9C%AC.%5C%22)%22%2C%22id%22%3A%22HUsR3%22%7D"></div><div>则会在控制台打印<code>1'm js脚本.</code>。</div><div><br /></div><div>2、若<code>j1.js</code>的代码是:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22var%20test%20%3D%20confirm('You%20need%20a%20js%E8%84%9A%E6%9C%AC%3F')%5Cnif(test)%5Cn%20%20%20%20console.log('1'm%20js%E8%84%9A%E6%9C%AC.')%5Cnelse%5Cn%20%20%20%20console.log('I%20can't%20help%20it.')%22%2C%22id%22%3A%22i3tw5%22%7D"></div><div>则会弹出确认框,显示的消息是<code>You need a js脚本?</code>。</div><div><br /></div><div>3、若<code>j1.js</code>的代码是:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22var%20script%20%3D%20document.querySelector('script')%5Cnconsole.log(script.src)%22%2C%22id%22%3A%22vwtzK%22%7D"></div><div>则会在控制台打印<code><a href="http://127.0.0.1:8081/j1.js" target="_blank">http://127.0.0.1:8081/j1.js</a></code>。</div><div><br /></div><div><span style="color: red;"><span style="color: red;">Y</span><span style="color: red;">o</span><span style="color: red;">u</span></span> <span style="color: red;"><span style="color: red;">a</span><span style="color: red;">re</span></span> <span style="color: red;"><span style="color: red;">O</span><span style="color: red;">K</span><span style="color: red;">。</span></span></div><div><span style="color: red;"><span style="color: red;"><br /></span></span></div><div>客户端是通过<code><script></code>标签发送js请求(写死了。。。),我们改一下。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22var%20script%20%3D%20document.createElement('script')%5Cnscript.src%20%3D%20'http%3A%2F%2F127.0.0.1%3A8081%2Fj1.js'%5Cndocument.body.append(script)%22%2C%22id%22%3A%227Dzwx%22%7D"></div><div>这种形式有一个名称——<span style="color: blue;"><span style="color: blue;">“</span><span style="color: blue;">动态添加</span><span style="color: blue;">j</span><span style="color: blue;">s</span><span style="color: blue;">脚本</span><span style="color: blue;">”</span></span>。</div><div><br /></div><div><span style="color: brown;"><span style="color: brown;">PS:</span></span>走到这一步,大家就知道jsonp是如何实现“跨域”了吧。。。</div><div><br /></div><div>可是,如果仅仅是这样,并没有什么意义,因为我们要实现的是客户端与服务端之间的数据交互。</div><div><br /></div><div>既然客户端都会对服务端响应的js脚本进行解释(动态解释),那么我们可以换一个思维。</div><div><br /></div><div>服务端怎样能在js脚本中将数传递给客户端呢?一个典型的办法:<span style="color: purple;"><span style="color: purple;">函数</span></span>。也就是这样:</div><div>客户端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22var%20script%20%3D%20document.createElement('script')%5Cnscript.src%20%3D%20'http%3A%2F%2F127.0.0.1%3A8081%2Fj1.js'%5Cndocument.body.append(script)%5Cnvar%20handlerRes%20%3D%20function(res)%20%7B%5Cn%5Ctconsole.log(res)%5Cn%7D%22%2C%22id%22%3A%22xpXBE%22%7D"></div><div><code>j1.js</code>的代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22typescript%22%2C%22code%22%3A%22handlerRes('I%5C%22m%20js%E8%84%9A%E6%9C%AC')%2F%2F%20%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E8%AF%AD%E5%8F%A5%5Cn%2F%2F%20%E5%9C%A8%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%AD%EF%BC%8ChandlerRes()%E7%A7%B0%E4%B8%BA%E2%80%9C%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E2%80%9D%22%2C%22id%22%3A%22FCjGK%22%7D"></div><div>这样就将字符串<code>'I"m js脚本'</code>传递给客户端了。</div><div><br /></div><div>还不够,这只是实现了服务端 → 客户端的数据传递,若要实现客户端 → 服务端的数据传递,则需要借助<code>request</code>对象,这就需要在服务端搭建服务器。</div><div><br /></div><blockquote><div>这里服务器是通过Node.js中的<code>express</code>模块进行搭建,详述可查阅博文《<a href="https://blog.csdn.net/m0_69908381/article/details/134544523" target="_blank">JS服务端技术—Node.js知识点锦集</a>》的第4项。</div></blockquote><div><br /></div><div>我们再改一下,也就是向服务器路由发送js请求。</div><div>客户端代码:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22var%20script%20%3D%20document.createElement('script')%5Cnscript.src%20%3D%20'http%3A%2F%2F127.0.0.1%3A8081%2Fg1%3Fuserid%3D1001'%5Cndocument.body.append(script)%5Cnvar%20handlerRes%20%3D%20function(res)%20%7B%5Cn%5Ctconsole.log(res)%5Cn%7D%22%2C%22id%22%3A%22c6X0V%22%7D"></div><div>服务器在<code>j1.js</code>中搭建,代码是:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22typescript%22%2C%22code%22%3A%22const%20express%20%3D%20require('express')%5Cnconst%20ser%20%3D%20express()%5Cnser.get('%2Fg1'%2C%20(req%2C%20res)%20%3D%3E%20%7B%5Cn%20%20%20%20var%20id%20%3D%20req.query.userid%5Cn%20%20%20%20console.log(id)%2F%2F%20%E6%89%93%E5%8D%B0%EF%BC%9A1001%5Cn%7D)%5Cnser.listen(8081%2C%20()%20%3D%3E%20%7B%5Cn%20%20%20%20console.log('create3')%5Cn%7D)%22%2C%22id%22%3A%22eKlRi%22%7D"></div><div>这样客户端 → 服务器的数据传递就实现了,可是服务器怎么响应呢?响应需要使用<code>res</code>对象,难道是这样?</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22res.send(id)%22%2C%22id%22%3A%22fekAI%22%7D"></div><div>没错,在F12的网络那里可以看到,响应是<code>1001</code>。的确成功响应了,可是没有意义(客户端没反应。。。或者说客户端处理不了这个<code>1001</code>)。怎么办呢?</div><div><br /></div><div>前面提到,只要服务器响应js脚本,客户端就可以进行“动态解释”。那么我们就把这个<code>1001</code>放进js脚本中,也就是这样:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22res.send('handlerRes('%20%2B%20id%20%2B')')%22%2C%22id%22%3A%22ZYqh2%22%7D"></div><div>这样响应体就是<code>'handlerRes(1001)'</code>,解释后打印<code>1001</code>。</div><div><br /></div><div>到这一步,基本功能实现了。。。既实现了客户端与服务器的数据交互(请求-响应),又解决了“跨域”问题。</div><h3 id="ad7Js"><a></a>2.2 扩展实现</h3><div>综上所述,jsonp解决“跨域”问题的代码是这样:</div><div>客户端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22var%20script%20%3D%20document.createElement('script')%5Cnscript.src%20%3D%20'http%3A%2F%2F127.0.0.1%3A8081%2Fg1%3Fuserid%3D1001'%5Cndocument.body.append(script)%5Cnvar%20handlerRes%20%3D%20function(res)%20%7B%5Cn%5Ctconsole.log(res)%5Cn%7D%22%2C%22id%22%3A%22DFkUo%22%7D"></div><div>服务端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22const%20express%20%3D%20require('express')%5Cnconst%20ser%20%3D%20express()%5Cnser.get('%2Fg1'%2C%20(req%2C%20res)%20%3D%3E%20%7B%5Cn%20%20%20%20var%20id%20%3D%20req.query.userid%5Cn%20%20%20%20res.send('handlerRes('%20%2B%20id%20%2B')')%5Cn%7D)%5Cnser.listen(8081%2C%20()%20%3D%3E%20%7B%5Cn%20%20%20%20console.log('create3')%5Cn%7D)%22%2C%22id%22%3A%222Qkmb%22%7D"></div><div>虽然功能实现了,不过代码的整体情况差强人意。例如:</div><div><br /></div><ol><li>每次客户端都需要动态添加<code><script></code>;(有点“冗余”)</li><li>服务器必须提前知道回调函数名是什么。</li></ol><div><br /></div><div>这样很不方便,我们可以再换个思维。。。在我们平时使用的技术中,哪一种跟上述这种情况类似?没错,<span style="color: red;"><span style="color: red;">aja</span><span style="color: red;">x</span></span>,于是客户端可以这样优化:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22%24.ajax(%7B%5Cn%20%20%20%20url%3A%20'http%3A%2F%2Flocalhost%3A8081%2Fg1%3Fuserid%3D1001'%2C%5Cn%20%20%20%20type%3A%20'get'%2C%5Cn%20%20%20%20success%3A%20res%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20console.log(res)%5Cn%20%20%20%20%7D%5Cn%7D)%5Cnvar%20handlerRes%20%3D%20function(res)%20%7B%5Cn%5Ctconsole.log(res)%5Cn%7D%22%2C%22id%22%3A%22s7syO%22%7D"></div><div>这样就出现了一个问题——<span style="color: red;"><span style="color: red;">跨域</span></span>。因为这只是一个普通的ajax异步请求,而不是js请求,自然受“同源策略”限制。</div><div><br /></div><div>无妨,我们在服务器加上这一条代码:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22res.set('access-control-allow-origin'%2C%20'*')%2F%2F%20%E5%85%81%E8%AE%B8%E4%BB%BB%E6%84%8F%E8%AF%B7%E6%B1%82%22%2C%22id%22%3A%225M6Dg%22%7D"></div><div>这是解决“跨域”问题的另一种方法(在此不讨论),不过,这样不就直接解决了“跨域”问题,与jsonp有什么关系?没错,的确没有直接关系。我这样写是为了便于大家看到效果,继续看。。。</div><div><br /></div><div>现在第一个问题解决了,第二个问题如何解决?我们可以给请求加一个参数<code>funName</code>,值为回调函数名。也就是这样:</div><div>客户端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22%24.ajax(%7B%5Cn%20%20%20%20url%3A%20'http%3A%2F%2Flocalhost%3A8081%2Fg1%3Fuserid%3D1001%26funName%3DhandlerRes'%2C%5Cn%20%20%20%20type%3A%20'get'%2C%5Cn%20%20%20%20success%3A%20res%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20console.log(res)%5Cn%20%20%20%20%7D%5Cn%7D)%5Cnvar%20handlerRes%20%3D%20function(res)%20%7B%5Cn%5Ctconsole.log(res)%5Cn%7D%22%2C%22id%22%3A%22xbnDu%22%7D"></div><div>服务端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22const%20express%20%3D%20require('express')%5Cnconst%20ser%20%3D%20express()%5Cnser.get('%2Fg1'%2C%20(req%2C%20res)%20%3D%3E%20%7B%5Cn%20%20%20%20res.set('access-control-allow-origin'%2C%20'*')%5Cn%20%20%20%20var%20id%20%3D%20req.query.userid%5Cn%20%20%20%20var%20fn%20%3D%20req.query.funName%5Cn%20%20%20%20res.send(fn%20%2B%20'('%20%2B%20id%20%2B')')%5Cn%7D)%5Cnser.listen(8081%2C%20()%20%3D%3E%20%7B%5Cn%20%20%20%20console.log('create3')%5Cn%7D)%22%2C%22id%22%3A%22uEz8Z%22%7D"></div><div>这样处理后,响应结果同上,即<code>'handlerRes(1001)'</code>。既然响应体相同了,是不是效果也相同?完全不同。</div><div>因为上一种是js请求,客户端会对响应体中的js脚本进行“动态解释”,即<code>'handlerRes(1001)'</code>会被“解释”为回调函数的调用语句,因此打印<code>1001</code>;而ajax并不会对响应体进行处理,即会将<code>'handlerRes(1001)'</code>视为字符串直接打印(这样客户端就无法处理<code>1001</code>,所以还不行。。。)。</div><div><br /></div><div>ajax与jsonp的区别在哪?就是是否会对响应体进行“动态解释”。也就是说,如果ajax也能实现“动态解释”,这个优化就成功了。。。</div><div><br /></div><div><span style="color: red;"><span style="color: red;">还真有。</span></span>由于ajax与jsonp这两种技术在调用方式和目的上都很相似,故jq把jsonp作为ajax的一种形式进行了封装,就是这样:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22%24.ajax(%7B%5Cn%20%20%20%20url%3A%20'http%3A%2F%2Flocalhost%3A8081%2Fg1%3Fuserid%3D1001'%2C%5Cn%20%20%20%20type%3A%20'get'%2C%5Cn%20%20%20%20dataType%3A%20'jsonp'%2C%5Cn%20%20%20%20jsonp%3A%20'funName'%2C%5Cn%20%20%20%20jsonpCallback%3A%20'handlerRes'%2C%5Cn%20%20%20%20success%3A%20res%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20console.log(res)%5Cn%20%20%20%20%7D%5Cn%7D)%5Cnvar%20handlerRes%20%3D%20function(res)%20%7B%5Cn%5Ctconsole.log(res)%5Cn%7D%22%2C%22id%22%3A%22CLu3T%22%7D"></div><div>可见,多了三个属性<code>dataType</code>、<code>jsonp</code>和<code>jsonpCallback</code>,大家对ajax都很熟悉了,<code>dataType</code>是响应体的数据类型;后面两个大家可能没见过,它们的含义暂不多言(大家结合上文便可知是做什么的)。</div><div>同时将服务器中的“跨域”设置去掉,最终的结果是:</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_30d4bb7f161b4b6e98d3af6f6e2fffb9.png%22%2C%22originWidth%22%3A603%2C%22originHeight%22%3A127%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A603%2C%22height%22%3A127%7D"></span></div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_afdb1e81d8e840978a4fd73905653113.png%22%2C%22originWidth%22%3A581%2C%22originHeight%22%3A121%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A581%2C%22height%22%3A121%7D"></span></div><div>这样大家就明白了吧(请求行和响应体与上一个示例相同)。我们再看一下控制台:</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_8605f5229c484c2c99f7ea0b8a8128f9.png%22%2C%22originWidth%22%3A627%2C%22originHeight%22%3A135%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A627%2C%22height%22%3A135%7D"></span></div><div><span style="color: red;"><span style="color: red;">???</span></span>怎么突然就既实现了“动态解释”,又解决了“跨域”问题?</div><div><br /></div><div>这就是<code>dataType: 'jsonp'</code>的作用了,也就是jq在ajax上对jsonp的封装。即:</div><div><br /></div><blockquote><div><code>dataType: 'jsonp'</code>设置使得客户端将响应体视为js脚本进行“动态解释”,从而解决“跨域”问题。</div></blockquote><div><span style="color: purple;"><span style="color: purple;"><br /></span></span></div><div><span style="color: purple;"><span style="color: purple;">PS:</span></span>言至于此,大家都已经完全明白了在使用ajax请求时如何通过jsonp解决“跨域”问题了吧。。。</div><div></div><div><span style="color: red;"><span style="color: red;">思考:</span></span></div><ol><li>另外两个属性到底是做什么的?或者说有没有必要自定义?</li><li>为什么控制台打印了两次<code>1001</code>?</li></ol><h3 id="8Prmo"><a></a>2.3 补充说明</h3><div>上面我留下了两个思考,我先回答第二个,因为:</div><div><br /></div><ul><li>首先,客户端将响应的字符串<code>'handlerRes(1001)'</code>视为js脚本进行“动态解释”,结果为回调函数的调用语句,于是打印一个<code>1001</code>;</li><li>然后,设置了<code>dataType: 'jsonp'</code>的ajax请求可是说是js请求,但本质仍是ajax请求。成功的ajax请求自然会调用<code>success(res)</code>,又打印一个<code>1001</code>。</li></ul><div><br /></div><div>所以控制台打印了两个<code>1001</code>。</div><div><br /></div><blockquote><div>旁白:我们IT人士普遍有一个“通病”——强迫症,或者说是“严谨”。</div></blockquote><div><br /></div><div>ajax请求既会调用<code>success(res)</code>,也会调用回调函数(也就是有两个位置可以处理响应体),这就有点“不严谨”了。换言之,肯定有某种情况的配置可以做到只有一个位置。</div><div><br /></div><div>这里唯一可以配置的地方就只有<code>jsonp</code>和<code>jsonpCallback</code>这两个属性,那我们就详细了解一个这两个属性:</div><div><br /></div><blockquote><div>jsonp的思想就是将响应的js脚本进行“动态解释”,从而添加进上下文,并执行,故需要js脚本中包含回调函数的调用语句。js脚本由服务器提供,那么服务器就必须知道回调函数名是什么,这个名称自然由客户端提供,那么,客户端在请求时会将回调函数名以某种形式(添加参数)封装在请求中(请求行),例如:<code>funName=handlerRes</code>。</div><div><br /></div></blockquote><ul><li><code>jsonp</code>:值为回调函数名对应的请求参数名,默认值为<code>'callback'</code>;</li><li><code>jsonpCallback</code>:值为回调函数名,默认值为由jq生成的随机字符串。</li></ul><div><br /></div><div>大致介绍是这样,效果如下:(在都不配置的情况下)</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_b7796242538b4ed19a42621c6644145b.png%22%2C%22originWidth%22%3A606%2C%22originHeight%22%3A130%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A606%2C%22height%22%3A130%7D"></span></div><div>现在我们写一个完整的示例。</div><div><br /></div><div>客户端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22%24.ajax(%7B%5Cn%20%20%20%20url%3A%20'http%3A%2F%2Flocalhost%3A8081%2Fg1%3Fuserid%3D1001'%2C%5Cn%20%20%20%20type%3A%20'get'%2C%5Cn%20%20%20%20dataType%3A%20'jsonp'%2C%5Cn%20%20%20%20success%3A%20res%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20console.log(res)%5Cn%20%20%20%20%7D%5Cn%7D)%22%2C%22id%22%3A%22CuiUy%22%7D"></div><div>服务器代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22const%20express%20%3D%20require('express')%5Cnconst%20ser%20%3D%20express()%5Cnser.get('%2Fg1'%2C%20(req%2C%20res)%20%3D%3E%20%7B%5Cn%20%20%20%20var%20id%20%3D%20req.query.userid%5Cn%20%20%20%20var%20fn%20%3D%20req.query.callback%5Cn%20%20%20%20res.send(fn%20%2B%20'('%20%2B%20id%20%2B')')%5Cn%7D)%5Cnser.listen(8081%2C%20()%20%3D%3E%20%7B%5Cn%20%20%20%20console.log('create3')%5Cn%7D)%22%2C%22id%22%3A%22dHNm8%22%7D"></div><div><span style="color: red;"><span style="color: red;">O</span><span style="color: red;">f </span></span><span style="color: red;"><span style="color: red;">C</span><span style="color: red;">o</span><span style="color: red;">u</span><span style="color: red;">rse</span><span style="color: red;">!!</span></span>控制台只打印了一个<code>1001</code>,功能实现。</div><div><br /></div><div>能不能再简化一点?这样:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22ser.get('%2Fg1'%2C%20(req%2C%20res)%20%3D%3E%20%7B%5Cn%20%20%20%20var%20id%20%3D%20req.query.userid%5Cn%20%20%20%20res.send('xx('%20%2B%20id%20%2B')')%5Cn%7D)%22%2C%22id%22%3A%22DnSXv%22%7D"></div><div>响应体是函数<code>xx()</code>的调用语句,就是忽略客户端发来的回调函数名。看一下控制台:</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_6d2c588ae992422380921246e38ba5af.png%22%2C%22originWidth%22%3A670%2C%22originHeight%22%3A121%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A670%2C%22height%22%3A121%7D"></span></div><div>报错了,因为找不到回调函数<code>xx()</code>,这样客户端会认为这个ajax请求失败了,也就不会调用<code>success()</code>(ajax还有一个属性<code>error(e)</code>,大家可以自行测试)。所以:</div><div><br /></div><blockquote><div>若想<code>success(res)</code>中的<code>res</code>能接收到响应数据(指服务器响应的js脚本中函数调用语句的实参),要求服务器响应的js脚本中的回调函数名必须与参数<code>jsonpCallback</code>的值相同。</div></blockquote><div><br /></div><div>也就是这样:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22%24.ajax(%7B%5Cn%20%20%20%20url%3A%20'http%3A%2F%2Flocalhost%3A8081%2Fg1%3Fuserid%3D1001'%2C%5Cn%20%20%20%20type%3A%20'get'%2C%5Cn%20%20%20%20dataType%3A%20'jsonp'%2C%5Cn%20%20%20%20jsonpCallback%3A%20'xx'%2C%5Cn%20%20%20%20success%3A%20res%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20console.log(res)%5Cn%20%20%20%20%7D%5Cn%7D)%22%2C%22id%22%3A%22aP529%22%7D"></div><div>或者都不指定(使用默认值),那么服务器响应的js脚本中的回调函数名与参数<code>jsonpCallback</code>的值都是<code>jQuery37107813896465729704_1702010749715</code>(随机字符串),故<code>res</code>可成功接收响应数据。</div><div><br /></div><blockquote><div>吐槽:研究了半天,转了这么一大弯,最后就只是在ajax那里添加一个属性<code>dataType: 'jsonp'</code>就解决了“跨域”问题,这不消遣人嘛。。。哈哈,虽同归,但殊途,我们这么研究过来就对jsonp有了一个全面的认识,并且对前端原生的底层逻辑有了更深的了解,而不是“套用式”学习。</div></blockquote><h2 id="oLbjI"><a></a>3、一个比较好的示例</h2><div>虽然上面已经可以实现“跨域访问”,但有点单调了(服务器的响应数据是<code>1001</code>,仅是一个数字)。我们来补充一下:</div><div><br /></div><div>客户端代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22%24.ajax(%7B%5Cn%20%20%20%20url%3A%20'http%3A%2F%2Flocalhost%3A8081%2Fg1%3Fuserid%3D1'%2C%5Cn%20%20%20%20type%3A%20'get'%2C%5Cn%20%20%20%20dataType%3A%20'jsonp'%2C%5Cn%20%20%20%20success%3A%20res%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20try%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20var%20user%20%3D%20JSON.parse(res)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20console.log(user)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20console.log('%E7%94%A8%E6%88%B7%E5%90%8D%EF%BC%9A'%20%2B%20user.name)%5Cn%20%20%20%20%20%20%20%20%7D%20catch(e)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20console.log(res)%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%7D%5Cn%7D)%22%2C%22id%22%3A%22ijPY8%22%7D"></div><div>服务器代码。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22javascript%22%2C%22code%22%3A%22const%20express%20%3D%20require('express')%5Cnconst%20ser%20%3D%20express()%5Cnvar%20users%20%3D%20%5B%5Cn%20%20%20%20%7B%5Cn%20%20%20%20%20%20%20%20id%3A%201001%2C%5Cn%20%20%20%20%20%20%20%20name%3A%20'%E8%BF%9B%E6%AD%A5'%2C%5Cn%20%20%20%20%20%20%20%20pass%3A%20'2023'%5Cn%20%20%20%20%7D%2C%20%7B%5Cn%20%20%20%20%20%20%20%20id%3A%201002%2C%5Cn%20%20%20%20%20%20%20%20name%3A%20'%E4%BA%8E%E8%BE%B0'%2C%5Cn%20%20%20%20%20%20%20%20pass%3A%20'2021'%5Cn%20%20%20%20%7D%5Cn%5D%5Cnser.get('%2Fg1'%2C%20(req%2C%20res)%20%3D%3E%20%7B%5Cn%20%20%20%20var%20id%20%3D%20req.query.userid%5Cn%20%20%20%20var%20fn%20%3D%20req.query.callback%5Cn%20%20%20%20var%20result%20%3D%20users.find(item%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20if%20(item.id%20%3D%3D%20id)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20var%20jsonstr%20%3D%20JSON.stringify(item)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20res.send(fn%20%2B%20%5C%22('%5C%22%2B%20jsonstr%20%2B%20%5C%22')%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20true%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%7D)%5Cn%20%20%20%20if(!result)%2F%2F%20%E6%9C%AA%E6%89%BE%E5%88%B0%5Cn%20%20%20%20%20%20%20%20res.send(fn%20%2B%20%5C%22('%E6%9C%AA%E6%89%BE%E5%88%B0')%5C%22)%5Cn%7D)%5Cnser.listen(8081%2C%20()%20%3D%3E%20%7B%5Cn%20%20%20%20console.log('create3')%5Cn%7D)%22%2C%22id%22%3A%22G23eZ%22%7D"></div><div>控制台:</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_fb422c1392e843658fc01a487417ce00.png%22%2C%22originWidth%22%3A625%2C%22originHeight%22%3A147%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A625%2C%22height%22%3A147%7D"></span></div><div>如果<code>userid</code>为<code>1003</code>,则:</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F3mfxe2r26qbaq_eb285defae384e909474d173bc8ee5a1.png%22%2C%22originWidth%22%3A665%2C%22originHeight%22%3A56%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A665%2C%22height%22%3A56%7D"></span></div><div>这是不是大家期望的效果?</div><div><br /></div><div><span style="color: red;"><span style="color: red;">PS:</span></span>至于为何要将对象(js对象 / json对象)转为<code>json字符串</code>再进行响应,这一点我暂未作研究,似乎与“数据在网络传输时格式必须是字符串”有关。当然,也是可以将<code>jsonstr</code>换成<code>item</code>,只是客户端无法处理。</div><h2 id="NP6pL"><a></a>4、最后</h2><div>首先,相信本文能帮助到大家。</div><div>在本文中,我写了很多个示例,大家可能看得有点晕。。。无妨,大家不用注意示例中具体的业务或目的,主要留意“前后变化”即可,关键是技术的运用,示例本身无关紧要。</div><div>jsonp其实很简单,就是实现对服务器响应的js脚本的“动态解释”,剩下的就是对js上下文的操作了。</div><div>js是一种“弱类型”的编程语言,在某种程度上说“很灵活”或者说“解释功能很强大”,所以“用函数调用语句将响应数据带回客户端”只是其中一种方法,至于其他方法需要大家自行扩展了。(本人暂且只想到这一种较好的方法,因此以这种方法作为阐述jsonp的示例)</div><div><br /></div><div>本文完结。</div></code></div></blockquote>

相关文章
|
1月前
|
机器学习/深度学习 人工智能 前端开发
探索未来前端技术发展趋势
随着科技的不断进步,前端技术在不断演进。本文将探索未来前端技术的发展趋势,并讨论其对用户体验、开发效率和安全性的影响。
|
3天前
|
前端开发 JavaScript PHP
技术心得:前端点击按钮,导入excel文件,上传到后台,excel接收和更新数据
技术心得:前端点击按钮,导入excel文件,上传到后台,excel接收和更新数据
|
8天前
|
机器学习/深度学习 人工智能 前端开发
跨界融合:前端技术与人工智能的奇妙联盟
在技术的世界里,前端开发不再是孤岛。本文探讨了前端技术如何与人工智能领域结合,为用户体验与功能提升开辟了新的可能性。从机器学习到智能推荐系统,我们将看到这些技术如何在前端应用中发挥其 ultimate 的作用。
|
1月前
|
缓存 移动开发 前端开发
在PWA的开发中,HTML与CSS作为前端技术的基础,发挥着至关重要的作用
【6月更文挑战第14天】PWA(渐进式网页应用)借助HTML和CSS,提供接近原生应用的体验。HTML构建页面结构和内容,响应式设计适应各种设备,语义化标签提升可访问性,Manifest文件配置应用元数据,离线页面保证无网时体验。CSS则用于定制主题样式,创建动画效果,实现响应式布局,并管理字体和图标。两者协同工作,确保PWA在不同环境下的优秀性能和用户体验。随着前端技术进步,HTML与CSS在PWA中的应用将更加深入。
30 2
|
1月前
|
前端开发 JavaScript Java
【前端技术】 ES6 介绍及常用语法说明
【前端技术】 ES6 介绍及常用语法说明
20 4
|
1月前
|
边缘计算 前端开发 Android开发
未来趋势下的前端开发:跨平台技术的崛起
随着技术的不断演进,前端开发领域也在迅速变化。本文探讨了未来趋势下前端开发的发展方向,着重分析了跨平台技术在前端开发中的崛起,并探讨了其对开发者和行业的影响。
|
13天前
|
前端开发 JavaScript API
只会用插件可不行,这些前端动画技术同样值得收藏-JavaScript篇(下)
只会用插件可不行,这些前端动画技术同样值得收藏-JavaScript篇(下)
14 0
|
13天前
|
监控 JavaScript 前端开发
只会用插件可不行,这些前端动画技术同样值得收藏-JavaScript篇(上)
只会用插件可不行,这些前端动画技术同样值得收藏-JavaScript篇(上)
16 0
|
13天前
|
前端开发 JavaScript UED
只会用插件可不行,这些前端动画技术同样值得收藏-CSS篇
只会用插件可不行,这些前端动画技术同样值得收藏-CSS篇
16 0
|
18天前
|
JavaScript 前端开发
必知的技术知识:esm前端模块化
必知的技术知识:esm前端模块化