函数
函数也是一个对象,函数可以封装一些功能(代码),在需要时可以执行这些功能(代码)。
定义一个函数
构造函数
- 换句话说,函数中可以保存一些代码,在需要的时候调用
创建一个函数对象:
var fun = new Function();
可以将要封装的代码以字符串的形式传递给构造函数。
var fun = new Function("document.write('你好');");
封装的函数中的代码不会立即执行。函数中的代码会在函数调用的时候执行,调用函数语法:函数对象();
,当调用函数时,函数中封装的代码会按顺序执行。在默认状态下,函数是不会被执行的。使用小括号可以激活并执行函数。在小括号中可以包含零个或多个参数。
fun();
函数声明
-
但是在开发中很少使用构造函数来创建一个函数对象!!那我们再来说下函数的声明。
-
语法:
function 函数名([形参1,形参2,形参3...]){ 语句... }
函数表达式
-
使用函数表达式的方式创建一个函数,语法:
var 函数名 = function([形参1,形参2,...]){语句...}
-
也称为匿名函数,即没有名字的函数。
-
匿名函数可以把函数作为一个值直接赋值给变量:
var f = function(a,b){
return a+b;
};
- 匿名函数作为值,可以参与更加复杂的表达式运算。(立即执行函数)
console.log(
(function(a,b){
return a+b;
})(1,2));//返回3
函数的参数
定义一个用来求两个数和的函数:
- 可以在函数的
()
中指定一个或多个形参(形式参数),多个形参之间使用,
隔开。形参就相当于在函数内部声明了对应的变量,但是并不赋值。 - 在调用函数时,可以在
()
中指定实参(实际参数),实参会赋值给函数中的形参。 - 上面的a,b就是形参,10和20就是实参。
- 调用函数时,解析器不会检查实参的类型(实参可以是任意的数据类型)。所以要注意是否有可能接收到非法的参数,如果有可能,则需要对参数进行类型的检查。
- 调用函数的时候,解析器也不会检查实参的数量。
- 多余的形参的默认值为undefined。在实际应用中,经常出现实参数量少于形参数量的情况,但是在函数内依然可以使用这形参,这是因为在定义函数时,已经对他们进行了初始化,设置了默认值。在调用函数时,如果用户不传递或少传递参数,则函数会采用默认值。
- 多余的实参不会被赋值。形式参数少于实际参数的比较少见,这种情况一般发生在参数数量不确定的函数中。
函数的返回
可以使用return设置函数的返回值,语法:return 值;
return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
function sum(a, b, c){
var d = a + b + c;
return d;
}
result = sum(3,6,9);
alert(result);
- 这样处理函数的结果更加灵活。
- 在函数中return后的语句都不会执行,仅能执行一条return。因此在函数体内可以进行分支结构决定函数返回值,或者使用return语句提前终止函数的运行。
- 如果return后面不跟任何值就相当于返回一个undefined。如果函数中不写return,则也会返回undefined。
方法
当一个函数被设置为对象的属性值时,称之为方法。如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用函数就说调用对象的方法(method)。只是名称的区别,本质上是一样的。
下面实例,创建一个obj对象,它有一个value属性,一个increment方法。increment方法就收一个可选参数,如果该参数不是数字,那么默认使用数字1。
var obj = {
value : 0;
increment : function(inc){
this.value += typeof inc == 'number'? inc:i;
}
}
obj.increment();
console.log(obj.value);//1
obj.increment(2);
console.log(obj.value);//3
在increment方法中可以使用this访问obj对象,然后使用obj.value方式读写value的值。
枚举对象中的属性
在开发中我们很多时候使用别人构建好的对象,并不知道对象中的属性值,所以我们需要枚举一下对象中的属性。我们要用到for…in语句。
语法:
for(var 变量 in 对象){
}
for... in
语句中,对象有几个属性,循环就会执行几次,每次执行时,会将对象中的一个属性的名字交给变量。
我们也可以将对象中的属性值取出来,不过需要通过中括号而不是点,因为这里面n是变量,中括号中可以放变量。
变量提升
我们先引入一个概念,变量的提升。
JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。以下两个实例将获得相同的结果:
x = 5; // 变量 x 设置为 5
elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x; // 在元素中显示 x
var x; // 声明 x
var x; // 声明 x
x = 5; // 变量 x 设置为 5
elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x; // 在元素中显示 x
变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部。
JavaScript 初始化不会提升。JavaScript 只有声明的变量会提升,初始化的不会。以下两个实例结果结果不相同:
var x = 5; // 初始化 x
var y = 7; // 初始化 y
elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y; // 显示 x 和 y
var x = 5; // 初始化 x
elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y; // 显示 x 和 y
var y = 7; // 初始化 y
第二个的 y 输出了 undefined,这是因为变量声明 (var y) 提升了,但是初始化(y = 7) 并不会提升,所以 y 变量是一个未定义的变量。
函数提升
- 使用函数声明形式创建的函数
function 函数名(){}
它会在所有的代码执行之前被创建,所以我们可以在函数声明前来调用函数。
//函数声明,会被提前创建
function fun(){
console.log("我是一个fun函数");
}
- 使用函数表达式创建的函数,不会被声明,所以不能在声明前调用。
//函数表达式,不会被提前创建
var fun2 = function(){
console.log("我是fun2函数");
}
函数的作用域
在JS中一共有两种作用域全局作用域和局部作用域(函数作用域)。
-
全局作用域:
-
直接编写在script标签中的JS代码,都在全局作用域。
-
全局作用域在页面打开的时候创建,在页面关闭时销毁。
-
在全局作用域中有一个全局对象window,我们可以直接使用。
-
在全局作用域中,创建的变量都会作为window对象的属性保存。
-
全局作用域中定义的变量,都是全局变量,在页面中的任意位置都可以访问到。
-
-
局部作用域:
- 调用函数时创建函数作用域,函数执行完毕后,函数作用域销毁。
- 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。
- 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。
- 当在函数作用域中操作一个变量时,它会先在自身作用域中寻找,如果没有就会去上一级中寻找。直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError。
- 在函数中要访问全局变量,可以使用window对象。
- 在函数作用域中也有变量提升的特性,使用var关键字声明的变量,会在函数中所有的代码执行之前被声明。函数声明也会在函数中所有的代码执行之前执行。
this和调用对象
解析器在调用函数时,每次向函数内部传递一个隐含的参数,这个隐含的参数就是this。this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据调用方式不同,this的指向不同对象。
- 以函数的形式调用时,this永远都是window。
function myFunction() {
return this;
}
//实例中,this 表示 myFunction 函数的所有者:[object Window]
- 以方法的形式调用时,this就是调用方法的那个对象。
var person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function() {
return this.firstName + " " + this.lastName;
}
};
//实例中,this 指向了 person 对象。因为 person 对象是 fullName 方法的所有者。
再看一个例子: