二面
1. 给一个数组排序+去重
去重:
let set = new Set(arr);
let result = [...set];
2. js写一个观察者模式
https://blog.csdn.net/q1056843325/article/details/53353850
let Event = (function(){
let clientList = {},
listen,
trigger,
remove;
listen = function(key, fn) {
if (!clientList[key]) {
clientList[key] = [];
}
clientList[key].push(fn);
}
trigger = function () {
let key = Array.prototype.shift.call(arguments),
fns = clientList[key];
if (!fns || fns.length==0) {
return false;
}
for (let i=0; i<fns.length; i++) {
let fn = fns[i];
fn.apply(this, arguments);
}
}
remove = function (key, fn) {
let fns = clientList[key];
if (!fns) {
return false;
}
if (!fn) {
fns && (fns.length = 0);
} else {
for (let i=fns.length-1; i>=0; i--) {
let _fn = fns[i];
if (_fn == fn) {
fns.splice(i, 1);
}
}
}
}
return {
listen,
trigger,
remove
}
)();
Event.listen('squareMeter88', function(price){ //小红订阅消息
console.log('价格= ' + price); //输出:'价格=2000000'
});
Event.trigger('squareMeter88', 2000000); //售楼处发布消息
3. 假设要综合cpu,gpu给一个手机性能做评价,怎么做
4. js实现双向绑定
发布订阅模式,使用PubSub
https://www.cnblogs.com/wilber2013/p/5811810.html
源代码:
https://github.com/WilberTian/Two-way-data-binding/tree/master/1.PubSub
实现数据双向绑定最直接的方式就是使用PubSub模式:
当model发生改变的时候,触发Model change事件,然后通过响应的事件处理函数更新界面
当界面更新的时候,触发UI change事件, 然后通过相应的事件处理函数更新Model,以及绑定在Model上的其他界面控件
根据这个思路,可以定义'ui-update-event'和'model-update-event'两个事件,然后针对Model和UI分别进行这两个事件订阅和发布。
- UI更新
对于所有支持双向绑定的页面控件,当控件的“值”发生改变的时候,就触发'ui-update-event',然后通过事件处理函数更新Model,以及绑定在Model上的其他界面控件
处理控件“值”的改变,发布“ui-update-event”事件,(这里只处理包含“t-binding”属性的控件):
// keyup和change事件处理函数
function pageElementEventHandler(e) {
var target = e.target || e.srcElemnt;
var fullPropName = target.getAttribute('t-binding');
if(fullPropName && fullPropName !== '') {
Pubsub.publish('ui-update-event', fullPropName, target.value);
}
}
// 在页面上添加keyup和change的listener
if(document.addEventListener) {
document.addEventListener('keyup', pageElementEventHandler, false);
document.addEventListener('change', pageElementEventHandler, false);
} else {
document.attachEvent('onkeyup', pageElementEventHandler);
document.attachEvent('onchange', pageElementEventHandler);
}
另外,对所有包含“t-binding”属性的控件都订阅了“'model-update-event”,也就是当Model变化的时候会收到相应的通知:
// 订阅model-update-event事件, 根据Model对象的变化更新相关的UI
Pubsub.subscrib('model-update-event', function(fullPropName, propValue) {
var elements = document.querySelectorAll('[t-binding="' + fullPropName + '"]');
for(var i = 0, len =elements.length; i < len; i++){
var elementType = elements[i].tagName.toLowerCase();
if(elementType === 'input' || elementType === 'textarea' || elementType === 'select') {
elements[i].value = propValue;
} else {
elements[i].innerHTML = propValue;
}
}
});
- Model更新
对于Model这一层,当Model发生改变的时候,会发布“model-update-event”:
// Model对象更新方法,更新对象的同时发布model-update-event事件
'updateModelData': function(propName, propValue) {
eval(this.modelName)[propName] =propValue;
Pubsub.publish('model-update-event', this.modelName + '.' + propName, propValue);
}
另外,Model订阅了“ui-update-event”,相应的界面改动会更新Model
// 订阅ui-update-event事件, 将UI的变化对应的更新Model对象
Pubsub.subscrib('ui-update-event', function(fullPropName, propValue) {
var propPathArr = fullPropName.split('.');
self.updateModelData(propPathArr[1], propValue);
});
5. 移动端适配怎么做
react-responsive 不同的MediaQuery适配不同宽度。 部署的时候打开不同的地址(m.taobao.com和taobao.com)
6. 移动端点透问题
https://segmentfault.com/a/1190000003848737
手机上的触摸事件touchstart
-> touchmove
-> touchend
PC端绑定click
, 手机页面绑定tap
。
手机同样能响应鼠标mouse事件,但是:
可以看到,当手触碰屏幕时,要过300ms才会触发
mousedown
事件,所以click
事件在手机上看起来就像慢半拍一样。这是因为浏览器在touchend后要判断用户是否有双击(double tap)行为。如果没有,则触发click事件,由此可以看出click事件触发代表一轮触摸事件的结束。image
整个容器里有一个底层元素的div,和一个弹出层div,为了让弹出层有模态框的效果,我又加了一个遮罩层。
<div class="container">
<div id="underLayer">底层元素</div>
<div id="popupLayer">
<div class="layer-title">弹出层</div>
<div class="layer-action">
<button class="btn" id="closePopup">关闭</button>
</div>
</div>
</div>
<div id="bgMask"></div>
然后为底层元素绑定 click 事件,而弹出层的关闭按钮绑定 tap 事件。
$('#closePopup').on('tap', function(e){
$('#popupLayer').hide();
$('#bgMask').hide();
});
$('#underLayer').on('click', function(){
alert('underLayer clicked');
});
点击关闭按钮,touchend首先触发tap,弹出层和遮罩就被隐藏了。touchend后继续等待300ms发现没有其他行为了,则继续触发click,由于这时弹出层已经消失,所以当前click事件的target就在底层元素上,于是就alert内容。整个事件触发过程为 touchend -> tap -> click。
而由于click事件的滞后性(300ms),在这300ms内上层元素隐藏或消失了,下层同样位置的DOM元素触发了click事件(如果是input框则会触发focus事件),看起来就像点击的target“穿透”到下层去了。
穿透的解决办法
1. 遮挡
由于 click 事件的滞后性,在这段时间内原来点击的元素消失了,于是便“穿透”了。因此我们顺着这个思路就想到,可以给元素的消失做一个fade效果,类似jQuery里的fadeOut
,并设置动画duration大于300ms,这样当延迟的 click 触发时,就不会“穿透”到下方的元素了。
同样的道理,不用延时动画,我们还可以动态地在触摸位置生成一个透明的元素,这样当上层元素消失而延迟的click来到时,它点击到的是那个透明的元素,也不会“穿透”到底下。在一定的timeout后再将生成的透明元素移除。具体可见demo
2. pointer-events
pointer-events
是CSS3中的属性,它有很多取值,有用的主要是auto
和none
,其他属性值为SVG服务。
取值 | 含义 |
---|---|
auto | 效果和没有定义 pointer-events 属性相同,鼠标不会穿透当前层。 |
none | 元素不再是鼠标事件的目标,鼠标不再监听当前层而去监听下面的层中的元素。但是如果它的子元素设置了pointer-events为其它值,比如auto,鼠标还是会监听这个子元素的。 |
关于使用 pointer-events 后的事件冒泡,有人做了个实验,见代码
因此解决“穿透”的办法就很简单,demo如下
$('#closePopup').on('tap', function(e){
$('#popupLayer').hide();
$('#bgMask').hide();
$('#underLayer').css('pointer-events', 'none');
setTimeout(function(){
$('#underLayer').css('pointer-events', 'auto');
}, 400);
});
3. fastclick
使用fastclick库,其实现思路是,取消 click 事件(参看源码 164-173 行),用 touchend 模拟快速点击行为(参看源码 521-610 行)。
FastClick.attach(document.body);
从此所有点击事件都使用click
,不会出现“穿透”的问题,并且没有300ms的延迟。解决穿透的demo
有人(叶小钗)对事件机制做了详细的剖析,循循善诱,并剖析了fastclick的源码以自己模拟事件的创建。请看这篇文章,看完后一定会对移动端的事件有更深的了解
7. 移动端300ms延迟怎么解决
http://web.jobbole.com/86114/
方案二:更改默认的视口宽度
一开始,为了让桌面站点能在移动端浏览器正常显示,移动端浏览器默认的视口宽度并不等于设备浏览器视窗宽度,而是要比设备浏览器视窗宽度大,通常是980px。我们可以通过以下标签来设置视口宽度为设备宽度。
<meta name="viewport" content="width=device-width"/>
因为双击缩放主要是用来改善桌面站点在移动端浏览体验的,而随着响应式设计的普及,很多站点都已经对移动端坐过适配和优化了,这个时候就不需要双击缩放了,如果能够识别出一个网站是响应式的网站,那么移动端浏览器就可以自动禁掉默认的双击缩放行为并且去掉300ms的点击延迟。如果设置了上述meta标签,那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了。
这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面。
方案二:FastClick
FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
8. nodejs的事件循环和浏览器的事件循环有何不同
https://ruiming.me/event-loop-in-nodejs-and-browser/
nodejs 和 chrome 的事件驱动实现上:
node.js 事件由 libuv 驱动,chrome 由 webkit 实现。
浏览器的事件循环:https://segmentfault.com/a/1190000010622146
现在我们知道了。在事件循环中,用户代理会不断从task队列中按顺序取task执行,每执行完一个task都会检查microtask队列是否为空(执行完一个task的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有microtask。然后再进入下一个循环去task队列中取下一个task执行...
那么哪些行为属于task或者microtask呢?标准没有阐述,但各种技术文章总结都如下:
-
macrotasks
: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering -
microtasks
: process.nextTick, Promises, Object.observe(废弃), MutationObserver
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
(代码来自Tasks, microtasks, queues and schedules,推荐观看原文的代码可视化执行步骤)
如果你测试的浏览器支持的Promise不支持Promise/A+标准,或是你使用了其他Promise polfill,运行结果可能有差异。
运行结果是:
script start
script end
promise1
promise2
setTimeout
解释一下过程。
- 一开始task队列中只有script,则script中所有函数放入函数执行栈执行,代码按顺序执行。
接着遇到了setTimeout
,它的作用是0ms后将回调函数放入task队列中,也就是说这个函数将在下一个事件循环中执行(注意这时候setTimeout执行完毕就返回了)。 - 接着遇到了
Promise
,按照前面所述Promise属于microtask,所以第一个.then()会放入microtask队列。 - 当所有script代码执行完毕后,此时函数执行栈为空。开始检查microtask队列,此时队列不为空,执行.then()的回调函数输出'promise1',由于.then()返回的依然是promise,所以第二个.then()会放入microtask队列继续执行,输出'promise2'。
- 此时microtask队列为空了,进入下一个事件循环,检查task队列发现了setTimeout的回调函数,立即执行回调函数输出'setTimeout',代码执行完毕。
Node的事件循环:https://www.jianshu.com/p/55546b28cafc
9. 如何做的服务器端缓存
https://www.cnblogs.com/kevingrace/p/6198287.html
web缓存位于内容源Web服务器和客户端之间,当用户访问一个URL时,Web缓存服务器会去后端Web源服务器取回要输出的内容,然后,当下一个请求到来时,如果访问的是相同的URL,Web缓存服务器直接输出内容给客户端,而不是向源服务器再次发送请求.Web缓存降低了内容源Web服务器,数据库的负载,减少了网络延迟,提高了用户访问的响应速度,增强了用户体验.
Nginx: proxy_cache和fastcgi_cache
proxy_cache相关指令集
(1)proxy_cache指令
语法: proxy_cache zone_name ;
该指令用于设置哪个缓存区将被使用,zone_name的值为proxy_cache_path指令创建的缓存区的名称。proxy_pass 指定获取静态内容的地址,其实proxy_cache的原理就是从一个指定的地址获取内容,然后缓存。当下次访问时,nginx会自动判断有没有缓存文件?如果有的话缓存文件是不是已经过期。
(2)proxy_cache_path指令
语法 proxy_cache_path path [levels=number]
keys_zone=zone_name:zone_size[inactive=time] [max_size=size];
该指令用于设置缓存文件的存放路径.
例如:
proxy_cache_path /usr/local/nginx/proxy_cache_dir levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g ;
解释:
path 表示存放目录
levels 表示指定该缓存空间有两层hash目录,第一层目录为1个字母,第二层目录为2个字母,
保存的文件名会类似/usr/local/nginx/proxy_cache_dir/c/29/XXXXXX ;
keys_zone参数用来为这个缓存区起名.
500m 指内存缓存空间大小为500MB
inactive的1d指如果缓存数据在1天内没有被访问,将被删除。相当于expires过期时间的配置;
max_size的30g是指硬盘缓存空间为30G
(3)proxy_cache_methods指令
语法:proxy_cache_methods[GET HEAD POST];
该指令用于设置缓存哪些HTTP方法,默认缓存HTTP GET/HEAD方法,不缓存HTTP POST 方法
(4)proxy_cache_min_uses指令
语法:proxy_cache_min_uses the_number
该指令用于设置缓存的最小使用次数,默认值为1
(5)proxy_cache_valid指令
语法: proxy_cache_valid reply_code [reply_code...] time ;
该指令用于对不同返回状态码的URL设置不同的缓存时间.
例如:
proxy_cache_valid 200 302 10m ;
proxy_cache_valid 404 1m ;
设置200,302状态的URL缓存10分钟,404状态的URL缓存1分钟.
(6)proxy_cache_key指令
语法: proxy_cache_key line ;
该指令用来设置Web缓存的Key值,Nginx根据Key值md5哈希存储缓存.一般根据$host(域名),$request_uri(请求的路径)等变量组合成proxy_cache_key .
proxy_cache缓存配置的完整示例(多数nginx缓存的配置):
1)下载nginx和第三方的ngx_cache_purge模块的编译安装包(官网:http://labs.frickle.com/nginx_ngx_cache_purge/),将ngx_cache_purge编译到到Nginx中,用来清除指定URL的缓存
[root@test-huanqiu ~]# yum install -y pcre pcre-devel openssl openssl-devel gcc //首先安装依赖
[root@test-huanqiu ~]# cd /usr/local/src
[root@test-huanqiu src]# wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
[root@test-huanqiu src]# wget http://nginx.org/download/nginx-1.8.0.tar.gz
[root@test-huanqiu src]# tar -zxvf ngx_cache_purge-2.3.tar.gz
[root@test-huanqiu src]# tar zxvf nginx-1.8.0.tar.gz
[root@test-huanqiu src]# cd nginx-1.8.0.tar.gz
[root@test-huanqiu nginx-1.8.0]# ./configure --user=www --group=www --add-module=../ngx_cache_purge-2.3 --prefix=/usr/local/nginx --with-http_ssl_module --with-http_flv_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre
[root@test-huanqiu src]# make && make install
2)接着,在同一分区下创建两个缓存目录,分别供proxy_temp_path , proxy_cache_path指令设置缓存路径.
注意:proxy_temp_path和proxy_cache_path指定的路径必须在同一磁盘分区,决不能跨区分,因为它们之间是硬链接的关系,避免不通文件系统之间的磁盘IO消耗。
[root@test-huanqiu src]# mkdir -p /usr/local/nginx/proxy_cache_path #注意,这两个目录的权限一定要是www.www,即是nginx进程权限
[root@test-huanqiu src]# mkdir -p /usr/local/nginx/proxy_temp_path #这是缓存文件的临时存放目录
3)在配置文件nginx.conf中对扩展名为gif,jpg,jpeg,png,bmp,swf,js,css的图片,flash,javascript , css文件开启Web缓存,其他文件不缓存。
三面
字符串匹配
正则
Jquery选择器怎么实现的
链式查找
介绍项目
二面
1. 自我介绍
2. 介绍一下自己感觉最有挑战性的项目。
介绍的思路是先说项目性质(个人,社团还是实验室之类的),然后说项目规模(人数,代码行数等),再说项目架构,最后说自己承担的工作。这一部分建议提前做好准备,对于项目的亮点和难点都要说(自己把握好语速,基本要能说20分钟以上),性能优化是非常出彩的地方,一定要重点讲。面试官会在你讲完之后问一些问题,比如当时问什么那么做,以及会和你讨论有没有更好的做法。问的问题比较多也比较细,所以一定要说自己参与的比较多的项目,事前再熟悉一遍。再后来问了一些我个人平时写着玩的小玩意,一个爬虫,一个原生html+js的小游戏,还有一个数学建模的进化论模型。我都详细说了说。
项目性质:全国高校云计算应用创新大赛 二等奖
项目规模:4人
项目亮点、难点:用户权限管理、mock
部署:
server
{
listen 666;
server_name 47.92.30.98;
root /home/www/antd-admin/dist;
location /api {
proxy_pass http://localhost:8000/api;
}
location / {
index index.html;
try_files $uri $uri/ /index.html;
}
}
版本更新:npm run build:new
依据package.json中的version字段打包到相应目录,如:当前"version":"4.3.0",将会
- 更改package.json中的version字段为4.3.1
- 静态文件copy到dist根目录,版本相关文件生成到dist/4.3.1目录
- dist/index.html将会引用dist/4.3.1/index.js,所以ngnix配置的时候只需指向dist/index.html
- 细心的观众会发现dist/4.3.1/index.html,这样做为了在打包4.3.2后发现有错误,还想快速切换至4.3.1,
- 只需将dist/4.3.1/index.html覆盖至dist/index.html
version.js做的事
- 新增版本号,注意只会增加以“v1.v2.v3”格式的v3的值,如需自定义,自行修改即可
- 清理最近5个版本号以前的文件。
关于mock:
-
mock的优点在于可以在后端未开发接口时,前端可以模拟数据接口进行开发。
-
roadhog为我们提供了一个完善的mock功能,在本项目中的接口均为mock的
-
在开发过程中,后端部分接口已经开发完成,我们怎么使用别人的接口呢?如果需要同时请求多个同事的电脑或者多台服务器上的接口呢?我是这样做的,roadhog的配置中有接口代理的功能(
.roadhogrc.js
),
自己mock的接口加上前缀‘/api/v1’,A同事的接口加上前缀‘/api/v2’,C同事的接口加上前缀‘/api/v3’,利用roadhog分别匹配‘/api/v2’,‘/api/v3’并代理到A、B同事的电脑上。这样就可以不跨域的情况下愉快的开发啦。 -
当发布到正式环境后,利用nginx或者其他工具,将‘/api/v1’,‘/api/v2’,‘/api/v3’分别代理到指定的端口或者服务器,也可以正常运行啦
CSS权重
https://www.w3cplus.com/css/css-specificity-things-you-should-know.html
权重记忆口诀:从0开始,一个行内样式+1000, 一个id+100, 一个属性选择器/class或者伪类(:hover, :focus. :visited)+10, 一个元素名或者伪元素(:after, :before, :first-letter, :first-line, :selection)+1
怎么用CSS实现一个正方形
(带尖角的:https://blog.csdn.net/sunhengzhe/article/details/43675615)
如果仅仅是设置width 和 height的话,这个问题就不用说了,这里考虑的问题主要是padding的百分比是相对于谁来说的
元素的padding 和 margin百分比都是想对应父元素的width(父元素必须有width这个值 否则往上查找知道body)来说的, 如下通用的css正方形方案
-
相对于父元素width
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.container {
width: 100px;
height: 100px;
}
.square {
width: 100%;
padding-bottom: 100%;
background: red;
}
</style>
</head>
<body>
<div class="container">
<div class="square">
</div>
</div>
</body>
</html>
-
可以设置高度,利用伪元素
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.square {
width: 100px;
background: green;
}
.square:after {
content: '';
display: block;
padding-bottom: 100%;
}
</style>
</head>
<body>
<div class="square">
</div>
</body>
</html>
设置一个伪元素,伪元素用的是利用 padding-bottom撑起父元素的高度,padding相对高度为 父元素的 width
-
不同单位 相对于视口 而且利用vw
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.container {
width: 5%;
height: 5vw;
background: red;
}
</style>
</head>
<body>
<div class="container">
</div>
</body>
</html>
CSS值的解析过程(不是页面,是CSS值)
http://jartto.wang/2017/11/13/Exploring-the-principle-of-CSS-parsing/
http://www.nowamagic.net/academy/detail/48110425
1.通过调用 CSSStyleSheet 的 parseString 函数,将上述 CSS 解析过程启动,解析完一遍后,把 Rule 都存储在对应的 CSSStyleSheet 对象中;
2.由于目前规则依然是不易于处理的,还需要将之转换成 CSSRuleSet。也就是将所有的纯样式规则存储在对应的集合当中,这种集合的抽象就是 CSSRuleSet;
3.CSSRuleSet 提供了一个 addRulesFromSheet 方法,能将 CSSStyleSheet 中的 rule 转换为 CSSRuleSet 中的 rule ;
4.基于这些个 CSSRuleSet 来决定每个页面中的元素的样式;
CSS后问的是JS,一个最基本的[]==true,答这种题的时候我的方法是先说结论,再说理由,如果理由思路清晰而且正确的话,就算结果是错的问题也不大。
CSS语法解析过程
1.先创建 CSSStyleSheet 对象。将 CSSStyleSheet 对象的指针存储到 CSSParser 对象中。
2.CSSParser 识别出一个 simple-selector ,形如 “div” 或者 “.class”。创建一个 CSSParserSelector 对象。
3.CSSParser 识别出一个关系符和另一个 simple-selector ,那么修改之前创建的 simple-selector, 创建组合关系符。
4.循环第3步直至碰到逗号或者左大括号。
5.如果碰到逗号,那么取出 CSSParser 的 reuse vector,然后将堆栈尾部的 CSSParserSelector 对象弹出存入 Vector 中,最后跳转至第2步。如果碰到左大括号,那么跳转至第6步。
6.识别属性名称,将属性名称的 hash 值压入解释器堆栈。
7.识别属性值,创建 CSSParserValue 对象,并将 CSSParserValue 对象存入解释器堆栈。
8.将属性名称和属性值弹出栈,创建 CSSProperty 对象。并将 CSSProperty 对象存入 CSSParser 成员变量m_parsedProperties 中。
9.如果识别处属性名称,那么转至第6步。如果识别右大括号,那么转至第10步。
10.将 reuse vector 从堆栈中弹出,并创建 CSSStyleRule 对象。CSSStyleRule 对象的选择符就是 reuse vector, 样式值就是 CSSParser 的成员变量 m_parsedProperties 。
11.把 CSSStyleRule 添加到 CSSStyleSheet 中。
12.清空 CSSParser 内部缓存结果。
13.如果没有内容了,那么结束。否则跳转值第2步。
()=>{a:1}该箭头函数返回值是什么,理由。
http://es6.ruanyifeng.com/#docs/function#箭头函数
undefined. 因为要表示直接返回一个对象应该外部用()包围,{}表示代码块。
上面代码中,原始意图是返回一个对象{ a: 1 },但是由于引擎认为大括号是代码块,所以执行了一行语句a: 1。这时,a可以被解释为语句的标签,因此实际执行的语句是1;,然后函数就结束了,没有返回值。
JSONP
function addScriptTag(src) {
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = src;
document.body.appendChild(script);
}
function myFn (data) {
alert(data + 'px');
}
//需要调用时:
//addScriptTag('b.com/data.aspx?callback=myFn');
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp</title>
</head>
<body>
<div id="result"></div>
<script>
(function(window,document,undefined){
var jsonp = function(url,data,callback){
// 回调函数+时间戳
var cbName = 'callback_' + new Date().getTime();
// 暴露全局函数给window
// 判读查询字符串最后一位是否为?或者是&
var queryString = url.indexOf('?') == -1 ? '?' : '&';
// 遍历传进来的data实参赋值给查询字符串
for(var k in data){
queryString += k + '=' + data[k] + '&';
}
// 查询字符串加上回调函数
queryString += 'callback=' + cbName;
// 创建script标签
var ele = document[0].createElement('script');
// 给script标签添加src属性值
ele.src = url + queryString;
window[cbName] = function(data){
callback(data);
document[0].body.removeChild(ele);
};
// 添加到body尾部
document[0].body.appendChild(ele);
}
//jsonp函数暴露给window
window.$jsonp = jsonp;
})(window,document,undefined);
</script>
<script>
$jsonp('http://api.douban.com/v2/movie/in_theaters',{
'count':1
},function(data){
document.getElementsByTagName('body')[0].innerHTML = JSON.stringify(data);
})
</script>
</body>
</html>
网友评论