7.1遞歸
經典遞歸例子
function factorial(num){
if(num <= 1){
return 1;
}else{
return num * factorial(num - 1);
}
}
var a = factorial; //①
factorial = null; //②
alert(a(3)); // ③ factorial is not a function
//原因 棧記憶體中存放一個factorial變量 指向堆記憶體中的函數體 第①句代碼 執行後 變量a 亦指向 堆記憶體中的函數體 第③句代碼執行後 factorial 變量 不再指向堆記憶體中的函數體 而執行第③句代碼時候 函數體内部調用了 factorial變量 此時的factorial已經為null 是以提示錯誤
//改造 arguments.callee函數 是一個指向正在運作的函數的指針 是以采用此方式可以避免 因名稱更換導緻的錯誤 此方式不适用嚴格模式
function factorial1(num){
if(num <= 1){
return 1;
}else{
return num * arguments.callee(num - 1);
}
}
var b = factorial1;
factorial1 = null;
alert(b(3)); // 6
// 命名函數表達式
var factorial2 = (function f(num){
if(num <= 1){
return num;
}else{
return num * f(num - 1);
}
});
var c = factorial2;
factorial2 = null;
alert(c(3)); // 6
7.2 閉包------是指有權通路另一個函數作用域中的變量的函數 個人了解 閉包就是函數A體内傳回B函數 ,B函數在外部環境執行時還依賴函數A的作用域鍊(無關于執行環境而依賴定義的環境)
function a(x){
return function b(y){
return x + y ;
}
}
var a1 = a(5); //①
var b1 = a1(5); //②
console.log(b1); //10 代碼①處 執行時 傳入變量x為5 代碼②則a1引用函數b 然後再傳入5 給a1 實際執行a函數内傳回的b函數 此時b函數依舊能通路a函數内部的變量x 是以結果 為10
閉包的副作用------閉包隻能取得函數體内變量的最後一個值,如下示例
function createFunctions1(){
var result = new Array();
for(var i=0; i< 10; i++){
result[i] = function(){
return i;
}
}
return result;
}
console.log(createFunctions1()[5]()); //10
//============改造==============
function createFunctions2(){
var result = new Array();
for(var i=0; i< 10; i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
console.log(createFunctions2()[5]()); //5
改造後并沒有直接把閉包指派給數組而是采用匿名函數的方式,并且立即執行這個匿名函數,傳回匿名函數内部的閉包 得以通路每個參數的值
7.2 .1關于this對象 --- this對象通常指的是整個函數執行環境, 而函數做為某個對象的方法調用時this則等于那個對象,由于閉包和匿名函數具有全局性 是以this對象通常指向window對象---如果以call或者apply去改變函數執行環境,那麼this就指向其他對象
7.2.2 記憶體洩漏 --- IE9 之前的版本采用的是引用計數的方式回收記憶體 ,而閉包是一個保留函數運作結果的全局變量,是以如果在IE9之前的浏覽器内運作使用了HTML元素對象的引用,那麼意味着該對象無法被銷毀
7.3 模仿塊級作用域
JS中沒有塊級作用域的概念,是以塊語句中定義的變量,實際上是包含在整個函數體内的,js解析器不會告知是否多次聲明了同一變量(不過,會執行變量的初始化)如下示例
function output1(){
for(var i=0; i< 2; i++){
continue;
}
var i;
console.log(i);
}
output1(); //2 通路了for循環體内的執行結果 是以等于2
function output2(){
for(var i=0; i< 2; i++){
continue;
}
console.log(i);//2 通路了for循環體内的執行結果 是以等于2
var i = 1; //對變量進行初始化
console.log(i); //1
}
output2();
匿名函數可以模仿塊級作用域來避免這樣的命名沖突--适合大型項目中采用 此方式在執行完内部的匿名函數後記憶體會被釋放掉 是以通路 i 的時候出錯
//模仿塊級作用域
function output3(){
(function(){
for(var i=0; i< 2; i++){
continue;
}
})();
console.log(i);// Uncaught ReferenceError: i is not defined
}
output3();
這種技術常在全局作用域中被用在函數外部,進而限制向全局作用域中添加過多的變量和函數,
7.4 私有變量 -------嚴格的說 js中沒有私有成員的概念;所有對象屬性都是公有的, 但是有私有變量的概念,任何在函數内部定義的變量,都可以認為是私有變量,因為不能在函數外部通路此變量 私有變量包括函數參數,局部變量和函數内部定義的其他函數
能夠通路私有變量和私有函數的方法稱之為特權方法 -- 如下示例:在構造函數内部定義了所有的私有變量和函數,然後建立來可以通路這些私有成員的特權方法 !
function MyObject(){
var privateName = 'zhangsan';
var privateFunction = function(){
return false;
}
this.publicFunction = function(){
console.log(privateName);
return privateFunction();
}
}
var person = new MyObject();
console.log(person.publicFunction()); // zhangsan ; false;
7.4.1靜态私有變量-------構造函數内部定義特權方法的缺點,就是必須使用構造函數模式來達到這個目的,而構造函數模式的缺點則是針對每個執行個體都會建立通用的方法,如果對象過多則會占用大量記憶體,基于此問題可以采用靜态私有變量來避免這個問題
(function(){
var privateName = 'zhangsan';
var privateFunction = function(){
return false;
};
MyObject = function(){}; //注意此處未使用var聲明 是以MyObject是一個全局變量
MyObject.prototype.publicFunction = function(){
console.log(privateName);
return privateFunction();
};
})();
var person = new MyObject();
console.log(person.publicFunction()); // zhangsan ; false;
7.4.2 子產品模式-為單例建立私有變量和特權方法
//js中單例的定義是以字面量來建立的
var singleton = {
name : 'zhangsan',
method : function(){
return name;
}
}
console.log(singleton.method()); // zhangsan
//子產品模式通過為單例添加私有變量和特權方法使其增強
var singleton = function(){
var privateName = 'zhangsan';
function privateFunction(){
return privateName;
}
return {
publicProperty : true,
publicMethod : function(){
return privateFunction();
}
};
}();
console.log(singleton.publicProperty); //true
console.log(singleton.publicMethod()); // zhangsan
7.4.3 增強的子產品模式 -----此模式适用于單例必須是某種類型的執行個體,同時還必須添加其他的屬性或方法對其加強
function CustomType(){
this.age = 18;
};
var singleton = function(){
var privateName = 'zhangsan';
function privateFunction(){
return privateName;
};
var Object = new CustomType();
Object.publicProperty = true;
Object.publicMethod = function(){
return privateFunction();
};
return Object;
}();
console.log(singleton.age); // 18 原執行個體中的年齡
console.log(singleton.publicProperty); //true 增強的屬性
console.log(singleton.publicMethod()); // zhangsan 增強的方法
小結: 使用函數表達式可以無需對函數命名,進而實作動态程式設計,匿名函數也稱拉達姆函數 !
函數表達式不同于函數聲明,函數聲明要求有名字,而函數表達式不需要,沒有名字的函數表達式也叫做匿名函數
遞歸函數應該始終使用arguments.callee來遞歸調用自身,而不要使用函數名稱;因為函數名稱可能會改變
函數内部定義了其他函數,就建立了閉包。閉包有權通路包含函數内部的所有變量 ,閉包的作用域鍊上包含這它自身的作用域,函數的作用域和全局作用域 ,通常函數的作用域以及其中所有變量都會在函數執行完畢後進行銷毀,但是由于閉包對函數的引用,這個函數的作用域将一直儲存直到閉包不存在為止;
塊級作用域 --建立并且立即調用一個函數,這樣既可以執行函數,又不會在記憶體中留下函數的引用,其函數内部所有變量都會立即銷毀
轉載于:https://www.cnblogs.com/shenwenbo/p/7853827.html