Skip to content

函数

function介绍

函数 function - 函数也是一个对象 - 函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码) - 函数中可以保存一些代码在需要的时候调用 - 使用typeof检查一个函数对象时,会返回function

我们在实际开发中很少使用构造函数来创建一个函数对象 使用构造函数创建一个函数对象 可以将要封装的代码以字符串的形式传递给构造函数

js
var fun = new Function("console.log('Hello 这是我的第一个函数');");

封装到函数中的代码不会立即执行 函数中的代码会在函数调用的时候执行 调用函数 语法:函数对象() 当调用函数时,函数中封装的代码会按照顺序执行

使用 函数声明 来创建一个函数,语法:

js
		function 函数名([形参1,形参2...形参N]){
			语句...
		}
		//例子:
		function fun2(){
			console.log("这是我的第二个函数~~~");
			alert("哈哈哈哈哈");
			document.write("~~~~(>_<)~~~~");
		}

使用 函数表达式 来创建一个函数,语法:

js
	  var 函数名  = function([形参1,形参2...形参N]){
		 语句....
	   }
	//例子 创建匿名函数:
	function(){
		console.log("我是匿名函数中封装的代码");
	};
	//调用匿名函数
	var fun3 = function(){
		console.log("我是匿名函数中封装的代码");
	};

函数的参数

例子:

js
	 // 	定义一个用来求两个数和的函数
	 // 	可以在函数的()中来指定一个或多个形参(形式参数)
	 // 	多个形参之间使用,隔开,声明形参就相当于在函数内部声明了对应的变量
	 // 	但是并不赋值
	function sum(a,b){
		console.log("a = "+a);
		console.log("b = "+b);
		console.log(a+b);
	}
	//在调用函数时,可以在()中指定实参(实际参数)
	// 	实参将会赋值给函数中对应的形参
	sum(1,2);
	sum(123,456);

调用函数时解析器不会检查实参的类型, 所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查 函数的实参可以是任意的数据类型

js
	sum(123,"hello");
	sum(true , false);

调用函数时,解析器也不会检查实参的数量 多余实参不会被赋值 如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined

js
sum(123,456,"hello",true,null);
sum(123);

函数的返回值

可以使用 return 来设置函数的返回值

语法:
	return 值

return后的值将会会作为函数的执行结果返回,可以定义一个变量,来接收该结果 在函数中return后的语句都不会执行 如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined return后可以跟任意类型的值

js
	function sum(a , b , c){
		//alert(a + b +c);		
		var d = a + b + c;	
		return d;		
	}

调用函数 变量result的值就是函数的执行结果 函数返回什么result的值就是什么

js
			var result = sum(4,7,8);			
			console.log("result = "+result);

返回值可以是任意的数据类型,也可以是一个对象,也可以是一个函数

js
			function fun2(){
				
				//返回一个对象
				return {name:"沙和尚"};
			}
js
			function fun3(){
				//在函数内部再声明一个函数
				function fun4(){
					alert("我是fun4");
				}
				//将fun4函数对象作为返回值返回
				return fun4;
			}

函数练习

js
 //定义一个函数,判断一个数字是否是偶数,如果是返回true,否则返回false		
			function isOu(num){		
				return num % 2 == 0;
			}		
			var result = isOu(15);
js
// 定义一个函数,可以根据半径计算一个圆的面积,并返回计算结果
			function mianji(r){		
				return 3.14*r*r;		
			}
			result = mianji(5);
js
/*
 * 创建一个函数,可以在控制台中输出一个人的信息(传递对象)
 * 	可以输出人的 name age gender address
 * 实参可以是任意的数据类型,也可以是一个对象
 * 	当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递
 */
			function sayHello(o){
				
				//console.log("o = "+o);
				console.log("我是"+o.name+",今年我"+o.age+"岁了,"+"我是一个"+o.gender+"人"+",我住在"+o.address);
			}
			//创建一个对象
			var obj = {
				name:"孙悟空",
				age:18,
				address:"花果山",
				gender:"男"
				
			};

所以:

js
//实参可以是一个对象,也可以是一个函数		
			function fun(a){
				console.log("a = "+a);
				a(obj);
			}
			fun(sayHello);
//将匿名函数作为实参
			fun(function(){alert("hello")});
js
		fun(mianji(10));
		fun(mianji);
