`
shaobenbin
  • 浏览: 18191 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Go语言的闭包学习心得

 
阅读更多

       最近对Go语言产生了点兴趣,也正好此玩意可以在eclipse中开发,就下载了玩玩。学习过程中发现Go语言也有闭包这个概念,由于之前自学JS的时候也学过闭包,所以对此特别关注学习了下,也在网上搜索了一些关于Go语言的闭包概念。但是介于golang.org一直进不去,待将来爬进去后再去深刻的恶补下golang的闭包概念.

          1、网上常见的三个闭包案例(前两个节选自小谈golang闭包)

          案例一、

          

package main

import (
	"fmt"
)

func main() {
	var interiorFn[10] func()
	for i := 0; i < len(interiorFn); i++ {
		interiorFn[i] = func() {
			fmt.Printf("定义在函数内部的闭包值:%d\n",i)
		}
	}

	for j := 0; j < len(interiorFn); j++ {
		interiorFn[j]()
	}

}

     案例二、

   

package main

import (
	"fmt"
)
func main() {
	var externalFn [10] func()
	for i := 0; i < len(externalFn); i++ {
		externalFn[i] = closureFunc(i)
	}

	for j := 0; j < len(externalFn); j++ {
		externalFn[j]()
	}

}

func closureFunc(id int) func() {
	return func() {
		fmt.Printf("外部定义的闭包值:%d\n",id)
	}
}

    案例一:输出

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     内部闭包值:10

     案例二:输出

     Golang的闭包实验:

     外部闭包值:0

     外部闭包值:1

     外部闭包值:2

     外部闭包值:3

     外部闭包值:4

     外部闭包值:5

     外部闭包值:6

     外部闭包值:7

     外部闭包值:8

     外部闭包值:9

     这两个案例的输出值不一样,因还没有看到官方的文档,所以我大胆猜测如下:

     案例一:由于闭包定义在main函数里面,for循环中定义的局部变量i对于此闭包而言就等于外部的变量,内部可以访问外部的变量,因此在第二个for循环的时候,闭包执行的fmt.Printf("定义在函数内部的闭包值:%d\n",i)的时候,其实就是打印出main函数中for循环定义的那个局部变量的值,由与变量i已经递增至10,所以在第二个for循环执行闭包的时候,打印出来10个10。这个应该好理解。

   案例二:当闭包定义在main函数外面的时候,有closureFunc函数产生闭包的时候,情形就完全与案例一不同了。首先查看closureFunc函数的参数定义,是int类型,属于值传递,在Go中值传递是通过复制的方式传递的。那么在第一个循环中,假设i递增到2,调用closureFunc(i)来获取一个闭包,由于值传递,所以等同于调用了closureFunc(2),closureFunc方法里定义的闭包fmt.Printf("外部定义的闭包值:%d\n",id)打印的id是closureFunc函数里面的id变量,由于外部传递进来的是2,所以id这个变量就变成了2.所以案例二的输出结果就变成如上的形式了。

   案例三:把案例二的值传递改成引用传递

   

package main

import (
	"fmt"
)


func main() {
	var externalFn [10]func()
	var context = []int{0}
	for i := 0; i < len(externalFn); i++ {
		context[0] = i;
		externalFn[i] = closureFunc(context)
	}

	for j := 0; j < len(externalFn); j++ {
		externalFn[j]()
	}

}


func closureFunc(id[] int) func() {
	return func() {
		fmt.Printf("外部闭包值:%d\n",id)
	}
}

  输出结果就变成了:

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

  外部闭包值:[9]

 

  2、闭包的作用

    上面演示了闭包的代码,闭包的作用很明显,利用函数内部可以访问当前函数定义的外部函数的变量的特性,使其它函数能够获取其它函数的局部变量(受保护变量?)的功能。

    比如在closureFunc的局部变量是id,main函数是获取不到这个id这个变量值的,但是通过闭包就可以获取到了。

  3、闭包的应用场景参考JS

    个人感觉在两种情况下,可以考虑使用闭包

          1. 局部变量驻留内存,不被垃圾回收站给回收掉(比如局部变量要延迟与当前函数调用)
          2. 局部变量能被外面函数调用

   4、一个闭包的使用案例

    在阅读Writing Web Applications的时候就看到了一个很典型的闭包应用.对于国内用户如果死活访问不了的用户可以访问用Go语言开发Web程序[翻译]一文,是个热心(大叔大哥?)对官方文档的翻译。

    

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		p = &Page{Title: title}
	}
	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	body := r.FormValue("body")
	p := &Page{Title: title, Body: []byte(body)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}
func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}
	renderTemplate(w, "view", p)
}
//闭包的使用
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		title := r.URL.Path[lenPath:]
		if !titleValidator.MatchString(title) {
			http.NotFound(w, r)
			return
		}
		fn(w, r, title)
	}
}

func main() {
	http.HandleFunc("/view/", makeHandler(viewHandler))
        http.HandleFunc("/edit/", makeHandler(editHandler))
        http.HandleFunc("/save/", makeHandler(saveHandler))
        http.ListenAndServe(":7777", nil)
}

 

    上面的代码是抽取出来的一部分,只是用于说明闭包的其中一种典型使用。

 

    editHandler这个函数是主处理业务逻辑。这个业务逻辑中需要对传递进来的参数title进行参数合法性校验。同样saveHandler,viewHandler也有同样的需求,为了追求良好的代码编写,我们希望把title放在一个统一的地方进行校验,所以使用makeHandler来封装editHandler,saveHandler,viewHandler这三个函数,在调用这三个函数前我们希望先对title进行合法性校验.

    在上述需求的指导下,大致的设计思路是,在调用editHandler,saveHandler,viewHandler之前首先需要校验title,然后根据用户请求的URL来选择对应的处理函数.

    4.1、通常情况下,我们会考虑如下的设计方案(伪代码

    1、在main函数绑定/这样的URL规则统一使用某一个函数来处理,比如A

    2、在A里面先获取操作名(view或edit或save)和title

    3、校验title是否正确

    4、根据操作名来调用对用的handler函数。(switch语句)

    上述业务逻辑合适么,当然能满足需求,它把URL的路由规则放到自己的函数里来处理,只是考虑将来如果一些handler不需要去校验title的情况下,那么这些handler的路由规则就需要放到main函数中去(这样路由有两个地方来分配就比较乱了)或者需要修改A函数(较复杂)来适应。总体来说,我们还是希望能够在main函数做路由规则。

    4.2 、golang的闭包设计方案

    1、设计一个函数B,该函数可以把handler函数当成参数传递进来

    2、函数B先校验title,然后执行传递进来的handler函数(有木有java的接口编程赶脚)。

    但是这里遇到一个问题就是http.HandleFunc绑定的执行函数只有两个参数w http.ResponseWriter, r *http.Request,所以不能传递额外的参数比如我们需要的执行handler函数名,所以我们考虑使用闭包,将函数B设计成:

    

//闭包的使用
func B(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//业务逻辑
               
	}
}
  这样调用B函数返回了一个http.HandleFunc可以绑定的函数,这个函数我们成为闭包函数C。由于闭包函数C是定义在函数B里面的,所以它可以调用函数B的局部变量也就也是B函数传递进来的参数fn(viewHnalder或editHandler或saveHandler).所以把代码改写成

 

   

//闭包的使用
func B(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//业务逻辑
               title : = 从URL上获取
               fn(w, r, title)
	}
}
    fn是函数B的局部变量,因为其被闭包函数C给调用了,所以可以使他一直在内存中驻留不被垃圾回收,实际上fn等同于变成了闭包函数C的局部变量了.
  4.3、案例总结
    在这个案例中,闭包做了一件最重要的事情就是扩充了http.HandleFunc绑定的执行函数的参数限制,可以额外的随意添加任意多的参数,利用闭包功能来让这些参数的生命周期延长到与闭包函数一样长,等同于这些参数变成了闭包函数的局部变量.所以:
    
func B(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//业务逻辑
               
	}
}
  类似于下面这段代码的感觉,功能是一样的
  
func C(w http.ResponseWriter, r *http.Request,fn func(http.ResponseWriter, *http.Request, string)){
}
  只是C是不能被http.HandleFunc绑定,而B(viewHandler)可以被http.HandleFunc给绑定.
分享到:
评论

相关推荐

    Go语言学习笔记.pdf 共174页

    Go语言学习笔记.pdf 共174页 Go语言学习笔记.pdf 共174页是一本关于Go语言的详细学习笔记,涵盖了Go语言的基础知识、函数、数组、Maps、Structs、接口、并发、程序结构、标准库等方面的内容。本笔记共分为三大部分...

    Go语言学习笔记

    "Go语言学习笔记" Go语言学习笔记是关于Go语言基础入门篇的笔记,主要介绍Go语言基础语法、数据类型、逻辑语句等相关知识点。 语言概述 Go语言是一种开源的编程语言,能让构造简单、可靠且高效的软件变得容易。Go...

    Go语言编程goland.pdf

    Go语言有很多特性,例如自动垃圾回收、更丰富的内置类型、函数多返回值、错误处理、匿名函数和闭包、类型和接口、并发编程、反射、语言交互性等。 自动垃圾回收 Go语言提供了自动垃圾回收机制,开发者不需要手动...

    go语言学习示例

    Go语言,又称Golang,是由Google开发的一种静态类型的、编译型的、并发型的、垃圾回收的、C风格的编程语言。它旨在提高程序员的生产效率和系统的可扩展性,特别适合构建大规模的网络服务和分布式系统。本资料包是...

    Go语言精进之路:从新手到高手的编程思想、方法和技巧1.docx

    学习 Go 语言的开发者也需要了解一些高级特性,例如闭包、通道和并发编程等。 在学习过程中,可以通过编写简单的程序来巩固基础知识,例如实现一些数学计算、字符串处理和文件操作等。在掌握基本的语法和编程习惯...

    Go 语言学习代码示例

    在“Go 语言学习代码示例v2”这个资源中,我们可以期待找到一系列有关 Go 语言编程的实例,这些实例涵盖了基础语法、数据类型、控制结构、函数、包、接口、并发机制(goroutines 和 channels)、网络编程以及错误...

    js闭包学习心得总结

    通过对标题《js闭包学习心得总结》的阅读和理解,我们可以了解到闭包定义、闭包的创建条件以及闭包的应用场景和在实际编码中可能遇到的问题。 闭包定义: 闭包(Closure)是函数和声明该函数的词法环境的组合。这...

    Go 语言学习代码示例v2

    上述内容提到的Go语言学习代码示例v2,展示了一系列示例文件及其功能,覆盖了数组、原子计数器、Base64编码、基本认证、规范主机、通道缓冲、通道方向、通道同步、闭包、集合函数、命令行参数和标志、常量、defer、...

    go语言学习笔记

    Go语言学习笔记的知识点涉及到了Go语言的基础知识、语法特性、核心概念以及并发编程等多个方面。下面根据提供的文件内容详细说明这些知识点: 1. Go语言简介 Go语言,也被称为Golang,是由Google开发的一种开源编程...

    学习go语言(Golang)

    Go语言,又称Golang,是由Google开发的一种静态类型的、编译型的、并发型的、垃圾回收的编程语言。它的设计目标是简化并发编程,并提供高效、简洁的语法,使得开发者能够快速编写出可靠的系统。《学习Go语言》这本书...

    Swift语言利用Closure闭包实现反向传值Demo

    在本Demo“Swift语言利用Closure闭包实现反向传值Demo”中,我们将深入探讨如何利用闭包在两个视图控制器之间实现数据的反向传递。 首先,理解闭包的基本概念。闭包本质上是一个函数,它可以访问并修改其外部作用域...

    Go语言学习资料

    标题:“Go语言学习资料”为我们指出了学习的主题,即Go语言,这是Google开发的一种静态类型、编译型语言,也称为Golang。Go语言以其简洁、快速、安全和现代特性,成为现代编程语言中的佼佼者,特别适合于构建大型...

    程序语言闭包讲解ppt

    闭包在程序设计中是一种非常重要的概念,尤其是在像PHP这样的动态类型语言中。闭包,简单来说,就是能够记住其被定义时的环境的函数,即使这个环境在函数执行时已经不再存在。它允许函数访问并操作外部作用域中的...

    js闭包学习

    总之,JavaScript的闭包是一种核心概念,理解和掌握闭包对于深入学习JavaScript以及使用jQuery等库进行前端开发至关重要。通过闭包,开发者可以实现更灵活和高效的设计模式,提高代码质量与可维护性。

    深入理解Go语言中的闭包

    Go语言中的闭包 先看一个demo: func f(i int) func() int { return func() int { i++ return i } } 函数f返回了一个函数,返回的这个函数就是一个闭包。这个函数中本身是没有定义变量i的,而是引用了它所在的...

    Swift闭包学习

    Swift闭包是编程语言中的一个核心特性,尤其在iOS应用开发中扮演着至关重要的角色。闭包(Closure)可以理解为一段能够捕获并存储其所在上下文中变量的代码块,它可以作为参数传递,也可以作为返回值。在Swift中,...

    Swift语言采用Closure闭包进行传值Demo

    Swift语言是苹果公司推出的一种强大的、现代化的编程语言,它被广泛用于开发iOS、macOS、watchOS和tvOS的应用程序。在Swift中,Closure(闭包)是一种强大的功能,可以捕获和存储其所在上下文中的引用。本Demo通过...

    Go 学习笔记 高清

    根据提供的文件内容,以下是对Go语言学习笔记的详细知识点阐述。 Go语言是Google开发的一种静态类型、编译型、并发型,并具有垃圾回收功能的编程语言。它由Robert Griesemer、Rob Pike和Ken Thompson于2007年9月...

    go语言基本语言源码

    Go语言,又称Golang,是由Google开发的一种静态类型的、编译型的、并发型的、垃圾回收式的、具有C风格的编程语言。它的设计目标是提高编程效率,简化多线程编程,以及优化网络和系统服务的开发。下面将详细讨论Go...

Global site tag (gtag.js) - Google Analytics