`

javascript 函数

阅读更多

1、函数定义
函数包含一组语句,它们是javascript的基础模块单元,用于代码复用、
信息隐藏和组合调用。函数用于指定对象的行为
2、函数的四种调用模式及this的初始化
第一种:方法调用模式
以下事例证明通过方法调用模式调用时,this绑定到拥有该方法的对象。

var person = {
		name	: "defaultName",
		setName : function(name){
			this.name = name;
		}
	};
	person.setName("zhangsan");
	alert(person.name);


第二种:函数调用模式
以下事例证明通过函数调用模式调用时,this绑定到全局对象上
如:var test = add(value1, value2);

var name = "defaultName";
	var person = {
		name	: "zhangsan", // person中定义的name
		getName : function(){
			// 通过此方法可以将test函数的this改变为person的this对象
			var that = this;  // 解决方案
			// getName中定义的name
			var name = "lisi";
			var test = function(){
				// 通过that来访问person中的对象
				// this指向Global对象
				// this.name = defaultName
				// that.name = zhangsan
				alert([this.name, that.name]);
			};
			test(); // 函数调用模式
		}

	}
	person.getName();


第三种:构造器调用模式

// 定义一个Person的构造器,在调用时一定要用new调用
	var Person = function(name){
		this.name = name;
	}
	// 添加一个方法到Person
	Person.prototype.getName = function(){
		return this.name;
	};
	
	// 构造一个Person对象
	var person = new Person("zhangsan");
	alert(person.getName()); // 调用getName获取person对象中name属性的值


第四种:Apply调用模式

<script type="text/javascript">
		// 定一个累加方法。如sum(1,2,3,4...)
		// 该方法位于window执行环境中。
		var displayName = function(){
			alert("sum的执行环境: " + typeof(this));
			alert("Name: " + this.name); // 取出当前执行环境中name属性
		}

		// 定一个Person对象
		var Person = {
			name	: "zhangsan"
		};
		displayName.apply(Person);
	</script>


3、Apply和call的区别

// 定一个对象,包含一个add方法,返回a、b的和
	var Person = {
		'add' : function(a, b){
			return a + b;
		}
	};
	
	// 显示a、b的和
	function showInfo(a, b){
		alert(this.add(a, b));
	}

	// 通过apply方法改变showInfo方法的this指向
	//	showInfo(1, 3); // 对象不支持次对象
	showInfo.apply(Person, [1, 3]);
	showInfo.call(Person, 1, 3);
	// 从上面可以看出,apply和call的区别是apply接受一个数组作为被调函数的参数,
	// 而call是通过将被调函数的所有参数以逗号分隔的形式展开


4、函数参数(arguments)
arguments并不是一个数组,只是与数组相似。arguments除了拥有length属性,数组的所有属性和方法都不具备。
用arguments来实现一个累加的函数。

function sum(){
		var total = 0;
		for(var i=0; i<arguments.length; i++){ // arguments.length返回sum函数调用时传递参数的个数
			total += arguments[i];
		}
		return total;
	}
	alert("sum: " + sum(1, 3, 2, 4));


5、函数返回值(return)
当一个函数被调用,通常会从函数的{开始执行到}结束。如果想提前结束该函数的执行
可以使用return语句,此时,return语句后面的所有语句将永远不会执行。如:

function test(){
		alert("first");
		return;
		alert("second"); // 该语句永远被不会执行
	}
	test();	// 一个函数总是会返回值,如果没有使用return返回值,默认返回undefined。如:
	function test(){
		alert("first"); 
	}
	alert(test()); // 输出:undefined
	// 如果函数前使用new方式调用,且返回值不是一个对象,则返回this(新对象)。如:
	function test(){
		alert("first");
	}
	var t = new test(); 
	alert(typeof t); // 输出:‘object’
	alert(t instanceof test); // 输出:true


6、异常(exception)
异常是干扰程序正常流程的非正常事故(可能人为有意的)。当检查出这样的事故,
应当抛出异常。如:

function add(a, b){ // 定义一个加法函数
		// 如果传递的参数不是数字类型,则抛出一个异常信息
		if(typeof a != 'number' || typeof b != 'number'){
			throw {
				'name'  	: "typeError", // 属性是自定义的,名字可以任意取
				'message'	: "add方法必须使用数字作为参数"
			};
		}
		return a + b;
	}
	
	(function(){
		// 捕获add方法可能产生的异常
		try{
			add(10, "");
		} catch(e){ 
			// 一个try语句只有一个catch语句,如果要处理多个异常,则通过异常的name属性来区别
			// 判断异常的类型
			if(e.name === "typeError"){
				alert(e.message);
			}
		}
	})();


7、给类型添加方法
javascript中允许给基本类型添加方法。如:boolean、string、Number
实例:在Function中添加一个method函数,该函数为Function添加其他自定义的函数(避免使用prototype),
然后利用method函数想Function中添加一个add函数,最后测试add函数在Function中确实存在。
该方法将func函数添加到Function中,以name命名。然后,返回Function的对象

Function.prototype.method = function(name, func){
		// 避免覆盖已有的方法
		if(!this.prototype[name]){
			this.prototype[name] = func;
		}
		return this;
	};
	
	// 通过Function.method方法添加一个加法函数到Function,该函数的名称为“add”
	Function.method("add", function(a, b){
		if(typeof a != 'number' || typeof b != 'number'){
			throw {
				'name'	  : "typeError",
				'message' : "add方法必须传入数字"
			};
		}
		return a + b;
	});
	
	// 调用Function的add方法是否存在
	(function(){
		try{
			alert(Function.add(1, 3)); // 输出:4
		} catch(e){
			if(e.name === 'typeError'){
				alert(e.message);
			}
		}
	})();
	
	// 去除字符串两端的空白
	String.method("trim", function(){
		return this.replace(/^\s+|\s+$/g, '');
	});
	alert('|' + "   hello world     ".trim() + '|'); // 输出: '|hello world|'

	// 添加数字的取整函数
	Number.method("integer", function(){
		// 可以通过此种方式调用函数,如:Math.random() == Math['random']() == Math["random"]()
		return Math[this < 0 ? 'ceil' : 'floor'](this);
	});
	alert((-10 / 3).integer());	 // 输出:-3


8、递归调用(arguments.callee)
递归调用就是自己调用自己。调用分为:直接调用和间接调用
下面展示使用递归调用来计算指定值的斐波那契数列。

// 求i的阶乘
	function factorial(i){
		if(i < 2){
			return 1;
		}
		return i*factorial(i-1); // 递归调用
	}
	alert(factorial(5)); // 求5的阶乘
	// 以上方式存在一个问题?如下:
	var factorial = function(i){
		if(i < 2){
			return 1;
		}
		return i*factorial(i-1); // factorial还能被调用吗?不能
	}
	var test = factorial; 
	factorial = null;  
	alert(test(2));
	// 解决方案:
	var factorial = function(i){
		if(i < 2){
			return 1;
		}
		return i*arguments.callee(i-1); // arguments.callee返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文
	}
	var test = factorial;
	factorial = null;
	alert(test(5));


9、作用域

// 在程序中,作用域控制着变量的可见性和生命周期。
	var name = "default"; // 全局作用域
	function getName(){
		var name = "getName"; // getName作用域下
		for(var i=0; i<2; i++){
			var inName = "inName";
		}
		alert(i + "," + inName); // 2,inName 注意:在js中没有块级作用域,及if、for、while中声明的变量是放在块所在的作用域下
		return name;
	}
	alert(getName()); // getName 注意:js存在函数作用域,所以在函数内部定义的变量在外部是不可见的
	alert(name); // default


注意:在现代的很多语言中,推荐将变量尽可能的延迟声明。如:java
      而在js中,却不推荐这样做,因为js不支持块级作用域。推荐在函数的开始就将所有用到的变量进行声明。

10、闭包
函数能够访问它被创建时环境的上下文称为闭包。
作用域的好处是,内部函数可以访问外部函数的所有变量(除this和arguments)。

var myObject = {
		value 	  : 0,
		increment : function(inc){
			this.value = typeof inc === 'number' ? inc : 1;
		},
		getValue  : function(){
			return this.value;
		}
	};
	myObject.increment(10);
	alert(myObject.value);
	alert(myObject.getValue());
	// 上面使用字面常量方式定义了一个myObject对象。但是value变量可以被外部对象访问
	var myObject = function(){
		var value = 0;
		return {
			increment: function(inc){
				value += typeof inc === 'number' ? inc : 1;
			},
			getValue : function(){
				return value;
			}
		};
	}();
	myObject.increment(10);
	alert(myObject.value); // 不能被外部对象访问
	alert(myObject.getValue()); // 10
	
	// 渐变body的背景色(黄色到白色)
	var fade = function(node){
		var level = 1;
		var step = function(){
			var hex = level.toString(16);
			node.style.backgroundColor = '#FFFF' + hex + hex;
			if(level < 15){
				level += 1;
				setTimeout(step, 500); // 如果level小于15,则内部函数自我调用
			}
		};
		setTimeout(step, 1); // 调用内部函数
	};
	fade(document.body);
	
	// 下面是一个很糟糕的例子
	<a href="#" name="test">点击我...</a><br> // 点击时显示3
	<a href="#" name="test">点击我...</a><br> // 点击时显示3
	<a href="#" name="test">点击我...</a><br> // 点击时显示3
	var add_the_handlers = function(nodes){
		var i;
		for(i = 0; i < nodes.length; i += 1) {
			nodes[i].onclick = function(e){ // 函数构造时的:i
				alert(i);
			};
		}
	};
	var objs = document.getElementsByName("test");
	add_the_handlers(objs);
	// 造成上面的原因是:a标签的事件函数绑定了变量i,则不是函数在构造时的i值。
	// 解决方案如下:
	var add_the_handlers = function(nodes){
		var i;
		for(i = 0; i < nodes.length; i += 1) {
			nodes[i].onclick = function(i){
				return function(e){
					alert(i); // 输出的i是构造函数传递进来的i,不是事件处理绑定的i。
				};
			}(i);
		}
	};
	var objs = document.getElementsByName("test");
	add_the_handlers(objs);



11、回调(callbacks)

// data表示参数,而call_function则表示回调函数
	function sendRequest(data, call_function){
		// setTimeout来模仿客户端请求服务端中传输数据的时间。
		// 当3秒钟后就调用回调函数(有客户端实现回调函数)
		setTimeout(function(){
			call_function(data); // 调用回调函数
		}, 3000);
	}
	// 测试sendRequest函数
	sendRequest("参数", function(context){
		alert("context=" + context);
	});



12、模块
模块是一个提供接口而隐藏状态和实现的函数或对象。
一般形式:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和
          函数的特权函数;最后返回这个特权函数,或者把他们保存到一个可以
          被访问到的地方。

Function.prototype.method = function(name,func){
		this.prototype[name] = func;
		return this;
	};

	String.method("deentityify",function(){
		var entity = {
			quot : '"',
			lt   : '<',
			gt   : '>'
		};
		return function(){
			return this.replace(/&([^&;]+);/g, function(a, b){ // 怎样知道a、b的值,了解正则表达式
				var r = entity[b];
				return typeof r === "string" ? r : a;
			});
		};
	}());
	alert("&lt;&quot;&gt;".deentityify()); // 测试:<">

注:模块模式通常结合单例模式使用,JavaScript的单例模式就是用对象字面量方式创建
的对象,对象的属性值可以是数值或函数,并且属性值在该对象的生命周期中不会发
生变化。

13、级联(链式操作)
对于一些不返回值的方法,我们返回this,而不是undefined,那么我们就可以
启动以级联(链式)去操作该对象。如下:

var $ = function(id){
		var obj = document.getElementById(id);
		obj.setColor = function(color){
			this.style.color = color;
			return this;
		};
		obj.setBgColor = function(color){
			this.style.backgroundColor = color;
			return this; // 返回this对象,启动级联
		};
		obj.setFontSize = function(size){
			this.style.fontSize = size;
			return this;
		};
		return obj;
	};

	$("test").setColor("red")
			 .setFontSize("30px")
			 .setBgColor("blue");
	// 改进后的代码:
	(function(id){
		var _$ = function(id){
			this.element = document.getElementById(id);
		};
		_$.prototype = {
			setColor : function(color){
				this.element.style.color = color;
				return this;
			},
			setBgColor : function(color){
				this.element.style.backgroundColor = color;
				return this;
			},
			setFontSize : function(size){
				this.element.style.fontSize = size;
				return this;
			}
		};
		
		// 添加到window原型链中
		window.$ = function(id){
			return new _$(id);
		};
	})();

	$("test").setColor("red")
			 .setFontSize("30px")
			 .setBgColor("blue");


14、套用
所谓套用就是将函数与传递给它的参数相结合,产生一个新的函数。
如:下面代码中定义一个add()函数,该函数能够返回一个新的函数,
并把参数值传递给这个新函数,从而实现连加操作。
// 第一种方式:

var add = function(a){
		return function(b){
			return a + b;
		}
	};
	alert(add(1)(2)); // 3
         // 第二种方式:用arguments实现
	var add = function(){
		var arg = arguments;
		return function(){
			var sum = 0;
			for(var i=0; i<arg.length; i++){
				sum += arg[i];
			}
			for(i=0; i<arguments.length; i++){
				sum += arguments[i];
			}
			return sum;
		}
	};
	alert(add(1,2,3)(4,5,6)); // 21	
	// 第三种方式:通过一个套用方法(curry)实现
	var add = function(){
		var sum = 0;
		for(var i=0; i<arguments.length; i++){
			sum += arguments[i];
		}
		return sum;
	};
	// 添加方法到Function的原型链上
	Function.prototype.method = function(name, func){
		this.prototype[name] = func;
		return this;
	};
	// 套用方法
	Function.method('curry', function(){
		// 通过数组Array的slice方法,使得arguments也具有concat方法
		var slice = Array.prototype.slice,
			args = slice.apply(arguments), that = this;
		return function(){
			return that.apply(null, args.concat(slice.apply(arguments)));
		};
	});

	alert(add.curry(1,2)(3,4)); // 10



15、记忆
函数可以用对象去记住先前操作的结果,从而能避免无谓的运算。这种优化被称为记忆。

var fibonacci = function(){
		var mome = [0,1]; // 存放计算后的数据
		var fib = function(n){
			var result = mome[n];
			// 如果不存在被计算过的数据,则直接计算。然后在将计算结果缓存
			if(typeof result !== 'number'){
				result = fib(n-1) + fib(n-2);
				mome[n] = result;
			}
			return result;
		};
		return fib;
	}();
	for(var i=0; i<=10; i++){
		document.writeln("// " + i + ": " + fibonacci(i) + "<br/>");
	}
	
	//==========================
	// 创建一个具有记忆的函数
	//==========================
	var memoizer = function(memo, fundamental){
		var shell = function(n){
			var result = memo[n];
			if(typeof result !== "number"){
				result = fundamental(shell, n);
				memo[n] = result;
			}
			return result;
		};
		return shell;
	};

	// 通过记忆函数memoizer完成斐波那契数列
	var fibonacci = memoizer([0,1], function(shell, n){
		return shell(n-1) + shell(n-2);
	});

	// 通过记忆函数memoizer完成阶乘
	var factorial = memoizer([1,1], function(shell, n){
		return n * shell(n-1);
	});

	for(var i=0; i<=15; i++){
		document.writeln("// " + i + ": " + factorial(i) + "<br/>");
	}

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics