通俗易懂,C#如何安全、高效地玩轉任何種類的內存之Span。 給萌新的Flexbox簡易入門教程 淺談Quartz定時任務調度 淺談JavaScript之Event(上篇) 探索JavaScript數

通俗易懂,C#如何安全、高效地玩轉任何種類的內存之Span。

 

前言

做爲.net程序員,使用過指針,寫過不安全代碼嗎?javascript

爲何要使用指針,何時須要使用它?php

若是能很好地回答這兩個問題,那麼就能很好地理解今天了主題了。C#構建了一個託管世界,在這個世界裏,只要不寫不安全代碼,不操做指針,那麼就能得到.Net相當重要的安全保障,即什麼都不用擔憂;那若是咱們須要操做的數據不在託管內存中,而是來自於非託管內存,好比位於本機內存或者堆棧上,該如何編寫代碼支持來自任意區域的內存呢?這個時候就須要寫不安全代碼,使用指針了;而如何安全、高效地操做任何類型的內存,一直都是C#的痛點,今天咱們就來談談這個話題,講清楚 What、How 和 Why ,讓你知其然,更知其因此然,之後有人問你這個問題,就讓他看這篇文章吧,呵呵。css

what - 痛點是什麼?

回答這個問題前,先總結一下如何用C#操做任何類型的內存:html

  1. 託管內存(managed memory )前端

    var mangedMemory = new Student();

    很熟悉吧,只需使用new操做符就分配了一塊託管內存,並且還不用手工釋放它,由於它是由垃圾收集器(GC)管理的,GC會智能地決定什麼時候釋放它,這就是所謂的託管內存。默認狀況下,GC經過複製內存的方式分代管理小對象(size < 85000 bytes),而專門爲大對象(size >= 85000 bytes)開闢大對象堆(LOH),管理大對象時,並不會複製它,而是將其放入一個列表,提供較慢的分配和釋放,並且很容易產生內存碎片。java

  2. 棧內存(stack memory )css3

    unsafe{ var stackMemory = stackalloc byte[100]; }

    很簡單,使用stackalloc關鍵字很是快速地就分配好了一塊內存,也不用手工釋放,它會隨着當前做用域而釋放,好比方法執行結束時,就自動釋放了。棧內存的容量很是小( ARM、x86 和 x64 計算機,默認堆棧大小爲 1 MB),當你使用棧內存的容量大於1M時,就會報StackOverflowException 異常 ,這一般是致命的,不能被處理,並且會當即幹掉整個應用程序,因此棧內存通常用於須要小內存,可是又不得不快速執行的大量短操做,好比微軟使用棧內存來快速地記錄ETW事件日誌。c++

  3. 本機內存(native memory )git

    IntPtr nativeMemory0 = default(IntPtr), nativeMemory1 = default(IntPtr); try { unsafe { nativeMemory0 = Marshal.AllocHGlobal(256); nativeMemory1 = Marshal.AllocCoTaskMem(256); } } finally { Marshal.FreeHGlobal(nativeMemory0); Marshal.FreeCoTaskMem(nativeMemory1); }

    經過調用方法Marshal.AllocHGlobalMarshal.AllocCoTaskMem來分配非託管內存,非託管就是垃圾回收器(GC)不可見的意思,而且還須要手工調用方法Marshal.FreeHGlobal or Marshal.FreeCoTaskMem 釋放它,千萬不能忘記,否則就產生內存碎片了。程序員

拋磚引玉 - 痛點

首先咱們設計一個解析完整或部分字符串爲整數的API,以下

public interface IntParser { // allows us to parse the whole string. int Parse(string managedMemory); // allows us to parse part of the string. int Parse(string managedMemory, int startIndex, int length); // allows us to parse characters stored on the unmanaged heap / stack. unsafe int Parse(char* pointerToUnmanagedMemory, int length); // allows us to parse part of the characters stored on the unmanaged heap / stack. unsafe int Parse(char* pointerToUnmanagedMemory, int startIndex, int length); }

從上面能夠看到,爲了支持解析來自任何內存區域的字符串,一共寫了4個重載方法。

接下來在來設計一個支持複製任何內存塊的API,以下

public interface MemoryblockCopier { void Copy<T>(T[] source, T[] destination); void Copy<T>(T[] source, int sourceStartIndex, T[] destination, int destinationStartIndex, int elementsCount); unsafe void Copy<T>(void* source, void* destination, int elementsCount); unsafe void Copy<T>(void* source, int sourceStartIndex, void* destination, int destinationStartIndex, int elementsCount); unsafe void Copy<T>(void* source, int sourceLength, T[] destination); unsafe void Copy<T>(void* source, int sourceStartIndex, T[] destination, int destinationStartIndex, int elementsCount); }

腦殼蒙圈沒,之前C#操縱各類內存就是這麼複雜、麻煩。經過上面的總結如何用C#操做任何類型的內存,相信大多數同窗都可以很好地理解這兩個類的設計,但我內心是沒底的,由於使用了不安全代碼和指針,這些操做是危險的、不可控的,根本沒法得到.net相當重要的安全保障,而且可能還會有難以預估的問題,好比堆棧溢出、內存碎片、棧撕裂等等,微軟的工程師們早就意識到了這個痛點,因此span誕生了,它就是這個痛點的解決方案

how - span如何解決這個痛點?

先來看看,如何使用span操做各類類型的內存(僞代碼):

  1. 託管內存(managed memory )

    var managedMemory = new byte[100]; Span<byte> span = managedMemory;
  2. 棧內存(stack memory )

    var stackedMemory = stackalloc byte[100]; var span = new Span<byte>(stackedMemory, 100);
  3. 本機內存(native memory )

    var nativeMemory = Marshal.AllocHGlobal(100); var nativeSpan = new Span<byte>(nativeMemory.ToPointer(), 100);

span就像黑洞同樣,可以吸取來自於內存任意區域的數據,實際上,如今,在.Net的世界裏,Span就是全部類型內存的抽象化身,表示一段連續的內存,它的API設計和性能就像數組同樣,因此咱們徹底能夠像使用數組同樣地操做各類內存,真的是太方便了。

如今重構上面的兩個設計,以下:

public interface IntParser { int Parse(Span<char> managedMemory); int Parse(Span<char>, int startIndex, int length); } public interface MemoryblockCopier { void Copy<T>(Span<T> source, Span<T> destination); void Copy<T>(Span<T> source, int sourceStartIndex, Span<T> destination, int destinationStartIndex, int elementsCount); }

上面的方法根本不關心它操做的是哪一種類型的內存,咱們能夠自由地從託管內存切換到本機代碼,再切換到堆棧上,真正的享受玩轉內存的樂趣。

why - 爲何span能解決這個痛點?

淺析span的工做機制

先來窺視一下源碼:

我已經圈出的三個字段:偏移量、索引、長度(使用過ArraySegment<byte> 的同窗可能已經大體理解到設計的精髓了),這就是它的主要設計,當咱們訪問span表示的總體或部份內存時,內部的索引器會按照下面的算法運算指針(僞代碼):

ref T this[int index] { get => ref ((ref reference + byteOffset) + index * sizeOf(T)); }

整個變化的過程,如圖所示:

上面的動畫很是清楚了吧,舊span整合它的引用和偏移成新的span的引用,整個過程並無複製內存,而是直接返回引用,所以性能很是高,由於新span得到並更新了引用,因此垃圾回收器(GC)知道如何處理新的span,從而得到了.Net相當重要的安全保障,而這些都是span內部默默完成的,開發人員根本不用擔憂,非託管世界依然美好。
正是因爲span的高性能,目前不少基礎設施都開始支持span,甚至使用span進行重構,好比:System.String.Substring方法,咱們都知道此方法是很是消耗性能的,首先會建立一個新的字符串,而後在複製原始字符串的字符集給它,而使用span能夠實現Non-Allocating、Zero-coping,下面是我作的一個基準測試:

使用String.SubString和Span.Slice分別截取長度爲10和1000的字符串的前一半,從指標Mean能夠看出方法SubString的耗時隨着字符串長度呈線性增加,而Slice幾乎保持不變;從指標Allocated Memory/Op能夠看出,方法Slice並無被分配新的內存,實踐出真知,能夠預見Span將來將會成爲.Net下編寫高性能應用程序的重要積木,應用前景也會很是地廣,微服務、物聯網都是它發光發熱的好地方。

基準測試示例

總結

看完本篇博客,應該對Span的What、Why、How瞭如指掌了,那麼個人目的就達到了,不懂的同窗能夠多讀幾遍,下一篇,我將會暢談Span的應用場景、優缺點,讓你們可以安全高效地使用好它,你們也能夠在評論留言本身的應用場景,我會在寫下一篇博客時多多參考。

最後

若是有什麼疑問和看法,歡迎評論區交流。
若是你以爲本篇文章對您有幫助的話,感謝您的【推薦】。
若是你對高性能編程感興趣的話能夠關注我,我會按期的在博客分享個人學習心得。
歡迎轉載,請在明顯位置給出出處及連接

延伸閱讀

https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md

https://msdn.microsoft.com/en-us/magazine/mt814808

https://github.com/dotnet/BenchmarkDotNet/pull/492

https://github.com/dotnet/coreclr/issues/5851

https://adamsitnik.com/Span

作一個有底蘊的軟件工程師
 
 
 

給萌新的Flexbox簡易入門教程

 

轉載請註明出處:葡萄城官網,葡萄城爲開發者提供專業的開發工具、解決方案和服務,賦能開發者。

【年底促銷】葡萄城 2018 歲末福利火熱放送中 

原文出處:https://www.sitepoint.com/flexbox-css-flexible-box-layout/

 

近幾年,CSS領域出現了一些複雜的專用佈局工具,用以代替原有的諸如使用表格、浮動和絕對定位之類的各類變通方案。Flexbox,或者說是彈性盒子佈局模塊(Flexible Box Layout Module)是這些新佈局工具中的第一個,接着是CSS網格佈局模塊(CSS Grid Layout Module)。咱們會在本文給出一個易於理解的flexbox入門介紹。

隨着CSS網格佈局的引入,你可能會問flexbox佈局是否真的還有必要。雖然它們所能作的事情有一些重疊,但其各自在CSS佈局中有着很是特別的目的。通常來講,flexbox在一維場景(好比,一串相似的元素)下有最佳應用,而網格是二維場景下理想的佈局方案(例如整個頁面的元素)。

即使如此,flexbox仍能夠用於整個頁面的佈局,這樣它能爲那些還不支持網格佈局的瀏覽器提供合適的兼容處理。(必須認可,網格佈局正在大多數現代瀏覽器中快速獲得支持,不過對flexbox的支持仍然更爲普遍,因此若是你想讓你的佈局在稍微老舊的瀏覽器中也生效,使用flexbox做爲網格佈局的降級方案是很容易的)。

使用Flexbox的好處

flexbox的一些好處是:

