JavaScript高级程序设计(反刍) 6

第七章:

在前面已经介绍过了定义函数的两种方式:函数声明函数表达式
函数声明定义函数的写法存在一个重要的特征就是“函数声明提升”,在执行脚本之前,编译器会先通读一遍脚本,发现函数声明则自动将其提升到脚本的最顶端,优先解析。这也是为什么可以将函数声明放在函数调用之后的原因。
函数表达式的写法则不同,函数表达式本质上并非是创建函数,而是创建了一个变量,变量的赋值使用的是函数。这种赋值式的创建方法创建出的函数其实是一个匿名函数。

递归:
递归的写法本质上就是函数自己通过名字调用自身的一种情况,这种方法在其他的语言中也存在。

function sum(num){
    if(num <= 1){
    	return 1;
	}else{
   		return num * arguments.callee(num-1);
	}
}

使用 callee() 方法降低函数的耦合性,callee()方法指向的是调用该方法的函数

var sum = (function f(num){
    if(num <= 1){
    	return 1;
	}else{
    	return num * f(num-1);
	}
});

在严格模式下不允许使用arguments.callee()方法,所以可以使用这种命名函数的写法达到同样的效果。

闭包:
( 闭包这个概念在整个JavaScript体系中都比较重要,大家在看着一部分的时候需要好好理解,我个人理解这个概念也相当有限,还是希望大家去看看原书的 P178 )
闭包就是指有权访问另一个函数作用域中的变量的函数。当内部函数被保存到外部时,就会生成闭包。
其实简单的讲就是因为在JavaScript中,允许使用内部函数,也就是函数当中嵌套函数。这些内部函数因为存在于外部函数内部的原因,根据解析的规则可以访问其外部函数所有的局部变量/方法/参数等,当这样的内部参数被包含他们的外部函数之外的函数调用时,就形成了闭包!
闭包
闭包的写法也比较多,一般来说满足上面条件的都会形成闭包。具体的实现方法可以参考这位大神的博客!

var Cirle = function(){
    var obj = new Object();
    obj.PI = 3.1415;
    obj.area = function(r){
        return this.PI * r * r;
}
return obj;
}
var c = new Cirle();
console.log(c.area(2));        //12.56 (不同浏览器存在精度差)
var cirle = {
	PI: 3.14,
	area: function(r){
	return this.PI * r * r;
	}
}

var a = cirle.area(2);        //注意这里的引用方式比较特殊
console.log(a);               //12.56

这里需要注意第二种闭包创建方式的写法,这里的引用方式比较特殊,是因为prototype属性定义的特殊性。

var dom = function(){};
	dom.sayName = function(){
	console.log("dom-sayName");
}

dom.prototype.sayAge = function(){
	console.log("dom-prototype-sayAge");
}

dom.sayName();               //dom-sayName
dom.sayAge();                //error

var person = new dom();
person.sayName();            //erroy
person.sayAge();             //dom-proyotype-sayAge

闭包创建
在JavaScript中,每个函数都有一个prototype属性,但是对象不存在这个属性。分析上面的代码后得出结论:

  1. 不使用prototype属性定义的对象方法,是静态方法,只能直接用类名进行调用!另外,此静态方法中无法使用this变量来调用对象其他的属性!
  2. 使用prototype属性定义的对象方法,是非静态方法,只有在实例化后才能使用!其方法内部可以this来引用对象自身中的其他属性!
var dom = function(){
    var Name = "Default";
    this.Sex = "Boy";
    this.success = function(){
       alert("Success");
    };
};
console.log(dom.Name);         //undefined
console.log(dom.Sex);          //undefined

这里使用函数表达式的方法,通过this创建的两个属性和一个方法,这里直接使用函数名点出属性的方式访问的结果为undefined
这是因为当使用这种方式创建函数时,函数的作用域起到了阻隔的作用。现在函数内的方法仅限于函数内的变量/方法引用,不允许外部方法直接调用,想要调用方法必须实例化出一个对象,通过对象进行调用。