//两者的区别:
		mianji()
			  	- 调用函数
			  	- 相当于使用的函数的返回值			  
		 mianji
			  	- 函数对象
			 	- 相当于直接使用函数对象

break continue return的区别

使用break可以退出当前的循环 continue用于跳过当次循环 使用return可以结束整个函数

js
function fun(){
				alert("函数要执行了~~~~");				
				for(var i=0 ; i<5 ; i++){								
					if(i == 2){
						//使用break可以退出当前的循环
						//break;						
						//continue用于跳过当次循环
						//continue;						
						//使用return可以结束整个函数
						//return;
					}					
					console.log(i);
				}				
				alert("函数执行完了~~~~");
			}

立即执行函数

立即执行函数: 函数定义完,立即被调用,这种函数叫做立即执行函数 立即执行函数往往只会执行一次

js
	//用括号标识匿名函数
	(function(){
				alert("我是一个匿名函数~~~");
			})();		
	(function(a,b){
		console.log("a = "+a);
		console.log("b = "+b);
	})(123,456);

将函数作为对象的属性(方法)

对象的属性值可以是任何的数据类型,也可以是个函数

js
			var obj = new Object();
			obj.name = "孙悟空";
			obj.age = 18;
	//对象的属性值可以是任何的数据类型,也可以是个函数
			obj.sayName = function(){
				console.log(obj.name);
			};

可以通过调用属性的方法调用该函数

js
			obj.sayName();//调方法

函数也可以称为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数时这个对象的方法 调用这个函数就说调用对象的方法(method) 但是它只是名称上的区别没有其他的区别 也可以通过字面量的方式来写

js
			var obj = {
				
				name:"猪八戒",
				age:18,
				sayName:function(){
					console.log(obj.name);
				}
				
			};
			
			.say.Name();

枚举对象中的属性(遍历)

js
<script type="text/javascript">
			
			var obj = {
						name:"孙悟空",
						age:18,
						gender:"男",
						address:"花果山"
					 };		 
			//枚举对象中的属性
			//使用for ... in 语句
			/*
			  语法:
			  	for(var 变量 in 对象){
			  	
			   }
			  
			  for...in语句 对象中有几个属性,循环体就会执行几次
			  	每次执行时,会将对象中的一个属性的名字赋值给变量
			 */
			
			for(var n in obj){
				console.log("属性名:"+n);
				
				console.log("属性值:"+obj[n]);
			}
		</script>

全局作用域

- 作用域指一个变量的作用的范围
- 在JS中一共有两种作用域:

	1.全局作用域
		- 直接编写在script标签中的JS代码,都在全局作用域
		- 全局作用域在页面打开时创建,在页面关闭时销毁
		- 在全局作用域中有一个全局对象window
			它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用
		- 在全局作用域中:
			创建的变量都会作为window对象的属性保存
			创建的函数都会作为window对象的方法保存
		- 全局作用域中的变量都是全局变量
			在页面的任意的部分都可以访问的到

	2.函数作用域

函数作用域

JS
函数作用域	
		- 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
		- 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
		- 在函数作用域中可以访问到全局作用域的变量
			在全局作用域中无法访问到函数作用域的变量
		- 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用
			如果没有则向上一级作用域中寻找,直到找到全局作用域,
			如果全局作用域中依然没有找到,则会报错ReferenceError
		- 在函数中要访问全局变量可以使用window对象
		
		- 在函数作用域也有声明提前的特性,
			使用var关键字声明的变量,会在函数中所有的代码执行之前被声明
			函数声明也会在函数中所有的代码执行之前执行
			
		-在函数中,不适用var声明的变量都会成为全局变量	
		-在函数中,定义形参就相当于在函数作用域中声明了变量

变量的声明提前

使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值), 但是如果声明变量时不适用var关键字,则变量不会被声明提前

函数的声明提前

使用函数声明形式创建的函数 function 函数(){} 它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用

JS
			//函数声明,会被提前创建
			function fun(){
				console.log("我是一个fun函数");
			}
			
			//函数表达式,不会被提前创建
			var fun2 = function(){
				console.log("我是fun2函数");
			};

this(当前对象)(类似于python的self)

解析器在调用函数每次都会向函数内部传递进一个隐含的参数, 这个隐含的参数就是this,this指向的是一个对象, 这个对象我们称为函数执行的 上下文对象, 根据函数的调用方式的不同,this会指向不同的对象 1.以函数的形式调用时,this永远都是window 2.以方法的形式调用时,this就是调用方法的那个对象

