zoukankan      html  css  js  c++  java
  • Windows Phone 7 入门XAML语法介绍

    什么是 XAML


    XAML 是一种声明性标记语言。如同应用于 .NET Framework 编程模型一样,XAML 简化了为 .NET Framework 应用程序创建 UI 的过程。您可以在声明性 XAML 标记中创建可见的 UI 元素,然后使用代码隐藏文件(通过分部类定义与标记相连接)将 UI 定义与运行时逻辑相分离。XAML 直接以程序集中定义的一组特定后备类型表示对象的实例化。 

    下面的示例演示如何创建作为 UI 一部分的按钮。此示例的目的仅在于供您初步了解 XAML 是如何表示常用 UI 编程形式的(它不是一个完整的示例)。

    <StackPanel>

      <Button Content="Click Me"/>

    </StackPanel>

     

     

    XAML 法概述


    XAML 象元素

    <StackPanel>

      <Button Content="Click Me"/>

    </StackPanel>

    此示例指定了两个对象元素:<StackPanel>(含有内容,后面有一个结束标记)和 <Button .../>(自结束形式,包含几个特性)。对象元素 StackPanel 和 Button 各映射到一个类名,这些类由 WPF 定义并且是 WPF 程序集的一部分。指定对象元素标记时会创建一条 XAML 处理指令来创建一个新实例。每个实例都是在分析和加载 XAML 时通过调用基础类型的默认构造函数来创建的。

    特性法(属性)

    对象的属性通常可表示为对象元素的特性。特性语法命名在特性语法中设置的属性,后跟赋值运算符 (=)。特性的值始终以包含在引号中的字符串的形式进行指定。

    特性语法是最简单有效的属性设置语法,并且对于曾使用过标记语言的开发人员而言在使用中是最直观的语法。例如,以下标记将创建一个具有红色文本和蓝色背景的按钮,还将创建指定为 Content 的显示文本。

    <Button Background="Blue" Foreground="Red" Content="This is a button"/>

     

     

    属性元素

    对于对象元素的某些属性,特性语法是不可能实现的,因为无法在特性语法的引号和字符串限制内充分地表达提供属性值所必需的对象或信息。对于这些情况,可以使用另一个语法,即属性元素语法。

    属性元素开始标记的语法为 <类型名称.属性名称>。通常,该标记的内容是类型的一个对象元素,属性会将该元素作为其值。指定内容之后,必须用一个结束标记结束属性元素。结束标记的语法为 </类型名称.属性名称>。

    如果可以使用特性语法,那么使用特性语法通常更为方便,且能够实现更为精简的标记,但这通常只是一个风格的问题,而不属于技术限制。下面的示例演示了在前面的特性语法示例中设置的相同属性,但这次对 Button 的所有属性使用了属性元素语法。

    <Button>

      <Button.Background>

        <SolidColorBrush Color="Blue"/>

      </Button.Background>

      <Button.Foreground>

        <SolidColorBrush Color="Red"/>

      </Button.Foreground>

      <Button.Content>

        This is a button

      </Button.Content>

    </Button>

    集合

    XAML 语言包含一些优化,可以生成可读性更好的标记。其中的一项优化是:如果某个特定属性采用集合类型,则您在标记中声明为该属性的值内的子元素的项将成为集合的一部分。在这种情况下,子对象元素的集合是设置为集合属性的值。

    下面的示例演示为 GradientStops 属性设置值的集合语法:

    <LinearGradientBrush>

      <LinearGradientBrush.GradientStops>

        <!-- no explicit new GradientStopCollection, parser knows how to find or create -->

        <GradientStop Offset="0.0" Color="Red" />

        <GradientStop Offset="1.0" Color="Blue" />

      </LinearGradientBrush.GradientStops>

    </LinearGradientBrush>

    XAML 内容属性

    XAML 指定了一个语言功能,通过该功能,一个类可以指定它的一个且仅一个属性为 XAML 内容属性。该对象元素的子元素用于设置该内容属性的值。换言之,仅对内容属性而言,您可以在 XAML 标记中设置该属性时省略属性元素,并在标记中生成更直观的父级/子级形式。

    例如,Border 指定内容属性 Child。系统处理下面两个 Border 元素的方式相同。第一个元素利用了内容属性语法而省略了 Border.Child 属性元素。第二个元素显式标明 Border.Child。

    <Border>

      <TextBox Width="300"/>

    </Border>

    <!--explicit equivalent-->

    <Border>

      <Border.Child>

        <TextBox Width="300"/>

      </Border.Child>

    </Border>

    作为 XAML 语言的规则,XAML 内容属性的值必须完全在该对象元素的其他任何属性元素之前或之后指定。例如,下面的标记无法进行编译:

    复制

    <Button>I am a

      <Button.Background>Blue</Button.Background>

      blue button</Button>

    有关 XAML 内容属性的此项限制的更多信息,请参见 XAML 语法详述的“XAML 内容属性”一节。

    文本内容

    有少量 XAML 元素可直接将文本作为其内容来处理。若要实现此功能,必须满足以下条件之一:

    • 类必须声明一个内容属性,并且该内容属性必须是可赋值给字符串的类型(该类型可以是 Object)。例如,任何 ContentControl 都将 Content 用作其内容属性,并且其类型为 Object,这样就支持实际的 ContentControl(例如,Button)上的如下用法:<Button>Hello</Button>。
    • 类型必须声明一个类型转换器,该类型转换器将文本内容用作其初始化文本。例如,<Brush>Blue</Brush>。这种情况实际上并不常见。
    • 类型必须为已知的 XAML 语言基元。

    内容属性和集合

    请看以下示例:

    <StackPanel>

      <Button>First Button</Button>

      <Button>Second Button</Button>

    </StackPanel>

    此例中,每个 Button 都是 StackPanel 的一个子元素。这是一个简单直观的标记,其中出于两个不同的原因省略了两个标记。

    <StackPanel>

      <StackPanel.Children>

        <!--<UIElementCollection>-->

        <Button>First Button</Button>

        <Button>Second Button</Button>

        <!--</UIElementCollection>-->

      </StackPanel.Children>

    </StackPanel>

    特性法(事件)

    特性语法还可用于事件成员,而不仅限于属性成员。在这种情况下,特性的名称为事件的名称。在 XAML 事件的 WPF 实现中,特性的值是实现该事件的委托的处理程序的名称。例如,以下标记将 Click 事件的一个处理程序指定给在标记中创建的 Button

    <Page

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      x:Class="ExampleNamespace.ExamplePage">

      <Button Click="Button_Click" >Click Me!</Button>

    </Page>

    XAML 中的大小写和空白


    XAML 通常区分大小写。出于解析后备类型的目的,WPF XAML 按照 CLR 区分大小写的相同规则区分大小写。按名称与程序集中的基础类型进行比较或者与类型的成员进行比较时,对象元素、属性元素和特性名称均必须使用区分大小写的形式指定。XAML 语言关键字和基元也区分大小写。值并不总是区分大小写。值是否区分大小写将取决于与采用该值的属性关联的类型转换器行为,或取决于属性值类型。例如,采用 Boolean 类型的属性可以采用 true 或 True 作为等效值,但只是因为将字符串转换为 Boolean 的本机 WPF XAML 分析器类型转换已经允许将这些值作为等效值。

    WPF XAML 处理器和序列化程序将忽略或删除所有无意义的空白,并规范化任何有意义的空白。这与 XAML 规范的默认空白行为建议一致。通常,只有当您在 XAML 内容属性中指定字符串时,此行为的重要性才会体现出来。简言之,XAML 将空格、换行符和制表符转化为空格,如果它们出现在一个连续字符串的任一端,则保留一个空格。有关 XAML 空白处理的完整说明不属于本主题的讨论范围。

    标记扩


    标记扩展是一个 XAML 语言概念。当用于提供特性语法的值时,大括号({ 和 })表示标记扩展用法。此用法指示 XAML 处理系统不要像通常那样将特性值视为一个文本字符串或者可转换为字符串的值。

    WPF 应用程序编程中最常用的标记扩展是 Binding(用于数据绑定表达式)以及资源引用 StaticResourceDynamicResource。通过使用标记扩展,即使属性通常不支持特性语法,也可以使用特性语法为属性提供值。标记扩展经常使用中间表达式类型实现一些功能,例如,推迟值或引用仅在运行时才存在的其他对象。

    例如,下面的标记使用特性语法设置 Style 属性的值。Style 属性采用了 Style 类的一个实例,该实例默认情况下未能用特性语法字符串实例化。但在本例中,特性引用了特定的标记扩展 StaticResource。当处理该标记扩展时,它返回对以前在资源字典中作为键控资源进行实例化的某个样式的引用。

    <Page.Resources>

      <SolidColorBrush x:Key="MyBrush" Color="Gold"/>

      <Style TargetType="Border" x:Key="PageBackground">

        <Setter Property="Background" Value="Blue"/>

      </Style>

     

     

    ...

     

     

    </Page.Resources>

    <StackPanel>

      <Border Style="{StaticResource PageBackground}">

     

     

    ...

     

     

      </Border>

    </StackPanel>

     

     

    有关特定在 WPF 中实现的所有 XAML 标记扩展的参考列表,请参见 WPF XAML 扩展。有关由 System.Xaml 定义并且可更广泛用于 .NET XAML 实现的标记扩展的参考列表,请参见 XAML 命名空间 (x:) 语言功能。有关标记扩展概念的更多信息,请参见标记扩展和 WPF XAML

    转换


    在一节中,曾提到特性值必须能够使用字符串进行设置。对字符串如何转换为其他对象类型或基元值的基本本机处理取决于 String 类型本身,以及对某些类型(如 DateTimeUri)的本机处理。但是很多 WPF 类型或这些类型的成员扩展了基本字符串特性处理行为,因此可以指定更复杂的对象类型的实例作为字符串和特性。

    Thickness 结构是一个类型示例,该类型拥有可使用 XAML 的类型转换。Thickness 指示嵌套矩形中的度量并用作一些属性(如 Margin)的值。通过对 Thickness 设置类型转换器,所有使用 Thickness 的属性都可以更容易地在 XAML 中指定,因为它们可指定为特性。下面的示例使用类型转换和特性语法来为 Margin 提供值:

    <Button Margin="10,20,10,30" Content="Click me"/>

     

     

    上面的特性语法示例与下面更为详细的语法示例等效,但在下面的示例中,Margin 改为通过包含 Thickness 对象元素的属性元素语法进行设置。而且设置 Thickness 的四个关键属性作为新实例的特性:

    <Button Content="Click me">

      <Button.Margin>

        <Thickness Left="10" Top="20" Right="10" Bottom="30"/>

      </Button.Margin>

    </Button>

     

     

    还有少数对象只能通过类型转换这种公开方式在不涉及到子类的情况下为该类型设置属性,因为类型本身并没有默认构造函数。一个示例是 Cursor

    有关如何支持类型转换及其在特性语法上的应用的更多信息,请参见 TypeConverters 和 XAML

    XAML 根元素和 XAML 命名空


    一个 XAML 文件只能有一个根元素,这样才能同时成为格式正确的 XML 文件和有效的 XAML 文件。对于典型的 WPF 方案,将使用在 WPF 应用程序模型中具有重要意义的根元素(例如,为页使用 WindowPage,为外部字典使用 ResourceDictionary 或为应用程序定义使用 Application)。下面的示例演示 WPF 页的典型 XAML 文件的根元素,此根元素为 Page

    <Page

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

     

     

    ...

     

     

    </Page>

     

     

    根元素还包含特性 xmlns 和 xmlns:x。这些特性向 XAML 处理器指明哪些 XAML 命名空间包含标记将要作为元素引用的后备类型的类型定义。xmlns 特性明确指示默认的 XAML 命名空间。在默认的 XAML 命名空间中,可以不使用前缀指定标记中的对象元素。对于大多数 WPF 应用程序方案以及 SDK 的 WPF 部分中给出的几乎所有示例,默认的 XAML 命名空间均映射到为 WPF 命名空间 http://schemas.microsoft.com/winfx/2006/xaml/presentation。xmlns:x 特性指示另外一个 XAML 命名空间,该命名空间映射 XAML 语言命名空间 http://schemas.microsoft.com/winfx/2006/xaml。

    使用 xmlns 定义用法范围和名称范围映射的做法符合 XML 1.0 规范。XAML 名称范围与 XML 名称范围的不同仅在于:XAML 名称范围还包含有关进行类型解析和分析 XAML 时名称范围的元素如何受类型支持的信息。

    请注���,只有在每个 XAML 文件的根元素上,xmlns 特性才是绝对必需的。xmlns 定义将适用于根元素的所有子代元素(此行为也符合 xmlns 的 XML 1.0 规范。)同时允许根以下的其他元素上具有 xmlns 特性,这些特性将适用于定义元素的任何子代元素。但是,频繁定义或重新定义 XAML 命名空间可能会导致 XAML 标记样式难以阅读。

    其 XAML 处理器的 WPF 实现包括可识别 WPF 核心程序集的基础结构。已知 WPF 核心程序集包含支持 WPF 到默认 XAML 命名空间的映射的类型。这是通过属于项目生成文件以及 WPF 生成和项目系统一部分的配置来实现的。因此,为了引用来自 WPF 程序集的 XAML 元素,只需将默认 XAML 命名空间声明为默认 xmlns。

    x:

    在上面的根元素示例中,前缀 x: 用于映射 XAML 命名空间 http://schemas.microsoft.com/winfx/2006/xaml,该命名空间是支持 XAML 语言构造的专用 XAML 命名空间。在这整个 SDK 的项目模板、示例以及文档中,此 x: 前缀用于映射该 XAML 命名空间。XAML 语言的 XAML 命名空间包含多个将在 XAML 中频繁用到的编程构造。下面列出了将用到的最常见的 x: 前缀编程构造:

    • x:Key:为 ResourceDictionary(或其他框架中的类似字典概念)中的每个资源设置唯一的键。在典型的 WPF 应用程序标记中的所有 x: 用法中,x:Key 将可能占到 90%。
    • x:Class:向为 XAML 页提供代码隐藏的类指定 CLR 命名空间和类名。必须具有这样一个类才能支持每个 WPF 编程模型的代码隐藏,而正是因此,即使没有资源,也几乎总是能看到映射的 x:。
    • x:Name:处理对象元素后,为运行时代码中存在的实例指定运行时对象名称。通常,您将为 x:Name 经常使用 WPF 定义的等效属性。此类属性特定映射到 CLR 后备属性,因此更便于进行应用程序编程,在应用程序编程中,您经常使用运行时代码从初始化的 XAML 中查找命名元素。最常见的此类属性是 FrameworkElement.Name。在特定类型中不支持等效的 WPF 框架级Name 属性时,仍然可以使用 x:Name。某些动画方案中会发生这种情况。
    • x:Static:启用一个返回静态值的引用,该静态值只能是一个 XAML 兼容属性。
    • x:Type:根据类型名称构造一个 Type 引用。它用于指定采用 Type(例如 Style.TargetType)的特性,但属性经常具有本机的字符串到 Type 的转换功能,因此使用 x:Type 标记扩展用法是可选的。

    x: 前缀/XAML 命名空间中还有其他一些不太常见的编程构造。有关详细信息,请参见 XAML 命名空间 (x:) 语言功能

    XAML 中的自定和自定义类


    对于您自己的自定义程序集或 PresentationCore、PresentationFramework 和 WindowsBase 的 WPF 核心以外的程序集,可以将该程序集指定为自定义 xmlns 映射的一部分。只要该类型能够正确地实现以支持您所尝试的 XAML 用法,就可以在 XAML 中引用该程序集中的类型。

    下面是一个说明自定义前缀如何在 XAML 标记中工作的基本示例。前缀 custom 在根元素标记中定义,并映射为随应用程序一同打包并可用于该应用程序的一个特定程序集。此程序集包含 NumericUpDown 类型,实现该类型的目的是在支持常规 XAML 用法之外,还可以使用允许在 WPF XAML 内容模型的此特定点执行插入的类继承。通过使用该前缀,此 NumericUpDown 控件的一个实例声明为对象元素,以便 XAML 分析器可找到包含该类型的 XAML 命名空间,从而找到包含该类型定义的后备程序集的位置。

    复制

    <Page

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"

        >

      <StackPanel Name="LayoutRoot">

        <custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>

    ...</StackPanel>

    </Page>

    有关 XAML 中自定义类型的更多信息,请参见 XAML 及 WPF 的自定义类

    有关 XML 命名空间与程序集中后备代码的命名空间如何相关的更多信息,请参见 WPF XAML 的 XAML 命名空间和命名空间映射

    事件和 XAML 码隐


    大多数 WPF 应用程序既包括 XAML 标记,也包括代码隐藏。在一个项目中,XAML 编写为 .xaml 文件,而 CLR 语言(如 Microsoft Visual Basic .NET 或 C#)用于编写代码隐藏文件。在 WPF 编程和应用程序模型中对 XAML 文件进行标记编译时,XAML 文件的 XAML 代码隐藏文件的位置是通过如下方式来标识的:以 XAML 的根元素的 x:Class 特性形式指定一个命名空间和类。

    在目前已介绍的示例中,您已看到几个按钮,但还没有一个按钮具有任何关联的逻辑行为。为对象元素添加行为的主要应用程序级机制是使用元素类的现有事件,并为在运行时引发该事件时调用的该事件编写特定的处理程序。事件名称以及要使用的处理程序的名称在标记中指定,而实现处理程序的代码在代码隐藏中定义。

    <Page

      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

      x:Class="ExampleNamespace.ExamplePage">

      <Button Click="Button_Click" >Click Me!</Button>

    </Page>

     

     

     

     

    Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        Dim b As Button = e.Source

        b.Foreground = Brushes.Red

    End Sub

     

     

    请注意,代码隐藏文件使用 CLR 命名空间 ExampleNamespace 并将 ExamplePage 声明为该命名空间内的一个分部类。这相当于在标记根中提供的 ExampleNamespace.ExamplePage 的 x:Class 特性值。WPF 标记编译器将通过从根元素类型派生一个类,为编译的任何 XAML 文件创建一个分部类。当您提供也会定义同一分部类的代码隐藏时,将在与编译的应用程序相同的命名空间和类中组合生成的代码。

    有关 WPF 中代码隐藏编程要求的更多信息,请参见WPF 中的代码隐藏和 XAML 中的“代码隐藏、事件处理程序和分部类要求”一节。

    如果您不想创建一个单独的代码隐藏文件,还可以将代码内联到 XAML 文件中。但是,内联代码是一种缺少多样性的方法,有很多的限制。有关详细信息,请参见WPF 中的代码隐藏和 XAML

    路由事件

    路由事件是一个特殊的事件功能,该功能是 WPF 的基础。路由事件使一个元素可以处理另一个元素引发的事件,前提是这些元素通过树关系连接在一起。使用 XAML 特性指定事件处理时,可以对任何元素(包括未在类成员表中列出路由事件的元素)侦听和处理该路由事件。这是通过以所属类名限定事件名特性来实现的。例如,在当前所讨论的 StackPanel / Button 示例中,父 StackPanel 可以通过在 StackPanel 对象元素上指定特性 Button.Click,并使用处理程序名作为特性值,为子元素按钮的 Click 事件注册一个处理程序。有关路由事件如何工作的更多信息,请参见路由事件概述

    XAML 命名元素


    默认情况下,通过处理 XAML 对象元素在对象图中创建的对象实例没有唯一标识符或对象引用。相反,如果在代码中调用构造函数,则几乎总是使用构造函数结果为构造的实例设置一个变量,以便以后在代码中引用该实例。为了对通过标记定义创建的对象提供标准化访问,XAML 定义了 x:Name 特性。您可以在任何对象元素上设置 x:Name 特性的值。在代码隐藏中,您选择的标识符等效于引用所构造的实例的实例变量。在任何方面,命名元素都像它们是对象实例一样工作(此名称引用该实例),并且代码隐藏可以引用该命名元素来处理应用程序内的运行时交互。实例和变量之间的这种连接是由 WPF XAML 标记编译器实现的,并且更具体涉及到功能和模式,例如本主题中将不详细讨论的 InitializeComponent

    WPF 框架级 XAML 元素继承 Name 属性,该属性等效于 XAML 定义的 x:Name 特性。其他某些类也为 x:Name(通常也定义为 Name 属性)提供属性级等效项。一般而言,如果您在所选元素/类型的成员表中找不到 Name 属性,则可以改用 x:Name。x:Name 值将通过特定子系统或通过诸如 FindName 等实用工具方法,为可在运行时使用的 XAML 元素提供标识符。

    下面的示例在 StackPanel 元素上设置 Name。然后,该 StackPanel 中的 Button 上的处理程序通过由 Name 设置的实例引用 buttonContainer 来引用 StackPanel

    <StackPanel Name="buttonContainer">

     

     

    ...

     

     

      <Button Click="RemoveThis">Click to remove this button</Button>

    </StackPanel>

     

     

    Private Sub RemoveThis(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

         Dim fe As FrameworkElement = e.Source

         If (buttonContainer.Children.Contains(fe)) Then

             buttonContainer.Children.Remove(fe)

         End If

    End Sub

     

     

    就像变量一样,实例的 XAML 名称受范围概念的控制,因此可以在可预测的某个范围内强制名称唯一。定义页面的主标记表示一个唯一的 XAML 名称范围,而该 XAML 名称范围的边界就是该页面的根元素。但是,其他标记源(如样式或样式中的模板)可以在运行时与页面交互,这种标记源常常具有自己的 XAML 名称范围,这些名称范围不一定与页面的 XAML 名称范围相关联。有关 x:Name 和 XAML 名称范围的更多信息,请参见 Namex:Name 指令WPF XAML 名称范围

    附加属性和附加事件


    XAML 指定了一个语言功能,该功能允许对任何元素指定某些属性或事件,而不管要设置属性或事件的元素的类型定义中是否存在该属性或事件。该功能的属性版本称为附加属性,事件版本称为附加事件。从概念上讲,可以将附加属性和附加事件视为可以在任何 XAML 元素/对象实例上设置的全局成员。但是,元素/类或更大的基础结构必须支持附加值的后备属性存储。

    通常通过特性语法来使用 XAML 中的附加属性。在特性语法中,您可以采用“所有者类型.属性名”的形式指定附加属性。

    表面上,这与属性元素用法类似,但在这种情况下,您指定的“所有者类型”始终是一种与从中要设置附加属性的对象元素不同的类型。“所有者类型”这种类型提供 XAML 处理器为获取或设置附加属性值所需要的访问器方法。

    使用附加属性的最常见方案是使子元素能够向其父元素报告属性值。

    下面的示例演示了 DockPanel.Dock 附加属性。DockPanel 类为 DockPanel.Dock 定义访问器,因此拥有附加属性。DockPanel 类还包括一个逻辑,该逻辑迭代其子元素并具体检查每个元素是否具有 DockPanel.Dock 设置值。如果找到一个值,将在布局过程中使用该值定位子元素。使用 DockPanel.Dock 附加属性和这种定位功能事实上是 DockPanel 类的激动人心的一面。

    <DockPanel>

      <Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>

      <Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>

    </DockPanel>

     

     

    在 WPF 中,大部分或所有附加属性还作为依赖项属性来实现。有关详细信息,请参见附加属性概述

    附加事件使用类似的“所有者类型.事件名”特性语法形式。就像非附加事件一样,XAML 中的附加事件的特性值指定对元素处理事件时调用的处理程序方法的名称。在 WPF XAML 中使用附加事件并不常见。有关更多信息,请参见附加事件概述

  • 相关阅读:
    iframe,modaldialog父子窗口相互通信的问题
    bootstrap基础知识
    基于原生js的图片延迟加载
    通过原生js的ajax或jquery的ajax获取服务器的时间
    转 fiddler教程
    正则表达式
    es6 对象
    ES6
    axios
    javascript闭包和闭包的几种写法和用法
  • 原文地址:https://www.cnblogs.com/linzheng/p/1948753.html
Copyright © 2011-2022 走看看