javascript部分高階自學筆記

高級函數

  • 安全的類型監測
    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);

拖放

元素必須是絕對定位

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章