js
			function fun(){
				//console.log("a = "+a+", b = "+b);
				console.log(this.name);
			}
			var obj = {
				name:"孙悟空",
				sayName:fun
			};
			//以方法的形式调用,this是调用方法的对象
			obj.sayName();//this就是obj

例子:

js
		<script type="text/javascript">
			
			//创建一个name变量
			var name = "全局";
			
			//创建一个fun()函数
			function fun(){
				console.log(this.name);
			}
			
			//创建两个对象
			var obj = {
					name:"孙悟空",
					sayName:fun
			};
			
			var obj2 = {
					name:"沙和尚",
					sayName:fun
			};			
			//我们希望调用obj.sayName()时可以输出obj的名字
			//obj.sayName();			
			obj.sayName();	
		</script>

补充进阶:

js
		<script type="text/javascript">
			/*
			 * 在调用函数时,浏览器每次都会传递进两个隐含的参数:
			 * 	1.函数的上下文对象 this
			 * 	2.封装实参的对象 arguments
			 * 		- arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
			 * 		- 在调用函数时,我们所传递的实参都会在arguments中保存
			 * 		- arguments.length可以用来获取实参的长度
			 * 		- 我们即使不定义形参,也可以通过arguments来使用实参,
			 * 			只不过比较麻烦
			 * 			arguments[0] 表示第一个实参
			 * 			arguments[1] 表示第二个实参 。。。
			 *		- 它里边有一个属性叫做callee,
			 * 			这个属性对应一个函数对象,就是当前正在指向的函数的对象
			 * 		
			 */
			
			function fun(a,b){
				//console.log(arguments.length);
				console.log(arguments.callee == fun);
			}
			
			fun("hello",true);
			
		</script>

date对象

Date对象创建
  • 在JS中使用Date对象来表示一个时间 创建一个Date对象,保存当前时间
js
			//创建一个Date对象
			//如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间
			var d = new Date();
			
			//创建一个指定的时间对象
			//需要在构造函数中传递一个表示时间的字符串作为参数
			//日期的格式  月份/日/年 时:分:秒
			var d2 = new Date("2/18/2011 11:10:30");

创建一个指定的时间对象

js
			//创建一个指定的时间对象
			//需要在构造函数中传递一个表示时间的字符串作为参数
			//日期的格式  月份/日/年 时:分:秒
			var d2 = new Date("2/18/2011 11:10:30");
方法
getDate()
js
			/*
			 * getDate()
			 * 	- 获取当前日期对象是几日
			 */
			var date = d2.getDate();
getDay()
js
			/*
			 * getDay()
			 * 	- 获取当前日期对象时周几
			 * 	- 会返回一个0-6的值
			 * 		0 表示周日
			 * 		1表示周一
			 * 		。。。
			 */
			var day = d2.getDay();
getMonth()
js
			/*
			 * getMonth()
			 * d2 = new Date("12/18/2011 11:10:30");
			 * - 获取当前时间对象的月份
			 * 	- 会返回一个0-11的值
			 * 		0 表示1月
			 * 		1 表示2月
			 * 		11 表示12月
			 */
			var month = d2.getMonth();
getFullYear()
			/*
			 * getFullYear()
			 * 	- 获取当前日期对象的年份
			 */
			var year = d2.getFullYear();
getTime()
			/*
			 * getTime()
			 * 	- 获取当前日期对象的时间戳
			 * 	- 时间戳,指的是从格林威治标准时间的1970年1月1日,0时0分0秒
			 * 		到当前日期所花费的毫秒数(1秒 = 1000毫秒)
			 * 	- 计算机底层在保存时间时使用都是时间戳
			 */
			
			var time = d2.getTime();

可以利用时间戳来测试代码的执行的性能

			//利用时间戳来测试代码的执行的性能
			//获取当前的时间戳
			var start = Date.now();
			
			for(var i=0 ; i<100 ; i++){
				console.log(i);
			}
			
			var end = Date.now();
			
			
			console.log("执行了:"+(end - start)+"毫秒");

math对象

Math和其他的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法 比如:Math.PI 表示的圆周率 方法:

