目录
一、函数声明
- 函数声明
function 函数名(形参){}
- 变量赋值函数表达式
var 函数名 = function(形参){}
注:函数声明与变量声明类似,也会进行提升
案例一:
test()//test调用
function test(){
console.log("test调用");
}
案例二:
注意:此函数表达式时赋值给了一个变量,但变量会提升,所以调用时,函数表达式尚未赋值给变量,所以为undefined;
test()//undefined
var test = function(){
console.log("test调用");
}
二、函数参数
1)argments
arguments是一个类数组对象,包含着传入函数中的所有参数
function foo(a, b) {
// 当传递的实参个数超过形参的个数的时候 不会报错 所有的实参都会保存在arguments里 arguments类数组
console.log(arguments);
// [Arguments] {
// '0': 1,
// '1': 2,
// '2': 3,
// '3': 4,
// '4': 5,
// '5': 6,
// '6': 7,
// '7': 8,
// '8': 9
// }
console.log(arguments[2]); //3
console.log(a,b);//1,2
}
foo(1, 2, 3, 4, 5, 6, 7, 8, 9)
2)this
this:执行环境上下文,取决于什么时候调用,被谁调用
1.this也可以在函数外部进行使用
2.但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
- 在对象方法中,this 表示该方法所属的对象。
- 如果单独使用,this 表示全局对象。
- 在函数中,this 表示全局对象。
- 在事件中,this 表示接收事件的元素。
- 在显式函数绑定时,我们可以自己决定this的指向;
1.方法中,this 表示该方法所属的对象。
// 谁调用了这个方法 this就指向谁
// 在方法中,this 表示该方法所属的对象
var person = {
name: 'zs',
sayName: function() {
console.log(this.name);
}
}
person.sayName()//zs
2.单独使用,this 表示全局对象。
在浏览器中,window 就是该全局对象为 [object Window]:
在node中,指向的是一个modules.export {}
浏览器中的this:
node中的this:
console.log(this);//{}
3.在函数中,this 表示全局对象。
在函数中,函数的所属者默认绑定到 this 上。
在浏览器中,window 就是该全局对象为 [object Window]:
在node中,指向的就是global对象
// 局部作用域 this 指向的就是全局对象 global
// 在浏览器环境下 this 指向的就是window对象
function foo() {
console.log(this);
}
foo()
4.在事件中,this 表示接收事件的元素。
<button onclick="this.style.display='none'"> 点我后我就消失了 </button>
5.在显式函数绑定时,我们可以自己决定this的指向;
参考第三点
三、函数调用
调用函数的方式不仅限于()执行,还有其他几种方式
• call(执行环境对象,实参列表)
• apply(执行环境对象,[实参列表])
• bind(执行环境对象,实参列表)(是参列表)第一次调用返回的是修改后的函数
1.函数名.call(执行环境对象,实参列表);
调用call方法,第一个参数就是要把b添加到哪个环境中,简单来说,this就会指向那个对象。
var obj = {
name: 'lz',
sayName: function(a) {
console.log(arguments);
console.log(this.name + a);
}
}
var b = obj.sayName
b()//NaN
//call 改变this的指向
//调用方法b用于obj
b.call(obj, 123);//lz123
2.函数名.apply(执行环境对象,实参列表数组);
apply方法和call方法有些相似,它也可以改变this的指向,但是不同的是,第二个参数必须是一个数组
var obj = {
name: 'lz',
sayName: function(a) {
console.log(arguments);//{ '0': 123, '1': 456 }
console.log(this.name + a);//lz123
}
}
var b = obj.sayName
b.apply(obj, [123, 456])
注意:如果call和apply的第一个参数是null,那么this在node环境下指向的是global对象,在HTML中指向的是window对象
var obj = {
name: 'lz',
sayName: function(a) {
console.log(this); //global
}
}
var b = obj.sayName
b.call(null)
3.函数名.bind(执行环境对象)(实参列表);
bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加
var obj = {
name: 'lz',
sayName: function(a) {
console.log(arguments); //{ '0': 1, '1': 2, '2': 3 }
console.log(this.name + a); //lz1
}
}
var b = obj.sayName;
var res = b.bind(obj, 1, 2)
res(3);
4.IIFE
IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。
// 如果后面跟的是立即执行函数 前面的结束语句必须要加分号
// 立即执行函数
(function(a) {
console.log(123 + a); //246
})(123)
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()
for (var i = 0; i < 6; i++) {
(function (j) {
console.log(j); //0,1,2,3,4,5
})(i)
// 因为 JS 中调用函数传递参数都是值传递 ,所以当立即执行函数执行时,首先会把参数 i 的值复制一份,然后再创建函数作用域来执行函数,循环5次就会创建5个作用域,所以每个输出访问的都是不同作用域的 i 的值 。
}
四、函数作用域
函数作用域:函数中声明的变量,会成为函数的局部变量。函数内部声明的变量,在函数外部不能访问。
全局作用域:函数外声明的变量,会成为全局变量函数外部声明的变量,在函数内部可以访问。
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突
这段代码中,当函数内部有变量a时,会产生局部作用域,外界全局作用域中的a不会对函数内部造成影响。如果把函数内部的变量a注释掉是,函数内部的a输出的就是全局作用域中的a
- 作用域链
首先认识一下什么叫做 自由变量 。当前作用域没有定义的变量,这成为自由变量 。
var a = 100
function fn() {
var b = 200
console.log(a) // 这里的a在这里就是一个自由变量 // 100
console.log(b)
}
fn()
自由变量的值如何得到 —— 要到创建这个函数的那个父级作用域寻找,如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链 。
五、闭包
闭包就是指有权访问另一个函数作用域中的变量的函数。 当函数嵌套,在这个时候,内部函数与外部函数的这个变量就组成了闭包。
闭包产生的条件:
a.函数嵌套函数;
b.存在内部函数引用外部函数中的数据;
c.变量保存在内存中不会被回收;
d.返回内部函数!
优点:防止变量被污染,内部维持变量可以做缓存;
缺点:变量不会被垃圾回收机制回收,造成内存泄漏,造成性能问题;
在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
function f1() {
var n = 999;
nAdd = function() { n += 1 } //没有声明,所以nAdd为全局变量
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result() //999
nAdd()
result() //1001
上述代码中,fn1赋值给result时执行了一次,result执行,代表执行了f2,因为nAdd为全局函数,其次,nAdd的值是一个匿名函数,而这个匿名函数本身也是一个闭包,所以可以在函数外部对函数内部的局部变量进行操作。
function test() {
var a = 10;
function fn() {
a--;
console.log(a, 'a');
}
return fn
}
var res = test();
res(); //9 a
res(); //8 a
res(); //7 a
res(); //6 a
上述代码中,函数的test()在赋值给res时执行一次后,res()执行了四次,而res执行后,内部的a变量一直未被销毁,一直保存在内存中,a会一直减一
function test() {
var a = 10;
function fn() {
a--;
console.log(a, 'a');
}
return fn
}
var res = test;
res()(); //9 a
res()(); //9 a
res()(); //9 a
res()(); //9 a
上述代码中,函数表达式test赋值给res,res()()执行了四次,第一个括号代表执行了一次test(),第二个括号代表执行了test中的fn(),但实行结束后,立即被销毁了,接下来又重复执行的三次同上操作,函数每次被执行都是,执行一次后立即被销毁。