1.函数介绍
作用:封装功能,代码复用
注意:函数也是一个对象,因此函数名实际上也是一个指向函数对象的指针
2.函数声明
function 函数名(形参){
函数体 }
var 函数名 = function(形参){
函数体 }
注意:
函数声明和var关键字声明变量类似,都会进行声明提升
当使用var 函数名 = function(形参){}创建函数时,
变量声明会提前,但是因为只会声明提前,并不会赋值,
所以直接使用调用函数的方法调用该函数,会报错(该变量不是函数)
3.函数的内部属性
概念:函数的内部属性只有在函数中才能够使用,在函数外不能够被直接访问到,需要使用this调用
arguments:在调用使用函数时,所有传递进来的实际参数都会保存在arguments数组中,如果
所传递的实际参数比所需要的参数多也不会报错并且保存在arguments在数组中。
callee:arguments的一个对象成员,能够调用当前的正在执行的函数对象
this:当前对象的一个引用,即是谁调用的,this所指向的就是谁
4.this的指向问题
在方法中的this:所指向的就是该方法所属的对象
var person = {
firstName: "LeBron",
lastName : "James",
id : 8888,
fullName : function() {
return this.firstName + " " + this.lastName;
}
};
当前this所指向的就是person对象,因为fullName方法所属的对象是person
单独使用this:
所指向的就是全局对象 在浏览器中的全局对象就是window 在node中的全局对象是{} var x = this;
在浏览器中会输出一个window对象
在node中会输出一个{}对象
函数中使用this:
所指向的是函数的所属者 在浏览器中的就是window 在node中所指向的就是global对象
function myFunction(){return this;}
在浏览器中会输出一个window对象
在node中会输出一个global对象
事件中使用this:所指向的是接收事件的HTML元素
<button onclick="this.style.display='none'"> 点我后我就消失了 </button>
所指向的是button标签
5.IIFE(Immediately Invoked Function Expression)立即调用的函数表达式
作用:在页面加载完成后调用只执行一次的设置函数 将设置函数的变量包裹在局部作用域中,防止泄露成为全局变量
IIFE函数的调用:
(function foo(){
var a = 10;
console.log(a);
})()
IIFE的各种写法:
对返回结果不进行处理
(function(形参){
函数体内容
})(实参);
(function(形参){
函数体内容
}(实参));
返回的值是布尔值,对其取反
!function(形参){
函数体内容
}(实参)
返回的值是数字,返回原来的结果,非数字返回NaN
+function(形参){
函数体内容
}(实参)
返回的值是数字,返回原来的值的负数,非数字返回NaN
-function(形参){
函数体内容
}(实参)
返回的值是数字,返回原来的值的负数并减1,非数字返回-1
~function(形参){
函数体内容
}(实参)
返回undefined
void function(形参){
函数体内容
}(实参)
IIFE的经典案例:
for (var i = 0; i < 6; i++) {
function output() {
console.log(i); // 为什么输出的是6,而不是0,1,2,3,4,5
// 因为输出的 i 是全局作用域的,当循环结束后 i 的值是 6,所以输出的 i 就是6。
}
}
// 当output执行时,循环已经结束,即i = 6;则输出6
output()
for (var i = 0; i < 6; i++) {
(function (j) {
console.log(j); //0,1,2,3,4,5
})(i)
// 因为 JS 中调用函数传递参数都是值传递 ,所以当立即执行函数执行时,首先会把参数 i 的值复制
//一份,然后再创建函数作用域来执行函数,循环5次就会创建5个作用域,所以每个输出访问的都是不同作
//用域的 i 的值 。
}
IIFE的好处:因为在es5中没有块级作用域的概念,只有全局和函数作用域,导致如果想要把一段代码中的变量 函数等隔离出来,只能封装到一个函数中,而函数的封装一般是为了进行代码的复用,但是有些代码只需要执行一次,因此IIFE的作用就是为了隔离作用域并且只执行一次
6.作用域
函数作用域/局部作用域:在函数中声明的变量就是局部变量,其作用域就是局部作用域,只能在函数中直接访问,在函数外不能访问
全局作用域:在函数外声明的变量,其作用域就是全局作用域,全局变量在函数中可以直接访问
作用:作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突
作用域链:在当前的作用域中使用了某个变量并且当前作用域中没有定义该变量,则会向上一级作用域进行寻找该变量,直到全局作用域中还没找到,则会报错,其形成的类似一条链路的就是作用域链
自由变量:当前作用域中没有定义的变量
经典案例:
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a) // 自由变量,顺作用域链向父作用域找 //100
console.log(b) // 自由变量,顺作用域链向父作用域找 //200
console.log(c) // 本作用域的变量 //300
}
F2()
}
F1()
var a = 10
function fn() {
var b = 20
function bar() {
console.log(a + b) //30
}
return bar
}
var x = fn(), // 执行fn() 返回的是bar
b = 200
x() //执行x,就是执行bar函数
注意:在自由变量向上级寻找值时,所选的值是作用域最靠近该自由变量的值
7.函数调用
函数调用的方式:
- 函数名(实参列表);
- 函数名.call(执行环境对象,实参列表);
- 函数名.apply(执行环境对象,实参列表数组);
- 函数名.bind(执行环境对象)(实参列表);
改变this的指向方式:call,apply,bind
使用方法:
函数名.call(执行环境对象,实参列表);
b.call(obj,1,2); // briup
函数名.apply(执行环境对象,实参列表数组);
b.apply(obj,[100,200]); // briup
函数名.bind(执行环境对象)(实参列表);
var b = obj.sayName;
var c = b.bind(obj);
c();
注意:call和apply都是改变了this的指向后立即执行这个函数,bind则是返回修改了this指向之后的函数
回调函数:就是回头调用的意思。主函数的事先做完,回头再调用传进来的那个函数
经典案例:
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B() {
// 模仿延时操作
setTimeout(() => {
console.log('我是回调函数');
}, 3000);
}
//调用主函数,将函数B传进去
A(B);
注意:回调函数是优先把主函数的事情先执行完毕,在回头调用传进来的函数,即主函数不会等待回调函数的执行,
而是顺序继续执行
8.闭包
概念:在一个作用域中有权访问另一个作用域的变量的函数
形成闭包的条件:
函数嵌套函数
内部函数引用外部函数的数据 对象 属性
参数和变量不会被回收
待定条件:一个函数返回另一个函数
闭包的作用:把函数内部和外部连接起来
闭包的用途:
读取函数内部的变量
把变量的值始终保存在内存中
经典案例:
function f1() {
var n = 999;
nAdd = function () { n += 1 }
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000
如果n的值没有保存在内存中,nAdd中的n+=1就不能访问到999这个值
使用闭包的注意点:
因为闭包会把数据变量保存在内存中,会消耗大量内存,滥用闭包会导致网页的性能问题
闭包会在父函数外部改变父函数内部的一些变量的值
闭包中的this指向案例:
<script>
var name = 'window'
var obj = {
name: 'obj',
say: function () {
return function () {
console.log(this.name);
}
}
}
var x = obj.say()
x()
</script>
由于调用x方法的对象是全局对象,则this的指向应该是全局对象window