  • 頁面元素能被任意方向地放置(靠左、靠右、從上往下甚至從下往上)
  • 佈局內容的可視順序可以被反轉或重排
  • 元素大小能「彈性」適應可用空間,並根據容器或者兄弟元素進行相應地對齊
  • 能輕鬆實現等列寬佈局(與每一列裏面的內容無關)

爲了闡述其多樣的屬性和可能性,讓咱們假設下面有這樣的佈局用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
< div  class="example">
   < header >
     header content here
   </ header >
   < main  class="main">
     < nav >
       nav content here
     </ nav >
     < div  class="content">
       main content here
     </ div >
     < aside >
       aside content here
     </ aside >
   </ main >
   < footer >
     footer content here
   </ footer >
</ div >

 

首先,是把元素一塊兒放進.main裏,好比,<nav>和<aside>。若是沒有flexbox,咱們可能會把三個元素所有進行浮動,但想讓它按理想的方式工做顯得並不直觀。並且,按傳統的方式作這件事會出現一個衆所周知的問題:每一列僅僅和它的內容同樣高。所以,你可能須要把三個元素都設置爲統一的高度,或者使用某種黑科技

讓flexbox來救場吧。

讓咱們Flex

flexbox的要點是出如今display屬性上的flex值,它須要被設置在容器元素上。如此設置會讓它的子元素變成「彈性項目(flex item)」。這些彈性項目擁有一些易於使用的默認屬性。好比,它們被緊挨着放置,那些沒有特別指明寬度的元素自動佔滿了剩餘的空間。

所以,若是你給.main設置了display:flex,它的子元素.content就被自動擠在<nav>和<aside>之間。不須要再多餘的計算,多麼方即是吧?做爲附加獎賞,全部三個元素神奇地擁有了相同的高度。  

1
2
3
.main {
   display : flex;
}

請查看下面的例子,包含了全部的細節:flexbox-demo-1

項的順序:Flebox的order屬性

另一個flexbox的能力,是可以輕鬆改變元素的顯示順序。讓咱們假設你爲一個客戶製做了上面的佈局,而她如今想要.content出如今<nav>以前。

一般,你須要深刻到HTML源碼中去,在那裏改變元素的順序。而有了Flexbox,你能夠徹底使用CSS完成這項任務。只需把.content的order屬性設置爲-1,那麼這一列就會出如今前面,這本例就是最左邊。

1
2
3
4
5
6
7
.main {
   display : flex;
}
 
.content {
   order:  -1 ;
}

本例中,你不須要改變其餘列的order。例子在flexbox-demo-2

若是你傾向於顯式地爲每一列指定order,你能夠將.content的order設爲1,把<nav>的order設爲2,把<aside>的設爲3。

HTML源碼獨立於CSS的Flexbox樣式

但你的客戶並不知足。她想讓<footer>成爲頁面的第一個元素,顯示在<header>以前。那好,一樣的,flexbox是你的朋友(雖然像在此例中,可能你得跟你的客戶好好談談,而不是跟隨指示)。由於你不只要重排列內部元素,還要重排外部的,display:flex規則將被設置在<div class="example">之上。注意這裏是如何在頁面中嵌套使用flex容器來達到你想要的效果的。

由於<header>,<main class="main">和<footer>相互堆疊着,你須要首先設置一個垂直上下文,它可以經過設置flex-direction:column來快速完成。還有,<footer>的order被設置爲-1,如此一來它就出如今頁面的最上頭。就這麼簡單。

1
2
3
4
5
6
7
8
.example {
   display : flex;
   flex- direction : column;
}
 
footer {
   order:  -1 ;
}

因此,若是你想把一行元素修改成一列,或者相反,你可使用flex-direction屬性,並設置它相應地爲column或row(row是默認值)。

完整的例子在flexbox-demo-3

然而,強大的能力也到來了更多的責任:謹記,一些用戶可能會使用鍵盤來導航你的基於flexbox的網站,若是你HTML源碼中元素的順序和屏幕上顯示的有所出入,那麼無障礙訪問的能力就成爲須要認真對待的問題。若是想了解得更多,請不要錯過HTML源碼順序 vs CSS顯示順序,網站無障礙訪問和易用性的專家Adrian Roselli針對這個問題給出了深刻討論。

如何在Flexbox中對齊子項

Flexbox能很是直觀地處理子項的水平對齊和垂直對齊。

你可使用align-items對flex容器中的全部子項設置統一的對齊。若是你想給個別元素設置不一樣的對齊方式,使用align-self。元素的對齊方式跟它所在父容器的flex-direction有關。若是它的值是row(意味着元素水平排列),對齊方式是指在垂直軸上。若是flex-direction被設置爲column(意味着元素垂直排列),對齊方式就是指在水平軸上。

例如,你讓一些元素在容器中分別有不一樣的對齊方式,你須要:

  • 設置每一個元素的align-self屬性爲合適的值。可能的值有:center,stretch(元素撐滿它的容器),flex-start,flex-end和baseline(元素被放置在父容器的baseline上)
  • 把容器元素設置爲display:flex
  • 最後,注意父容器的flex-direction屬性,由於它關係到子元素的對齊方式。  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.example {
   display : flex;
   flex- direction : column;
}
 
. red  {
   align-self:  center ;
}
 
. blue  {
   align-self: flex-start;
}
 
.pink {
   align-self: flex-end;
}

試試在下面的例子中,把父容器的flex-direction在row和column之間切換,看看它們引發的實時變化。

例子flexbox-demo-4.html

若是想要容器中全部的元素有統一的對齊方式,你能夠在容器上使用align-items。能夠的值有center,flex-start,flex-end,stretch(默認值:子項被拉伸以適應它們的容器)和baseline(子項被放置在父容器的baseline上)。

1
2
3
4
.example {
   display : flex;
   align-items:  center ;
}

像往常同樣,試着把父容器的flex-direction在row和column之間切換,看看它們如何影響着你設置align-items值時所發生的做用。

例子flexbox-demo-5.html

在Flexbox裏兩端對齊

另外一個控制對齊的屬性是justify-content,當你想讓多個元素等分空間時很是有用。

可接受的值有:center,flex-start,flex-end, space-between(元素利用主軸之間的空間而排布)和space-around(元素利用主軸以前、之間和以後的空間而排布)。

例如,在以前你一直使用的簡單HTML模板裏,你能夠在<main>裏找到三個元素:<nav>,.content和<aside>。以前,它們都被擠在頁面的左邊。若是你想讓它們之間有一些空間,可是不讓第一個元素的左邊有空間,也不想讓最後一個元素的右邊有空間,你能夠把.main(即它們的父容器)裏的justify-content設置爲space-between。

1
2
3
4
.main {
   display : flex;
   justify- content : space-between;
}

也試一下設置爲space-around,觀察不一樣的結果。例子在flexbox-demo-6

在上面的例子中,我一樣把<header>中的文字水平和垂直對齊了,分別是把justify-content(水平居中)和align-items(垂直居中)都設置爲center。

1
2
3
4
5
6
header {
   height 100 vh;
   display : flex;
   justify- content center ;
   align-items:  center ;
}

Flexbox中彈性子項的大小

使用flex屬性,你可以對照flex容器中其餘元素來控制彈性子項的大小。

這個屬性是如下獨立屬性的簡寫:

