2017-08-11 08:47:28jzzjyyyunyi2

JavaScript高級程序設計 學習筆記 js高級技巧

第十八章 高級技巧
1.高級函數
1.1 作用域安全的構造函數
①直接調用構造函數而不適用new操作符時,由於this對象的晚綁定,它將映射在全局對象window上,導致對象屬性錯誤增加到window。
復制代碼 代碼如下:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
}
Var person = Person("Jay",29,"singer"); //屬性增加到window對象上。

②作用域安全構造函數
復制代碼 代碼如下:
function Person(name,age,job){
if(this instanceof Person){
this.name = name;
this.age = age;
}else{
return new Person(name,age);
}
}

③上述作用域安全的構造函數,如果使用構造函數竊取模式的繼承且不使用原型鏈,那麼這個繼承很可能被破壞。
□如果構造函數竊取結合使用原型鏈或者寄生式組合則可以解決這個問題。
復制代碼 代碼如下:
function Polygon(side){
if(this instanceof Polygon){
this.sides = sides;
this.getArea = function{return 0;};
}else{
return new Polygon(sides);
}
}
function Rectangle(width,height){
Polygon.call(this,2);
this.width = width;
this.height = height;
this.getArea = function(){
return this.width * this.height;
};
}
Rectangle.prototype = new Polygon();
var rect = new Rectangle(5,10);
alert(rect.sides); //2

1.2 惰性載入函數
①惰性載入表示函數執行的分支僅會發生一次:既第一次調用的時候。在第一次調用的過程中,該函數會被覆蓋為另一個按合適方式執行的函數,這樣任何對原函數的調用都不用再經過執行的分支了。
■優點:
□要執行的適當代碼只有當實際調用函數時才進行。
□盡管第一次調用該函數會因額外的第二個函數調用而稍微慢點,但後續的調用都會很快,因避免了多重條件。
復制代碼 代碼如下:
function create XHR(){
if(typeof XMLHttp Request != "undefined"){
createXHR = function(){
return new XMLHttpRequest();
};
}else if(typeof ActiveXObject != "undefined"){
createXHR = function(){
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
for(vai I = 0, len = versions.length; I < len; i++){
try{
Var xhr = new ActiveXObject(version[i]);
Arguments.callee.activeXString = version[i];
Return xhr;
}catch(ex){
//skip
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
}else{
createXHR = function(){
throw new Error("No XHR Object available.");
};
}
return createXHR();
}

1.3 函數綁定
①函數綁定要創建一個函數,可以在特定環境中以指定參數調用另一個函數。
②一個簡單的bind()函數接受一個函數和一個環境,並返回一個在給定環境中調用給定函數的函數,並且將所有參數原封不動傳遞過去。
復制代碼 代碼如下:
function bind(fn, context){
return function(){
return fn.apply(context, arguments);
};
}

③被綁定函數與普通函數相比有更多的開銷——它們需要更多內存,同時也因為多重函數調用而稍微慢一點——所以最好只在必要時使用。
1.4 函數柯裡化
定義:用於創建已經設置好了一個或多個參數的函數。函數柯裡化的基本方法和函數綁定是一樣的:使用一個閉包返回一個函數。兩者的區別在於,當函數被調用時,返回函數還需要設置一些傳入的參數。
復制代碼 代碼如下:
function bind(fn, context){
var args = Array.prototype.slice.call(arguments, 2);
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(context,finalArgs);
};
}

2.高級定時器
①JavaScript是單線程程序,定時器是在間隔時間後將代碼添加到列隊。
②執行完一套代碼後,JavaScript進程返回一段很短的時間,這樣頁面上的其他處理就可以進行了。
2.1 重復的定時器
①setInterval()僅當沒有該定時器的任何其他代碼實例時,才將定時器代碼添加到隊列中。
□某些間隔會被跳過。
□多個定時器代碼執行之間的間隔可能會比預期小。
②避免setInterval()的兩個缺點,使用鏈式setTimeout()調用:
復制代碼 代碼如下:
setTimeout(function(){
//處理
if(condition){
setTimeout(arguments.callee, interval);
}
},interval);

2.2 Yielding Processes
①JavaScript長時間運行腳本制約:如代碼運行超過特定的時間或特定的語句數量就不會讓它繼續執行。
②當某個函數要花200ms以上的事件完成,最好分割為一系列可以使用定時器的小任務。
③數組分塊技術:為要處理的項目創建一個隊列,然後使用定時器取出下一個要處理的項目進行處理,接著再設置另一個定時器。
復制代碼 代碼如下:
function chunk(array, process, context){
setTimeout(function(){
var item = array.shift();
process.call(context,item);
if(array.length>0){
setTimeout(arguments.callee, 100);
}
}
}

2.3 函數節流
①DOM操作比起非DOM交互需要更多內存和CPU時間。連續嘗試進行過多的DOM相關操作可能會導致浏覽器掛起,有時甚至崩潰。
②函數節流思想:某些代碼不可以在沒有間斷的情況連續重復執行。
□示例
復制代碼 代碼如下:
var processor = {
timeoutId : null,
//實際進行處理的方法
performProcessing : function(){
//實際執行的方法
},
//初始處理調用的方法
process : function(){
clearTimeout(this.timeoutId);
var that = this;
this.timeoutId = setTimeout(function(){
that.performProcessing();
},100);
}
};
//嘗試開始執行
Processor.process();
□簡化模式
function throttle(method,context){
clearTimeout(mehtod.tId);
mehtod.tId = setTimeout(function(){
method.call(context);
},100);
}

3.自定義事件
①事件是一種叫做觀察者的設計模式,這是一種創建松散耦合代碼的技術。
□對象可以發布事件,用來表示該對象聲明周期中某個有趣的時刻到了。
□其他對象可以觀察該對象,等待有趣的時刻到來並通過運行代碼來響應。
②觀察者模式由兩類對象組成:主體和觀察者。
□主體負責發布事件,同時觀察者通過訂閱這些事件來觀察主體。
□主體並不知道觀察者的任何事情,它可以獨立自存在並正常運作即使觀察者不在。
③自定義事件:創建一個管理事件的對象,讓其他對象監聽那些事件。
復制代碼 代碼如下:
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor : EventTarget,
addHandler : function(type,handler){
if(typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire : 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,len=handlers.length; i<len; i++){
handlers[i](event);
}
}
},
removeHandler : function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i=0,len=handlers.length; i<len; i++){
if(handlers[i] === handler){
break;
}
}
Handlers.splice(i,1);
}
};

