JavaScript函数基础

基础

1. 函数声明与函数表达式

javaScript 中,声明一个函数有两种方式,分别是函数声明和函数表达式

// 函数声明
function name(params) {
	// function body
}

// 函数表达式
cosnt f = function(params) {
	// function body
}

可以看到,函数声明就是直接使用 function 关键字 直接声明一个函数,而 函数表达式 则是将一个函数赋值给一个变量,这将导致在解析阶段的解析顺序不同,具体会在后面的 变量提升 处分析

当然,也可以通过 Function 构造函数创建新的函数,但是并不推荐,因为执行一次 new Function,就会解析一次函数体,而函数声明或函数表达式不会,相同的函数体只会解析一次

2. 函数属性

2.1 name

函数的名字,匿名函数的名字是空字符串,函数表达式的函数名就是变量名,如果函数表达式的函数有具体名字,则为函数的具体名字

函数名只是指向函数的一个引用,不会与某个函数绑定

2.2 length

函数形参列表的个数,虽然表示函数形参列表的个数,但是 JavaScript 中,对传参的数量基本认为是没有限制的,即使没有在形参列表中声明,传递后在函数中也能访问到

2.3 prototype

函数的原型对象,使用面向对象编程时,在上面声明实例方法,所有 函数实例 共享

3. 函数方法

3.1 call、apply

这两个方法都可以改变函数在执行的时候,并且调用方法时会直接执行方法,函数内部的 this 指向,区别在于,call 方法必须将参数一个个的传入,而 apply, 可以使用一个数组一次性将参数传入

3.2 bind

bind 方法用于将函数的 this 指向 绑定到指定的对象上,该方法返回一个绑定后的函数,传入无效值则 绑定为 window 对象

作用域

从字面上来看,作用域就好像是用来限制什么的一样,实际上,它的作用也是差不多的,作用域 就是存储变量的一套规则,用于方便的查询变量,其限定了变量的作用范围

函数的作用域在函数声明的时候就已经确定了,和其在什么地方调用无关,参考后面的执行环境部分

1. 变量声明

如果在函数内多次声明同一个变量,其实只会 声明一次,其余的声明会被忽略,但是 赋值并不会被忽略

function f() {
	var a = 1;
	console.log(a);	// => 1
	var a = 2;
	console.log(a);	// => 2
}

// 等同于下面的代码
function f() {
	var a = 1;
	console.log(a);
	a = 2;
	console.log(a);
}

当函数名和变量名相同了怎么办,一个变量只能有一个值,那么先保留函数还是先保留变量呢,看下面代码

function f() {
	function a() {
		console.log(1);
	}
	console.log(a);	// 函数 a
	var a = 2;
	console.log(a);	// 2
}

可以看到,函数是优先的

变量的声明规则:

  1. 变量提升,函数优先
  2. 发现已经有声明的变量名或者函数名,若是变量,则跳过当前声明,若是函数,则覆盖

变量提升

一般来讲,代码都是从上到下解析,但是在 JavaScript 中,某些情况下是不一样的,比如这里要讲的变量提升

所谓 变量提升,指的是,使用 var 关键字 定义变量、函数声明 定义函数的时候,会将 变量、函数 提升到 函数顶部 声明,记住,只有声明会被提前,赋值并不会

注意:只有使用 var 声明的变量会进行变量提升,使用 cosntlet 声明的变量并不会

1. 变量的提升

function f() {
	a = 2;
	var a;
	console.log(a);
}
a() // => 2

function f1() {
	console.log(a);
	var a = 2;
}
f1() // => undefined

我们来看上面的两个函数,是变量的提升,至于打印的结果是为什么,我们根据前面的话来分析一下

首先,使用 var 变量定义的变量会被提升到函数顶部声明,那么,上面的代码其实就等于下面的代码:

function f() {
	var a;
	a = 2;
	console.log(a);
}

function f1() {
	var a;
	console.log(a);
	a = 2;
}

而且,变量提升只会 提升变量的声明,而不会提升赋值,所以赋值语句在原地等待从上到下解析执行,到这里,结果打印什么已经很明白了

如果不知道为什么打印 undefined:在 JavaScript 中,声明了未赋值的变量的值都是 undefined

2. 函数的提升