  • flex-grow:一個數字,指明元素如何相對其餘flex項來拉伸
  • flex-shrink:一個數字,指明元素如何相對其餘flex項來收縮
  • flex-basis:元素的長度。可接受的值有:auto,inherit或者一個數字後面緊跟着%,px,em或其餘長度單位。

例如,想獲得三個等寬的列,只需給每一列設置flex:1,其餘什麼都不用作:  

1
2
3
nav, aside, .content {
   flex:  1 ;
}

若是你須要.content佔據<nav>和<aside>的兩倍寬,那麼就把.content設爲flex:2,讓其餘兩個爲1。

例子在flexbox-demo-7.html

那僅僅是對flex屬性最簡單的應用。一樣能夠設置flex-grow,flex-shrink和flex-basis這些值,不過那超出本文的話題範圍了。

進一步的資源

若是你準備好繼續前進,並想學着精通flexbox的更多東西,請查看下面的資源:

總結

如你所見,若是咱們想控制元素在網頁中的佈局,flexbox可讓咱們的生活更加輕鬆。它很是穩固和可靠,讓之前那些咱們天天使用的諸如使 讓容器坍縮之類的奇技淫巧,成爲了過去。

像咱們說的,現在,在針對整個頁面進行佈局時,CSS網格是更好的方案,但咱們仍然值得去了解flexbox能作的那些事情。flexbox的最佳應用場景,體如今對元素的一維排列上,但若是有須要,它也能在稍老舊的瀏覽器中,爲CSS網格佈局提供方便的替代方案。

 

推薦

【年底促銷】葡萄城 2018 歲末福利火熱放送中 



本文是由葡萄城技術開發團隊發佈,轉載請註明出處:葡萄城官網

瞭解開放易用的 Web 生成平臺,請前往活字格Web應用生成平臺

瞭解可嵌入您系統的在線 Excel,請前往SpreadJS純前端表格控件

 

 

 

淺談Quartz定時任務調度

 

一  開發概述

       對於具備必定規模的大多數企業來講,存在着這樣一種需求:存在某個或某些任務,須要系統按期,自動地執行,然而,對大多數企業來講,該技術的實現,倒是他們面臨的一大難點和挑戰。

 對於大部分企業來講,實現如上功能,挑戰在哪裏?

挑戰一:如何作一個自動服務的系統?

      是從0到1開發(費時費力花錢,還不必定開發成功,即便開發成功,也未必好用),仍是購買第三方服務(花錢)。

挑戰二:如何實現複雜的「按期規則」?

     對於簡單的按期規則,能夠藉助於windows自帶的執行計劃來執行,但如果複雜的按期規則,windows執行計劃未必可行,然而,Quartz的cron卻很好地解決了該問題,

(能夠說,cron在表達時間規則方面,無所不能),除此以外,Quartz能很好地配合windows執行計劃,實現系統的按期,自動執行任務。

      經過如上概述,咱們知道Quartz能很好地解決該問題,那麼,什麼是Quartz呢?

      簡言之,Quartz就是一種任務調度計劃。

  • 它是由OpenSymphony提供的、開源的、java編寫的強大任務調度框架
  • 幾乎能夠集成到任何規模的運用程序中,如簡單的控制檯程序,複雜的大規模分佈式電子商務系統
  • 可用於建立簡單的或複雜的計劃任務
  • 包含不少企業級功能,如支持JTA和集羣等

      本篇文章,主要從Quartz框架核心組件,Quartz基本運行原理,Quartz核心概念和Quartz基本功能實現(代碼)等方面來介紹Quartz。

二  Quartz

當要深刻研究一個技術時,研究它的體系結構和內部運行原理,不失爲一種較好的方式。同理,咱們在研究Quartz時,也採用相似的方法,

下圖爲Quartz的大體結構圖。

 

(一)Quartz關鍵組件

Quartz比較關鍵的兩個核心組件分別爲Job和Trigger

  • job--表示任務是什麼
  • trigger--表示什麼時候觸發任務

 

(二)Quartz幾個關鍵概念

1.IJob

IJob表示一個接口,該接口只有一個方法簽名

1
2
3
4
public  interface  IJob
     {
         void  Execute(JobExecutionContext context);
     }

在Quartz中,全部的job任務,必須實現該接口

1
2
3
4
5
6
7
public  class  MyJob : IJob
     {
         public  void  Execute(JobExecutionContext context)
         {
             Console.WriteLine( "Quartz基本功能測試。" );
         }
     }

2.JobDetail

 JobDetail,顧名思義,就是表示關於每一個Job的相關信息,它主要包括兩個核心組件,即Job Task和JobData Map

3.Trigger

Trigger,表示觸發器,根據配置規則來觸發執行計劃調度job,它主要包括兩個核心組件,即SimpleTrigger和CronTrigger

4.IJobStore

IJobStore,表述任務存儲器,主要存儲job和trigger相關信息。

5.ISchedulerFactory

ISchedulerFactory,表示任務計劃工廠,用來管理任務計劃IScheduler。

6.IScheduler

IScheduler,表述任務計劃,它至關於一個容器,具體job和job相關trigger就可以被注入其中,從而實現任務計劃調度。其主要經常使用的方法:

  • Start --啓動執行計劃
  • Shutdowm --關閉執行計劃

接口Code:

複製代碼
namespace Quartz
{
    public interface IScheduler
    {
        bool IsStarted { get; }
        string SchedulerName { get; }
        string SchedulerInstanceId { get; }
        bool InStandbyMode { get; }
        bool IsShutdown { get; }
        IJobFactory JobFactory { set; }
        string[] JobGroupNames { get; }
        string[] TriggerGroupNames { get; }
        SchedulerContext Context { get; }
        IList GlobalJobListeners { get; }
        string[] CalendarNames { get; }
        IList GlobalTriggerListeners { get; }
        ISet TriggerListenerNames { get; }
        ISet JobListenerNames { get; }
        IList SchedulerListeners { get; }