④使用EventTarget類型的自定義事件可以如下使用:
復制代碼 代碼如下:
function handleMessage(event){
alert("message received:" + event.message);
}
//創建一個新對象
var target = new EventTarget();
//添加一個事件處理程序
target.addHandler("message",handleMessage);
//觸發事件
target.fire({type:"message",message:"hello world!"});
//刪除事件處理程序
target.removeHandler("message",handleMessage);

⑤使用實例
復制代碼 代碼如下:
function Person(name,age){
eventTarget.call(this);
this.name = name;
this.age = age;
}
inheritPrototype(Person, EventTarget);
Person.prototype.say = function(message){
this.fire({type:"message", message:message});
};
function handleMessage(event){
alert(event.target.name + "says: " + event.message);
}
//創建新person
var person = new Person("Nicholas",29);
//添加一個事件處理程序
Person.addHandler("message",handleMessage);
//在該對象上調用1個方法,它觸發消息事件
person.say("Hi there");

4.拖放
功能:①拖放②添加了自定義事件
復制代碼 代碼如下:
var DragDrop = function(){
var dragdrop = new EventTarget();
var dragging = null;
var diffX = 0;
var diffY = 0;
function handleEvent(event){
//獲取事件和對象
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
//確定事件類型
switch(event.type){
case "mousedown" :
if(target.className.indexOf("draggable")>-1){
dragging = target;
diffX = event.clientX - target.offsetLeft;
diffY = event.clientY - target.offsetTop;
dragdorp.fire(
{
type:"dragstart",
target : dragging,
x : event.clientX,
y : event.clientY
}
);
break;
case "mousemove" :
if(dragging !== null){
//獲取事件
event = EventUtil.getEvent(event);
//指定位置
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
//觸發自定義事件
dragdrop.fire(
{
type : "drag",
target : dargging,
x : event.clientX,
y : event.clientY
}
);
}
break;
case "mouseup" :
dargdorp.fire(
{
type : "dragend",
target : dragging,
x : event.clientX,
y : event.clientY
}
);
dragging = null;
break;
}
}
//公共接口
dragdrop.enable = function() {
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
};
dragdrop.disable = function(){
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
};
return dragdrop;
}();

来源:JavaScript高級程序設計 學習筆記 js高級技巧