在JS中使用变量,使用=号拷贝,如
obj1 = obj2
是浅拷贝,即改变obj1内容的时候也会改变obj2.
有时候我们是不希望看到这种情况的,JS使用深拷贝有很多方法,介绍一个简单好用的
var obj1 = JSON.parse(JSON.stringify(obj2));
这种用法会破坏obj2的构造类型,但一般情况是足够了。
在JS中使用变量,使用=号拷贝,如
obj1 = obj2
是浅拷贝,即改变obj1内容的时候也会改变obj2.
有时候我们是不希望看到这种情况的,JS使用深拷贝有很多方法,介绍一个简单好用的
var obj1 = JSON.parse(JSON.stringify(obj2));
这种用法会破坏obj2的构造类型,但一般情况是足够了。
最近遇到任务,需要对外提供接口。发现最困难的事是写文档,因为代码经常在修改,每天更新word很困难。然后就发现了文档神器YUI doc,以及神器的剑鞘smartdoc:http://www.cnblogs.com/zhh8077/p/4010991.html。
简单介绍一下使用方法和遇到的坑。
YUI doc可以看成一种标准,主要看这里http://www.cnblogs.com/zhh8077/p/4011769.html或者YUI doc的官方文档。
module写法
/** * XX模块,包括以下几部分: * * account-账号管理模块</br> * pay-支付管理模块</br> * * * @module XX */
class写法
/** * account-账号管理模块 * @class account */
method常用写法
/** * 登陆 * @method login * @param param {Object} 登陆相关信息</br> * @param param.accountName {string} 用户名 * @param param.password {string} 密码 * @example * // example * account.login({ * accountName: userName, * password:password, * }); */
其中 param.xx是二级参数的写法。
event与method一样
常量
/** * ACCOUNT_STATUS_LOGOUT是一个常量,账号状态为登出,value = "logout" * @property ACCOUNT_STATUS_LOGOUT * @final * @type string */
smartdoc的使用就比较简单了,参考其主页文档。
这里主要想说的是 使用过程中遇到的一些坑。
1.生成的method event等等按照字母顺序排列,而不是按照我们文档中的顺序排列。
.YUIDoc生成的API文档目录不按源文件注释顺序
YUIDoc默认将所有的class、method、properties、events等按字母进行排序。
而且这个是在生成文档时进行排序的,所以如果除去按字母进行排序这种默认行为。
就必须修改YUIDoc的工具文件。修改C:\Users\Administrator\AppData\Roaming\npm\node_modules\yuidocjs\lib 下的 builder.js 文件
第1313-1316行,就是这几句罪魁祸首,注释掉后就可以按源文件注释顺序,当然你也可以自己弄其他排序方法。
方法源自:http://www.cnblogs.com/lovesong/p/3341453.html
2.event的example不显示。使用smartdoc生成文档后,其他地方都挺好,但event的example就是显示不出来。chrome上面,直接F12,看document,看到example部分,display:none。method增加了一个active的class,display:block。很简单,看看这个css是在哪里写的。
C:\Users\用户名\AppData\Roaming\npm\node_modules\smartdoc\theme-smart\assets\css
路径下的main.css。改一下其中的example的display就可以了。
修改整个页面的布局也是修改这里。这里就是我们使用的主题,自己可以改,也可以搞一套。
Webstorm是一个很优秀的js IDE。今天打算学习less的时候,创建一个less文件后发现webstorm提示是否AddWatcher,点开后发现是一个自动编译less文件的工具,可惜不能直接使用,需要简单配置一下。
所有资料来自于
https://www.jetbrains.com/webstorm/help/transpiling-sass-less-and-scss-to-css.html。
洋洋洒洒一大坨,读起来有点费劲。实际操作了一下,发现简单的要命。
首先,你要装好node环境,没装的请自行百度。
1.View-ToolWindows-Terminal
2.输入 npm install -g less
搞定。
再次打开AddWatcher窗口,你就发现所有选项已经自动填好了。
less文件左边出现了一个小箭头,点开看到了css文件,完全同步。
获得某个element在parent中的index。
jquery提供了index接口,可以直接拿到。
如果没有jquery的话,
将document查询到的HTML Collection转为Array,
然后使用Array的index接口拿到index。
var ItemList = Array.prototype.slice.call( document.getElementsByClassName("item")); var _currentFocus = document.getElementsByClassName("item focus")[0]; var _position = ItemList.indexOf(_currentFocus);
之前写css,一直是把position:absolute当做android的FrameLayout用的,其他类似。
后来看了下面的文章,满面羞愧。
这些基础的东西,还是要了解透彻的,一直不求甚解的搞下去,自己都不知道做的什么,为什么会出现这种错误,希望能从下面的内容中,总结出一个类似于Androidlayout的规则。
关于position看下面的文章就够了
http://blog.csdn.net/chen_zw/article/details/8741365
css属性大全
http://css.doyoe.com/
这里主要记一下看完自己的理解:
文档流
首先需要了解什么是文档流,可以理解为方块布局,从上到下,从左到右的布局方式就是文档流。
1.margin和padding是占用文档流的,也就是说,layout函数一定是这么写的
element.margin + elment.width/height +element.padding = dom真正占用的空间。
position:static很容易理解,完全遵循文档流。
position:relative 有点特殊,本身遵循文档流,但可以在文档流中使用top left bottom right设置偏移,偏移不遵循文档流。需要注意的是,后面的dom会按照relative元素不偏移来排列。layout函数中完全可以把relative按照static先处理,最后在当前位置进行偏移。
注:relative static无父辈时以body为参考;
position:absolute 脱离文档流,使用top left bottom right设置偏移,偏移参照为父辈最近的非static元素(之前一直认为absolute参照window,深刻检讨)。
注:absolute无父辈时以html为参考。
1、body默认有9个px的margin
2、absolute的元素没有设置top、left时,默认是文档流的位置,会造成设置了absolute,看起来没有效果的现象。
position:fixed fixed以window为参考(即不管你怎么scroll,位置都不会变),设置偏移。
float:absolute会屏蔽掉float,其他的可以共存。
float不遵循文档流。
clear:clear是配合float使用的。意思是把dom的哪边给清除掉。
如clear:both,意思是两边都不许浮动。
如果我来写 html的layout,
1.dom tree static,relative的按照文档流布局,relative单独处理下偏移,完全不影响文档流。
fixed很简单粗暴。
2.处理absolute float
absolute屏蔽float
absolute需要寻找父辈非static元素直到html,比较蛋疼。这个webkit在渲染的时候会把absolute的元素单独出来一层。
float是浮动排版,按文档流布局就差不多。
当然,css3还有更多的排版方式,上面几种已经基本够用,如果还要学习的话就是box了。
经常碰到JS中的这三兄弟,记一下他们的用法和区别。
1.改变函数上下文
2.就算不为了改变,但JS那坑爹的作用域下,也为了能够明确知道函数上下文
(为什么要改变请参考JS的作用域链)
区别:
call与apply
call与apply用法很相似,区别就是一个是一个一个传参数,一个是传一个参数数组
fun().call(object, p1, p2);
fun().apply(object, [p1,p2]);
或者经常直接继承父函数参数
fun().apply(object, arguments);
call和apply都会立刻执行,只是改变了fun()里面的this
bind不会立刻执行,bind会返回一个指定上下文的函数
var fun1 = fun().bind(object, p1, p2); //参数传递跟call一样
bind最适合作为回调函数使用,尤其是setTimeout
func(function(){}.bind(this));
我们知道,setTimeout的回调函数上下文会被置为window(use strict下是null),在setTimeout中没办法使用原有实例,bind就可以了
setTimeout(function(){}.bind(this), 1000);
HTML5中提供了transform transition等动画方式,已经能够满足绝大部分动画需求。
但在移动端,使用Transform等还是会出现不流畅的情况,比如背景上一个无限循环的动画,不管是使用setInterval 还是捕捉每次的AnimationEnd来实现,都会有一定的问题,因为我们设定的延时还是动画时间都不能得到保证,并且会影响页面性能。
优化是无尽的,所以学习使用Canvas
使用Canvas能做什么?
1.知道/控制每帧的绘制
2.预加载img来绘制
3.canvas保证了性能
如何使用Canvas?
http://www.w3school.com.cn/html5/html_5_canvas.asp
W3C等有canvas的简单介绍,使用Canvas的基本步骤就是
var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d");
拿到canvas标签的dom,调用dom的getContext接口拿到Canvas类,就可以使用canvas的各种接口了
Canvas接口手册:http://www.w3school.com.cn/tags/html_ref_canvas.asp
canvas简单使用
1.绘制矩形
cxt.fillStyle="#FF0000"; cxt.fillRect(0,0,150,75);
2.绘制线
cxt.moveTo(10,10); cxt.lineTo(150,50); cxt.lineTo(10,50); cxt.stroke();
3.绘制圆形
cxt.fillStyle="#FF0000"; cxt.beginPath(); cxt.arc(70,18,15,0,Math.PI*2,true); cxt.closePath(); cxt.fill();
4.绘制渐变
var grd=cxt.createLinearGradient(0,0,175,50); grd.addColorStop(0,"#FF0000"); grd.addColorStop(1,"#00FF00"); cxt.fillStyle=grd; cxt.fillRect(0,0,175,50);
5.绘制图
var img=new Image() img.src="flower.png" cxt.drawImage(img,0,0);
这些绘制组合使用基本上可以满足我们的一般绘制需求。
绘制动画基本流程
But,如果要绘制动画,还需要配合另一个神接口,
requestAnimationFrame
requestAnimationFrame的用法请自行google。大概原理就是,requestAnimationFrame需要传入一个函数,浏览器每绘制一帧都会通过requestAnimationFrame来调用这个函数。通常,我们把这个函数命名为step,在step中,放入我们的draw函数(这里与Android的draw流程很类似,不过Android更方便些)。
draw与requestAnimationFrame绑定
this.show = function() { var step = function() { this.draw(); this.animationId = requestAnimationFrame(step.bind(this)); }; this.animationId = requestAnimationFrame(step.bind(this)); this.element.style.visibility = "visible"; };
解绑(这里把绑定与show/hide放到了一起)
this.hide = function() { cancelAnimationFrame(this.animationId); this.element && (this.element.style.visibility = "hidden"); };
draw函数
this.draw = function() { this.flash++; this.context.clearRect(0, 0, this.element.width, this.element.height); this.rect0.update(); this.rect0.draw(this.context); };
draw函数中我们把绘制分为两步,一步是update(和Android类比就是在这里做mesure,确定View中各个元素的位置),第二步是绘制。
子View中实现各自的update和draw
需要注意的是,Canvas的使用宽高默认为300 150,我们设定的宽高只会对Canvas进行缩放。
update-动画曲线
使用css动画时,可以方便的给一个动画曲线,比如easeinout等,使用canvas绘制就需要我们不断的update绘制范围。
this.update = function() { if (!this.isAnimating) { return; } this.t += 0.05; switch (this.direction) { case "right": this.x = easeInOutQuint(this.t) * 70; break; case "down": this.y = easeInOutQuint(this.t) * 70; break; case "left": this.x = (1 - easeInOutQuint(this.t)) * 70; break; case "up": this.y = (1 - easeInOutQuint(this.t)) * 70; break; } if (this.t >= 1) { this.nextDirection(); this.t = 0; } };
在update里面,我们完成让一个矩形以指定动画曲线在四个方向转圈的位移。
缓动函数就负责返回位移的值
下面的easeInOutQuint按照步进返回值
var easeInOutQuint = function (t) { return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t; };
更高明的做法,比如Jquery中,以时间为参数
function easeOutBounce(progress, currentTime, begin, change, duration) { if ((currentTime/=duration) < (1/2.75)) { return change*(7.5625*currentTime*currentTime) + begin; } else if (currentTime < (2/2.75)) { return change*(7.5625*(currentTime-=(1.5/2.75))*currentTime + .75) + begin; } else if (currentTime < (2.5/2.75)) { return change*(7.5625*(currentTime-=(2.25/2.75))*currentTime + .9375) + begin; } else { return change*(7.5625*(currentTime-=(2.625/2.75))*currentTime + .984375) + begin; } }
draw就比较简单了
this.draw = function(ctx) { ctx.fillStyle = this.color; ctx.fillRect(this.x, this.y, 70, 70); };
在自己的服务器上跑一个页面,页面出错,调试后发现,载入.json还有自定义类型(.tpl)时404。
查了一下,是window上IIS默认不支持这两种格式。
添加了这两种格式分别为text/xml text/html后,可以正常加载。
WebWorker类似于浏览器中的多线程操作。之前的JS中,无论你是使用setTimeout setIntever 还是 使用了XMLHttpRequest,都是在一个线程里面,前两个使用消息队列,XMLHttpRequest则是浏览器会帮你进行闲时进行,归根结底,都是在一个线程里面跑。如果用户想进行一些阻塞操作,很可能会产生卡住页面的情况。甚至于我们想实现一个类似于Android的专用 公用Service,那该怎么办?
H5新标准提出了WebWorker的概念,各个浏览器都各自实现了,先来看一下WebWorker能做什么。
WebWorker特点,在后台线程执行JS的能力,与页面通过send message这种方式通信。
WebWorker有两种,专用worker(dedicatedworker)与公用worker(sharedworker)。
疑问:worker与主线程如何同步,worker与主线程同时操作了一个DOM元素,会不会产生脏数据?所以,worker的能力被加以限制,不能访问DOM元素。
worker在chrome中是如何实现的?
chrome浏览器是多进程架构,分为Browser进程以及Render进程,每打开一个页面,浏览器都会为其分配一个Render进程,webkit以及js都运行在这个进程内。如果要起一个DedicatedWorker,Chrome会在Render进程中起一个线程。如果要起一个SharedWorker就稍微复杂一点,必须起一个专门的进程,并且,相同的SharedWorker不管你创建多少次,都只存在一个。
Android中的Chrome有一个限制,限定9个进程。
1.如果用户创建太多SharedWorker,可能第二个标签页都打不开?
2.SharedWorker的优先级如何定义?如果使用SharedWorker的页面都在后台,其优先级如何?
目前Android上的SharedWorker还处于讨论阶段,未实现。
下面看一下worker的基本用法,DedicatedWorker:
worker.js
this.addEventListener('message', function(e) { var data = e.data; console.log("worker: " + data); this.postMessage(data + 1); });
worker中需要一个this.onmessage接收消息
postMessage发送消息
参数在 event.data中
main.js
if (window.Worker) { var worker = new Worker("./worker.js"); worker.onmessage = function(e) { document.getElementById("worker").innerHTML = e.data; }; document.addEventListener('keydown', function(evt) { worker.postMessage(evt.keyCode); }); }
使用Worker这个API来创建DedicatedWorker。
同样通过worker实例的onmessage和postMessage通信。
结束Worker:
在worker中,可以通过close()来kill掉自己
main中则调用worker.terminate()
如果worker运行中出现错误,在main中使用worker.onerror可以接收到错误消息
SharedWorker:
https://github.com/mdn/simple-shared-worker
SharedWorker跟DedicatedWorker有两个不同的地方:
1.通信不再直接通过worker,而是worker的port类
2.worker中需要实现onconnect,且其参数中有一个port列表,但目前只使用到了第一个
sharedworker.js
onconnect = function(e) { var port = e.ports[0]; port.onmessage = function(e) { var workerResult = 'Result: ' + (e.data[0] * e.data[1]); port.postMessage(workerResult); } }
main.js
if (!!window.SharedWorker) { var myWorker = new SharedWorker("worker.js"); myWorker.port.postMessage([squareNumber.value,squareNumber.value]); myWorker.port.onmessage = function(e) { result2.textContent = e.data; console.log('Message received from worker'); } }
当然 还可以写另一个页面,同样可以使用跟main.js类似的方法跟sharedworker通信。
ServiceWorker:
ServiceWorker是WebWorker的一种,它更加复杂,所以也更加强大。
首先,ServiceWorker有独立的生命周期:
1.Register:主线程调用API注册ServiceWorker
2.Installing:浏览器启动安装过程,加载和缓存一些静态资源
有可能失败进入Error状态
3.Activated:激活阶段,此阶段可以升级ServiceWorker
4.激活后,ServiceWorker会接管页面,如果页面是刚刚注册,本次不会被接管,下次加载页面才会接管。
5.ServiceWorker接管页面后,如果有fetch和message事件,会处于onfetch和onmessage,其他情况可能被终止。
ServiceWorker的特性:
1.它是一个worker,同样不能操作dom元素,同样可以通过postMessage与调用线程通信
2.ServiceWorker增加了网络处理,onfetch
3.ServiceWorker不被使用的时候,它会自己终止,再次使用会被重新激活,不要依靠它的内存来保存信息,请用webStorage或者indexDB。
4.ServiceWorker大量使用Promise,就是封装的一个callback标准
5.ServiceWorker权限很大,即所有网络请求都经过它,可以劫持连接,伪造和过滤响应,所以只能在https网页上注册ServiceWorker(是只能么?)。
6.ServiceWorker只作用于同域的fetch。onfetch有一个缓存的例子,我们通过缓存类缓存一些request和response,下次request,直接去缓存找response,处理失败了再去网络实时请求。注:response的类型需要是basic,即同域请求。
8.ServiceWorker的自动更新。
当网页激活时,浏览器会检查ServiceWorker是否有更新(有一个字节不同就会认为有更新),浏览器后台下载。
下载完开始运行,进入install状态,之后进入waitting状态。因为此时旧的ServiceWorker仍然在运行。
当页面被杀掉,旧去新来。
当然,如果你之前缓存了request,更新后需要清理一下。
现有问题:
如果在Install时失败了,页面无法感知。
ServiceWorker主要作用是在onfetch里面缓存/处理request。
https://github.com/GoogleChrome/samples
中有大量的onfetch与cache结合使用做离线应用的例子,在此不多赘述。
来自
http://blog.csdn.net/yl02520/article/details/14446763
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
遇到引擎不能处理相对路径的问题,写了一个简单的相对路径转绝对路径的函数,留下备用:
function rel2Abs(path) {
var _path = path;
var locationStr = location.href.toString();
console.log(locationStr);
var result = '';
var relative;
while (relative = _path.substring(0, _path.indexOf("\/"))) {
console.log(relative);
if (relative === '.') {
// 遇到. path去掉最前面的./
// 遇到.当前路径去掉/后面内容
_path = _path.substring(_path.indexOf('\/') + 1, _path.length);
locationStr = locationStr.substring(0, locationStr.lastIndexOf('\/') + 1);
} else if (relative === '..') {
// 遇到.. path去掉最前面的../
// 遇到.. 当前路径去掉/xxx,然后再去掉/后面的内容
_path = _path.substring(_path.indexOf('\/') + 1, _path.length);
locationStr = locationStr.substring(0, locationStr.lastIndexOf('\/'));
locationStr = locationStr.substring(0, locationStr.lastIndexOf('\/') + 1);
} else {
break;
}
}
var result = locationStr + _path;
path = result;
return result;
}
原理很简单,只支持/,且遇到错误不能自动处理