js函数(函数声明、函数参数、函数调用、函数作用域、闭包)

一、函数声明

  • 函数声明
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(),但实行结束后,立即被销毁了,接下来又重复执行的三次同上操作,函数每次被执行都是,执行一次后立即被销毁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值