函数和变量是不一样的,因为函数可以被执行,在提升到顶部以后,函数可以直接被执行

function f() {
	console.log(a);	// => 函数 a
	a();	// => 1
	function a() {
		console.log(1);
	}
}

// 提升后的代码
function f() {
	function a() {
		console.log(1);
	}
	console.log(a);	// => 函数 a
	a();	// => 1
}

但是,还记得我们开头的 函数声明和函数表达式 吗,既然 函数声明 会被提升,那么函数表达式呢,看下面的代码

function f() {
	console.log(b);
	b();
	var b = function() {
		console.log(2);
	}
}

上面这段代码的结果是什么,会和之前一样,先打印 函数 b 的函数体,然后再打印 2 吗,可以尝试执行一下

是不是发现报错了,下面我们看一下提升之后的代码长什么样子:

function f() {
	var b;
	console.log(b);
	b();
	b = function() {
		console.log(2);
	}
}

现在知道为什么报错了吧,因为函数表达式是 声明一个变量,将匿名函数赋值给该变量,所以应用的是 变量的提升,所以函数表达式能够保证在函数创建以后才被执行,而不是像函数声明,可以声明在函数底部,而在函数顶部执行

3. 变量和函数重名

上面我们已经理解了变量和函数的提升,但是当变量名和函数名重复的时候,该怎么提升呢,是以谁为准呢,看下面代码

function f() {
	console.log(a);  // 1
	var a = 1;
	console.log(a);  // 2
	function a() {
		console.log(2);
	}
}

执行一下上面的代码,会发现 1 处 打印的是 函数 a 的函数体,这就说明了,先提升函数,而 2 处 又打印了 1,说明 赋值并不会被提升,只有声明会被提升

执行环境(执行上下文)

每个函数都有与之对应的执行环境,全局的执行环境就是 window 对象,执行环境中包含三个重要的东西:变量对象、作用域链、this

执行环境在函数声明的时候就已经被创建

1. 变量对象

变量对象中包含的是当前函数作用域内声明的 变量、函数、形参 以及 arguments 对象

所以之前在作用域那里说的:作用域是存储变量的规则而变量对象就是其实现

在函数刚声明创建的时候,变量对象中只有 arguments 对象,并且其值为 null,我们可以通过 Chrome 断点看一下:
变量对象-函数创建时
可以看到,当声明了 函数 a 之后,还没有执行的时候,函数 a 的 arguments 对象已经存在,并且值为 null,这就证明了执行环境是在函数创建的时候就被创建了

在函数执行的过程中,变量对象渐渐被函数内声明的变量填充,变得有货了,就转变成为了 活动对象,所以,变量对象活动对象 其实本质上是一个对象,只是在函数不同阶段的状态,看下图:
变量对象-函数执行时
可以看到函数执行的时候,arguments 对象已经有值了,变量也有值了,当然,上图是执行完函数之后,如果断点进行到刚开始执行的时候,变量的值是 undefined,因为毕竟还没有执行赋值语句

当函数执行完毕,当前的函数的活动对象会被销毁,然后函数被弹出函数执行栈,所以函数执行完毕,函数的活动对象又会变回成函数的变量对象

2. 作用域链

我们知道,当访问一个变量的时候,会先在当前函数作用域中查找,如果没找到,就会去它的外层函数作用域再查找,直到全局作用域,相信大家已经猜到了,变量查找通过的就是作用域链,而通过前面的介绍我们也知道了作用域的具体实现就是 变量对象,所以作用域链的本质就是 函数的活动对象的集合,只不过这个集合是有顺序的,当前正在执行的函数的活动对象,一定在作用域链的最前端

在函数刚声明创建的时候,会创建作用域链,只不过这时候这里只包含外层函数的 活动对象,如下图:
作用域链-函数创建时

当函数执行的时候,复制一份创建的作用域链,并且将当前函数的 活动对象 放到作用域链的前端,这时就构成了当前函数的作用域链,访问变量的时候,通过作用域链一层层的向上查询就可以了
作用域链-函数执行时

3. this

在函数创建的时候,并没有 this 变量,只有当函数执行的时候,才会初始化 this 指向

函数的 this 指向可以通过 call、apply、bind 方法改变

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值