js
			 *	abs()
			    -可以用来计算一个数的绝对值
			 * Math.ceil()
			 * 	- 可以对一个数进行向上取整,小数位只有有值就自动进1
			 * Math.floor()
			 * 	- 可以对一个数进行向下取整,小数部分会被舍掉
			 * Math.round()
			 * 	- 可以对一个数进行四舍五入取整
			 			 * Math.random()
			 * 	- 可以用来生成一个0-1之间的随机数
			 *  - 生成一个0-10的随机数
			 * 	- 生成一个0-x之间的随机数
			 * 		Math.round(Math.random()*x)
			 * 
			 * 	- 生成一个1-10
			 * 	- 生成一个x-y之间的随机数
			 * 		Math.round(Math.random()*(y-x)+x)
			 * max() 可以获取多个数中的最大值
			 * min() 可以获取多个数中的最小值
			 * Math.pow(x,y)
			 * 	返回x的y次幂
			 * 	Math.sqrt()
			 *  用于对一个数进行开方运算

包装类

			 * 基本数据类型
			 * 	String Number Boolean Null Undefined
			 * 引用数据类型
			 * 	Object
			 * 
			 * 在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
			 * 	String()
			 * 		- 可以将基本数据类型字符串转换为String对象
			 * 	Number()
			 * 		- 可以将基本数据类型的数字转换为Number对象
			 *  Boolean()
			 * 		- 可以将基本数据类型的布尔值转换为Boolean对象
			 * 	但是注意:我们在实际应用中不会使用基本数据类型的对象,
			 * 		如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果
方法和属性之能添加给对象,不能添加给基本数据类型
当我们对一些基本数据类型的值去调用属性和方法时,
浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法
调用完以后,在将其转换为基本数据类型

批量创建对象(使用工厂方法创建对象)

创建一个对象

js
			var obj = {
					name:"孙悟空",
					age:18,
					gender:"男",
					sayName:function(){
						alert(this.name);
					}
			};

使用工厂方法创建对象,通过该方法可以大批量的创建对象

js
			function createPerson(name , age ,gender){
				//创建一个新的对象 
				var obj = new Object();
				//向对象中添加属性
				obj.name = name;
				obj.age = age;
				obj.gender = gender;
				obj.sayName = function(){
					alert(this.name);
				};
				//将新的对象返回
				return obj;
			}

然后通过这样的函数就可以批量创建同类对象

js
			var obj2 = createPerson("猪八戒",28,"男");
			var obj3 = createPerson("白骨精",16,"女");
			var obj4 = createPerson("蜘蛛精",18,"女");

使用工厂方法创建的对象,使用的构造函数都是Object 所以创建的对象都是Object这个类型, 就导致我们无法区分出多种不同类型的对象

js
		//创建一个狗的对象
		function createDog(name , age){
			var obj = new Object();
			obj.name = name;
			obj.age = age;
			obj.sayHello = function(){
				alert("汪汪~~");
			};
			
			return obj;
		}
		//和创建人的一样,检查类型都是Object

构造函数

构造函数就是一个普通的函数,创建方式和普通函数没有区别,
不同的是构造函数习惯上首字母大写

构造函数和普通函数的区别就是调用方式的不同
普通函数是直接调用,而构造函数需要使用new关键字来调用

 构造函数的执行流程:
 1.立刻创建一个新的对象
 2.将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
 3.逐行执行函数中的代码
 4.将新建的对象作为返回值返回
 
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。
我们将通过一个构造函数创建的对象,称为是该类的实例

this的情况:
  	1.当以函数的形式调用时,this是window
  	2.当以方法的形式调用时,谁调用方法this就是谁
  	3.当以构造函数的形式调用时,this就是新创建的那个对象

创建一个构造函数,专门用来创建Person对象的

JS
			function Person(name , age , gender){
				this.name = name;
				this.age = age;
				this.gender = gender;
				this.sayName = function(){
					alert(this.name);
				};
			}

使用instanceof可以检查一个对象是否是一个类的实例 如果是,则返回true,否则返回false

语法:
   对象 instanceof 构造函数

所有的对象都是Object的后代, 所以任何对象和Object左instanceof检查时都会返回true

   创建一个Person构造函数
   - 在Person构造函数中,为每一个对象都添加了一个sayName方法,
   	目前我们的方法是在构造函数内部创建的,
   		也就是构造函数每执行一次就会创建一个新的sayName方法
   	也是所有实例的sayName都是唯一的。
   	这样就导致了构造函数执行一次就会创建一个新的方法,
   		执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的
   		这是完全没有必要,完全可以使所有的对象共享同一个方法
   		function Person(name , age , gender){
   			this.name = name;
   			this.age = age;
   			this.gender = gender;
   			向对象中添加一个方法
   			this.sayName = fun;
   		}

