高級函數
- 安全的類型監測
javascript內置類型監測並非完全靠譜,比如typeof
監測null 返回function。 再比如instanceof操作符,在存在多個全局作用域(比如一個頁面包含有多個frame)也是問題多多。
var isArray = value instanceof Array;
以上代碼要顯示true,value除了是一個數組外還必須與Array構造函數在同一個全局作用域
。Array 是window的屬性,如果value是另一個frame中定義的,以上代碼返回false
監測某個對象是否是原生對象還是開發人員自定義,也會有問題:因爲瀏覽器開始原生支持JSON對象了
解決上述問題的辦法都一樣
任何值上調用Object原生
的toString()方法,都會返回[object NativeConstructorName]格式字符串,每個類的內部都有一個[[Class]]屬性,這個屬性這制定了上述字符串中的構造函數名。Object.prototype.toString().call(value);//[object Array]
。原生數組的構造函數名和全局作用域無關,因此保證返回一致的值。IE中有誤,IE中所以的DOM都是以COM對象實現,意味DOM原生與原生javascript對象的行爲或活動不一致
。監測原生JSON對象,window.JSON && Object.prototype.toString().call(JSON);
區分原生和非原生javascript對象很重要,這樣才能確保知道某個對象到底有啥功能
- 作用域安全的構造函數
構造函數就是使用new操作符的函數,調用new,函數內的this指向新創建的對象實例。當不用new去調用構造函數的時候,由於this是在運行時綁定,直接調用this會映射到window對象,導致對象屬性的意外增加
。
function A(side){
if(this instanceof A){
this.side = side;
}else{
return new A(side);
}
}
function AB(width){
A.call(this,2);
this.width = width;
}
AB.prototype = new A();//沒這句 就無法獲取side屬性 寄生組合繼承
var s = new AB(5);
s.side;//2
- 惰性載入函數:函數執行的分支僅會發生一次
如果每次調用的函數的分支結果都是不變的(沒必要每次都執行)
,那麼這樣代碼的運行速遞更快了,解決方案就是惰性載入。
//按照合適的方式執行的函數
function doSomething(){
if(...){
doSomething = function(){}
}else if(...){
doSomething = function(){}
}
return doSomething();
}
//聲明時指定,代碼在首次加載的時候損失性能,在第一次調用就不會損失性能
var doSomething = (function (){
if(...){
return function(){};
}else if(...){
return function(){};
}
})();
- 函數綁定:bind()
ES5給所以的函數定義了原生的bind()方法
var handler = {
message: "hello",
hclick: function(){
alert(this.message);
}
}
btn.addEventListener("click",function(){
handler.hclick();//產生閉包,無法獲取message;handler.hclick.bind(handler)這樣纔可以
})
- 函數柯里化:用於創建已經設置了一個或多個參數的函數
function sum(a,b){ return a+b;}
function curry(fn){
var args = Array.prototype.slice.call(arguments,1);
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.cancat(innerArgs);
return fn.apply(null,finalArgs);
};
}
var all = curry(sum,5);
all(3);//8
var allTest = curry(sum,5,12);
allTest();//17
Es5 bind()
也實現了函數柯里化,函數柯里化會增加額外開銷(閉包)
防篡改對象
JavaScript共享本質一直是開發人員的頭痛,因爲任何對象都可以被在同一環境中的代碼修改
對象常量
writable和configurable 設置爲false 即可
一旦定義爲防篡改,就無法撤銷
Object.preventExtensions()
防止擴展
默認情況所以的對象都是可擴展的,也就是可以添加屬性和方法。調用Object.preventExtensions()
方法後,就無法給對象添加新屬性和方法。但絲毫不會影響修改和刪除已有的成員。Object.isExtensible()
還可以確定對象是否可擴展。- 密封對象
Object.seal()
不可擴展,已有對象的[[Configurable]]將設置爲false。意味着屬性和方法不可刪除。就算定義去回去也是undefined。先在現有的對象上面調用Object.preventExtensions(),然後設置現有屬性的configurable: false
Object.isSealed()
判斷是否被密封了。 - 凍結對象
凍結對象既不可擴展,也是密封的而且對象屬性的[[Wtiteable]]設置爲false。如果頂定義[[Set]]函數,訪問器屬性任然是可寫的。Object.freeze()/isFronzen()
先調用Object.seal() 然後設置屬性的writable:false
高級定時器
JavaScript單線程環境,頁面下載完代碼運行,事件處理,Ajax回調等實際上是瀏覽器負責排序,指派某段代碼在某個時間運行。
除了主JavaScript執行進程,還需要一個程序下一次空閒時執行代碼的隊列
定時器的時間間隔表示的是何時將定時器加入到代碼隊列,而不是何時執行實際的代碼。
- 重複的定時器
定時器代碼可能再次被添加到隊列之前,還沒完成執行,導致定時器連續運行好幾次,而中間沒有停頓。
setTimeout(function(){
setTimeout(arguments.callee,interval);
},interval)
這樣做的好處就是在前一個定時器執行完代碼之前,不會向隊列插入新的定時器代碼
確保了不會有任何缺失的間隔
數組分塊處理
function(array.concat(),process,context){ //保證原數組不被修改
setTimeout(function(){
var item = array.shift();
possess.call(context,item);
if(array.length > 0){
setTimeout(arguments.callee,interval);
}
},interval);
}
- 函數節流:resize事件中很常見,
週期性執行的代碼都需要使用節流
DOM操作比非DOM操作需要更多的內存和CPU
,連續嘗試過多的DOM操作會導致瀏覽器掛起,甚至奔潰。尤其IE中onresize事件處理程序,調整大小的時候會連續觸發,在事件內部嘗試更改DOM可能會導致瀏覽器崩潰。爲繞開這個問題,可以通過定時器對事件節流。
確保在時間間隔只調用一次
,控制瀏覽器的處理頻率
function throttle(method,context){
clearTimeout(method.tId);
method.tId = setTimeout(function(){
method.call(context);
},100);
}
沒有第二個參數的時候,是全局執行這個方法
自定義事件
JavaScript事件模式是觀察者設計模式,一種鬆耦合代碼技術。
主體
:負責發佈事件,DOM元素就是主體。觀察者
:訂閱這些事件來觀察主體。
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addEvent: function(type, handler){
if(typeof this.handlers[type] == 'undefined'){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fireEvent: function(event){
if(!event.target){
event.target = this;
}
if(this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for(var i = 0; i < handlers.length; i++){
handlers[i](event);
}
}
},
removeEvent: function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i = 0; i < handlers.length; i++){
if(handlers[i] == handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
function handlerMessage(event){
alert(event.message);
}
var target = new EventTarget();
target.addHandler("message",handlerMessage);
target.fire({type:"message",message:"Hello"});
target.removeHandler("message",handlerMessage);
拖放
元素必須是絕對定位