        void AddCalendar(string calName, ICalendar calendar, bool replace, bool updateTriggers);
        void AddGlobalJobListener(IJobListener jobListener);
        void AddGlobalTriggerListener(ITriggerListener triggerListener);
        void AddJob(JobDetail jobDetail, bool replace);
        void AddJobListener(IJobListener jobListener);
        void AddSchedulerListener(ISchedulerListener schedulerListener);
        void AddTriggerListener(ITriggerListener triggerListener);
        bool DeleteCalendar(string calName);
        bool DeleteJob(string jobName, string groupName);
        ICalendar GetCalendar(string calName);
        string[] GetCalendarNames();
        IList GetCurrentlyExecutingJobs();
        IJobListener GetGlobalJobListener(string name);
        ITriggerListener GetGlobalTriggerListener(string name);
        JobDetail GetJobDetail(string jobName, string jobGroup);
        IJobListener GetJobListener(string name);
        string[] GetJobNames(string groupName);
        SchedulerMetaData GetMetaData();
        ISet GetPausedTriggerGroups();
        Trigger GetTrigger(string triggerName, string triggerGroup);
        ITriggerListener GetTriggerListener(string name);
        string[] GetTriggerNames(string groupName);
        Trigger[] GetTriggersOfJob(string jobName, string groupName);
        TriggerState GetTriggerState(string triggerName, string triggerGroup);
        bool Interrupt(string jobName, string groupName);
        bool IsJobGroupPaused(string groupName);
        bool IsTriggerGroupPaused(string groupName);
        void PauseAll();
        void PauseJob(string jobName, string groupName);
        void PauseJobGroup(string groupName);
        void PauseTrigger(string triggerName, string groupName);
        void PauseTriggerGroup(string groupName);
        bool RemoveGlobalJobListener(IJobListener jobListener);
        bool RemoveGlobalJobListener(string name);
        bool RemoveGlobalTriggerListener(ITriggerListener triggerListener);
        bool RemoveGlobalTriggerListener(string name);
        bool RemoveJobListener(string name);
        bool RemoveSchedulerListener(ISchedulerListener schedulerListener);
        bool RemoveTriggerListener(string name);
        DateTime? RescheduleJob(string triggerName, string groupName, Trigger newTrigger);
        void ResumeAll();
        void ResumeJob(string jobName, string groupName);
        void ResumeJobGroup(string groupName);
        void ResumeTrigger(string triggerName, string groupName);
        void ResumeTriggerGroup(string groupName);
        DateTime ScheduleJob(Trigger trigger);
        DateTime ScheduleJob(JobDetail jobDetail, Trigger trigger);
        void Shutdown(bool waitForJobsToComplete);
        void Shutdown();
        void Standby();
        void Start();
        void StartDelayed(TimeSpan delay);
        void TriggerJob(string jobName, string groupName);
        void TriggerJob(string jobName, string groupName, JobDataMap data);
        void TriggerJobWithVolatileTrigger(string jobName, string groupName);
        void TriggerJobWithVolatileTrigger(string jobName, string groupName, JobDataMap data);
        bool UnscheduleJob(string triggerName, string groupName);
    }
}
複製代碼

 (三)核心UML圖

1.命名空間

不一樣版本的Quartz命名空間有所區別,但差異不大,以下爲版本1.0.3命名空間

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using  Quartz;
using  Quartz.Core;
using  Quartz.Impl;
using  Quartz.Impl.AdoJobStore;
using  Quartz.Impl.AdoJobStore.Common;
using  Quartz.Impl.Calendar;
using  Quartz.Impl.Matchers;
using  Quartz.Impl.Triggers;
using  Quartz.Listener;
using  Quartz.Logging;
using  Quartz.Logging.LogProviders;
using  Quartz.Simpl;
using  Quartz.Spi;
using  Quartz.Util;
using  Quartz.Xml;
using  Quartz.Xml.JobSchedulingData20;
using  System;

2.關鍵組件繼承關係

    在Quartz中,許多組件是能夠經過配置來促使做業執行的,如線程程序(Tread Procedure)決定如何執行計劃任務線程(Quartz Scheduler Thread)

 

三  代碼

本示例,咱們將使用.net 控制檯程序,基於VS2017來使用Quartz創建一個任務:

任務要求:要求在控制檯每隔2秒輸出:Quartz基本功能測試。

1.首先使用Nuget下載Quartz

本示例使用的Quartz版本爲1.0.3

2.按照以下步驟操做

代碼:

第一階段:建立實現IJob接口的MyJob類

1
2
3
4
5
6
7
public  class  MyJob : IJob
{
     public  void  Execute(JobExecutionContext context)
     {
         Console.WriteLine( "Quartz基本功能測試。" );
     }
}

第二階段:按規則調用Quartz組件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static  void  Main( string [] args)
         {
             //每一個2秒執行一次
             string  cronParam =  "*/2 * * * * ?" ;
             //建立計劃任務抽象工廠
             ISchedulerFactory sf =  new  StdSchedulerFactory();
             //建立計劃任務
             IScheduler sched = sf.GetScheduler();
             //建立job
             JobDetail job =  new  JobDetail( "myJob" , "group" typeof (MyJob));
             //建立觸發器
             Trigger trigger =  new  CronTrigger( "myTrigger" , "group" ,cronParam);
             //將job和trigger注入到計劃任務中
             sched.ScheduleJob(job, trigger);
             //啓動計劃任務
             sched.Start();
             //關閉計劃任務
             //sched.Shutdown();
 
             Console.Read();
         }

3.測試結果

 

四  參考文獻

【01】http://www.quartz-scheduler.org/

【02】https://www.ibm.com/developerworks/library/j-quartz/index.html

【03】https://www.w3cschool.cn/quartz_doc/

 

 

 

淺談JavaScript之Event(上篇)

 

一  簡述JavaScript及其在瀏覽器中的地位

(一)  瀏覽器主要構成

雖然不一樣瀏覽器之間存在差別(如Google Chrome,Firefox,Safari和IE等),但單從瀏覽器構成來講,大同小異,大體可歸結爲以下幾類:

1.User Interface(用戶界面):所謂用戶界面,就是經過瀏覽器渲染出來,讓用戶可見的界面,如地址欄,書籤菜單欄等;

2.Browser Engine(瀏覽器引擎):主要操做呈現的引擎界面;

3.Rendering Engine(渲染引擎):負責渲染響應請求內容,如負責解析HTML和CSS;

4.Networking(網絡):負責網絡呼叫處理,如http請求;

5.JS Interpreter(JavaScript 解釋器):負責解析和執行javascript代碼;

6.UI Back(UI後端):用於繪製組合框和窗口等基本組建;

7.Data Persistence(數據持久):一般用來持久化存儲少許數據,如cookie等;

 

(二)JavaScript在瀏覽器中的地位

 如上圖,javascript處於瀏覽器中的核心位置,負責解釋和執行js腳本,內置於瀏覽器中,經過瀏覽器提供的API來訪問。

(三)JavaScript構成

關於javascript的構成,大體可歸結爲三個部分:ECMAScript,DOM和BOM。

1.ECMAScript是對js的約束和規範,如基本語法結構;

2.DOM就是文檔對象模型,是交互的資源,如html文檔;

3.BOM主要是對瀏覽器自己描述,如瀏覽器名稱,版本號等;

 

(四)JavaScript基本執行原理

 這裏不深刻談及javascript的深層次執行原理,只是大體描述一下,關於更深層次的,在後續文章推出,與你們分享。

 JS的執行原理,用一句話來歸結之:單線程異步。下圖很好地表述該過程。

全部的執行函數統一放在隊列中進行排隊。

 

二  事件流

所謂事件流,也可理解爲事件的軌跡。通常地,將事件流分爲三個階段:捕獲階段,目標階段和冒泡階段。

下圖爲三個階段的大體流程圖。

 (一)捕獲階段

捕獲階段處於事件流的第一階段,該階段的主要做用是捕獲截取事件。在DOM中,該階段始於Document,結束於body(固然,在如今的很

多高版本瀏覽器中,該過程結束於目標元素,只不過不執行目標元素而已,這也體現了目標元素具備雙重範圍)。

 

 (二)目標階段

目標階段處於事件流的第二階段,該階段的主要做用是執行綁定事件。通常地,該階段具備雙重範圍,即捕獲階段的結束,冒泡階段的開始;

(三)冒泡階段

冒泡階段處於事件流的第三階段,該階段的主要做用是將目標元素綁定事件執行的結果返回給瀏覽器,處理不一樣瀏覽器之間的差別,主要在該階段完成。

(四)三階段在Dom中的完整流程

 

三   事件處理程序

js事件處理程序按照種類來劃分,大體可分爲五大類:HTML事件處理程序,DOM0級事件處理程序,DOM2級事件處理程序,IE事件處理程序和跨瀏覽器事件處理程序。

尤爲是DOM0,DOM2和IE事件處理程序,利用它們之間的差別化有效地解決瀏覽器差別問題,從而實現跨瀏覽器的兼容性問題。

 

(一)html事件處理程序

所謂html事件處理程序,就是在dom結構中嵌套js代碼。在html中,元素支持的全部事件,均可以使用與相應事件處理程序同名的html特性來指定,這個特性的值應該是能執行的js代碼。

如點擊事件。

<body>
    <!--html事件處理程序-->
    <input type="button" value="請點擊" onclick="alert('測試html事件處理程序!!')"/>
</body>

固然,通常不採用如上方法,常規的作法是,將js代碼定義在目標元素外部。所以,與如上相同功能的定義爲:

目標元素

<body>
    <!--html事件處理程序-->
    <input type="button" value="請點擊" onclick="HtmlEventHandlerProc()"/>
</body>

外部js

<script>
    function HtmlEventHandlerProc() {
        alert('測試html事件處理程序!!');
    }
</script>

Tip:

1.事件處理程序中的代碼,可以訪問全局做用域中的任何變量;

2.每一個function()存在一個局部變量,即事件對象event,經過event變量,能夠直接訪問事件對象。

<body>
   <input type="button" value="請點擊" onclick="alert(event.type)"/>
</body>

執行結果

3.在函數內部,this值等於事件的目標元素。

<body>
    <input type="button" value="請點擊" onclick="alert(this.value)"/>
</body>

執行結果

