一、初识函数
函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。
通过函数可以封装任意多条语句,而且可以在任何地方,任何时间进行调用和执行
二、创建函数
(1)function命令, 使用关键词 function来声明函数
注:关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数。
-
被提前
变量的声明被提前:使用var关键字声明的变量,会在所有的代码执行之前被声明但不会赋值
函数的声明被提前,使用function声明的函数,会在所有的代码执行之前就被创建
(2)函数表达式, 使用变量初始化函数(字面量方式定义函数)
- 注意:以表达式定义的函数并没有“被提前”。
- 调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。
(3) Function()
三、函数的调用
函数会在什么情况下调用:
1、当事件发生时(点击按钮,例如轮播图中的小圆点)
2、当js代码调用时(cfb();)
3、函数创建后自动调用(如下)
- 立即调用的函数表达式(IIFE)
IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数
- 通常情况下,只对匿名函数使用这种“立即执行的函数表达式
立即调用的函数表达式目的有两个:
一.是不必为函数命名,避免了污染全局变量;
二.是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
4、间接调用,通过它们的call()和apply()方法间接调用
四、函数的参数
形参:形式上的参数,没有实际值,形参只作用于函数内部(函数体后面()中的参数是形参)
实参:实际上有值的参数(调用函数的时候()中传入的参数值是实参)
注意:实参传多了不会出错,代码只会对应的取前面的参数;
实参传少了,会出错输出NAN(非数值类型)
五、全局变量和局部变量,作用域(scope)
1、函数外部的变量函数中可以使用(全局变量)
2、但是函数中的变量,函数外不能使用。(局部变量)
3、函数内部不用var声明,直接赋值方式声明的变量是全局变量。
六、带有返回值的函数
- Return返回执行结果,不会在页面中显示,需要用变量来接收
- 函数遇到return,会立即返回结果,并终止函数
- 返回值类型可以是任意类型,也可以是一个对象,也可以是一个函数
七、将函数作为参数(回调函数)
常见的回调函数?(面试)
- Dom事件回调函数
- 定时器回调函数
- Ajax回调请求
- 生命周期回调函数
八、匿名函数
匿名函数:就是没有函数名
1、第一种匿名函数:函数没有函数名,而是直接赋值给一个变量通过变量调用或者直接调用
注:1.匿名函数的调用,必须放在函数声明之后
2.普通函数调用,是可以在函数声明之前的
2、第二种匿名函数:完全匿名也没有变量
1.自调用
2.当事件发生时调用
3.函数中调用
九、递归函数
函数可以调用自身,这就是递归(recursion)
递归函数就是自己调自己
十、函数的内部属性和方法
1.Arguments
Arguments是一个函数中自带的对象
arguments 是一个对应于传递给函数的参数(实参)的类数组对象。
arguments.length 传递给函数的参数数量。(实参)
函数的length属性是只读属性,代表函数形参的数量,也就是在函数定义时给出的形参个数。
2.prototype属性(原型)
- 每一个函数都包含一个prototype属性,这个属性指向一个对象的引用,这个对象称做“原型对象”(prototype object)。
- prototype 属性允许您向对象添加属性和方法
- 注意: Prototype 是全局属性,适用于所有的Javascript对象。
- 这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身
3.函数对象自带的call()方法和apply()方法
每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
定义:call和apply可以用来重新定义函数的执行环境,也就是this的指向;call和apply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。
call()
调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:
apply()
和call()方法一样,只是参数列表不同,语法:
//obj:这个对象将代替Function类里this对象
//argArray:这个是数组,它将作为参数传给Function
//说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和obj任何一个参数,那么Global对象将用作obj。
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
function People(name, age) {
this.name = name;
this.age = age;
}
//为什么add.call(sub, 2, 1)的执行结果是3呢,因为call()方法改变了this的指向,使得sub可以调用add的方法,
//也就是用sub去执行add中的内容
//再来看一个例子:
function Student(name, age, grade) {
People.call(this, name, age);
this.grade = grade;
}
var student = new Student('小明', 21, '大三');
console.log(student.name + student.age + student.grade);//小明21大三
//总结一句话就是call()可以让括号里的对象来继承括号外函数的属性。
总结一句话就是call()可以让括号里的对象来继承括号外函数的属性。
console.log(add.call(sub, 2, 1));//3
- Apply()和call()中的参数是谁,那么this就指向谁
## 作用
共同作用:修改this指向
其次就是它们不同的传参方式:注意上一句话中说他们的作用时有两个关键词 ‘函数’和‘this’,想要修改this 的指向,那么必然有一个this修改后的指向,而函数必然后关系到传参问题:call方法可以传给该函数的参数分别作为自己的多个参数,而apply方法必须将传给该函数的参数合并成一个数组作为自己的一个参数:
## 区别
不同之处是:
- call() 方法接受的是**一个参数列表**
- apply() 方法接受的是**一个包含多个参数的数组**。
如果要使用数组而不是参数列表,则 apply() 方法非常方便。
- person.fullName.apply(person1, ["Oslo", "Norway"]);
- person.fullName.call(person1, "Oslo", "Norway");
十一、构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
- new 在执行时会做四件事情: (重点)
在内存中创建一个新的空对象。
让 this 指向这个新的对象。
执行构造函数里面的代码,给这个新对象添加属性和方法。
返回这个新对象(所以构造函数里面不需要 return )
- this (重点)
this (重点)
1.this出现在全局函数中,永远指向window ;
2.当某个函数为对象的一个属性时,在这个函数内部this指向这个对象;
3.this出现在构造函数中,指向构造函数新创建的对象;
函数内部使用this,谁调用的函数this指向谁;
十二、原型
原型是function 对象的一个属性,它定义了构造函数制造对象的公共祖先,通过该构造函数产生的对象可以继承该原型的属性和方法,原型也是对象。prototype原型最大的作用,共享属性和方法
JavaScript对象具有“自有属性”,也有一些属性是从原型对象Object.prototype 继承而来的。
如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”。
十三、原型链
1)每个函数function都有一个prototype,即显式原型(属性)
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数
2)每个实例对象都有一个__proto__,可称为隐式原型(属性)
3)对象的隐式原型的值为其对应构造函数的显式原型的值
4)内存结构(图)
十四、操作符
(1)instanceof运算符
instanceof运算符返回一个布尔值,表示指定对象是否为某个构造函数的实例。
instanceof运算符的左边是实例对象,右边是构造函数。它的运算实质是检查右边构建函数的原型对象,是否在左边对象的原型链上。
(2)检测一个对象是否是另一个对象的原型,可以使用isPrototypeOf()方法。
isPrototypeOf() 方法用来检测一个对象是否存在于另一个对象的原型链中,如果存在就返回 true,否则就返回 false
(3)Object.getPrototypeOf()
Object.getPrototypeOf方法返回一个对象的原型。这是获取原型对象的标准方法