闭包的使用:
立即执行函数:
在一个项目中的全部函数,有些函数仅仅需要执行一次,执行完后即销毁,这种情况下可以使用闭包

(function (参数){
   //函数执行
})(参数);

这种立即执行函数的写法最典型的一个好处就是其封闭性,该函数体内存在的所有变量和方法都仅在该函数中起作用,外部无法引用其内部的变量,执行完成后立即释放全部资源。不污染全局变量,且函数内变量名可以随意定义

结果缓存:
大型项目中往往会出现许多繁杂的函数,处理时间长,页面响应速度慢,使用闭包将函数的执行结果进行封装缓存,当函数调用时,率先查看缓存中是否存在,不存在再执行函数。

var CachedSearchBox = (function(){    
    var cache = {},    
        count = [];    
    return {    
        attachSearchBox : function(dsid){    
            if(dsid in cache){    //如果结果在缓存中    
               return cache[dsid];  //直接返回缓存中的对象    
            }    
            var fsb = new uikit.webctrl.SearchBox(dsid);   //新建    
            cache[dsid] = fsb;    //更新缓存    
            if(count.length > 100){   //保正缓存的大小<=100    
               delete cache[count.shift()];   //当大于100时将先进去的删除 
            }    
            return fsb;          
        },    
     
        clearSearchBox : function(dsid){    
            if(dsid in cache){    
               cache[dsid].clearSelection();      
            }    
        }    
    };    
})();    
     
CachedSearchBox.attachSearchBox("input");

因为闭包会携带包含他的函数的作用域,所以闭包会比其他的函数占内存,过度使用闭包可能造成页面缓存进度慢,所以大家谨慎使用,但是闭包的概念非常重要!

this对象:
this对象在前面已经有提到过,this是基于运行时函数的执行环境的,this在谁的执行环境中,它就指向谁。

全局环境中this指向window,对象调用时this指向该对象,匿名函数的执行环境具有全局性,所以匿名函数的this通常指向window,不过通过call和apply改变执行环境,this就会改变指向。

内存泄漏:
内存泄漏通常是闭包形成的重要问题,当闭包的作用域链中出现HTML元素时,该元素将无法被销毁,即使闭包不再引用该元素,但闭包的包含元素也会保存这一个引用,所以尽可能在使用HTML元素时,在最后解除对于它的引用!

模仿块级作用域:
使用立即执行函数模仿块级作用域,前面已经介绍过JavaScript中不存在块级作用域。

function Apple(){
    for(var i=0; i<10; i++){
    	console.log(i)
     }
console.log(i);               //10
}

(function (){
   for(var j=0; j<10; j++){
        console.log(j);
   }
})();
Console.log(j);              //error

私有变量:
在函数的内部定义的变量就是私有变量,不允许其他函数访问

特权方法:
把有权访问私有变量的方法称之为特权方法。
其实特权方法就是在函数内部创建一个闭包的方法,将函数内部的属性/方法在闭包的内部获得,因为闭包能够被外部函数获取的特殊性,就形成了外部函数可以获取另一个函数内部属性/方法的情况,也就形成了这种特权方法。

function apple(){
	var name = "abc";
	var age = 12;
	function privation(){
		return 1;
	}
	this.tq = function (){              //特权方法
		console.log(name+",,,"+age);
	privation();
	};
}

function bababan(){
	var person = new apple();
	person.tq();                        //abc12
}
bababan();

静态私有变量:
在私有作用域中也可以构建特权方法,也就是在立即执行函数的内部构建特权方法,这种方法普遍用于封装代码,这种方式封装出来的代码复用性更强,不必担心变量名重复等问题。

(function apple(){
	var name = "abc";
	var age = 12;
	MyObject = function(name){
			this.name = name;
	}
	MyObject.prototype.tq = function (){
		var ab = name + age;
		return ab;
	};
})();

var person = new MyObject();
console.log(person.tq());  					//abc12

猜你喜欢

转载自blog.csdn.net/Feng_ye__/article/details/89362302
今日推荐