1

不管你承不承认,任何人在社会环境中都会被潜在的分为三六九等,而JavaScript中的函数就是一等公民。

什么是函数

函数是以function关键字定义的一组用来执行特定功能的语句。定义函数有三种方式:函数声明,函数表达式和构造函数。

函数声明

function sum(num1,num2){
    return num1+num2;
}

函数表达式

var sum=function(num1,num2){
    return num1+num2;
}

构造函数

var sum = new Function('num1', 'num2', 'console.log( num1 + num2)');
sum(2, 6);

构造函数的第三个参数是一个包括JavaScript语句的字符串

函数没有重载

function addNumber(num){
    return num+10;
}

function addNumber(count){
    return count+20;
}
addNumber(10);//30

上面两个函数名一样,但是参数及计算方式不一样,结果却是执行第二个函数,因为后面的函数覆盖了前面的函数。实际上与下面的代码没有区别:

var addNumber=function(num){
    return num+10;
};
addNumber=function(count){
    return count+20;
}
addNumber(10);//30

可以把函数名理解为指针,同一个函数名指向同一个指针。

函数声明与函数表达式的区别
1、函数声明是函数名跟在function后面,函数表达式是把一个函数赋值给一个变量;
2、函数声明可以在代码的任何位置,调用不受限制(可访问作用域),函数表达式必须在调用前定义。

console.log(addNumber(10));//20
function addNumber(num){
    return num+10;
}
console.log(addNumber(10));//Uncaught TypeError: addNumber is not a function
var addNumber=function(num){
    return num+10;
};

再看一段代码

function addNumber(num){
    return num+10;
}

var addAnotherNumber=function addNumber(count){
    return count+20;
}
console.log(addAnotherNumber.name);//addNumber

函数表达式不一定是匿名函数,定以后函数表达式的函数名是function后面的函数名。

重点注意:所有函数的参数都是按值传递

也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。
要理解上面这段话有点费劲。既然是复制,不是基本类型复制后互不影响,引用类型复制后会互相影响吗,为什么是按值传递不是按引用传递?

在函数传递一个基本类型参数时,被传递的值会复制给函数内一个局部变量(arguments中的一个),传递一个引用类型的值时,会把该引用类型的指针地址复制给函数内一个局部变量,当局部变量发生变化时会反映在函数的外部。举个例子:

函数参数传递一个数字,函数内部修改后

function addNumber(num){
    num=num+10;
    console.log(num);//20
}
var num=10;
addNumber(num);
console.log(num);//10

如果传递的是一个object,在函数内部修改对象属性时,外部对象也发生变化了。

function setName(obj){
   obj.name="李四"
   console.log(obj.name);//李四
}
var obj={
    name:"张三"
};
setName(obj);
console.log(obj);//obj.name="李四"

这不是与上面说的还值传递自相矛盾?我们修改几行代码再看:

function setName(obj){
   obj.name="李四"
   obj=new Object();
   obj.name="王麻子";
   console.log(obj.name);//王麻子
}
var obj={
    name:"张三"
};
setName(obj);
console.log(obj);//obj.name="李四"

这段代码与上面的区别是obj传递给函数后,其属性name被重新赋值,创建一个新对象赋值给obj,再修改obj.name。如果obj是按引用传递,那么修改obj后应该也反映到函数外部,但原始引用并没有发生变化。原因是在函数内部重写obj后实际上创建了一个新的对象引用,该变量引用是局部变量,函数执行完后即被销毁。

总之,函数参数都是局部变量。

回到主题,JavaScript函数为什么是一等公民?

在编程语言中,一等公民有几个条件:可以作为函数参数,可以作为函数返回值,可以赋值给变量。

按照上面的条件,其实JavaScript中的数据都可以认为是一等公民~
无论是基础数据类型还是引用类型都是满足上面条件的。一般说JavaScript函数是一等公民实际上是相对其它编程语言而言,因为并不是所有的编程语言中函数都能满足上述条件。

下面具体介绍下JavaScript中函数是如何满足上述三个条件的。

函数作为参数

实际上就是我们经常说的函数回调。举个例子:

element.addEventListener(event,eventHandler,useCapture)

其中eventHandler就是作为函数参数,元素监听到对应事件后进行调用。这种情况在JavaScript中非常常见。比如ajax,promise等等。

函数作为返回值

比如闭包:

function getName(){
  var name="jack";
  return function(){
    return name;
  }
}
var getNameFun=getName();
var name=getNameFun();
console.log(name);//'jack'

正常情况下函数外部是无法访问函数内部变量的,但是通过返回一个引用函数内部变量的函数就可以实现,也就是我们常说的闭包。

函数作为变量
var getTime=function(){
    console.log(new Date().getTime());
}
setInterval(getTime,1000);

将函数作为变量赋值给变量getTime,然后作为参数传递给setTimeInterval。

函数作为一等公民是函数式编程的基础。高阶函数就是将函数作为参数的函数,React中的高阶组件也可以理解是高阶函数。


沉默术士
29 声望1 粉丝