# function基础知识及作用域

165 阅读8分钟

function基础知识

1. 函数的两部分

1.1 创建函数

  • 语法:function函数名(形参){...}
  • 过程:
    • 创造值
      • 开辟一个堆内存
      • 把函数体中的代码当做字符串存在堆中
      • 把对地址放到栈中
    • 创造变量
    • 让变量和地址关联
  • 注意:只创造函数,其实就是创造了一个存储一堆字符串的堆而已,并没有实际作用

1.2 执行函数

  • 语法:函数名(实参)
  • 目的: 把创造的函数执行(把函数体中的代码执行)
  • 依赖条件:
    • 栈内存
    • 供代码执行的上下文
  • 过程:
    • 函数每一次执行

      1. 创造一个全新的执行上下文,把执行上下文压缩到栈内存中去执行(进展执行)
      1. 在这个上下文中,也存一个AO(变量对象),用来存储当前上下文代码执行中所创建的变量
      • 这些变量是“私有变量”
      • 除当前上下文可以使用这些变量的值,上下文以外的环境不能直接使用私有变量的值
      1. 代码执行
      1. 当上下文中的代码都执行完后,如果该上下文中的信息没有被外界占用的情况,则执行完出栈(释放掉,减少栈内存中的空间)

2. 参数形式

2.1 形参与实参

  • 形参:

    • 创造函数的时候,我们并不清楚需要处理的数据是什么,只有当函数执行的时候,我们才会知道,此时我们需要定义相应的入口
    • 入口在js函数中被称为形参,“用来存储执行函数时,传递进来的信息的”(所以形参是变量)
  • 实参:

    • 函数执行的时候,传递进来的值会复制给形参变量
    • 传递的具体值在js函数中被称为实参,“实参就是具体传递的值”
  • 形参与实参关系:

    • 1.设定形参变量,但是执行的时候没有传递对应的值,则形参默认值是underfined
    • 2.形参只有两个,实参有三个时,则第三个实参并没有对应的形参接受(但是传递给函数了)
  • 注意:

    • 形参是创造函数时候设定的变量
      • 形参是变量
    • 实参是执行函数时候给的形参传递的具体值
      • 实参是值
      • sum(1===1?“OK”:“NO”)
      • 需要先把三元运算的结果作为实参传递给形参

2.1 arguments

  • 定义:

    • 函数内置的实参集合
    • 不管我们什么时候设置形参,或是否传递了实参,arguments始终都会存在
    • 只能出现在函数中
  • 形式:

    • arguments是一个类数组集合
    • 类似数组,但不是数组,和元素集和HTMLCollection类似
  • 原理:

    • 根据索引记录了每一个传递进来的实参信息
    • arguments中包含了所有传递进来的实参信息
    • length属性代表传递实参的个数

3.返回值return

  • 定义:

    • 如果,外面想用当前上下文中的一些私有信息,则需要函数提供对应的出口,把信息提供给外面使用,而这个出口在js函数中被称为“返回值return”
  • 作用:

    • 基于return把变量的值暴露给外面使用
      • 在外边创建一个变量,用来接受函数执行返回的值(也就是return后面的值)
      • 告知函数体重下面代码不再执行
  • 注意:

    • return后面放的一定是变量
    • 例如:
      • console.og(sum)
        • 这里输出的是sum函数本身
        • sum=函数
      • console.log(sum())
        • 代表让函数执行
        • 这里输出的是函数的返回值
        • 如果函数中没有写return,默认返回值是underfined

4. 函数表达式

  • 实名函数:有函数名的

  • 匿名函数:

      1. 函数表达式
      • 把一个函数当做值赋值给变量
      • 事件绑定
      1. 自执行函数
      • 函数创建完后就立即执行了
      • 语法规范
        • (function(n){...})(实参)
        • +function(n){...} (实参)
        • -function(n){...} (实参)
        • ~function(n){...} (实参)
        • |function(n){...} (实参)
  • 箭头函数

    • let func=(x,y)=>{...}:

5.函数的作用域

5.1 全局作用域

  • 全局作用域是最大的作用域,包含了局部作用域。在全局作用域中定义的变量可以在如何地方使用
    • 全局作用域在页面打开的时候创建,页面关闭时才会销毁
    • 编写在 script 标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到
    • 在全局作用域中有全局对象window ,代表一个浏览器窗口,由浏览器创建,可以直接调用
    • 全局作用域中声明的变量和函数会作为window对象的属性和方法保存
