精华内容
下载资源
问答
  • 主要介绍了css列表标签list与表格标签table详解的相关资料,需要的朋友可以参考下
  • StringTable详解

    千次阅读 2020-04-17 20:03:19
    文章目录StringTable1.什么是stringtable?2. 字符串拼接原理2.1字符串变量拼接2.2 字符串常量拼接3. 字符串延迟加载4. intern()--将串池中还没有的字符串对象放入串池5. 串池中的对象也会被GC回收6. stringtable ...

    StringTable

    1.什么是stringtable?

    字符串池在JDK1.7之后存在于堆中的一块区域,String s1 = "abc"这样声明的字符串会放入字符串池中,String s1 = new String("abcd")会在字符串池有一个"abcd"的字符串对象,堆中也有1个,2个不同。

    • 字符串池可以避免重复创建字符串对象

      String s1 = "a";
      String s2 = "a";
      boolean isEqual = (s1 == s2);//为true,因为都是字符串池中的一个对象
      
      
    • 常量池中的字符串仅是符号,第一次用到时才变为对象

    • 它的结构为hash表结构,相同的字符串只存在一份

    2. 字符串拼接原理

    2.1字符串变量拼接

    (代码中 s1 + s2)的原理是 new StringBuilder(),再调用它的append方法,最后调用它的toString(),实际是new String
    在这里插入图片描述

    2.2 字符串常量拼接

    (代码中"a" + "b")的原理是编译期优化,编译器认为是常量,拼接结果唯一,去字符串池中找

    3. 字符串延迟加载

    利用idea中的Memory验证字符串的延迟加载,执行到的时候才加载,不是一次加载完(观察字符串个数)

    • Memory可以查看运行时每类对象的个数
      在这里插入图片描述

    4. intern()–将串池中还没有的字符串对象放入串池

    可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池

    1. 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串 池中的对象的引用返回
    2. 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池, 会把串池中的对象返回
      在这里插入图片描述

    5. 串池中的对象也会被GC回收

    6. stringtable 性能调优

    相关虚拟机参数

    • -XX:+PrintStringTableStatistics

    • -XX:StringTableSize

    6.1 调整虚拟机参数-XX:StringTableSize

    stringtable的数据结构为哈希表,如果程序里字符串常量的个数非常多,可以适当修改虚拟机参数-XX:StringTableSize(桶的个数,最小为1009)的大小:原理是增大桶的数量,减小冲突,提升效率

    eg:逐行读取一个包含每行都是一个单词,共计48W个单词的文本文件,读取到一个字符串变量,并调用intern()方法加入到stringtable中,修改虚拟机参数StringTableSize分别为不设置,1009,200000观察读取花费的总时间。结论:stringtablesize越小,耗时越多.

    /**
     * 演示串池大小对性能的影响
     * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009
     */
    public class Demo1_24 {
    
        public static void main(String[] args) throws IOException {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();
                while (true) {
                    line = reader.readLine();
                    if (line == null) {
                        break;
                    }
                    line.intern();
                }
                System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
            }
    	}
    }
    

    6.2 考虑将字符串入池

    前提是程序中存在大量(几十万,百万个)字符串,且有很多重复的情况


    以上弄懂,下面这些就能搞明白啦

    String s1 = "a";
    String s2 = "b"; 
    String s3 = "a" + "b"; 
    String s4 = s1 + s2; 
    String s5 = "ab"; 
    String s6 = s4.intern();
    //  System.out.println(s3 == s4); //false
    System.out.println(s3 == s5);   //true
    System.out.println(s3 == s6);
    String x2 = new String("c") + new String("d"); 
    String x1 = "cd"; 
    x2.intern();
    // 如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢 
    System.out.println(x1 == x2);
    
    展开全文
  • EBS Table详解

    2012-05-22 14:24:18
    EBS Table,report开发利器
  • Antd Pro V4 protable详解(ps:踩坑日记)

    千次阅读 2020-08-08 18:39:37
    Antd Pro V4 protable详解(ps:踩坑日记) 写在前面: 在这篇文章中,你会了解到: protable 中的cloumns属性详解 protable初始化数据的几种方法 一、ProTable概要 1.为什么需要ProTable? antd pro 中的大部分组件...

    Antd Pro V4 protable详解(ps:踩坑日记)

    写在前面:
    在这篇文章中,你会了解到:

    • protable 中的cloumns属性详解
    • protable数据加载和处理(两种方法,直接使用service或者使用models)
    • 一些笔者踩过的小坑
    • 在将model与Protable结合时的疑问
    一、ProTable概要

    1.为什么需要ProTable?
    antd pro 中的大部分组件来自于 antd , 而 antd pro table 则是基于 antd 的 table 组件再封装了一层, 熟练使用 antd pro table, 能够覆盖大部分增删改查业务的需要, 关键是只需要极少的配置, 就能得到一个完善的表格。引用官方的话来说,那就是

    antd 作为服务于企业级产品的设计体系的组件库,已经提供了强大的 Table,但是业务的不同导致我们仍有需要进行一些定制,不同的单元格有很多不同的数据格式,金额,日期,数字等,包括一些常用的操作,页码切换时的重新请求,刷新数据等,这些都是很简单的重复劳动,但是却不可避免。

    ProTable 就是为了解决这些问题,在 Table 的层面上提供了一些预设,你可以通过 valueType 来支持各种类型的数据,预设了金额,日期,序号,进度条 等多种类型,并且支持通过 valueEnum 来映射枚举,解决非常烦人的各种枚举配置,可以大大的简化代码。

    综上,一言以蔽之,ProTable is fucking awesome!(But还是有一些小问题需要解决。)

    2.ProTable的主要板块
    在这里插入图片描述
    在大部分场景中,我们只需要配置这四种颜色的板块即可:

    • 红色:search属性,搜索表单,传入对象时为搜索表单的配置,设置是否显示,默认为true
    • 绿色:通过,toolBarRender进行配置,渲染工具栏,支持返回一个 dom 数组,会自动增加 margin-right
    • 蓝色:通过rowSelection(已经选择项),tableAlertRender(当前共选中 1 项,共有 1 项未解决),tableAlertOptionRender(自定义 清空)三个属性进行配置
    • 棕色:通过columns进行配置,也就是表头。
    <ProTable
          columns={columns}
          request={async (params = {}) =>   //请求数据
            request('https://proapi.azurewebsites.net/github/issues', {
              params,
            })
          }
          rowKey="id"
          rowSelection={{}}
          tableAlertRender={({ selectedRowKeys, selectedRows }) =>
            `当前共选中 ${selectedRowKeys.length} 项,共有 ${selectedRows.reduce((pre, item) => {
              if (item.state === 'open') {
                return pre + 1;
              }
              console.log(selectedRowKeys,selectedRows)
              return pre;
            }, 0)} 项未解决 `
          }
          tableAlertOptionRender={props => {
            const { onCleanSelected } = props;
            return (
              <Space>
                <a>自定义</a>
                <a onClick={onCleanSelected}>清空</a>
              </Space>
            );
          }}
          dateFormatter="string"
          headerTitle="批量操作"
          toolBarRender={(_, { selectedRowKeys }) => [   //配置绿色框中的内容
            <Button key="3" type="primary">
              <PlusOutlined />
              新建
            </Button>,
            selectedRowKeys && selectedRowKeys.length && (
              <Button
                key="3"
                onClick={() => {
                  window.alert(selectedRowKeys.join('-'));
                }}
              >
                批量删除
              </Button>
            ),
          ]}
        />
    
    二、ProTable详解

    1.cloumns
    以下笔者把一些需要尝试才能试出来的属性标注,其余一看文档就懂的属性,笔者就不赘述了

    const columns = [
      {
        title: '序号',  // 对应表头的文字
        dataIndex: 'index',  //对应你的字段名称
        valueType: 'indexBorder',  //indexBorder就是proTable自动帮你生成的1,2,3,4的序号
        width: 72,    //通过width属性, 可以自行调节这一栏的宽度,不过总宽度不要超过100%哦
      },
      {
        title: '标题',
        dataIndex: 'title',
        copyable: true,    //多出来一个蓝色的复制icon,点击就直接进行复制
        ellipsis: true,   //用...代替没有显示的文本,并且在鼠标移到相应的文本上会显示全部的相应文本
        tip: '标题过长会自动收缩',   //显示在表头作为一个提示
        width: '30%',
        hideInSearch: true,   //在最上面的搜索栏不显示该字段
      },
      {
        title: '状态',
        dataIndex: 'state',
        initialValue: 'all',
        filters: true,
        valueEnum: {
          all: {  //key表示选中该状态后,所获得的值
            text: '全部',  //text表示实际显示出来的文本
            status: 'Default',  //表示文字前面的那一个点是什么颜色
          },
          open: {
            text: '未解决',
            status: 'Error',  //表示文字前面的那一个点是什么颜色 :红色
          },
          closed: {
            text: '已解决',
            status: 'Success', //表示文字前面的那一个点是什么颜色 :绿色
          },
          processing: {
            text: '解决中',
            status: 'Processing',
          },
        },
        width: '10%',
      },
      {
        title: '标签',
        dataIndex: 'labels',
        width: '10%',
        render: (_, row) => (  //自定义渲染方法,返回一个ReactNode,row就是这一整行的数据
          <Space>
            {row.labels.map(({ name, id, color }) => (
              <Tag color={color} key={id}>
                {name}
              </Tag>
            ))}
          </Space>
        ),
      },
      {
        title: '创建时间',
        key: 'since',
        dataIndex: 'created_at',
        valueType: 'dateTime',
        width: '20%',
      },
      {
        title: '操作',
        valueType: 'option',
        render: (text, row, _, action) => [  
        //自定义渲染方法,返回一个ReactNode,row就是这一整行的数据
          <a href={row.html_url} target="_blank" rel="noopener noreferrer" key="link">
            链路
          </a>,
          <a href={row.html_url} target="_blank" rel="noopener noreferrer" key="warning">
            报警
          </a>,
          <a href={row.html_url} target="_blank" rel="noopener noreferrer" key="view">
            查看
          </a>,
          <TableDropdown
            key="actionGroup"
            onSelect={() => action.reload()} 
            // action 是Form.useForm暴露出来的对象,便于手动触发表格操作
           
            menus={[
              {
                key: 'copy',
                name: '复制',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ]}
          />,
        ],
      },
    ];
    

    2.数据加载:跳过models,直接使用service进行API管理

    首先是页面初始化的时候,通过request属性加载相应的数据,request属性,携带当前页数(current)和页面数据量(pageSize)两个变量,但是在改变当前页数,都会再次触发request发起请求。
    在这里插入图片描述
    index.jsx

    import {getData,changeData} from './service'
    ....省略
     <ProTable
        ...省略
        request = {async (params)=>{
        	let data = Object.assign(params,...把其它需要的参数,绑定到params)
    		let res = await getData(data)
            return {
                data:data ,
                success:true
                total:data.total
            }
        }}
        
        
      />
    

    service.js
    小tips,request中,如果key为params会将参数拼接在地址后面,而data,则是会将参数放在请求体中,并且PUT,DELETE方法也是使用data属性

    import request from '@/utils/request'
    
    export async function getData(params){
        return request('/api/test',{
            params:params
        })
    }
    
    export async function changeData(params){
        return request('/api/test',{
            data:params,
            methods:"POST"
        })
    }
    

    返回数据格式的坑:
    在这里插入图片描述
    大家要注意的一点是,request属性对于返回的数据格式是有要求的

    request={async (params = {}) =>
          {
            console.log("params",params)
            return {
    				data: dataSource,
    			    total: tableListDataSource.length,
    			    success: true,
    			    pageSize,
    			    current: parseInt(`${params.currentPage}`, 10) || 1,
    			}
          }
        }
    
    

    特别是前三个data,total,success字段,如果没有的话,页面是没有数据的,刚开始用pro的时候,笔者在这里踩过坑, 标注一下!

    ps:如果用户在进入页面的时候不需要自动触发request请求数据,需要用户手动查询之后,才会获取到数据,只需要添加一个。manualRequest = {true}属性即可,但注意,添加这个属性之后,最上方的serach就是不可隐藏的了。

    3.数据更新,调用action

    /**
     * 添加节点
     * @param fields
     */
    
    const handleAdd = async fields => {   //添加数据
      const hide = message.loading('正在添加');
    
      try {
        await addRule({ ...fields });  //调用相应的service中的api即可
        hide();
        message.success('添加成功');
        actionRef.current.reload()   //刷新表格,触发request属性拉取最新数据
      } catch (error) {
        hide();
        message.error('添加失败请重试!');
        
      }
    };
    

    其它action操作

    // 刷新
    ref.current.reload();
    // 加载更多
    ref.current.fetchMore();
    // 重置到默认值
    ref.current.reset();
    // 清空选中项
    ref.current.clearSelected();
    

    ps:这里只是对于是否成功发起请求进行了判断,但是如果要进行错误处理的话,目前根据umi-request官方的说法,最多只能在model中的effect或者reducer中进行处理,无法直接在umi-request使用中间件或者用其它方法在源头处理,umi-request即使请求失败,依旧会把失败的相应返回给用户。

    4.疑问:将model与ProTable结合
    这是问题示范,笔者还没有实践成果过,大家注意
    (1)写service

    import request from '@/utils/request'
    
    export async function getData(params){
        return request('https://proapi.azurewebsites.net/github/issues',{
            params:params
        }) //此处笔者直接使用官方提供的api作为测试api
    }
    

    (2)写model

    import {getData,changeData} from './service'
    
    const TestModel = {
        namespace:"TestModel",
        state:{
            testData:[]
        },
        effects:{
            *fetchTestData({payload},{put,call}){
                const response = yield call(getData,payload)  //调用api
                yield put({  //触发saveData这个reducer
                    type:"saveData",
                    payload:response
                })
            }   
        },
        reducers:{
            saveData(state,action){
                return {...state,testData:action.payload || {}}  //将获取到的数据合并到state中
            }
        }
    }
    
    export default TestModel
    

    (3)将dispatch和state连接到组件上

    import {connect} from 'dva'
    export default connect(({TestModel})=>({  //TestModel是相应的namespace
        testData:TestModel.testData
    }))(Test)
    

    (4)将dispatch写入request属性中,并且将testData赋给dataSource属性(用来初始化table)

    <ProTable
                。。。省略
                request = {(params)=>{
    
                    dispatch({
                        type:"TestModel/fetchTestData",
                        payload:params
                    })
                }}
                dataSource={testData}
                
                
            />
    

    这样即使数据格式正确,pro仍然无法正确显示数据,一脸懵逼。。。。


    以上就是关于Protable的全部内容
    详情请查阅官方文档ProComponents

    感谢阅读,欢迎批评指正,希望大家能够在追求卓越中不断进步,让优秀成为一种习惯~

    展开全文
  • console.table详解

    千次阅读 2018-08-16 16:10:59
    之前开发上一直往浏览器打印都是使用console.log进行输出,接触到console.table之后,感觉这个输出还是挺ok的,n能输出打印一张表格: 用法: console.table() 这个方法接收一个强制的参数,它必须是一个数组...

    之前开发上一直往浏览器打印都是使用console.log进行输出,接触到console.table之后,感觉这个输出还是挺ok的,n能输出打印一张表格:

    用法:

    console.table()

    这个方法接收一个强制的参数,它必须是一个数组或者是一个对象,还可以接受一个额外的参数描述表格的列数。

    它把数据以table的形式打印出来, 在数组中的每一个元素(或对象中可枚举的属性)将会以行的形式显示在table中。

    table的第一列是index。如果数据是一个数组,那么值就是索引。 如果数据是一个对象,那么它的值就是属性名称。 注意(在FireFox中)console.table 被限制了不能显示超过1000行(第一行用来标记索引)。

    1、打印数组

    这里写图片描述

    2、打印json

    展开全文
  • 主要介绍了JS表格组件神器bootstrap table,bootstrap table界面采用扁平化的风格,用户体验比较好,更好兼容各种客户端,需要了解更多bootstrap table的朋友可以参考下
  • 主要以实例的方式为大家再次介绍了JS表格组件神器bootstrap table,bootstrap table界面采用扁平化的风格,用户体验比较好,更好兼容各种客户端,需要了解更多bootstrap table的朋友可以参考下
  • Lua基础之table详解

    万次阅读 多人点赞 2015-12-13 15:46:09
    概要:1.table特性;2.table的构造;3.table常用函数;4.table遍历;5.table面向对象

    概要:1.table特性;2.table的构造;3.table常用函数;4.table遍历;5.table面向对象

     原文地址:http://blog.csdn.net/dingkun520wy/article/details/50231603

    1.table特性

    table是一个“关联数组”,数组的索引可以是数字或者是字符串,所有索引值都需要用 "[""]" 括起来;如果是字符串,还可以去掉引号和中括号; 即如果没有[]括起,则认为是字符串索引

    table 的默认初始索引一般以 1 开始,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;

    table 的变量只是一个地址引用,对 table 的操作不会产生数据影响

    table 不会固定长度大小,有新数据插入时长度会自动增长

    table 里保存数据可以是任何类型,包括function和table

    table所有元素之间,总是用逗号 "" 隔开;


    2.table的构造

    创建table

    t = {}			--定义一个空表
    t["jun"] = 6	--字符串key值的属性
    t[1] = 1		--数字key值的属性
    t.jun = 16		--字符串key值的简写
    t.test = {num=28,str="test"}	--属性可以是table
    print(t.test.num)				--输出28
    t.testFunction = function() print("函数") end  --属性可以是function
    t.testFunction()				--调用函数
    t:testFunction()				--同上
    
    --上面的table还可以这么写
    t=
    {
    	1,
    	jun = 6,
    	test=
    	{
    		num = 28,
    		str = "test",
    	}
    	testFunction = function() print("函数") end,
    }


    3.table常用函数

    table.pack(...)

    获取一个索引从 1 开始的参数表 table,并会对这个 table 预定义一个字段 n,表示该表的长度

    function table_pack(param, ...)
        local arg = table.pack(...)
        print("this arg table length is", arg.n)
        for i = 1, arg.n do
            print(i, arg[i])
        end
    end
     
    table_pack("test", "param1", "param2", "param3")

    table.concat(table, sep,  start, end)

    table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。除了table外, 其他的参数都不是必须的, 分隔符的默认值是空字符, start的默认值是1, end的默认值是数组部分的总长.

    sep, start, end这三个参数是顺序读入的, 所以虽然它们都不是必须参数, 但如果要指定靠后的参数, 必须同时指定前面的参数.

    lua 中字符串的存储方式与 C 不一样,lua 中的每个字符串都是单独的一个拷贝,拼接两个字符串会产生一个新的拷贝,如果拼接操作特别多,就会影响性能,所以对于密集型的字符并接,table.concat 比用 ".." 连接更高效。

    local tbl = {"apple", "pear", "orange", "grape"}
     
    print(table.concat(tbl))
     
    print(table.concat(tbl, "、"))
     
    print(table.concat(tbl, "、", 2))
     
    print(table.concat(tbl, "、", 2, 3))

    table.unpack(table, start, end)

    用于返回 table 里的元素,参数 start 是开始返回的元素位置,默认是 1,参数 end 是返回最后一个元素的位置,默认是 table 最后一个元素的位置,参数 start、end 都是可选

    local tbl = {"apple", "pear", "orange", "grape"}
    print(table.unpack(tbl))
     
    local a, b, c, d = table.unpack(tbl)
    print(a, b, c, d)
     
    print(table.unpack(tbl, 2))
    print(table.unpack(tbl, 2, 3))
    table.maxn(table)

    返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.

    tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
    print(#tbl)           --3因为26和之前的数字不连续, 所以不算在数组部分内
    print(table.maxn(tbl))	 --26
    tbl[91.32] = true
    print(table.maxn(tbl))	--91.32


    table.getn(table)

    返回table中元素的个数

    t1 = {1, 2, 3, 5};
    print(getn(t1))
    --4


    table.insert(table, pos, value)

    用于向 table 的指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.

    local tbl = {"apple", "pear", "orange", "grape"}
     
    table.insert(tbl, "watermelon")
    print(table.concat(tbl, "、"))
     
    table.insert(tbl, 2, "watermelon")
    print(table.concat(tbl, "、"))

    table.remove(table, pos)

    删除并返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起,并且参数 pos 的类型只能是数字 number 类型。

    local tbl = {"apple", "pear", "orange", "grape"}
    table.remove(tbl, 2)
    print(table.concat(tbl, "、"))
     
    table.remove(tbl)
    print(table.concat(tbl, "、"))

    table.sort(table, comp)

    用于对 table 里的元素作排序操作,参数 comp 是一个排序对比函数,它有两个参数 param1、param2,如果 param1 排在 param2 前面,那么排序函数返回 true,否则返回 false。参数 comp 可选,缺省 comp 的情况下是对表作升序排序。

    local tbl = {"apple", "pear", "orange", "grape"}
    local sort_func1 = function(a, b) return a > b end
    table.sort(tbl, sort_func1)
    print(table.concat(tbl, "、"))
     
    local sort_func2 = function(a, b) return a < b end
    table.sort(tbl, sort_func2)
    print(table.concat(tbl, "、"))
    local tbl = {"apple", "pear", "orange", "grape"}
    table.sort(tbl)
    print(table.concat(tbl, "、"))


    table.foreachi(table, function(i, v))

    会期望一个从 1(数字 1)开始的连续整数范围,遍历table中的key和value逐对进行function(i, v)操作

    t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
    table.foreachi(t1, function(i, v) print (i, v) end) ; 
    --结果1 2
    --2 4
    --3 6
    --4 8
    --5 10
    --6 12

    table.foreach(table, function(i, v))

    与foreachi不同的是,foreach会对整个表进行迭代

    t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
    table.foreach(t1, function(i, v) print (i, v) end) ;
    --[[
    输出结果:
    1 2
    2 4
    3 6
    4 8
    5 10
    6 12
    web hello lua
    language Lua
    version 5
    ]]


    4.table遍历

    for key, value in pairs(tbtest) do      
         print(value)
    end  
    
    这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。 for key, value in ipairs(tbtest) do
         print(value)
    end  
    这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。

    5.table面向对象

    和编译型的面向对象语言不同,在lua中不存在类的定义这样一个概念,不管是类的定义还是类的实例都需要通过lua table来模拟。

    Test = {jun = 0}
    function Test.withdraw(self,v)
         self.jun = self.jun - v
     end
     --下面是代码的调用:
    a = Test
    a.withdraw(a,10)
    Lua提供了一种更为便利的语法,即将点 (.) 替换为冒号 (:) ,这样可以在定义和调用时均隐藏self参数
    function Test:withdraw(v)
         self.jun = self.jun - v
     end
    a = Test
    a:withdraw(10)

    Lua 没有类的概念,不过可以通过元表(metatable)来实现与原型 prototype 类似的功能,而 prototype 与类的工作机制一样,都是定义了特定对象行为。Lua 里的原型特性主要使用元表的 __index 事件来实现,这样当调用对象没定义的方法时,会向其元表的 __index 键(事件)查找。例如有 a 和 b 两个对象,想让 b 作为 a 的原型 prototype,只需要把 b 设置为 a 元表的 __index 值就行:当对象 a 调用任何不存在的成员都会到对象 b 中查找,a 可以拥有或调用 b 的属性或方法,从某种意义上看,b 可以看作是一个类,a 是 b 的对象。

    --[[
    在这段代码中,我们可以将Account视为class的声明,如Java中的:
    public class Account 
    {
        public float balance = 0;
        public Account(Account o);
        public void deposite(float f);
    }
    --]]
    --这里balance是一个公有的成员变量。
    Account = {balance = 0}
    
    --new可以视为构造函数
    function Account:new(o)
        o = o or {} --如果参数中没有提供table,则创建一个空的。
        --将新对象实例的metatable指向Account表(类),这样就可以将其视为模板了。
        setmetatable(o,self)
        --在将Account的__index字段指向自己,以便新对象在访问Account的函数和字段时,可被直接重定向。
        self.__index = self
        --最后返回构造后的对象实例
        return o
    end
    
    --deposite被视为Account类的公有成员函数
    function Account:deposit(v)
        --这里的self表示对象实例本身
        self.balance = self.balance + v
    end
    
    --下面的代码创建两个Account的对象实例
    
    --通过Account的new方法构造基于该类的示例对象。
    a = Account:new()
    --[[
    这里需要具体解释一下,此时由于table a中并没有deposite字段,因此需要重定向到Account,
    同时调用Account的deposite方法。在Account.deposite方法中,由于self(a对象)并没有balance
    字段,因此在执行self.balance + v时,也需要重定向访问Account中的balance字段,其缺省值为0。
    在得到计算结果后,再将该结果直接赋值给a.balance。此后a对象就拥有了自己的balance字段和值。
    下次再调用该方法,balance字段的值将完全来自于a对象,而无需在重定向到Account了。
    --]]
    a:deposit(100.00)
    print(a.balance) --输出100
    
    b = Account:new()
    b:deposit(200.00)
    print(b.balance) --输出200


    继承

    --需要说明的是,这段代码仅提供和继承相关的注释,和类相关的注释在上面的代码中已经给出。
    Account = {balance = 0}
    
    function Account:new(o)
        o = o or {}
        setmetatable(o,self)
        self.__index = self
        return o
    end
    
    function Account:deposit(v)
        self.balance = self.balance + v
    end
    
    function Account:withdraw(v)
        if v > self.balance then
            error("Insufficient funds")
        end
        self.balance = self.balance - v
    end
    
    --下面将派生出一个Account的子类,以使客户可以实现透支的功能。
    SpecialAccount = Account:new()  --此时SpecialAccount仍然为Account的一个对象实例
    
    --派生类SpecialAccount扩展出的方法。
    --下面这些SpecialAccount中的方法代码(getLimit/withdraw),一定要位于SpecialAccount被Account构造之后。
    function SpecialAccount:getLimit()
        --此时的self将为对象实例。
        return self.limit or 0
    end
    
    --SpecialAccount将为Account的子类,下面的方法withdraw可以视为SpecialAccount
    --重写的Account中的withdraw方法,以实现自定义的功能。
    function SpecialAccount:withdraw(v)
        --此时的self将为对象实例。
        if v - self.balance >= self:getLimit() then
            error("Insufficient funds")
        end
        self.balance = self.balance - v
    end
    
    --在执行下面的new方法时,table s的元表已经是SpecialAccount了,而不再是Account。
    s = SpecialAccount:new{limit = 1000.00}
    --在调用下面的deposit方法时,由于table s和SpecialAccount均未提供该方法,因此访问的仍然是
    --Account的deposit方法。
    s:deposit(100)
    
    
    --此时的withdraw方法将不再是Account中的withdraw方法,而是SpecialAccount中的该方法。
    --这是因为Lua先在SpecialAccount(即s的元表)中找到了该方法。
    s:withdraw(200.00)
    print(s.balance) --输出-100

    私密性

    私密性对于面向对象语言来说是不可或缺的,否则将直接破坏对象的封装性。Lua作为一种面向过程的脚本语言,更是没有提供这样的功能,然而和模拟支持类与继承一样,我们仍然可以在Lua中通过特殊的编程技巧来实现它,这里我们应用的是Lua中的闭包函数。

    --这里我们需要一个闭包函数作为类的创建工厂
    function newAccount(initialBalance)
        --这里的self仅仅是一个普通的局部变量,其含义完全不同于前面示例中的self。
        --这里之所以使用self作为局部变量名,也是为了方便今后的移植。比如,以后
        --如果改为上面的实现方式,这里应用了self就可以降低修改的工作量了。
        local self = {balance = initialBalance} --这里我们可以将self视为私有成员变量
        local withdraw = function(v) self.balance = self.balance - v end
        local deposit = function(v) self.balance = self.balance + v end
        local getBalance = function() return self.balance end
        --返回对象中包含的字段仅仅为公有方法。事实上,我们通过该种方式,不仅可以实现
        --成员变量的私有性,也可以实现方法的私有性,如:
        --local privateFunction = function() --do something end
        --只要我们不在输出对象中包含该方法的字段即可。
        return {withdraw = withdraw, deposit = deposit, getBalance = getBalance}
    end
    
    --和前面两个示例不同的是,在调用对象方法时,不再需要self变量,因此我们可以直接使用点(.),
    --而不再需要使用冒号(:)操作符了。
    accl = newAccount(100.00)
    --在函数newAccount返回之后,该函数内的“非局部变量”表self就不再能被外部访问了,只能通过
    --该函数返回的对象的方法来操作它们。
    accl.withdraw(40.00)
    print(acc1.getBalance())










    展开全文
  • Python pivot_table详解

    千次阅读 2019-08-17 16:46:47
    再来介绍pandas库里的pivot_table()函数。pivot_table()与pivot()比较类似。其官方定义如下所示: DataFrame.pivot_table(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=...
  • R语言data.table详解

    千次阅读 2019-05-04 18:31:06
    1.如何结合管道函数使用data.table提高代码效率 2.如何高效导入大数据文件 3.如何使用for loop进行文件批处理 管道函数与data.table 管道函数的使用方法: 日常项目中常用的管道函数有以下三种: %in%:表示...
  • oracle alter table详解

    万次阅读 2011-04-14 17:18:00
    //建测试表 create table dept( deptno number(3) primary key, dname varchar2(10), loc varchar2(13) ); create table employee_info( empno number(3), deptno number(3)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 124,242
精华内容 49,696
关键字:

table详解