- 第1个
- 第2个
- 第3个
- 第4个
- 第5个
- 第6个
- 第7个
权限系统
RBAC基本概念
基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。
RBAC 支持三个著名的安全原则:最小权限原则、责任分离原则和数据抽象原则
- 最小权限原则之所以被 RBAC 所支持,是因为 RBAC 可以将其角色配置成其完成任务所需要的最小的权限集。
- 责任分离原则可以通过调用相互独立互斥的角色来共同完成敏感的任务而体现,比如要求同一个计账员和财务管理员共同参与同一过账。
- 数据抽象可以通过权限的抽象来体现,如账务操作用借款、存款等抽象权限,而不用操作系统提供的典型读、写、执行权限。然而这些原则必须通过 RBAC 各部件的详细配置才能得以体现。
CSS-Web高级程序设计
1. 有关 XHTML 和 CSS 的最佳实例
1.1. 把结构和表现标记硬挤到一起
1.2. 学习并热衷于使用标记
1.2.1. XHTML:新热点
HTML 有块级元素(如div、p、table等)和内联元素(如a、em、strong等)之分,内联元素永远不能包含块级元素。
XHTML 特点
- DOCTYPE 声明
- 保持标记具有良好的架构
- 关闭每一个元素
- 把元素和属性值设置为小写
- 必须为每个属性指定一个值
1.2.2. 从结构提取样式
1.3. CSS:添加样式层
1.3.1. 更好的了解选择符
- 1.类型选择符
1 | h1 { |
- 2.通配选择符
1 | * { |
- 3.后代选择符
1 | ul em { |
- 4.类选择符
1 | .text { |
- 5.id选择符
1 | h1 #page-title { |
1.3.2. 其他选择符
- 1.子选择符
1 | body>p { |
大于符号 > 指示用户代理选择子一级的所有 p 元素而不是所有后代。
- 2.属性选择符
1.3.3. 多重声明组合
1.3.4. 对选择符进行分组
1.3.5. 继承
- 1.检查元素的层次
不是氖属性都可以继承,外边距和内边距就是两个例外。这些属性只能单独应用于某个元素,而不能被其后代继承。
- 2.重写继承
1.3.6. 综合应用
1.4. 了解层叠
1.4.1. 探寻样式来源
样式表三个方面来源
- 用户代理
- 用户
- 作者
1.4.2. 根据优先级排序
把理论应用于实践
基于可靠浏览器进行构建
CSS hack:基于不同浏览器写不同CSS代码的过程
理性对待 hack
Google 的 blogger.com 翻转器和设计思想
设计人员访谈
CSS 驱动的翻转器
改变链接的颜色和背景色(简单)
1 | <style> |
改变链接的颜色和背景色(复杂)
JQuery源码解读
AMD规范
什么是ADM
全称是Asynchronous Module Definition,即异步模块加载机制。
从它的规范描述页面看,AMD很短也很简单,但它却完整描述了模块的定义,依赖关系,引用关系以及加载机制。从它被requireJS,NodeJs,Dojo,JQuery使用也可以看出它具有很大的价值,没错,JQuery近期也采用了AMD规范。在这篇文章中,我们就将介绍AMD的性质,用法,优势以及应用场景。从AMD中我们也能学习到如何在更高层面去设计自己的前端应用。
AMD构成
作为一个规范,只需定义其语法API,而不关心其实现。AMD规范简单到只有一个API,即define函数:
1 | define([module-name?],[array-of-dependencies?],[module-factory-or-object]) |
其中:
- module-name:模块标识,可以省略。
- array-of-dependencies:所依赖的模块,可以省略。
- module-factory-or-object:模块的实现,或者一个JavaScript对象。
从这个define函数AMD中的A:Asynchronous,我们也不难想到define函数具有的另外一个性质,异步性。当define函数执行时,它首先会异步的去调用第二个参数中列出的依赖模块,当所有的模块被载入完成之后,如果第三个参数是一个回调函数则执行,然后告诉系统模块可用,也就通知了依赖于自己的模块自己已经可用。
匿名模块
define 方法允许你省略第一个参数,这样就定义了一个匿名模块,这时候模块文件的文件名就是模块标识。
jQuery 对象与 dom 对象转换
jQuery 转换成 dom 对象
- [index]
1 | var $j =$("#j") ; //jQuery对象 |
- .get(index)
1 | var $j=$("#v"); //jQuery对象 |
dom 对象转换成 jQuery
对于已经是一个DOM对象,只需要用$()把DOM对象包装起来,就可以获得一个jQuery对象了。
1 | var v=document.getElementById("v"); //DOM对象 |
$.data()
$.data(dom对象, ‘tree’);
$.extend()的深拷贝和浅拷贝详细讲解
1 | 语法:jQuery.extend([deep],target,object1[,objectN]) |
- 浅拷贝(false默认):如果第二个参数对象有的属性第一个参数对象也有,那么不会进行相同参数内部比较,直接将第一个对象的相同参数覆盖。
- 深拷贝:如果第二个参数对象有的属性第一个参数对象也有,还要继续在这个相同的参数向下一层找,比较相同参数的对象中是否还有一样的属性,如果有,将其继承到第一个对象,如果没有,则覆盖。
1 | var object1 = { |
jQuery 的应用
$.each的用法
1 | $.each(parentData,function(index,childData){ |
checkbox
1、获取单个checkbox选中项(三种写法)
1 | $("input:checkbox:checked").val() |
2、 获取多个checkbox选中项
1 | $('input:checkbox').each(function() { |
3、设置第一个checkbox 为选中值
1 | $('input:checkbox:first').attr("checked",'checked'); |
4、设置最后一个checkbox为选中值
1 | $('input:radio:last').attr('checked', 'checked'); |
5、根据索引值设置任意一个checkbox为选中值
1 | $('input:checkbox).eq(索引值).attr('checked', 'true');索引值=0,1,2.... |
6、选中多个checkbox同时选中第1个和第2个的checkbox
1 | $('input:radio').slice(0,2).attr('checked','true'); |
7、根据Value值设置checkbox为选中值
1 | $("input:checkbox[value='1']").attr('checked','true'); |
8、删除Value=1的checkbox
1 | $("input:checkbox[value='1']").remove(); |
9、删除第几个checkbox
1 | $("input:checkbox").eq(索引值).remove();索引值=0,1,2.... |
10、遍历checkbox
1 | $('input:checkbox').each(function (index, domEle) { |
11、全部选中
1 | $('input:checkbox').each(function() { |
12、全部取消选择
1 | $('input:checkbox').each(function () { |
Emmet语法
1. Emmet Html 语法
1.1. 元素
- 输入元素名称,自动生成标签,如 div
1 | <div></div> |
- 输入 ! 或 html:5 自动补全基本结构
1 |
|
1.2. 嵌套操作
- child:使用 “>” 生成子元素
div>ul>li
1 | <div> |
- Sibling: 使用符号 “+” 生成兄弟元素
div+p+bq
1 | <div></div> |
- Climb-up:使用 “^” 生成父元素,与 “>” 相反
div+div>p>span+em^bq
1 | <div></div> |
- Multiplication:使用 “*” 操作符生成多个元素
div>ul>li*5
1 | <div> |
- Grouping:使用 “()” 操作符将元素分组,实现更复杂的简写任务
div>(header>ul>li*2)+footer>p
1 | <div> |
1.3. 属性操作
- id 与 class
简写时,元素与 id 属性值之间用 “#” 分隔,与 class 属性值之间用 “.” 分隔。
div#header+div.page+div#footer.class1.class2.class3
1 | <div id="header"></div> |
- 其它属性
使用 [attr] 标记添加其他属性。
1 | <td title="hello" colspan="3"></td> |
注意:
- 方括号中可添加任意数量的属性
- 不给定属性值,则属性值为""。td[colspan title]将得到
1
<td colspan="" title=""></td>
- 属性值可用单引号或双引号,输出统一为双引号
- 如果属性值中没有空格,则引号可省略
- 为条目编号
1 | li.item$*3 |
可在 “$” 后添加 “@n” 修改编号的起始值为n。
1 | li.item$@3*3 |
可在 “$” 后添加 “@-” 修改编号的方向。
1 | li.item$@-3*3 |
1.4. 添加文本
使用花括号 “{}” 操作符为元素添加文本节点。
1 | // before |
因为文本也是节点,所以 a[href=me.htm]{click me} 与 a[href=me.htm]>{click me} 等价。
但有多个元素时则要注意。
1 | // before |
打造自己的linqProvider
认识表达式树
表达式树是一种抽象语法或者数据结构,通过解析表达式目录可以实现一些特定功能。
如何构造表达式树,最简单的方法莫过于使用 Lambda 表达式
1 | Expression<Func<int,int,int> expression = (a,b) => a*b +2; |
在我们将Lambda表达式指定给Expression<TDelegate>类型的变量(参数)时,编译器将会发出生成表达式目录树的指令,如上面这段代码中的Lambda表达式(a, b) => a * b + 2将创建一个表达式目录树,它表示的是一种数据结构,即我们把一行代码用数据结构的形式表示了出来,具体来说最终构造出来的表达式目录树形状如下图所示:
这里每一个节点都表示一个表达式,可能是一个二元运算,也可能是一个常量或者参数等,如上图中的ParameterExpression就是一个参数表达式,ConstantExpression是一个常量表达式,BinaryExpression是一个二元表达式。我们也可以在Visual Studio中使用Expression Tree Visualizer来查看该表达式目录树:
.NET Framework到底提供的表达式类型:
它们都继承于抽象的基类Expression,而泛型的Expression
1 | static void Main(string[] args) |
对于一个表达式目录树来说,它有几个比较重要的属性:
- Body:指表达式的主体部分;
- Parameters:指表达式的参数;
- NodeType:指表达式的节点类型,如在上面的例子中,它的节点类型是Lambda;
- Type:指表达式的静态类型,在上面的例子中,Type为Fun<int,int,int>。
表达式目录树与委托
大家可能经常看到如下这样的语言,其中第一句是直接用Lambda表达式来初始化了Func委托,而第二句则使用Lambda表达式来构造了一个表达式目录树,它们之间的区别是什么呢?
1 | static void Main(string[] args) |
其实看一下IL就很明显,其中第一句直接将Lambda表达式直接编译成了IL,如下代码所示:
1 | .method private hidebysig static void Main(string[] args) cil managed |
而第二句,由于告诉编译器是一个表达式目录树,所以编译器会分析该Lambda表达式,并生成表示该Lambda表达式的表达式目录树,即它与我们手工创建表达式目录树所生成的IL是一致的,如下代码所示,此处为了节省空间省略掉了部分代码:
1 | .method private hidebysig static void Main(string[] args) cil managed |
现在相信大家都看明白了,这里讲解它们的区别主要是为了加深大家对于表达式目录树的区别。
执行表达式目录树
前面已经可以构造出一个表达式目录树了,现在看看如何去执行表达式目录树。我们需要调用Compile方法来创建一个可执行委托,并且调用该委托,如下面的代码:
1 | static void Main(string[] args) |
运行后输出的结果:
这里我们只要简单的调用Compile方法就可以了,事实上在.NET Framework中是调用了一个名为ExpressionCompiler的内部类来做表达式目录树的执行(注意此处的Compiler不等同于编译器的编译)。另外,只能执行表示Lambda表达式的表达式目录树,即LambdaExpression或者Expression<TDelegate>类型。如果表达式目录树不是表示Lambda表达式,需要调用Lambda方法创建一个新的表达式。如下面的代码:
1 | static void Main(string[] args) |
访问与修改表达式目录树
在.NET Framework中,提供了一个抽象的表达式目录树访问类ExpressionVisitor,但它是一个internal的,我们不能直接访问。幸运的是,在MSDN中微软给出了ExpressionVisitor类的实现,我们可以直接拿来使用。该类是一个抽象类,微软旨在让我们在集成ExpressionVisitor的基础上,实现自己的表达式目录树访问类。现在我们来看简单的表达式目录树:
1 | static void Main(string[] args) |
输出后为:
现在我们想要修改表达式目录树,让它表示的Lambda表达式为(a,b)=>(a - (b * 2)),这时就需要编写自己的表达式目录树访问器,如下代码所示:
1 | public class OperationsVisitor : ExpressionVisitor |
使用表达式目录树访问器来修改表达式目录树,如下代码所示:
1 | static void Main(string[] args) |
结果如下:
似乎我们是修改表达式目录树,其实也不全对,我们只是修改表达式目录树的一个副本而已,因为表达式目录树是不可变的,我们不能直接修改表达式目录树,看看上面的OperationsVisitor类的实现大家就知道了,在修改过程中复制了表达式目录树的节点。
为什么需要表达式目录树
就拿LINQ to SQL为例
当我们在C#语言中编写一个查询表达式时,它将返回一个IQueryable类型的值,在该类型中包含了两个很重要的属性Expression和Provider,如下面的代码:
我们编写的查询表达式,将封装为一种抽象的数据结构,这个数据结构就是表达式目录树,当我们在使用上面返回的值时,编译器将会以该值所期望的方式进行翻译,这种方式就是由Expression和Provider来决定。可以看到,这样将会非常的灵活且具有良好的可扩展性,有了表达式目录树,可以自由的编写自己的Provider,去查询我们希望的数据源。经常说LINQ为访问各种不同的数据源提供了一种统一的编程方式,其奥秘就在这里。然而需要注意的是LINQ to Objects并不需要任何特定的LINQ Provider,因为它并不翻译为表达式目录树,后面会说到这一点。
IEnumerable<T>接口
1 | static void Main(string[] args) |
为什么在LINQ to Objects中返回的是IEnumerable
在LINQ to Objects中查询表达式或者Lambda表达式并不翻译为表达式目录树,因为LINQ to Objects查询的都是实现了IEnmerable
1 | public interface IEnumerable<T>:Ienumerable |
至于LINQ to Objects中所有的标准查询操作符都是通过扩展方法来实现的,它们在抽象类Enumerable中定义,如其中的Where扩展方法如下代码所示:
1 | public static class Enumerable |
注意到这里方法的参数Func
同样还有一点需要说明的是,在IEnumerable
1 | static void Main(string[] args) |
运行这段代码,虽然它的输出结果与上面的示例完全相同,但它们查询的机制却完全不同:
IQueryable<T>接口
这里有两个很重要的属性Expression和Provider,分别表示获取与IQueryable 的实例关联的表达式目录树和获取与此数据源关联的查询提供程序,我们所有定义在查询表达式中方法调用或者Lambda表达式都将由该Expression属性表示,而最终会由Provider表示的提供程序翻译为它所对应的数据源的查询语言,这个数据源可能是数据库,XML文件或者是WebService等。该接口非常重要,在我们自定义LINQ Provider中必须要实现这个接口。同样对于IQueryable的标准查询操作都是由Queryable中的扩展方法来实现的,如下代码所示:
1 | public static class Queryable |
最后还有一点,如果我们定义的查询需要支持Orderby等操作,还必须实现IOrderedQueryable<T> 接口,它继承自IQueryable<T>,如下图所示:
IQueryProvider接口
在认识了IQueryable接口之后,我们再来看看在自定义LINQ Provider中另一个非常重要的接口IQueryProvider。它的定义如下图所示:
看到这里两组方法的参数,其实大家已经可以知道,Provider负责执行表达式目录树并返回结果。如果是LINQ to SQL的Provider,则它会负责把表达式目录树翻译为T-SQL语句并并传递给数据库服务器,并返回最后的执行的结果;如果是一个Web Service的Provider,则它会负责翻译表达式目录树并调用Web Service,最终返回结果。
这里四个方法其实就两个操作CreateQuery和Execute(分别有泛型和非泛型),CreateQuery方法用于构造一个 IQueryable
扩展LINQ的两种方式
通过前面的讲解,我们可以想到,对于LINQ的扩展有两种方式,一是借助于LINQ to Objects,如果我们所做的查询直接在.NET代码中执行,就可以实现IEnumerable
1 | public class MyData<T> : IEnumerable<T> |
第二种扩展LINQ的方式当然就是自定义LINQ Provider了,我们需要实现IQueryable
1 | public class QueryableData<TData> : IQueryable<TData> |
上面这两个接口都没有完成,这里只是示意性的代码,如果实现了这两个接口,我们就可以像下面这样使用了(当然这样的使用是没有意义的,这里只是为了演示):
1 | static void Main(string[] args) |
现在再来分析一下这个执行过程,首先是实例化QueryableData