 this具備擴展做用域的功能,其功能至關於

複製代碼
function myfunction() {
        with (document) {
            with (this) {
                //add your logic
           }
      }
 }
複製代碼

若是當前元素是一個表單元素,則做用域還會包含訪問表單元素(父元素)的入口。

複製代碼
  function myfunction() {
        with (document) {
            with (this.form) {
                with (this) {
                    //add your logic
                }
            }
        }
    }
複製代碼

這樣作,有什麼本質意義呢?固然是想讓事件處理程序更快捷訪問表單其餘字段(無需引用表單元素就能訪問)

 <form>
         <input type="text" name="userName" value="Alan_beijing" />
         <input type="button" value="測試表單元素" onclick="alert(userName.value)" />
     </form>

執行結果

 4.html事件處理程序存在哪些缺點?

   缺點一:時差問題

   缺點二:擴展的做用域鏈在不一樣瀏覽器中會致使不一樣結果

   缺點三:html代碼與js代碼高度耦合

(二)DOM0級事件處理程序

DOM0級事件很好地解決了html和js代碼強耦合的問題。

1.爲元素綁定事件

1
2
3
4
var  btn = document.getElementById( 'myBtn' );
btn.onclick =  function  () {
     alert( 'Clicked' );
}

2.爲元素解除事件

btn.onclick = null;

(三)DOM2級事件處理程序

DOM2級事件定義了兩個方法來爲目標元素綁定事件處理程序(addEventListener())和解除事件處理程序(removeEventListener()),全部節點中都包含這兩個方法,而且他們都接收三個參數:

要處理的事件名,事件處理程序和一個布爾值(true表示是在捕獲階段進行,false表示在冒泡階段進行)

1.爲事件添加click事件處理程序(冒泡階段進行)

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function () {
     alert(this.id);
}, false);

執行結果:

2.添加多個事件處理程序

複製代碼
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function () {
   alert(this.id);
}, false);

btn.addEventListener("click", function myfunction() { alert("添加的第二個事件處理程序"); }, false);
複製代碼

執行結果:

 3 移除事件處理程序

經過addEventListener()添加的事件處理陳旭只能使用removeEventListener()來移除,移除時,傳入的參數與添加的程序時使用的參數相同

(這意味着匿名函數不能經過removeEventListener()來刪除)

匿名函數不能刪除

複製代碼
var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function () {
        alert(this.id);
    }, false);

    //不能刪除,由於是匿名函數
    btn.removeEventListener("click", function () {
        alert(this.id);
    }, false);
複製代碼

非匿名函數能刪除

複製代碼
    var btn = document.getElementById("myBtn");
    var handler = function () {
        alert(this.id);
    };

    btn.addEventListener("click", handler, false);
    //刪除事件處理程序
    btn.removeEventListener("click", handler, false);
複製代碼

(四)IE事件處理程序

IE提供了兩個方法來綁定和卸載事件處理程序,attachEvent()和detachEvent(),這兩個方法均接收兩個參數,即事件處理程序名稱和事件處理程

序函數,而且在冒泡階段添加(IE8及更早版本只支持冒泡)

1.爲目標按鈕添加綁定事件

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function () {
   alert("IE事件處理程序!!");
});

2.爲目標按鈕添加綁定事件

多個執行綁定事件的結果是倒過來的。

複製代碼
 var btn = document.getElementById("myBtn");
 btn.attachEvent("onclick", function () {
       alert("IE事件處理程序1!!");
 });

 btn.attachEvent("onclick", function () {
      alert("IE事件處理程序2!!");
 });
複製代碼

3.爲目標元素移除事件處理程序

注意:匿名事件處理程序是不可以移除的

複製代碼
    var btn = document.getElementById("myBtn");
    var handler = function () {
        alert("IE事件處理程序");
    };
    //綁定事件
    btn.attach("onclick", handler);
    //移除事件
    btn.detachEvent("onclick", handler);
複製代碼

(五)跨瀏覽器事件處理程序

在跨瀏覽器中,無非就是三種選擇:DOM0級選擇(不經常使用,基本被廢棄),DOM2級選擇和IE選擇(不經常使用,IE8及如下版本)。

複製代碼
//定義EventUtil
    var EventUtil = {
        addHandler: function (element, type, handler) {
            if (element.addEventListener) {
                element.addEventListener(type, handler, false);
            } else if (element.attachEvent) {
                element.attachEvent("on" + type, handler);
            }
        },
        removeHandler: function (element, type, handler) {
            if (element.removeEventListener) {
                element.removeEventListener(type, handler, false);
            } else if (element.detachEvent) {
                element.detachEvent("on" + type, handler);
            } else {
                element["on" + type] = null;
            }
        }
    }

    //調用
    var btn = document.getElementById("myBtn");
    var handler = function () {
        alert("事件處理程序跨瀏覽器!!");
    };
    //綁定事件處理程序
    EventUtil.addHandler(btn, "click", handler);
    //移除事件處理程序
    EventUtil.removeHandler(btn, "click", handler);
複製代碼

四  事件類型

內容相對比較簡單,這裏就暫且列舉主要內容,如有須要,會在下篇文章論述。

五  事件對象及其事件委託

介於文章篇幅有限,暫且列舉主要內容,代碼部分將在下篇文章論述。

 

 

 

探索JavaScript數組奧祕

 

 一  概述

     JavaScript數組同後端語言同樣,具備它本身的數據結構,歸根結底,這種數據結構,本質就是一種集合。

     在後端語言中(如java,.net等),數組是這樣定義的:數組是用來存儲相同數據類型的集合。這個定義,「相同數據類型」6個字限制了數據只能存儲相同的數據類型,如int[]數組只能存儲數字,而不能存儲字符串,以下定義方式,是錯誤的,由於string

不屬於整型

int[] arr = { 10, 20,"string" };

   然而,在JavaScript中,數組的定義倒是很是寬鬆的,這也決定了其能存儲一切數據的特色。JavaScript數組具備以下特色

   特色1:存儲相同基本數據類型的數據;

   特色2:存儲不一樣基本數據類型的數據;

   特色3:存儲對象

   這三個特色,咱們可歸結爲一句話:JavaScript存儲一切對象,而不像後端語言那樣,只能存儲相同數據類型。除此以外,JavaScript數組還提供了不少豐富的操做方法。以下爲經常使用的操做方法。

 本篇文章將主要結合代碼來論述JavaScript數組。

二  對數組的基本操做

(一)建立數組

第一種方式:構造函數方式

   //第一種建立數組的方式:構造函數方式
    var colors = new Array();//未知數組長度
    //var colors = new Array(4);//已知數組長度
    //var colors = new Array('green', 'yellow', 'white', 'red');//建立數組同時,給數組賦值
    //var colors = Array();//在建立時,能夠省略new關鍵字

第二種方式:字面量方式

   //第二種建立數組方式:使用數組字面量
    var colors = ['green', 'yellow', 'white', 'red'];
    var name = [];//建立空數組

(二)訪問數組

訪問數組,經過數組的下標來訪問

   //建立數組
    var colors = ['green', 'yellow', 'white', 'red'];
    //輸出索引值
    for (var i = 0; i < colors.length; i++) {
        alert(colors[i]);//green,yellow,white,red
    }

提示:for...in...訪問數組屬性(索引),而不是數組屬性值

    //建立數組
    var colors = ['green', 'yellow', 'white', 'red'];
    //輸出數組索引
    for (var propAttr in colors) {
        alert(propAttr);//0,1,2,3
    }

(三)爲數組添加元素

第一種方式:棧方式(後進先出,從數組尾部加入數據)

複製代碼
   //建立數組
    var colors = ['green', 'yellow', 'white', 'red'];
    //第一種方式:棧方式
    colors.push("orange");
    for (var i = 0; i < colors.length; i++) {
        alert(colors[i]);//green,yellow,white,red,orange
    }
複製代碼

第二種方式:隊列方式(先進先出,從數組頭部加入數據)

var colors = ['green', 'yellow', 'white', 'red'];
colors.unshift('orange');
    for (var i = 0; i < colors.length; i++) {
        alert(colors[i]);//orange,green,yellow,white,red
    }

(四)移除數組元素

