Js 函数基础

August 17, 2017

记录一下函数基础的知识

函数的定义

函数其实就是一个封装一段代码段的对象,那函数名其实仅是用来引用函数对象的一个普通变量

写代码的时候我们避免不了要重复用一些代码,一直重复写很耗时,而且不美观也不利于维护,因此函数的出现就是来让代码重用,便于维护。

一段代码,可能被反复使用,可以定义为函数,然后调用函数来使用这段代码。

函数的创建

function 声明

function 函数名(参数列表) {
  函数体
  return 返回值
}

直接量声明

var 函数名 = function(参数列表) {
  函数体
  return 返回值
}

用 new 创建

因为 Function 是内置类型,本身有一个 Function 的构造函数,是内置类型,所以是可以 new

var 函数名 = new Function("参数名1","参数名2",...,"函数体; return 返回值")

这里注意,参数和函数体都要用引号引起来,但是一般函数的创建都不会这样创建,以前面的两种为主,那其实前面两种的创建是存在一定的差别的,下面会提到

函数的参数

函数执行时必须的数据变量,它分为显示参数(Parameters)与隐式参数(Arguments)

显示参数

function fun(name, id) {
  //函数体
}

这种直接传进来的 name 和 id 就是显示参数,也就是你能看到的。其实函数传递的参数就相当于在函数体内又声明了一个局部变量

var i = 10
function fun(i) {
  i++
  console.log(i)
}
fun(i)

上面的代码,就相当于下面的代码

var i = 10
function fun() {
  var i = 10 //这个值就是传进来的参数的值
  i++
  console.log(i)
}
fun(i) //在此处传入i

隐式参数

每个 JavaScript 函数内部都有一个对象 arguments 对象,其实是一个类数组的对象,它会自动接受所有传入函数的参数值。

function func() {
  //arguments[          ]
}

值得一说的是,arguments 有下标有长度,可以通过下标来获得传入的参数,比如 arguments[0] 就是第一个参数, length 就可以遍历这个类数组对象,但是,毕竟他不是数组,所以不能进行一些数组特有的操作,比如 sort

声明提前

在开始执行程序前,js 引擎会首先查找 var 声明的变量和 function 声明的函数,将其提前到当前作用域的顶部集中创建,而将赋值操作保留在原地,这里特别说一下,未用 var 声明的变量不会声明提前.

console.log(a) //a is not defined
a = 10
console.log(a) //undefined
var a = 10
console.log(a) //10

但是在他下面声明并赋值变量 a,因为声明提前,其实代码会变成下面这个样子

var a
console.log(a)
a = 10
console.log(a)

这样看,一切都变得很合理。。

函数也是一样

function fun() {
  console.log(1)
}
fun() //2
function fun() {
  console.log(2)
}
fun() //2

控制台会输出两个 2,因为 function 声明的函数也会声明提前,代码其实是下面这个样子

function fun() {
  console.log(1)
}
function fun() {
  console.log(2)
}
fun() //2
fun() //2

第二次声明因为方法名字一样,后者覆盖了前者,所以再调用的时候就会调用最后这个

但是声明提前会增加程序解读的难度,因此我们在写程序时,尽量避免声明提前所带来的危害

声明提前的解决方法

那我们既然知道会有声明提前这种操作,就在变量和函数的声明时都放在当前作用域的顶部。

在 ES6 中 可以用 let 代替 var,不过要求在当前作用域中 let 变量之前不允许出现声明的变量

也可以用直接量声明变量的方法

var fun = function() {
  console.log(1)
}
fun() //1
var fun = function() {
  console.log(2)
}
fun() //2

这种当然也会声明提前,那我们看一下声明提前后的代码

var fun
var fun
fun = function() {
  console.log(1)
}
fun() //1
fun = function() {
  console.log(2)
}
fun() //2

声明提前但是赋值还是留在原地,所以虽然有声明提前,但是并不会改变我们原本想要的结果。也就解决了声明提前带来的危害。

匿名函数

函数创建时没有指定函数名

匿名函数使用后自动释放,会节约内存,他会划分临时作用域,避免全局变量污染全局。

用处

callback

将一个函数作为参数传入另一个函数内,被其他函数调用

举个栗子:chestnut:

arr.sort(function(a, b) {
  return a - b
})
str.replace(/reg/g, function(kw,$1,$2,...){return 替换值})

自调

定义函数后自己调用自己,调用结束后,立刻释放,不占内存

举个例子:chestnut:

;(function(参数列表) {
  函数体
  return 返回值
})(参数值列表)

会定义一个临时的作用域,减少使用全局变量,避免全局污染。

重载

相同函数名,不同参数列表的多个函数。在调用时,根据传入参数的不同,自动选择匹配的函数执行。

这样可以减少 api 的数量,减轻调用者的负担。

听起来很诱人,但是 js 语法不支持重载,原因是 js 不允许多个同名函数同时存在,后声明的函数会覆盖前面声明的。(哇。js 不支持你在这里说什么,神经病啊).

既然说重载,那肯定是可以通过某些方法实现的。这个方法就是利用 arguments

function test() {
  if (arguments.length == 0) {
    //不传值的操作
  } else if (arguments.length == 1) {
    //传一个值进行的操作
  } else {
    //....等等
  }
}

其实也不是真正意义上的重载,因为并没有创建同名的多个函数,但是实现的效果是和重载差不多的。