一、函数定义
函数是完成某一个具体功能的代码块。
JavaScript 使用关键字 function 定义函数。
1. 声明函数
函数有两种声明方式:
1)自定义函数方式(命名函数)
利用函数关键字 function 自定义函数
// 声明函数
function functionName(parameters) {
执行的代码
} // 不以分号结束
// 调用函数
functionName();
- function——声明/定义/创建函数的关键字【标志】
- functionName——函数名称【自己定义】
- (parameters)——参数列表/传递参数【参数是引入函数之外的数据进入本函数】
- {}——函数体,包含具体功能的实现代码
函数声明后不会立即执行,在需要的时候去调用后才会执行。
分号是用来分隔可执行JavaScript语句。
由于函数声明不是一个可执行语句,所以不以分号结束。
// 案例1:自定义函数
function test1(a, b) {
return a * b;
}
var x = test1(4, 5); // 有返回值,声明一个变量x保存返回值
console.log('案例1的x是:' + x); // 20
2)函数表达式方式(匿名函数)
JavaScript 函数可以通过一个表达式定义。
var fn = function(){...}; // 匿名函数后面跟分号结束
fn(); // 函数调用必须写到函数体下面
- fn 是变量名,不是函数名
- 因为函数没有名字,所以也被称为匿名函数
- 这个 fn 里面存储的是一个函数 ,而不是值
- 函数调用的代码必须写到函数体后面
匿名函数以分号结尾,因为它是一个执行语句。
// 案例2:函数表达式
var x = function (a, b) {
return a * b;
}
console.log('案例2的x是:' + x); // 变量x里面存储的是函数,不是一个值
console.log(x(3, 4)); // 把变量x当做函数名来调用函数,得到函数的执行结果 12
2. Function() 构造函数
创建函数有2种方式:
- 通过关键字 function 定义(即以上我们学习过的,关键字function是小写的)
- 通过内置的 JavaScript函数构造器Function()定义(构造函数Function首字母是大写的)
// 案例3:构造函数
var test2 = new Function('a', 'b', 'return a*b'); // 参数、函数执行代码都写在()内
var res = test2(5, 6); // 传参并调用函数,将返回结果保存在变量res中
console.log(res); // 30
3. 函数提升
函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
fn();
function fn() {
console.log('打印');
}
结果:控制台打印字符串 — ”打印“
注意:函数声明代表函数整体,所以函数提升后,函数名代表整个函数,但是函数并没有被调用!
函数表达式声明函数问题
函数表达式创建函数,会执行变量提升,而不是函数提升。
fn();
var fn = function() {
console.log('想不到吧');
}
结果:报错提示 ”fn is not a function"
解释:该段代码执行之前,会做变量声明提升,fn在提升之后的值是undefined;而fn调用是在fn被赋值为函数体之前,此时fn的值是undefined,所以无法正确调用。
// 相当于:
var fn;
fn();
fn = function() {
console.log('想不到吧');
}
4. 自调用函数
-
不能自调用声明的函数。
-
函数表达式可以 “自调用”。
-
通过添加括号,来说明它是一个函数表达式。
-
表达式后面紧跟 () ,则会自动调用。
(function () {
console.log('我是自己调用的'); // 把整个函数用()括起来,表示它是一个函数表达式
})(); // 表达式后面紧跟(),自动调用函数
以上函数实际上是一个 匿名自我调用的函数 (没有函数名)。
5. 函数是对象
在 JavaScript 中使用 typeof 操作符判断函数类型将返回 “function” 。
但是JavaScript 函数描述为一个对象更加准确,因为函数有 属性 和 方法。
// 案例5:函数有属性和方法
function test5(a, b, c) {
return arguments.length;
}
// arguments.length 属性返回函数调用过程接收到的参数个数:
console.log(test5(7, 8, 9)); // 3 接收到的参数个数是3个
// toString() 方法将函数作为一个字符串返回:
console.log(test5.toString());
二、函数参数
参数是引入函数之外的数据进入本函数进行元素的变量
1. 显式参数(Parameters)与隐式参数(Arguments)
- 函数显式参数——在函数定义时列出。
- 函数隐式参数——在函数调用时传递给函数真正的值。
function test1(name, age) { // name,age 是显示参数
return name + ',' + age;
}
console.log(test1('张三', 25)); // '张三',25是隐式参数
2. arguments 对象
arguments 是 JavaScript 函数的一个内置对象,包含了函数调用的参数数组。
- 具有 length 属性
- 按索引方式储存数据
// 案例2:找参数里的最大值
function findMax() {
var max = arguments[0]; // 把数组里的第一个数当做最大的,用变量max保存起来
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] > max) { // 用数组里的每一个数和max去比,当哪一个>max时,
max = arguments[i]; // 就把这1个赋值给max,从而保证max的值就是最大的那个
}
}
return max;
}
var res = findMax(5, 6, 80, 99, 32, 41);
console.log(res); // 99
// 案例3:求数组中的最大值
function getArrMax(arr) {
var max2 = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max2) {
max2 = arr[i];
}
}
return max2;
}
var res2 = getArrMax([5, 6, 80, 99, 32, 41]);
console.log(res2); // 99
// 案例4:求所有数值的和
function sumAll() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum = sum + arguments[i];
}
return sum;
}
var x = sumAll(2, 6, 66, 8, 12, 30, 44,);
console.log('这些数的和是:' + x); // 168
3. 通过值传递参数
- 在函数中调用的参数是函数的隐式参数。
- 隐式参数通过值来传递:函数仅仅只是获取值。
- 如果函数修改参数的值,不会修改显式参数的初始值(在函数外定义)。
- 隐式参数的改变在函数外是不可见的。
4. 通过对象传递参数
- 在JavaScript中,可以引用对象的值。
- 因此我们在函数内部修改对象的属性就会修改其初始的值。
- 修改对象属性可作用于函数外部(全局变量)。
- 修改对象属性在函数外是可见的。
function test5(name, age) {
return name + ',' + age;
}
var re = test5('李四', 28); // '李四', 28--是参数的具体值【值传递】
var canshu1 = '王五';
var canshu2 = 30;
console.log(canshu1.charAt(0)); // 王 canshu1有charAt()方法,canshu1变量就是对象
var res = test5(canshu1, canshu2); // 参数是变量,它有方法,参数也是对象--【对象传递】
console.log(res); // 王五,30
三、函数调用
函数中的代码在函数被调用后执行,JavaScript 函数有 4 种调用方式。
每种方式的不同在于 this 的初始化。
this 关键字
一般而言,在Javascript中,this指向函数执行时的当前对象。
1. 作为一个函数调用
// 案例1:全局函数
function test1(a, b) {
return a * b;
}
console.log(test1(10, 5)); // 50
console.log(window.test1(10, 5)); // 50
console.log(this); // this指向的是Window
- 以上函数不属于任何对象。
- 但是在 JavaScript 中它始终是默认的全局对象。
- 在 HTML 中默认的全局对象是 HTML 页面本身,所以函数是属于 HTML 页面。
- 在浏览器中的页面对象是浏览器窗口(window 对象),所以以上函数会自动变为 window 对象的函数。
- 所以test1() 和 window.test1() 是一样的。
- 以上案例1返回 this 的值是 window 对象
全局对象
当函数没有被自身的对象调用时 this 的值就会变成全局对象。
在 web 浏览器中全局对象是浏览器窗口(window 对象)。
2. 函数作为方法调用
可以将函数定义为对象的方法。
var obj = {
firstName: '张',
lastName: '三',
fullName: function () {
return this.firstName + this.lastName; // this指向obj对象
}
}
document.write(obj.fullName()); // 张三
函数作为对象方法调用,会使得 this 的值成为对象本身。
3. 使用构造函数调用函数
如果函数调用前使用了 new 关键字, 则是调用了构造函数。
这看起来就像创建了新的函数,但实际上 JavaScript 函数是重新创建的对象。
function test3(a, b) {
this.firstName = a;
this.lastName = b;
}
var x = new test3('李', '四') // 构造函数的调用会创建一个新的对象x
document.write(x); // [object Object]
document.write(x.firstName); // 李 新对象x继承了构造函数的属性
构造函数的调用会创建一个新的对象。新对象会继承构造函数的属性和方法。
4. 作为函数方法调用函数
在 JavaScript 中, 函数是对象。JavaScript 函数有它的属性和方法。
function test4(a, b) {
return a * b;
}
var myObj;
myObj = test4.call(myObj, 5, 6); // call传入的是实际参数 第一个参数必须是对象本身
document.write(myObj); // 30
var myArr = [7, 8];
myObj = test4.apply(myObj, myArr); // apply传入的是一个参数数组 第一个参数必须是对象本身
document.write(myObj); // 56
-
call() 和 apply() 是预定义的函数方法。
-
可用于调用函数,第一个参数必须是对象本身。
-
两个方法都使用了对象本身作为第一个参数。
-
两者的区别在于第二个参数:
- apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入
- 而call则作为call的参数传入(从第二个参数开始)
-
在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。
-
在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。
通过 call() 或 apply() 方法你可以设置 this 的值, 且作为已存在对象的新方法调用。