Dart_03_函数是一等公民

608 阅读3分钟

函数是一等公民

所谓一等公民只是一种称呼,表示该等级最高。在OC中,类是第一公民,因此我们可以将类创建的对象当做参数或者返回值。在Dart中函数是第一公民,所以可以将函数作为参数或者返回值

将函数作为参数传递

  • 因为函数是最高等级,所以在传参时候我们可以声明参数类型为一个函数,调用时候传入一个函数
//函数可以作为另外一个函数的参数,Function表示函数,所以调用test传值不能传入一个常量类型,必须为一个函数
void test(Function foo){
  print("调用test1");
  var result = foo("lilei");
  print("test.result  $result");

 
}

String bar([String name]){
  print("name $name");
  return name;
}

这里我们声明test的参数为一个函数Function(函数)类型,所以我们在传参数时候就可以传入一个函数bar

//因为函数是一等公民,所以我们可以将函数直接作为一个参数传递过去
test(bar);

在test里面调用的时候,foo就是bar函数,调用foo("lilei"),就是调用bar函数传入一个string类型的参数lilei,bar又返回一个string类型的值

打印结果

调用test
name lilei
test.result  lilei

通过log我们可以发现bar作为一个函数传递到test里面,test里面实现了bar的调用,注意test里面调用函数参数时候传值一定要跟bar匹配上,也要注意必选可选参数的传递,参考函数的基本用法里必选可选参数

上面的调用方式,当将函数作为参数传递时候,还需要再声明一个函数bar,在开发中比较费时,所以这时候就有了匿名函数的出现

匿名函数

匿名函数不需要单独声明,在传参时候直接声明这个函数就行

首先我们声明一个test


void test1(Function foo){
  print("调用test");
  var result = foo();
  print("test.result  $result");
}

当我们调用test传入函数时候,直接将函数声明实现在参数里

  
  test1(([int value = 1]){
      print("调用匿名函数");
      return value*2;
  });

打印结果

调用test1
调用匿名函数
test1.result  20

通过log我们可以发现匿名函数这时候我们简称为foo1,会先调用test1函数,在test1函数中我们log以后调用了foo(10),这里的foo就对应匿名函数foo1,所以调用foo1传入参数10,也就是调用了我们的匿名函数传入参数10,所以这时候log "调用匿名函数"然后返回value*2,返回20给test1里的result,然后继续往下print result 20

  • 匿名函数和直接声明一个函数作为参数在实现上没有区别的,仅仅是省去了单独声明参数函数的步骤,直接在参数里声明函数

箭头匿名函数

箭头匿名函数只是匿名函数的一种,属于匿名函数的一种简写,但是要求函数体只能有一行代码

 //箭头函数: 条件,函数体质只能有一行代码
  test(() =>print("箭头函数被调用"));
  //将箭头函数拉开写,会发现print后面不能加分号来结束,或者逗号,所以说明箭头函数必须只有一行执行代码
  test(()=>{
    print("箭头函数")//当加分号会报错,所以就决定了箭头函数必须只能有一行执行
  });

test里调用参数函数时候就会打印log

箭头函数虽然只能有一行代码体但是我们可以通过代码体里执行其他函数来实现复杂事件响应

test(()=>bar("箭头函数1"));//我们可以在这里调用其他函数来实现
上面代码我们在声明test函数时候,参数声明为一个函数Function foo,这时候并没有规定foo的格式,我们可以通过在声明时候来确定下foo的格式类型

确定声明参数函数的格式

//这里我们就给foo声明的更加详细,调用时候需要传入2个必传参数num1,num2,会返回一个int类型的值
void test2(int foo(int num1, int num2)){
   int result =  foo(10,20);

   print("test2.result $result");
}

调用的时候传入的参数函数就需要遵循这个格式,包含2个int类型的必选参数,同时返回一个int类型的值,这里我们通过匿名函数方式来传参,方便更加清晰观察

test2(( num1, num2){
      return num1*num2;
  });

上面声明方式在代码可读性上不够好,我们可以通过拆分的形式来约定foo的格式


//上面test2方式声明的函数,虽然满足了我们的需求,但是代码阅读性上比较差,我们可以通过下面方式来写
typedef FooText = int Function(int num1,int num2);

void test3(FooText foo){
    int result = foo(10,20);
    print("test3.result $result");
}

这里我们声明一个函数FooText以及格式,在test3参数里直接用FooTest来声明一个函数foo,这个foo的格式包含2个必选int类型的参数,切返回一个int类型的值 调用方式和test2方式一样

  test3((num1,num2){
    return num1*num2;
  });

将函数作为一个返回值

因为函数是一等公民的原则,所以我们在函数返回值时候,同样可以返回一个函数

Function test4(){
  return (int num1, String num2){
    return num1*int.parse(num2);
  };
}

调用test4

  var foo = test4();
  print("foo $foo ${foo.runtimeType}");

打印结果

foo Closure: (int, String) => int (int, String) => int

通过结果我们可以看出来test4()调用以后返回的结果是一个参数为2个必选参int和sring,返回int类型值的参数,这时候我们可以在调用foo来实现这个返回的参数

var value = foo(10,"20");
print("valur $value");

//这时候foo就相当于一个
//(int num1, String num2){
//    return num1*int.parse(num2);
//};的函数

打印结果

value 200

这里test4的返回值我们定义Function只是一个函数,同样更加详细的定义返回的函数类型

typedef FooText = int Function(int num1,int num2);
//还可以声明返回函数类型
FooText test5(){
  //返回的函数类型要符合FooText的类型,传入2个int类型,然后返回int值
  return (num1,num2){
    return num1*num2;
  };
}

调用方式和test4相同

  var fooTextClassFoo = test5();
  print("fooTextClassFoo $fooTextClassFoo ${fooTextClassFoo.runtimeType}");
  var value1 = fooTextClassFoo(20,30);
  print("value1 $value1");

打印结果

fooTextClassFoo Closure: (int, int) => int (int, int) => int
value1 600