一.函数的介绍
函数:就是将一些功能或语句进行封装,在需要的时候,通过调用的形式,执行这些语句。
- 函数也是一个对象。
- 使用typeof( )函数检查一个函数对象时,会返回function
- 函数的使用包含两个步骤:
- 定义函数 —— 封装 独立的功能
- 调用函数 —— 享受 封装 的成果
1.2 定义函数
- 定义函数的过程是对某些功能的封装过程
- 在之后的开发中, 我们会根据自己的需求定义很多自己的函数
1.3 调用函数
- 调用函数是让已存在的函数为我们所用
- 这些函数可以是刚刚自己封装好的某个功能函数
- 当然, 我们也可以去使用JavaScript或者其他三方库定义好的函数.(拿来主义)
1.4 函数的作用
- ==将大量重复的语句写在函数里,以后使用这些语句的时候,可以直接调用函数,避免重复劳动。==
- ==简化编程,让编程模块化。==
1.5 函数的例子
计算1+2+3+4+5+6+7......+100?
var sum=0; function sumN(){ for(var i=1;i<=100;i++){ sum+=i; } return sum; } console.log(sumN(100));//5050
二.函数的定义和调用
2.1 函数的定义方式
2.1.1 使用==函数声明==来调用一个函数。
-
语法:
function 函数名([形参1,形参2,....]){//形参是可选的,备注:语法中的中括号表示“可选” 语句; }
-
特点
function:是一个关键字,中文是“函数”,"功能".
函数名字:命名规定和变量的规定一样。==只能是字母、数字、下划线、$,不能数字开头==
参数:可选。
大括号里面,是这个函数的语句。
方法定义是,形参的类型不用写,返回值类型也不写。
方法是一个对象,如果定义名称相同的方法,会覆盖
在JS中,方法的调用只与方法的名称有关,和参数列表无关
在方法声明中有一个隐藏的内置对象(数组),arguments,封装所有的实际参数
2.2.2 使用==函数表达式==来创建一个函数.
- 之前定义的函数被称之为函数的声明,除了声明之外,还有其他函数形式:
- 函数表达式:==匿名函数表达式和命名函数表达式==
- 函数的name属性:所有的函数都有一个name属性用于获取函数的名称
- *函数声明方式的name属性:函数名
- 命名函数表达式的name属性:函数名
- 匿名函数表达式的name属性:变量名
语法:
var 函数名=function([形参1,形参2,....]){//形参是可选的
语句;
}
函数表达式写法
2.2.3 立即执行函数
(function([形参1,形参2,....]){//形参是可选的
语句;
} )()
- 类比函数表达式:==( 这个括号里面就相当于一个匿名函数)(此括号里就是传递形参)==
函数声明和调用
2.2 函数形参和实参
function add(a,b){//a,b就是形式参数
return a+b;
}
//下面的add里面的为实参
console.log(add(10,20));//30
console.log(add("10","20"));//1020
console.log(add("hello","World"));//helloWorld
console.log(add(1,2,3,4,5));//3,只有两个参数,后面多余的参数无效
2.2.1 形参
- 可以在函数的()中来指定一个或多个形参
- 多个形参之间使用逗号隔开,声明形参就相当于在函数的内部声明了对应的变量,但是并不赋值
2.2.2 实参
- 在调用函数时,可以在()中指定实参
- 实参将会赋值给函数中对应的形参
2.2.2.1 实参的类型
- 函数的实参可以是任意的数据类型
- 用函数时解析器不会检查实参的类型,所以需要注意,是否有可能需要对参数进行类型的额检查
2.2.2.2 实参的数量
注意:JS里不会对函数实际传入的参数进行检测。可以传值,也可以不传值,也可以任意多个值
调用函数时,解析器也不会检查实参的数量:
==多余实参不会被赋值==
==如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined.==
-
例如:
2.2.3 arguments
可以通过arguments获取所有实际传递的参数
案例:
var total=0;
var sum=function(){
console.log(arguments);
for(var i=0;i
三.函数返回值
function sum(a,b){
return a+b;
}
sum(10,20);
return的作用是结束方法.
注意:
return后的值将会作为函数的执行结果,可以定义一个变量,来接收该结果
在函数中的return后的语句都不会执行(函数在执行完return语句之后停止并立即退出)
如果return语句后不跟任何值,就相当于返回一个undefined
如果函数中不写return,则也会返回undefined
-
返回值可以是任意的数据类型,可以是对象,也可以是函数。
//函数的返回值可以是函数 function sum(a,b){ var add=function(){ return a+b; } return add;//返回的整个add函数 return add();//30,这是函数的调用 } console.log(sum(10,20));
四.==函数名、函数体和函数加载问题(重要)==
-
函数名==整个函数
//函数名其实就是函数本省 var add=function(a,b){ return a+b; } console.log(add)==console.log(function(a,b){ return a+b; });
我们知道,当我们在调用一个函数时,通常使用函数()这种格式;但此时,我们是使用函数这种格式,它的作用相当于整个函数。
函数的加载问题:JS加载函数时,只加载函数名,所以如果像使用内部的成员变量,需要调用函数。
五. ==fn( )和fn的区别【重要】==
- fn( ):调用函数,相当于获取了函数的返回值。
- fn:函数对象,相当于直接获取了函数对象
六. 立即执行函数
-
什么是立即执行函数?
- 专业名字:Immediately-Invoked Function Expression(IIFE 立即调用函数表达式)
-
表达的含义是一个函数定义完后被立即执行
第一部分是定义了一个匿名函数,这个函数有自己独立的执行上下文环境。
-
第二部分是后面的(),表示这个函数被执行了
-
这个东西有什么用?
- ==会创建一个独立的执行上下文环境,可以避免外界访问或修改内部的变量,也避免了对内部变量的修改==
6.1 立即执行函数其他写法
-
立即执行函数必须是一个表达式,不能是函数声明:
- 下面的这种写法会报错,因为是一个函数声明,不是一个函数表达式
- 当圆括号出现在匿名函数的末尾想要调用函数时,它会默认将函数当成是函数声明。
- 当圆括号包裹函数时,它会默认将函数作为表达式去解析,而不是函数声明。
- 下面是一个函数表达式,所以可以执行
-
声明式函数立即执行函数的两种写法
(function () { console.log("我是声明式函数"); })()
(function () { console.log("我是声明式函数"); }());
立即执行函数如下:
//立即执行函数
var result=(function(a,b){
return a+b;
})(10,20);
console.log(result);
七. 方法
函数也可以成为对象的属性,如果一个函数作为一个对象的属性保存,那么我们成为这个函数是这个对象的方法。调用这个函数就说调用对象的方法(method).相比于方法,它只是名称上的区别,并没有其他的区别。
函数举例:
//调用函数
fn();
方法举例:
//调用方法
obj.fn();
我们可以这样说,如果直接使用fn(),那就说明是函数调用。如果是发现XX.fn()这种方式,那就说明是方法调用
八.this关键字
解析器在调用函数每次都会向函数内部传递一个硬汉年的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称之为函数执行的上下文对象。
==根据函数的调用方式不同,this会指向不同的对象:==
1.以函数的形式调用时,this永远是window。比如fun( );相当于Window.fun( );
2.以方法的形式调用时,this是调用方法的那个对象
3.以构造函数的形式调用时,this是新创建的那个对象
-
4.使用call和apply调用时,this是指定的那个对象
举例:
Document
8.1 改变this的指向
格式:
修改this的指针:
1.函数名.call(对象);
2.函数名.apply(对象);
3.bind();
改变this指向有三种方式:apply,call,bind
apply和call的不同点在于传值不同:apply传值使用的是数组,call是直接写就可以
/* 修改this的指针: 1.函数名.call(对象); 2.函数名.apply(对象); 3.bind(); */ var Student={ name:"jason", like:function(){ console.log(this); console.log(this.name+"钓鱼"); } } //Student.like();//this指向Student对象 var fn=Student.like; fn();//this指向window console.log(window.fn===fn);//true //改变this的指针 var teacher={ name:"老王", } //call; //fn.call(teacher); fn.apply(teacher)
-
==apply,call,bind的使用案例:==
Document
九.arguments
在调用函数时,浏览器每次都会传递两个隐含的参数:
- 1.函数的上下文对象 this
- 2.封装实参的对象 arguments
例如:
var abc=function(){
console.log(arguments);
console.log(typeof arguments);
}
abc();
arguments是一个类数组对象,它可以通过索引来操作数据,也可以获取长度。
arguments代表的是实参。在函数调用时,我们所传递的实参都会在arguments中保存,它只能在函数中使用
4.1 返回函数实参的个数:arguments.length
arguments.length可以用来获取实参的长度。
4.2 返回正在执行的函数:arguments.callee
arguments里面有一个属性叫callee,这个属性对应的函数对象,就是当前正在指向的函数对象。
在使用函数递归调用时,推荐使用arguments.callee代替函数名本身。
4.3 arguments可以修改元素
之所以说arguments是伪数组,是因为:arguments可以修改元素,但不能改变元素数组的长短。
举例:
/*
函数的arguments对象
*/
var abc=function(a,b,c,d){
console.log(arguments);
console.log(typeof arguments);//Object
//长度
console.log(arguments.length);//4
console.log(arguments.callee);//
//修改元素
if(arguments[0]){
arguments[0]=100;
}
console.log(arguments);
console.log(a);//100,同时也修改了实参
}
abc(1,2,3,4);
3.1 Function:函数(方法)对象
//当形式参数与实际参数长度不一致时
function fun4(a,b,c){
//document.write(a+b+c);
document.write(c);//当实际参数长度小于形式参数长时,形式参数默认为undefined类型
}
//fun4(1,2);
function fun5(a,b){
document.write(arguments[2]);//当实际参数长度大于形式参数时,在方法声明里内置对象(数组),arguments,封装所有实际参数
}
//fun5(1,2,3);
```
//例:求不定长度的和
function sum(){
var total=0;//注意这里一定后面赋值0要加上
for (var i = 0; i
十.函数练习题
10.1 练习一:实现一个加法计算器
function addCalculate() {
var total = 0;
for (var i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(addCalculate(10, 30, 60));
10.2 练习二:定义一个函数,传入宽高,计算矩形区域的面积
10.3 练习三:定义一个函数,传入半径,计算圆形的面积
10.4 练习四:定义一个函数,传入n(n为正整数),计算1~n数字的和
10.5 练习五:定义一个函数,传入一个数组,对数组进行翻转
10.6 练习六:定义一个函数,传入一个数字数组,对数组中的数字进行排序
10.7 练习七:定义一个函数,传入一个数字,求对应的菲波那切数列
斐波那契数列递归调用
十二.函数的调用栈
十三.全局变量和局部变量
- 在JavaScript(ES5之前)中没有块级作用域的概念,但是函数可以定义自己的作用域。
- 什么是全局变量和局部变量?
- ==定义在函数内部的变量,被称之为局部变量。==
- ==定义在script标签中的变量,被称之为全局变量。==
- 在函数中,访问变量的顺序是什么呢?
- ==优先访问自己函数中的变量,没有找到时,在全局中访问。==
变量作用域
十四.值传递和引用传递
1.在 JavaScript 中数据类型可以分为两类:
- 原始数据类型值 primitive type,比如Undefined,Null,Boolean,Number,String。
- 引用类型值,也就是对象类型 Object type,比如Object,Array,Function,Date等。
2.声明变量时不同的内存分配
- 原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。
- 引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。
14.1 按值传递
//1.值传递
function test(n) {
n = "kobe";
}
var name = 'why';
test(name);
console.log(name); //why
14.2 按引用传递
//2.引用传递
function func(arr) {
arr[0] = "jason";
}
var array = [1, 2, 3];
func(array)
console.log(array); //['jason',2,3]