//下面两个变量都是存在全局作用域下面的,都是可以在任意地方使用的
var num1 = 100;
var num2 = 200;
console.log(window.num1) // 100
console.log(window.num2) // 200

5.2 局部作用域

  • 局部作用域就是在全局作用域下面,开辟出来的一个相对小一些的作用域。在 js 中只有函数能生成一个局部作用域,别的不行, 在局部作用域中定义的变量只能在这个局部作用域内部使用
    • 调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
    • 每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
// 这个 num 是一个全局作用域下的变量 在任何地方都可以使用
var num = 100;

function fn(){
	//下面这个变量就是一个 fn 局部作用域内部的变量
	//只能在 fn 函数内部使用
	var num2 = 200;
}
fn();

6. 作用域上下级

  • 有了作用域以后,变量就有了使用范围,也就有了使用规则

6.1 变量定义机制

  • 一个变量(函数)
  • 定义在哪一个作用域里面
  • 只能在当前作用域或下级作用域里使用
  • 上一级作用域不能使用
// 全局作用域下的 a
var a = 100;
function fn(){
	// 局部作用域下的 b
	var b = 200;
}
fn();
console.log(a);	// 100
console.log(b);	// 报错:b is not defined

6.2 变量使用机制(作用域链)

  • 当你需要使用一个变量(函数)
    • 首先,在自己的作用域内部查找,如果有,就直接拿来使用
    • 如果没有,就去上一级作用域查找,如果有,就拿来用
    • 如果没有,就进行去上一级作用域查找,以此类推
    • 如果一直到全局作用域 window 都没有这个变量,那么就会直接报错(该变量 is not defined)
var num = 100;

function fn(){
	var num2 = 200;
	function fun(){
		var num3 = 300;
		//自己fun函数作用域里面有,直接拿过来用
		console.log(num3);
		//自己作用域内没有,就去上一级,就是 fn 的作用域里面找,发现有,拿过来用
		console.log(num2);
		//自己这没有,去上一级 fn 那里也没有,再上一级到全局作用域,发现有,直接用
		console.log(num);
		// 自己没有,一级一级找上去到全局都没有,就会报错
		console.log(a);
	}
	fun();
}
fn();
  • 变量的使用机制也叫做作用域的查找机制
  • 作用域的查找机制只能是向上找,不能向下找

6.3 变量赋值机制

  • 当你想给一个变量赋值的时候,那么就先要找到这个变量,在给他赋值
  • 变量赋值规则:
    • 先在自己作用域内部查找,有就直接赋值
    • 没有就去上一级作用域内部查找,有就直接赋值
    • 在没有再去上一级作用域查找,有就直接赋值
    • 如果一直找到全局作用域 window 都没有,那么就把这个变量定义为全局变量,在给他赋值(不会报错)
function fn(){
	// 声明私有变量 num ,只能在 fun 内部使用,fun 外面不能用
	var num1 = 100;
	console.log(num1);	// 100
}
fn();
console.log(num1);	// 报错
function fun(){
	// 声明私有变量 num ,只能在 fun 内部使用,fun 外面不能用
	num2 = 200;
	console.log(num2);	// 200
}
fun();
console.log(num2);	// 200
//  全局变量 num
var num;
function fn(){
	var num;
	function fun(){
		// 不声明,只赋值
		num = 100;
	}
	console.log(num);	// undefined
	fun();	// 这个函数执行完后,才给 fn 私有变量 num 赋值
	console.log(num);	// 100	
}
/*fun 调用以后,要给 num 赋值
查看自己的作用域内部没有 num 变量
就会向上一级查找,上一级 fn 有 num 变量
那么就会把 num 赋值给 fn 下的 num 变量
赋值后的 num,由于是 fn 的私有变量,所以不会再给全局变量的 num 赋值了
*/
console.log(num);	// undefined
fn();
console.log(num);	// undefined

7. 作用域预解析

具体用法见预解析

7.1 全局预解析

  • 会在页面打开的时候就进行了
  • 只解析属于全局的内容
// 刚打开页面
var a = 100;	// 会被解析
function fn(){	// 会被解析
	var b = 200;// 不会被解析
}

7.2 局部预解析

  • 当函数执行的时候进行预解析
  • 函数内部的预解析,只属于函数内部
// 刚打开页面
var a = 100;	// 会被解析
function fn(){	// 会被解析
	var b = 200;// 等函数执行后,才会被解析
}
fn();	// 此时函数执行了,开始解析函数内部