也可以 将sayName方法在全局作用域中定义

js
			function fun(){
				alert("Hello大家好,我是:"+this.name);
			};

将函数定义在全局作用域,污染了全局作用域的命名空间,而且定义在全局作用域中也很不安全 对应此问题,我们提出原型的概念

原型

原型 prototype

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象

如果函数作为普通函数调用prototype没有任何作用
当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,
指向该构造函数的原型对象,我们可以通过__proto__来访问该属性

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,
我们可以将对象中共有的内容,统一设置到原型对象中。

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,
如果没有则会去原型对象中寻找,如果找到则直接使用

以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,
这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了

图示:

例子:

js
			function MyClass(){
				
			}
			
			//向MyClass的原型中添加属性a
			MyClass.prototype.a = 123;
			
			//向MyClass的原型中添加一个方法
			MyClass.prototype.sayHello = function(){
				alert("hello");
			};
js
			function Person(name , age , gender){
				this.name = name;
				this.age = age;
				this.gender = gender;
				//向对象中添加一个方法
				//this.sayName = fun;
			}
			//向原型中添加sayName方法
			Person.prototype.sayName = function(){
				alert("Hello大家好,我是:"+this.name);
			};
js
			/*
			 * 创建一个构造函数
			 */
			function MyClass(){
				
			}
			
			//向MyClass的原型中添加一个name属性
			MyClass.prototype.name = "我是原型中的名字";
			
			var mc = new MyClass();
			mc.age = 18;
			
			//console.log(mc.name);
			
			//使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
			//console.log("name" in mc);
			
			//可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
			//使用该方法只有当对象自身中含有属性时,才会返回true
			//console.log(mc.hasOwnProperty("age"));
			/*
			 * 原型对象也是对象,所以它也有原型,
			 * 	当我们使用一个对象的属性或方法时,会现在自身中寻找,
			 * 		自身中如果有,则直接使用,
			 * 		如果没有则去原型对象中寻找,如果原型对象中有,则使用,
			 * 		如果没有则去原型的原型中寻找,直到找到Object对象的原型,
			 * 		Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined
			 */

toString方法

当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值(会显示[object Object],对象的构造函数类) 如果我们希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法

js
			function Person(name , age , gender){
				this.name = name;
				this.age = age;
				this.gender = gender;
			}
			
			//修改Person原型的toString(重写toString方法,现在浏览器版本用不到)
			Person.prototype.toString = function(){
				return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
			};
			//创建一个Person实例
			var per = new Person("孙悟空",18,"男");
			var per2 = new Person("猪八戒",28,"男");

函数对象的方法(call()和apply())

js
			function fun(a,b) {
				console.log("a = "+a);
				console.log("b = "+b);
				//alert(this);
			}
			
			var obj = {
				name: "obj",
				sayName:function(){
					alert(this.name);
				}
			};
			/*
			 * call()和apply()
			 * 	- 这两个方法都是函数对象的方法,需要通过函数对象来调用
			 * 	- 当对函数调用call()和apply()都会调用函数执行
			 * 	- 在调用call()和apply()可以将一个对象指定为第一个参数
			 * 		此时这个对象将会成为函数执行时的this
			 * 	- call()方法可以将实参在对象之后依次传递
			 * 	- apply()方法需要将实参封装到一个数组中统一传递
			 * 
			 * 	- this的情况:
			 * 		1.以函数形式调用时,this永远都是window
			 * 		2.以方法的形式调用时,this是调用方法的对象
			 * 		3.以构造函数的形式调用时,this是新创建的那个对象
			 * 		4.使用call和apply调用时,this是指定的那个对象
			 */
call()

call()方法可以将实参在对象之后依次传递

apply()

apply()方法需要将实参封装到一个数组中统一传递

垃圾回收(GC)

JS
- 就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾
	这些垃圾积攒过多以后,会导致程序运行的速度过慢,
	所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾
- 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,
	此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,
	所以这种垃圾必须进行清理。
- 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,
	我们不需要也不能进行垃圾回收的操作
- 我们需要做的只是要将不再使用的对象设置null即可
	var obj = new Object();
	obj = null;