第一種方式:棧方式(後進先出,從數組尾部移除數據)

複製代碼
    //建立數組
    var colors = ['green', 'yellow', 'white', 'red'];
    //從尾部彈出數據
    colors.pop();
    for (var i = 0; i < colors.length; i++) {
        alert(colors[i]);//green,yellow,white
    }
複製代碼

第二種方式:隊列方式(先進先出,從數據頭部移除數據)

複製代碼
   //建立數組
    var colors = ['green', 'yellow', 'white', 'red'];
    //從頭部移除數據
    colors.shift();
    for (var i = 0; i < colors.length; i++) {
        alert(colors[i]);//yellow,white,red
    }
複製代碼

第三種方式:length方式(藉助length屬性可訪問性來操做)

複製代碼
    //建立數組
    var colors = ['green', 'yellow', 'white', 'red'];
    //從尾部移除數據,與pop()同樣
    colors.length = 3;
    for (var i = 0; i < colors.length; i++) {
        alert(colors[i]);//green,yellow,white
    }
複製代碼

三  數組方法

(一)檢測方法

數組檢測方式,可經過typeof,instranceof和Array.isArray()來檢測。

(二)轉換方法

全部對象都具備toLocaleString(),toString()和valueOf()三個方法,數組也如此。

1.toString()

toString()將數據的每一個屬性值轉化爲對應的字符串,而後再輸出轉換後的字符串值。

    var colors = ['red','green','yellow'];
    alert(colors.toString());//red,green,yellow

而下列代碼與如上代碼是同樣的,由於alert()接收的是字符串,因此會在後臺調用toString()方法

    var colors = ['red','green','yellow'];
    alert(colors);//red,green,yellow

2.valueOf()

valueOf()方法同toString()方法同樣,也是返回數組的字符串

    var colors = ['red', 'green', 'yellow'];
    alert(colors.valueOf());//red,green,yellow

3.toLocaleString()

toLocaleString()返回數組的字符串形式。

    var colors = ['red', 'green', 'yellow'];
    alert(colors.toLocaleString());//red,green,yellow

4 三者之間關係

關係1:當不顯示指出調用哪一個方法時(toString(),toLocaleString()和valueOf()),默認調用每一個屬性的toString();

關係2:當顯示地指出調用哪一個方法時,就調用每一個屬性的該方法;

關係3:關於valueOf問題,暫留

複製代碼
   var person1 = {
        toString: function () {
            return "Alan";
        },
        toLocaleString: function () {
            return "Alan_beijing";
        },
        valueOf: function () {
            return "valueOf1";
        }
    };

    var person2 = {
        toString: function () {
            return "Alan1";
        },
        toLocaleString: function () {
            return "Alan_beijing1";
        }
    }

    var people = [person1, person2];
    alert(people.toString());//Alan,Alan1
    alert(people.toLocaleString());//Alan_beijing,Alan_beijing1
    alert(people.valueOf());//Alan,Alan1
    alert(people);//Alan,Alan1
複製代碼

(三)棧方法

棧是一種數據結構,其算法爲:LIFO(Last input First out)後進先出,其兩個核心方法爲push()和pop();

1.push()

push()表示將數據壓入棧中,且放在棧的最後位置,即從棧的尾部壓入數據。對於數組,則在數組的尾部加入數據,操做的順序爲:先把數組length+1,再壓入數據。

    var arr = [10, 20, 30];
    arr.push('新加入的數據');
    alert(arr.toString());//10,20,30,新加入的數據

2.pop()

push()表示將數據從棧中彈出,且從棧的最後位置彈出。對於數組,則在數組的尾部彈出數據,操做的順序爲:先彈出數據,再數組length-1

    var arr = [10, 20, 30];
    arr.pop();
    alert(arr.toString());//10,20

(四)隊列

隊列是一種數據結構,其算法爲:FIFO(First input First out)後進先出,其兩個核心方法爲unshift()()和shift();

1.unshift()

unshift()表示從隊列頭部加入數據。對於數組,則從數組索引最小位置加入數據,操做順序爲:先將數length+1,再將當前數組屬性值日後移動1個位置,最後將新數據添加到索引0處。

    var arr = [10, 20, 30];
    arr.unshift(40);
    alert(arr.toString());//40,10,20,30

2.shift()

shift()表示從隊列頭部移除數據。對於數組,則從數組索引最小位置移除數據。

    var arr = [20, 30];
    arr.shift();
    alert(arr.toString());//30

(五)排序方法

在js數組中,兩個重要的重排序方法:reverse()和sort()

1.reverse()

reverse(),顧名思義,逆序方法。

    var arr = [1,2,3,4,5];
    alert(arr.reverse());//5,4,3,2,1

2.sort()

sort()是比較靈活的排序方法了,支持自定義排序規則,默認排序爲升序

默認爲升序

    var arr = [3, 1, 2, 5, 4];
    alert(arr.sort());//1,2,3,4,5

自定義排序規則

複製代碼
    var arr = [3, 1, 2, 5, 4];
    alert(arr.sort(Compare));//1,2,3,4,5
    //自定義排序規則:正序
    function Compare(value1, value2) {
        if (value1 > value2) {
            return 1;
        } else if (value1 < value2) {
            return -1;
        } else {
            return 0;
        }
    }
複製代碼

 (六)位置方法

js數組提供了兩個位置方法:indexof()和lastIndexOf()

indexOf()表示首次知足條件的位置;而lastIndexOf()則表示最後知足條件的位置

    var arr = [20, 30,20,40,10];
    alert(arr.indexOf(20)); //0
    alert(arr.lastIndexOf(20));//2

(七)迭代方法

ECMAScript5提供了5個迭代方法:every(),filter(),forEach(),map()和some()

這個五個方法,均接收2個參數。

1.every()

對數組中的每項運行給定函數,若是該函數對每一項都返回ture,則返回true,不然返回false.

複製代碼
    //every
    var num = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    var everyResult = num.every(function (item, index,array) {
        return item>2
    });

    alert(everyResult);//fasle
複製代碼

2.some

對數組中的每項運行給定函數,若是該函數對任意一項返回ture,則返回true,不然返回false

複製代碼
    //some
    var num = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    var someResult = num.some(function (item, index, array) {
        return item > 2;
    });

    alert(someResult);//true
複製代碼

3.filter

對數組中的每項運行給定函數,返回該函數會返回true的項組成的數組

複製代碼
    //filter
    var num = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    var filterResult = num.filter(function (item, index, array) {
        return item > 2;
    });

    alert(filterResult);//3,4,5,4,3
複製代碼

4.map

對數組中的每項運行給定函數,返回每次函數調用的結果組成的數組

複製代碼
    //map
    var num = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    var mapResult = num.map(function (item, index, array) {
        return item * 2;
    });

    alert(mapResult);//2,4,6,8,10,8,6,4,2
複製代碼

 

5.forEach

對數組中的每項運行給定函數。注意,該方法沒返回值

    //forEach
    num.forEach(function (item, index, array) {
        //執行想要執行的操做
    });

(八)求和方法

ECMAScript提供了2個縮減方法:reduce()和reduceRight()

reduce和reduceRight,至關於.net的斐波拉列數列,計算算法爲:f(n)=f(n-1)+f(n-2);

1.reduce

reduce計算數組時,按照從左到右的順序

    var values = [1, 2, 3, 4, 5];
    var sum = values.reduce(function (prev, cur, index, array) {
        return prev + cur;
    });

    alert(sum);//15

2.reduceRight

reduceTight計算數組時,從右到左順序

複製代碼
    var values = [1, 2, 3, 4, 5];

    var sum1 = values.reduceRight(function (prev, cur, index, array) {
        return prev + cur;
    });

    alert(sum1);//15
複製代碼

 

(九)其餘方法

ECMAScript爲數組操做提供了不少方法,如concat()和slice()

1.concat()

concat()方法是將兩個對象合併,從而生成新對象,合併同時,不會改變原來對象的值,只是作簡單的拷貝

複製代碼
    var color1 = ['red', 'green', 'yellow'];
    var color2 = ['white', 'blue'];
    //添加數組
    var concatColor = color1.concat(color2);
    alert(concatColor);//red,green,yellow,white,blue

    //添加單個值
    var concatSingelColor = color1.concat('orange');
    alert(concatSingelColor);//red,green,yellow,orange

    //不添加值,只是簡單copy

    var concatColor1 = color1.concat();
    alert(concatColor1);//red,green,yellow
複製代碼

2.slice

slice()方法用於從目標數據中截取新數據,不會改變被截取對象的值。

複製代碼
    var color1 = ['red', 'green', 'yellow'];

    //不傳遞參數:表示截取整個數組
    var color2 = color1.slice();
    alert(color2);//red,green,yellow

    //傳遞一個參數:表示從該位置處開始截取數據,直到數組最後一個數
    var color3 = color1.slice(1);
    alert(color3);//green,yellow

    //傳遞2個參數:第一個參數表示從該位置開始截取,第二個參數減1表示所截數據的最後位置
    var color4 = color1.slice(1, color1.length);
    alert(color4);//green,yellow
複製代碼

 

 

【詳解JavaScript系列】JavaScript之函數(一)

 

       在編程語言中,不管是面向過程的C,兼備面過程和對象的c++,仍是面向對象的編程語言,如java,.net,php等,函數均扮演着重要的角色。固然,在面向對象編程語言JavaScript中(嚴格來講,JS屬於弱面向對象編程語言),函數(function)更扮演着極其重要的角色和佔有極其重要的地位。在本篇文章中,不論述什麼是JS,JS解決什麼問題等之類問題,而是重點闡述JS中的函數(function)。

一  JavaScript函數

(一)何爲函數

關於函數的定義,咱們先從兩個角度來思考:數學角度和編程語言角度。

1.數學角度:在數學領域,關於「函數」二字,再熟悉不過,如三角函數,反三角函數,冪函數,對數函數,指數函數,微積分函數等;

2.編程角度:在編程領域,你們最熟悉且最早接觸的應該是"Main函數"了,除此外,如日期函數(Date),數學函數(Math)等,固然除了內置函數外,還包括用戶自定義函數;

      綜合1,2點,能夠將函數定義以下:

函數是解決某類問題的集合,是某類問題的高度抽象,它具備必定的通用性和複用性。

(二)定義函數

在Javascript中,存在三種經典的函數定義方式:函數聲明式,函數表達式和函數構造式

1.函數聲明式

1 //定義兩個數相加函數
2     function AddNum(num1, num2) {
3         return num1 + num2;
4     }

注意:函數聲明式,不能用匿名函數,以下方式定義錯誤的

1 function (num1, num2) {
2         return num1 + num2;
3     }

2.函數表達式

1  //定義兩個數相加函數
2     var AddFun=function AddNum(num1, num2) {
3         return num1 + num2;
4     }

注意:函數表達式通常用匿名函數,由於調用時,不用函數名,所以,如上調用也可寫成以下:

1 var AddFun = function(num1, num2) {
2         return num1 + num2;
3     }

3.函數構造式

1 var sum = new Function('num1', 'num2', 'return num1 + num2')
2  console.log(sum(10,20));//30

 (三)函數調用

 在JavaScript中,函數的調用採用「函數名(參數)」的格式來進行調用。

須要注意的是,JavaScript函數調用與後端語言函數調用(如java,.net等)存在細微差異,即JavaScript沒有函數重載,參數的參數個數,由實參決定,而不是由形參決定。

1.聲明式調用

 調用方式一:通常調用

複製代碼
1 //定義兩個數相加函數
2 function AddNum(num1, num2) {
3     return num1 + num2;
4 }
5 
6 
7 console.log(Add(10,20));//30
複製代碼

調用方式二:自調用

1 (function AddNum(num1, num2) {
2       console.log(num1 + num2);
3   })(10,20);//30

注意:自調用通常用匿名函數,由於在調用時,不須要函數名,所以,也可寫成以下方式:

1  (function (num1, num2) {
2         return num1 + num2;
3     })(10, 20);//30

2.表達式調用

1 //定義兩個數相加函數
2  var AddFun = function (num1, num2){
3            return num1 + num2;
4  }
5      
6  console.log(AddFun(10, 20));//30

二 函數變量

在JavaScript編程語言中,變量的定義是經過var關鍵字來定義的(若變量不經過var定義,則爲全局變量,但不推薦這麼作),與其餘編程語言同樣,變量也分爲兩大類,即局部變量和全局變量。

(1)局部變量:做用域爲其所在的函數;

(2)全局變量:做用域爲整個過程;

(3)變量做用域:JS中的變量做用域是經過this指針,從當前的做用域開始,從當前做用域由內向外查找,直到找到位置,這裏分爲幾個邏輯:

a.從當前做用域由內向外查找,若找到,就中止查找,不然,繼續查找,直到查到window全局做用域爲止;

b.當內部做用域變量名與外部做用域變量名相同時,內部做用域的覆蓋外部做用域。

咱們來看一個例子:

複製代碼
 1 var dateTime='2018-09-16';
 2 function GetUserInfo(){
 3     var age=120;
 4     var name="Alan_beijing";
 5     function Say(){
 6        var name="老王";
 7        var address="shanghai";
 8        console.log(address+"-"+name+"-"+age+"-"+dateTime);//shanghai-老王-2018-06-05
 9     }
10    return Say();
11 }
12 
13 
14 GetUserInfo();//shanghai-老王-120-2018-09-16
複製代碼

來分析一下變量及其做用域:

如上圖,有4個做用域,當函數執行以下語句時,發生以下過程:

1 console.log(address+"-"+name+"-"+age+"-"+dateTime);

a.js當前this環境做用域爲4做用域;

b.this指針尋找變量:addresss,name,age,dateTime,從當前做用域向外做用域逐層尋找,直到尋找到變量爲止,若尋找到最外層做用域任然沒找到,則會出錯,提示該變量未聲明;

c.當內外層變量相同時,內層變量覆蓋外層變量,如4做用域的name覆蓋3做用域的name;

 

三   函數聲明式定義存在的問題

 在js中,存在聲明提早問題,看看以下例子。

1 var globleName="Alan_beijing";
2 function Say(){
3    console.log(localName);  // undefined,不報錯,是由於變量聲明提早
4    var localName="Alan";
5    console.log(localName);// Alan
6 }

看過如上代碼,你可能會問,函數執行到console.log(localName); 時,應該報錯,由於localName未定義。

若是在後端語言,如java,.net中,可能會報錯,可是在js中,卻不會,不報錯的緣由是:在js中存在聲明提早。

如上代碼至關於以下代碼:

複製代碼
1 var globleName="Alan_beijing";
2 function Say(){
3    var localName;
4    console.log(localName);
5    localName="Alan";
6    console.log(localName);
7 }
複製代碼

 

四  函數幾大關鍵點

1.匿名函數

匿名函數,顧名思義,就是沒名字的的函數,咱們來看看以下兩個例子:

函數表達式

1 //定義兩個數相加函數
2     var AddFun=function (num1, num2) {
3         return num1 + num2;
4     }

當即執行函數

(function AddNum(num1, num2) {
      console.log(num1 + num2);
   })(10,20);//30

從如上,不難看出,匿名函數主要用域函數表達式和當即執行函數。

2.閉包

閉包的根源在於變量的做用域問題。

咱們先來考慮這樣一個問題,假設在面向對象編程語言中,某個方法的變量被定義爲私有變量,其餘函數要獲取訪問該變量,.net怎麼處理?

方法一:構造函數

方法二:單例模式

一樣地,在js中,一樣存在外部函數調用內部函數變量問題,js運用的技術就叫作閉包。

所謂閉包,就是將不可訪問的變量做爲函數返回值的形式返回來,從而實現函數外部訪問函數內部變量目的。

複製代碼
1 //閉包
2     function GetName() {
3         var name = "Alan_beijing";
4         var age = function () {
5             var age = 30;
6             return age;
7         }
8         return name + age;
9     }
複製代碼

3.js多態問題(重載問題)

在面向對象編程語言,如.net中,實現多態的方式大體有以下:

a.接口

b.抽象類

c.虛方法

d.方法重載

然而,在js中,沒有面向對象之說(OO),那麼js是如何實現多態的呢?根據方法實際傳遞的參數來決定。

1  //重載
2 function SetUserInfo(userName, age, address, tel, sex) {
3     console.log(arguments.length);//4
4 }
5 
6 SetUserInfo('Alan_beijing',44,'china-shanghai','xxxx');

從如上能夠看出,傳遞多少個參數,就接收多個參數,若是在現象對象編程語言中實現該功能,至少須要寫一堆代碼,這也是體現js強大之一。

4.遞歸

來看看一個遞歸階乘函數

複製代碼
1 //遞歸
2     function factorial(num) {
3         if (num < 1) {
4             return 1;
5         } else {
6             return num * arguments.callee(num-1);
7         }
8     }
複製代碼

若是是.net,咱們通常會這樣寫

複製代碼
1 //遞歸
2     function factorial(num) {
3         if (num < 1) {
4             return 1;
5         } else {
6             return num * factorial(num-1);
7         }
8     }
複製代碼

然而,這樣寫,卻會存在異常狀況

1  var factorial1 = factorial;
2     factorial = null;//將factorial變量設置爲null
3     console.log(factorial1(4));//出錯

 5.原型和原型鏈

面向對象編程語言的顯著特徵之一是面向對象,然而,在js中,沒有對象,那麼js是如何面向對象的功能的呢(封裝,繼承,多態)?固然是經過原型和原型鏈來實現的。

 

 

 

C# Oracle.ManagedDataAccess 批量更新表數據

    這是我第一次發表博客。之前常常到博客園查找相關技術和代碼,今天在寫一段小程序時出現了問題,

但在網上沒能找到理想的解決方法。故註冊了博客園,想與新手分享(由於本人也不是什麼高手)。

    vb.net和C#操做Oracle數據庫已經用了N多年了。因爲是作工程自動化項目的,業主只對軟件的功能和

界面是否友好來斷定成果的好壞。因此一直都是採用直接OracleCommand.ExecuteNonQuery(sqlString,conn)

的方式很直白的Insert、update和delete數據庫表的。因爲工程項目並無很高的實時性,因此......

    最近手頭沒太多事情,就在博客園逛逛。看到了ODP.NET,發現了本身有點落伍了,因而照貓畫虎的練練。

在Insert時順風順水的,但Update時出現了「ORA-01722: 無效數字」。各類找問題,網上查資料無果。

測試表只有兩個字段,varchar2和number。問題代碼以下:

using Oracle.ManagedDataAccess.Client;

 

複製代碼
private void UpdateTable()
        {
            int valueStart = (int)NudStartValue.Value;
            int valueCount = (int)NudValueCount.Value;
            int returnValue = 0;
            int[] columnValue = new int[valueCount];
            string[] columnStr = new string[valueCount];
            string sql = string.Empty;

            OracleParameter[] parameters = new OracleParameter[]
            {
                new OracleParameter(":sname", OracleDbType.Varchar2),
                new OracleParameter(":svalue",OracleDbType.Int32)
                
            };
            parameters[0].Direction = ParameterDirection.Input;
            parameters[1].Direction = ParameterDirection.Input;

            for ( int i = 0 ; i < valueCount ; i++ )
            {
                columnStr[i] = "No:" + ( i + valueStart ).ToString();
                columnValue[i] = i + valueStart + 100;
            }
            
            parameters[0].Value = columnStr;
            parameters[1].Value = columnValue;
            sql = "update DIST_TEST set SVALUE=:svalue where SNAME=:sname";
            returnValue = db.RunUpdateSQL(sql, parameters, valueCount); 
MessageBox.Show(returnValue.ToString());
}
複製代碼

 

複製代碼
        public int RunUpdateSQL(string sqlStr,OracleParameter[] parameters,int paraCount)
        {
            int returnValue = 0;            
            try
            {
                Open();
                OracleCommand cmd = new OracleCommand()
                {
                    Connection = Conn,
                    ArrayBindCount=paraCount,
                    CommandText=sqlStr,
                    CommandTimeout=240
                };                
                cmd.Parameters.AddRange(parameters);                
                returnValue=cmd.ExecuteNonQuery();                
                cmd.Dispose();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());                
            }
            return returnValue;
        }
複製代碼

解決方法:將parameters的元素按sqlStr的順序更改一下OK了。

 

複製代碼
private void UpdateTable()
        {
            int valueStart = (int)NudStartValue.Value;
            int valueCount = (int)NudValueCount.Value;
            int returnValue = 0;
            int[] columnValue = new int[valueCount];
            string[] columnStr = new string[valueCount];
            string sql = string.Empty;

            OracleParameter[] parameters = new OracleParameter[]
            {                
                new OracleParameter(":svalue",OracleDbType.Int32),
                new OracleParameter(":sname", OracleDbType.Varchar2)

            };
            parameters[0].Direction = ParameterDirection.Input;
            parameters[1].Direction = ParameterDirection.Input;

            for ( int i = 0 ; i < valueCount ; i++ )
            {
                columnStr[i] = "No:" + ( i + valueStart ).ToString();
                columnValue[i] = i + valueStart + 100;
            }
            parameters[0].Value = columnValue;
            parameters[1].Value = columnStr;
            sql = "update DIST_TEST set SVALUE=:svalue where SNAME=:sname";
            returnValue = db.RunUpdateSQL(sql, parameters, valueCount); 
MessageBox.Show(returnValue.ToString());
}
複製代碼

    注意上面的代碼,第一個出現的是:svalue,第二個出現的是:sname。OracleParameter[]按這個順序添加就OK了。

其實如今問題是解決了,但還沒能理解,:sname和:svalue是對應的parameter.value的,爲什麼必定要按照update語句中

變量的順序呢。但願高手提示一下,多謝!

    但願能幫到遇到一樣問題的童鞋們。

 

 

VS2015經常使用快捷鍵總結

 

生成解決方案 F6,生成項目Shift+F6

調試執行F5,終止調試執行Shift+F5

執行調試Ctrl+F5

查找下一個F3,查找上一個Shift+F3

附加到進程Ctrl+Alt+P,逐過程F10,逐語句執行F11

切換斷點F9(添加或取消斷點)

運行至光標處Ctrl+F10

跳出當前方法Shift+F11

新建解決方案:Ctrl+Shift+N

打開解決方案:Ctrl+Shift+O

保存文件Ctrl+S,保存全部文件Ctrl+Shift+S

查看解決方案窗口Ctrl+W,Ctrl+S

查看屬性窗口Ctrl+W,Ctrl+P

錯誤列表顯示Ctrl+W,Ctrl+E

輸出列表顯示Ctrl+W,Ctrl+O

書籤窗口Ctrl+W,B

切換書籤Ctrl+B,T

切換到下一個書籤Ctrl+B,N

切換到上一個書籤Ctrl+B,P

清除書籤Ctrl+B,C

活動窗口切換Ctrl+Tab

瀏覽器窗口Ctrl+W,W

斷點窗口Ctrl+D,B

即便窗口Ctrl+D,I

工具箱Ctrl+W,Ctrl+X

全屏切換Shift+Alt+Enter,向後導航Ctrl+-,向前導航Ctrl+Shift+-

項目中添加類Shift+Alt+C

項目中添加新項Ctrl+Shift+A

項目中添加現有項Shift+Alt+A

查找Ctrl+F,在文件中查找Ctrl+Shift+F

漸進式搜索Ctrl+I,反式漸進式搜索Ctrl+Shift+I

替換Ctrl+H,在文件中替換Ctrl+Shift+H

轉到行號Ctrl+G

剪切板循環Ctrl+Shift+V 注:在剪貼板中能夠循環保存20項,您能夠任意的調用你剪切過的內容,只要不斷的按Ctrl+Shift+V鍵直到找到須要的那一項

遊標移動一個單詞 Ctrl+左右箭頭

滾動代碼屏幕,但不移動光標位置Ctrl+上下箭頭

刪除當前行Ctrl+Shift+L

隱藏或展開當前嵌套的摺疊狀態 Ctrl+M,M

全部隱藏或展開嵌套Ctrl+M,L

摺疊到定義Ctrl+M,O

切換顯示空白Ctrl+E,S

選擇矩形文本Shift+Alt+方向鍵

全變爲大寫:Ctrl+Shift+U 全變爲小寫Ctrl+U

強制智能感知 Ctrl+J

查看方法參數信息Ctrl+Shift+空格

查看當前代碼快速信息Ctrl+K,I

註釋選中行Ctrl+K,C或Ctrl+E,C

取消註釋行Ctrl+K,U或者Ctrl+E,U

插入代碼段Ctrl+K,X

插入外側代碼段Ctrl+K,S

轉到定義F12

生成方法存根,光標定位在調用的方法上,按Shift+Alt+F10

顯示查看實現接口方法,光標定位在類要實現的接口上,按Shift+Alt+F10

查看全部引用,Ctrl+K,R

查看調用層次Ctrl+K,T

除上述這些經常使用的快捷鍵外,若是開發人員想查看系統快捷或擴展本身的快捷鍵,能夠打開vs-【工具】-【選項】

在窗口中選中【環境】-【鍵盤】

搜索想要添加的快捷鍵功能。

 

出處:http://www.cnblogs.com/xuwendong/ 

相關文章
相關標籤/搜索