特斯拉將在9 月發表電動半掛卡車,充電後可行駛300 英里

馬斯克(Elon Musk)先前曾在推特表示,特斯拉將在9 月發表載貨用的全電動半掛卡車(Tesla Semi)原型,近期路透社報導披露了一些內容,讓有興趣的人能夠一窺其細節。

在馬斯克宣布要打造全電動重型卡車後,業界一直就對其負載量和續航力有所質疑,畢竟美國一些跑長途的柴油半掛卡車滿油續航力可以到達1,000 英里(約1,610 公里)左右,要能夠與之競爭還是有些困難。

 

Tesla Semi truck unveil set for September. Team has done an amazing job. Seriously next level.

— Elon Musk (@elonmusk)

 

如今答案揭曉了,特斯拉似乎沒有打算一開始就與這些超長程的卡車競爭。

邁阿密貨運公司Ryder System 今年稍早時曾與特斯拉進行技術討論,執行長Scott Perry 向路透社透露,Tesla Semi 是沒有臥鋪的日間車(day cab),續航里程約為200~300 英里(約322~483 公里),與一般長程重型卡車仍有著些許差距,但仍可以稱得上是長程卡車「入門款」。

對於半掛卡車的續航力,特斯拉並不願意做出評論,但它們先前確實曾經表示正在與潛在客戶直接合作,針對電動半掛卡車的開發需求進行交流。

雖然傳聞的Tesla Semi 原型似乎與一般柴油半掛卡車的續航力還有些許差距,但如果真的能夠順利做出並交付,還是有著一定的應用空間。數據分析公司Fleet Complete 策略總監Sandeep Kar 指出,從統計數據來看,美國約有30% 卡車的工作範圍是在100~200 英里之間(約161~322 公里)。

除此之外,相較汽柴油車的引擎,電動車的電動機維護需求較少,電力也比柴油更加的便宜,這些都很有可能吸引到希望降低排放量和營運成本的運輸公司。

但專家指出,對於橫跨各地的載運卡車來說,目前充電站仍不夠普及,而且需要的電池體積、重量將會擠壓到能夠附載的貨物空間。

Ryder System 和其客戶都認為,電動卡車的本體成本可能會比柴油車高出許多,但維護成本將會非常便宜,燃料成本也可以預測,在電池越來越便宜、環保監管越來越嚴格的情況下,電動卡車可能會逐漸出現增長。

(合作媒體:。圖片出處:public domain CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

Hook原理–逆向開發

今天我們將繼續講解逆向開發工程另一個重要內容–Hook原理講解。Hook,可以中文譯為“掛鈎”或者“鈎子”,逆向開發中改變程序運行的一種技術。按照如下過程進行講解

  1. Hook概述
  2. Hook技術方式
  3. fishhook原理及實例
  4. 符號表查看函數名稱
  5. 總結

一、Hook概述

在逆向開發中是指改變程序運行流程的技術,通過Hook可以讓自己的代碼運行在別人的程序中。需要了解其Hook原理,這樣就能夠對惡意代碼攻擊進行有效的防護。

 

二、Hook技術方式

2.1 Method Swizzle方式

Method Swizzle 上次已經講到,是利用OC的Runtime的特性,去動態改變SEL(方法編號)與IMP(方法實現)的對應關係,達到OC方法調用流程更改的目的。也是主要用於OC方法。

2.2 Cydia Substrate方式

Cydia Substrate 原名叫做Mobile SubStrate,主要作用為針對C函數,OC函數以及函數的地址進行Hook操作。並且有個很大的優勢,Cydia Substrate 並不是僅僅是針對iOS設計,Andriod一樣也可以使用。

2.2.1

Cydia Substrate定義了一系列的函數和宏,底層調用了objc的runtime和fishHook來替代目標函數或者系統方法。

其中有兩個函數

  • MSHookMessageEx主要用於OC方法
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
  • MSHookFunction主要用於C++和C函數
void MSHookFunction(voidfunction,void* replacement,void** p_original)

2.2.2 MobileLoader

MobileLoader主要用於加載第三方dylib運行的應用程序中。啟動時MobileLoader會根據指定的第三方動態庫加載進去,第三方動態庫也是我們寫的破解程序。

2.2.3 safe mode

破解程序的本質在於dylib,寄生於別人程序進程中。但是系統進程一旦出現錯誤,可能會導致整個進程崩潰,也可能會導致iOS程序崩潰。在Cydia Substrate 中引入了安全模式,如果一旦錯誤,三方的dylib會被禁用,便於查錯和修復。

2.3 fishHook

fishHook是Facebook提供一種動態修改鏈接Mach-O文件的工具。此利用Mach-O文件加載原理,通過修改非懶加載和懶加載兩個表的指針達到C函數的Hook的目的。

今天我們主要講解第三種方式fishHook達到更改程序的目的。

 

三、fishhook原理及實例

3.1 概述

fishhook的源碼地址為

fishhook的主要方法有兩個還有一個結構體

查看代碼結構為,將紅色圈起來部分移入到代碼中,即可使用fishhook來hook代碼。

 

 3.2 實例

3.2.1 Demo1實例1

// rebinding 結構體的定義 // struct rebinding { // const char *name; // 需要 HOOK 的函數名稱,字符串 // void *replacement; // 替換的新函數(函數指針,也就是函數名稱) // void **replaced; // 保存原始函數指針變量/地址的指針(它是一個二級指針!) // }; // C 語言傳參是值/址傳遞的,把它的值/址穿過去,就可以在函數內部修改函數指針變量的值

- (void)viewDidLoad {
    [super viewDidLoad];
     NSLog(@"123"); //rebinding結構體
    struct rebinding nslog;
    nslog.name = "NSLog";// 函數名稱
    nslog.replacement = myNslog; // 新的函數指針
    nslog.replaced = (void *)&sys_nslog;// 保存原始函數地址的變量的指針
    //rebinding結構體數組
    struct rebinding rebs[1] = {nslog};
    /**
     * 存放rebinding結構體的數組
     * 數組的長度
     */
    rebind_symbols(rebs, 1);
}
//---------------------------------更改NSLog-----------
//函數指針,用來保存原始的函數地址 (C 語言語法,函數指針類型變量)
static void(*sys_nslog)(NSString * format,...);
//定義一個新的函數
void myNslog(NSString * format,...){
    format = [format stringByAppendingString:@"勾上了!\n"];
    //調用原始的
    sys_nslog(format);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"點擊了屏幕!!");
}

上面的代碼運行結果如下:

3.2.2 Demo2實例2

void func(const char * str){
    NSLog(@"%s",str);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //rebinding結構體
    struct rebinding nslog;
    nslog.name = "func";
    nslog.replacement = new_func;
    nslog.replaced = (void *)&old_func;
    //rebinding結構體數組
    struct rebinding rebs[1] = {nslog};
    /**
     * 存放rebinding結構體的數組
     * 數組的長度
     */
    rebind_symbols(rebs, 1);
}
//---------------------------------更改NSLog-----------
//函數指針
static void(*old_func)(const char * str);
//定義一個新的函數
void new_func(const char * str){
      NSLog(@"%s + 1",str);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    func("哈哈");
}

運行結果如下:

從上面可以看出自定義的交換方法為什麼交換不了呢?首先可以肯定的是代碼是OK的,下面我們講解原理,為什麼自定義的方法不行呢?

 

 3.3 原理探究

Mach-O文件是如何加載的?

Dyld工具動態加載,加載MachO文件完成后,開始加載依賴的動態庫,也就是通過上篇博客的image List 可看到相關的類庫。

PIC(Promrammable Interrupt Controller)位置代碼獨立,由外設發出中斷請求需要中斷控制器來處理。

Mach-O文件內部調用系統函數時:

  • Mach-O _data段建立了一個指針(也就是符號,實現指向內部的函數調用,指向了外部的函數地址),指向了外部函數(dyld),可讀可寫,當Mach-O被加載進去,就會指向所指的函數。
  • Dyld會動態的綁定,將Mach-O中的data 段中指針指向了外部的函數,也是Dyld為什麼叫做動態綁定的原因。

這也回答了上面的問題,為什麼內部/自定義的函數不能修改,只能修改Mach-O文件的外部函數,如果是另外一個動態庫或者需要動態符號綁定的就可以(符號表中能找到才可以實現)

 

下面我們是真實查看內容,通過實例

利用第一個Demo來測試,運行起來,然後查看可執行文件,通過MachoView工具

 

從圖2看出offset偏移地址為3028,也就是NSLog函數文件的偏移地址,懶加載此表時在Mach-O文件偏移地址+函數偏移的地址。

下面以Demo1查看,在Demo1打斷點,查看Mach-O函數偏移地址,通過指令image list 第一個就是Mach-O內容和地址(本人上篇博客地址即可)

Mach-O在內存的偏移地址也就是Mach-O的真實地址,發現為 0x000000010a9c5000

通過上面紅色加重算法,計算Mach-O文件Data段的函數指針

發現執行完只有就會被綁定。NSLog函數文件就會被綁定。

下面再看一下,對於屏幕點擊的,hook如下

前提是我們去除ViewDidLoad方法裏面的NSLog(@“123”)這句代碼,運行代碼,最後將斷點斷在touchesBegan裏面,此時開始看地址和內容

截圖的前兩次打印是程序運行時,但是未曾點擊touchesBegan,后兩次是點擊屏幕時斷點進入到了裏面,再看內容,打印的對象是NSLog還是myNslog,通過上面發現是myNslog,說明Hook成功。

通過上面可看出,fishhook能夠Hook c函數,是因為Mach-O文件特點,PIC位置代碼獨立造就了靜態語言C也有動態的部分,之後通過Dyld進行動態綁定的時機,在這其中我們就可以做手腳,替換自定義的方法。

fishhook是根據方法字符串的名字“NSLog”,它是怎麼找到的呢?下面將講解利用符號表查看函數名稱字符串。

 

四、符號表查看函數名稱

 再次查看Mach-O文件,查看懶加載表中的NSLog函數

懶加載表是和動態符號表是一一對應關係,通過上面發現NSLog函數時第一個,而對應的Dynamic Symbol table也是第一個,打開Dynamic Symbol table

查看Dynamic Symbol Table 第一個也是NSLog,查看Data值為7A,對應的十進製為122,然後到Symbols Table裏面查看122,如下:

 

查看Symbols Table的data值為0000009B,然後在String Table Index去看函數偏移值為0000009B的內容,如下:

 

 為什麼選擇00004F94查看NSLog呢,我們從上面得知Symbols Table的data值為0000009B,然後加上String Table的函數第一個地址為00004F04,然後將0000009B + 00004F04 = 0X4F9F,最後看00004F94裡面包含了0X4F9F,藍色內容看出是NSLog內容,也就是找到啦。完美!!!

以上過程可以在fishhook中github上有說明圖:

 

上面的說明圖也就是通過符號表查看函數名稱以及反過來也可以逆查的過程。配上說明圖,方便大家熟悉流程。

 

五、總結

上面講述了Hook的幾種技術方式以及fishhook的原理探究,以及如何讓別人的app實現自己的代碼。下面我們對此總結一下,寫了一個本篇博客的整個過程便於大家整理,希望對大家有所幫助加深理解。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

CSS如何設置列表樣式屬性,看這篇文章就夠用了

列表樣式屬性

  • HTML中有2種列表、無序列表和有序列表,在工作中無序列表比較常用,無序列表就是ul標籤和li標籤組合成的稱之為無序列表,那什麼是有序列表呢?就是ol標籤和li標籤組合成的稱之為有序列表,列表的基礎知識就簡單說明下,本章內容主要說明的是如何給列表設置樣式,若有不懂列表是什麼的園友筆者建議去進行學習。
  • 列表樣式常用的屬性有4種如:list-style-typelist-style-positionlist-style-imagelist-style,在這裏就是簡單說明下列表常用的屬性名稱而已,具體使用或每一個屬性值的介紹,在下面會具體的說明,愛學習的園友不用擔心哦。

list-style-type屬性

  • list-style-type屬性作用:就是設置列表前面項目符號的類型。
  • list-style-type屬性值說明表。
屬性值 描述
none 將列表前面項目符號去除掉。
disc 將列表前面項目符號設置為實心圓。
circle 將列表前面項目符號設置為空心圓。
square 將列表前面項目符號設置為實心小方塊。

屬性值為none使用方式

  • 讓我們進入列表的list-style-type屬性值為none實踐,實踐內容如:使用class屬性值為.box將列表前面項目符號去除掉。
  • 我們在實踐列表的list-style-type屬性值為none之前看看列表前面項目符號是什麼,讓初學者有一個直觀的印象。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為none實踐</title>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

  • 現在愛學習的園友們知道了什麼是列表前面的項目符號了,那我們就進入列表的list-style-type屬性值為none實踐咯。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為none實踐</title>
    <style>
        .box{
            list-style-type: none;
        }
    </style>
</head>
  
<body>
    <ul class="box">
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

  • 既然能看到這裏說明園友已經掌握了,列表的list-style-type屬性值為none使用,恭喜恭喜恭喜。

屬性值為disc使用方式

  • 在這裏說明下列表的list-style-type屬性值為disc,列表的list-style-type屬性值默認就是disc,如果是細心的園友已經發現了,上面有現成的列子在這裏就不過多的介紹了,這個屬性值為disc就跳過了哈。

屬性值為circle使用方式

  • 讓我們進入列表的list-style-type屬性值為circle實踐,實踐內容如:將列表前面的項目符號設置為空心圓。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為circle實踐</title>
    <style>
        .box{
            list-style-type: circle;
        }
    </style>
</head>
  
<body>
    <ul class="box">
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

屬性值為square使用方式

  • 讓我們進入列表的list-style-type屬性值為square實踐,實踐內容如:將列表前面項目符號設置為實心方塊。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>列表的list-style-type屬性值為square實踐</title>
    <style>
        .box{
            list-style-type: square;
        }
    </style>
</head>
  
<body>
    <ul class="box">
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
</body>
</html>
  • 結果圖

list-style-position屬性

  • list-style-position屬性作用:設置列表前面項目符號的位置,list-style-position屬性有2個屬性值,分別是outsideinside,具體說明看下面的屬性值說明表。

list-style-position屬性值說明表

屬性值 描述
outside 將列表前面項目符號設置在外面。
inside 將列表前面項目符號設置在裏面。

屬性值為outside使用方式

  • 在實踐list-style-position屬性值為outside之前,我們先看看列表前面的項目符號的默認位置在哪,筆者為了讓初學者有一個直觀的印象,筆者將HTML頁面中的ul標籤li標籤設置了一些樣式。
  • ul標籤樣式如::width寬度設置為300px像素、height高度為150px像素、border邊框為(1px像素、显示是實線、邊框顏色為藍色)、樣式。
  • ul標籤中的li標籤設置樣式如:width寬度設置為280px像素、height高度為30px像素line-height行高為30px像素、border邊框為(1px像素、显示是實線、邊框顏色為紅色)、樣式。
  • 如果園友沒有掌握border邊框的知識,愛學習的園友不用擔心以後會寫border邊框的文章,若有想了解border邊框知識的園友可以去進行學習。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>屬性值為outside使用方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 現在大家應該很清楚的看到了列表前面項目的符號默認在ul標籤和li標籤之間的位置,現在我們知道了列表前面的項目符號的默認位置,那我們進行list-style-position屬性值為outside實踐了,實踐內容:將HTML頁面中的列表前面的項目符號設置為外面。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>屬性值為outside使用方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style-position: outside;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:為什麼給列表設置了list-style-position屬性值為outside,運行結果沒有發生任何變化呢,因為列表前面的項目符號默認就在外面的位置,列表前面的項目符號外面的位置就是ul標籤和li標籤之間的位置。

屬性值為inside使用方式

  • 通過介紹list-style-position屬性值為outside,大家已經知道了列表前面項目符號外邊的位置了,接下來我們將列表前面項目符號設置在裏面咯。
  • 讓我們進入list-style-position屬性值為inside實踐,將列表前面項目符號位置設置在裏面。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>屬性值為inside使用方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style-position: inside;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:list-style-position屬性值為inside作用就是將列表前面項目符號位置設置在li標籤中,這就是裏面的位置。

list-style-image屬性

  • list-style-image屬性作用:將列表前面項目符號設置為一張圖片。

list-style-image屬性說明表

屬性值名稱 描述
url 設置列表前面項目符號的圖片的路徑
  • 讓我們進入list-style-image屬性的實踐,實踐內容將列表前面項目符號更換一張圖片。

  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>使用list-style-image屬性方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 280px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style-image: url(./img/001.png);
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:圖片路徑一定要寫在url(./img/001.png);括號當中,不然不會被渲染的,圖片路徑可以是相對路徑也可以絕對路徑。

list-style屬性

  • list-style屬性是(list-style-type屬性、list-style-position屬性、list-style-image屬性)的一個簡寫屬性,它集成了(list-style-type屬性、list-style-position屬性、list-style-image屬性)的功能。
  • 讓我們進入 list-style屬性實踐,既然看到了這裏想必園友都已經掌握了本章的內容了。
  • 代碼塊

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>使用list-style屬性方式</title>
    <style>
        ul {
            width: 300px;
            height: 150px;
            border: 1px solid #00F;
        }
         ul li {
          
            width: 290px;
            height: 30px;
            line-height: 30px;
            border: 1px solid red;
            list-style: none inside  url(./img/001.png) ;
        }
 
    </style>
</head>
  
<body>
    <ul>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
        <li>成功不是打敗別人,而是改變自己。</li>
    </ul>
   
</body>
</html>
  • 結果圖

  • 注意:list-style屬性值可以也1個或23個,順序沒有要求,若有不明白的園友可以做個實例看看就明白了,學習就要多嘗試不要偷懶呦。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

GoStation電池交換站突破400座,電池交換成為市場主流

 

結合智慧能源與智慧交通的新創科技品牌Gogoro(睿能創意股份有限公司)7 日公布全台建置與營運中的GoStation 電池交換站已達400 站,再度創造新的里程碑。從2015 年7 月至今,Gogoro 在基隆到屏東的台灣西半部地區,平均每1.8 天即新增一座電池交換站,最近一個月,每日提供將近4 萬名車主接近17,000 顆的電池交換服務,電池交換服務已經成為台灣消費者購買電動機車時的首要選擇。

自從Gogoro 於2015 年在台北市設立首座電池交換站以來,在短短兩年多的時間,建置了400 座電池交換站,廣布於基隆到屏東的各個縣市,推升Gogoro 電動機車市佔率至85.1%,並穩居台灣機車市場第四名的寶座。在今年7 月開通雲嘉地區電池交換站後,暢騎台灣西半部,不再是夢想。同時六都的電池交換站建置更來到一公里一站。

Gogoro 行銷總監陳彥揚說:「我們會依據人口密集度、車輛密極度以及道路的重要性來建置及調度電池交換站。根據車主換電的大數據分析,換電最密集的電池交換站位於Gogoro 永和中正店,而換電的尖峰時刻不外乎是上、下班的時間。有趣的是,雖然全台已經有將近400 座電池交換站,但每名消費者平均只會造訪其中的3-4 站來更換電池。證明Gogoro 能源網路的大數據分析,能計算出消費者換電池的使用行為模式,滿足車主們的需求。」

走在環保、綠能尖端的Gogoro,目前共建置了兩座太陽能換電站,分別是八里公兒四電池交換站和Gogoro 師大和平店站,這兩站設有物聯網智慧平台,透過分析供電情況的螢幕,說明了包括減少碳排量、減少樹木砍伐面積、綠能總儲電量、城市電網和太陽能發電量等訊息,讓每名換電的民眾,清楚的知道,自己對環境的貢獻度。

陳彥揚說:「Gogoro 致力發展潔淨的智慧能源,希望具備能源調度能力的智慧電網,能成為城市的電力調節樞紐,以促成電力平衡。對於Gogoro 車主而言,Gogoro 不再僅是都會的通勤工具,而是更進一步深入使用者的生活,同時讓生活環境更環保、更健康。」

Gogoro 目前擁有近4 萬名車主,總共累積超過570 萬次的電池交換,總里程數超過1 億100 公里,已經替地球減少將近840 萬公斤的二氧化碳排放,隨著未來再生能源比例逐漸提升,Gogoro 的車主們將更對地球與環境產生更多正面的影響力。而Gogoro 更會透過大數據進行科學的規劃,以調控電池供應,未來,即便新增的萬名車主同步上路,也能確保能源及電池的調配無虞。

(合作媒體:。圖片出處:科技新報)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

三大方向深化汽車產業變革 – 新能源汽車,智慧汽車,輕量化設計

隨著汽車產業的變革,新能源汽車時代已經到來,同時更具顛覆性的智慧汽車也在加速發展,汽車設計中的輕量化也成為了行業重要課題之一,這些都對整個行業的關鍵技術創新提出了更高的要求。中國新能源汽車產業已進入規模化發展新階段及政策和市場共同驅動的快速成長期,而智慧汽車也將引領智慧交通進入一個新的發展階段,智慧汽車已經超越了汽車的概念,是一種智慧出行工具的理念,隨著節能減排的深入人心及政策導向,汽車設計輕量化成為了節能減排的重要途徑之一。2017全球新能源智慧汽車大會將進行一次全面行業熱點方向及最新技術的分享。

 

2017全球新能源智慧汽車大會(第二屆上海斯圖加特汽車及動力技術國際研討會SSSAET是由上海市人民政府德國巴登符騰堡州政府上海市嘉定區人民政府指導,同濟大學斯圖加特大學主辦,上海車犇資訊技術有限公司承辦的大型會議,將於2017年的1026-27在上海隆重舉行。會議主要分為智慧汽車,新能源汽車,整車設計三個主題方向,由六大主題分論壇組成,分別為“燃料電池,動力電池,電驅動,智慧網聯,汽車設計,汽車輕量化”

 

兩天大會將會邀請來自相關政府機構嘉賓,國內外40+所著名汽車研究機構,學者,整車廠,零部件廠商參與研討,演講嘉賓由2位院士及43位國內外重量級演講嘉賓強大陣容組成(已有36位嘉賓確認,其中外籍為16位),聚集500+位的國內外行業精英參與討論。且本次會議將在投稿件中,精選40篇品質較高的優秀學術論文在同濟大學學報增刊上發表。

 

大會亮點:

最高演講規模: 2位院士,43位國內外重量級演講嘉賓

最高演講嘉賓確認率:已確認36位,外籍專家16

最具權威和專業性:40篇論文,100%EI檢索

最大規模之一:500+行業人員蒞臨;80+主機廠整車商專業人士參與

最全最新議題:3大論壇6大熱點主題全覆蓋

 

2017全球新能源智慧汽車大會(第二屆上海– 斯圖加特汽車及動力技術國際研討會)期待您的參與!如需更多會議資訊請聯繫:

 

連絡人: Latika LIU(劉小姐)

電話:021 6093 0815

郵箱:

網站: www.sssaet.com

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

程序員一般通過什麼途徑接私活?

二哥,你好,我想知道一般程序猿都如何接私活,我也想接,能告訴我一些方法嗎?

上面是一個讀者“煩不煩”問我的一個問題。其實不止是“煩不煩”,還有很多讀者問過我類似這樣的問題。

我接的私活不算多,掙到的錢也沒有多少,加起來不到 20W。說實話,這個數目說出來我是有點心虛的,畢竟太少了,大家輕噴。但我想,恰好配得上“一般程序員”這個稱號啊。畢竟蒼蠅再小也是肉,我也算是有經驗的人了。

唾棄接私活、做外包的程序員有很多很多,曾經高傲的我也嫌棄過。但沒辦法,為了掙點零花錢,我垂下了高昂的頭。記得有位朋友曾說過,當年沈從文為了生計,寫了很多稱不上他自己喜歡的文字給報刊。

聽朋友這麼一說,我也不再覺得“接私活”是多麼一件值得羞愧的事情了。人首先要活着,才有體力講情懷啊。好了,言歸正傳,我來替“煩不煩”同學介紹幾個容易上手的操作。

01、朋友介紹

大體上,天底下做生意都只有一條捷徑:從熟人下手。

“哥們,聽說你有個朋友是做程序員的,我這有台電腦不知道為啥黑屏了,能問問他知道什麼原因嗎?要是能修好,保准請你吃頓大餐。”

“老弟啊,我有一個朋友說最近流行炒鞋,我想你不是程序員嘛,找你最合適了,要不我把他推薦給你,談成的話給我發個紅包就行了。”

我的第一個私活,就是之前在蘇州的一個同事介紹的。不過最後黃了。我搞了兩周時間(技術框架用的 JEPF),同事說甲方換方案了,沒把我氣壞。

同事礙於情面,說有機會請我吃頓飯。這一等就是 3 年,3 年過去了,飯還是沒有吃到。主要是因為我這位朋友在蘇州,我在洛陽,吃飯是沒辦法遠程完成啊。

第二個私活,是之前在蘇州的一個領導介紹的。由同事升級為領導,多少靠譜了點。這次做的是蘇州相城區的一個电子商務網站。前後做了三個多月,最後拿到手的錢也就不到一萬塊錢。

現在感覺自己當時是在出售廉價勞動力,何止是廉價,簡直是公益事業。不過,第一次接私活,拿到錢買了個華為的 MateBook,真香。

第二個私活做完后,領導可能覺得虧待了我,良心難安,就介紹了第三個私活給我。這次蠻輕鬆的,一個月搞定,還不累,兩萬塊到手。

既然是私活,當然都是利用業餘時間做的。這個投入的成本和實際得到的回報是一定要考慮的

我第一個私活打了水漂,辛苦了兩周,零回報。不過,這也是接私活常有的事,需要用平常心來對待。

第二個私活說實話非常辛苦,有幾次熬到半夜兩三點,當時覺得太不划算了。但當初自己接了,就只能忍着拼到底。畢竟咱是敬業愛崗的好同志。

第三個私活就相對輕鬆多了,單位時間內的收益非常高,算下來一個小時有 500 的工時費吧,就彷彿是對前兩個的補償。

總結一下,朋友介紹的項目相對來說還是比較靠譜的,前提條件是要有一定的“人情世故”原始成本積累。如果我當時在蘇州表現得不夠優異,和同事、領導的關係相處的不夠融洽,那自然他們也不會時隔多年後再找到我。

記住一點,做事的同時要好好的做人。當你既有能力,又值得信任的時候,私活就會找上門來

02、個人品牌

既然是朋友,自然就不會有很多。也就意味着,單純依賴朋友介紹的私活來源是有限度的。那如果想接更多的私活,該怎麼辦呢?

這就需要個人品牌了。

我平常不是喜歡寫作嘛,分享了很多技術文章在各大平台上,瀏覽量還算不錯。博客園上的排名和瀏覽量都能拿得出手。

博客地址:

當你做了一件事,並且一直在堅持,況且還做出了一定的成績,自然就會有生意主動找上門來——花香蜂自來嘛

寫博客的好處有很多,比如說吸引一批忠實的讀者,他們追隨你的文字,喜歡你的風格;再比如說勾引一些出版社,他們欣賞你的文字,願意合作互利共贏。

最後,還會有一些做私活的甲方。以前,我總覺得這是不可能發生的事情,他們是怎麼找到我的?很不可思議,但互聯網就是這麼神奇,你覺得不可能,它卻悄悄地發生着。

第一個通過這個途徑找到我的甲方,姓康。康哥找到我后,一上來就對我一頓吹捧(甭管是真是假)。信任建立起來后,他就說自己在醞釀一個很牛逼的項目,看我有沒有意向一起做。

然後呢,承諾項目成功后,再給我一定數額的獎勵金,並且寫到了合同里。吃完他這個大餅,我很飽,忍不住打了好幾個嗝。

再然後,我們就開始整理需求,然後我出報價,他再砍價;他再提需求,我再加價。最後呢,項目總款談到 7.5 萬,兩個多月的工期。合同的細節也敲定的差不多了。

結果,黃了。和我合夥的一個開發人員小何覺得甲方新提的需求需要再追加 600 塊,甲方覺得這點錢擱不住再追加了。總之呢,7.5W 的項目就因為這個細節黃了,很遺憾。

第二個通過這個途徑找到我的甲方,叫鵬哥。開發一個網站,總價一萬多,吃了上次的虧后,我自己就不想參与了,就找了一個讀者(小李)做。

結果這個項目爛尾了。小李交付的產物我自己都覺得不好意思,bug 非常多。在我看來,既然項目的訂金已經收了,作為開發人員,至少應該交付一個說得過去的產物——負責任吧。

很遺憾,個人品牌招攬來的前兩個私活最後都搞砸了。這裡有必要總結一下:作為程序員,既然打定主意要接私活,那麼接到的時候一定要珍惜。如果一開始覺得價錢低,就趁早拒絕,免得因為需求變動等等原因砸了招牌

當然了,通過這個途徑也做成了四單,每單的價格差不多兩萬。這裏就不再詳談了。

個人品牌的確可以引流來更多的私活,但與此同時,也會浪費很多時間。

像這種泛泛之談的意向客戶有很多。話說,我啥時候變成“社會王”了,我特么是正兒八經的“王老師”好不好?

03、外包平台

外包平台有很多,我就不再一一列舉了。只說幾個我認為還不錯的平台,也不打算細說,免得有些讀者“誇我”良苦用心地在打廣告。

04、一點忠告

在我寫這篇文章的時候,突然收到朋友的一條信息,說她們公司剛剛辭退了一位員工,還通報批評了,就因為接私活被舉報了——她們公司一般不辭退員工,這下子相當於鐵飯碗丟了。

所以說呢,接私活是有風險的。並且在我看來,如果主業沒有遇到瓶頸,強烈不建議接私活。就好比一個小孩子走路還不會,就要求他要跑起來。

時間對於一個程序員來說很寶貴,尤其是一個正在成長中的程序員。

如果你確實急用錢,價格又合適,那就去做。如果不怎麼缺錢,我再強調一次,別去接私活。私活的錢不好掙是一個方面,更重要的是如果你把做私活的時間花在提升自己上,產生的價值就要大得多。等你提升了自己,提升了固定薪水,遠比拿的這點私活的錢划算。千萬不要“撿了芝麻丟了西瓜”。

如果你像我,主業上遇到了瓶頸,平時的時間比較充分,想有一些額外的收入,同時為了保持技術的熟練度,這種情況下,是可以考慮接一些私活的。對於那種投入時間巨大,回報很可憐的項目,千萬不要接!

另外呢,如果甲方只提供幾個簡單的想法,甚至幾張圖片,更或者發一個參照的效果網站,就可以直接忽視了,這類通通不靠譜!

最後呢,還要說一句,如果訂金都收了,自己就算是覺得吃了虧,也應該有點職業素質,把像樣的產品交付,千萬別應付。

謝謝大家的閱讀,原創不易,喜歡就隨手點個贊,這將是我最強的寫作動力。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

你真的會用JavaScript中的sort方法嗎

  在平時的業務開發中,數組(Array) 是我們經常用到的數據類型,那麼對數組的排序也很常見,除去使用循環遍曆數組的方法來排列數據,使用JS數組中原生的方法 sort 來排列(沒錯,比較崇尚JS原生的力量)。

1、舉個栗子

  數組中能夠直接用來排序的方法有:reverse() 和 sort(),由於 reverse()方法不夠靈活,才有了sort()方法。在默認情況下,sort()方法按升序排列數組。

var arr=[1,3,5,9,4];
console.log(arr.sort());
// 輸出: [1, 3, 4, 5, 9]

這時發現數據按照從小到大排列,沒問題;於是再把數組改成:var arr=[101,1,3,5,9,4,11];,再調用sort()方法打印排序結果。

var arr=[101,1,3,5,9,4,11];
console.log(arr.sort());
// 輸出: [1, 101, 11, 3, 4, 5, 9]

這個時候發現數組101,11都排在3前面,是因為 sort() 方法會調用數組的toString()轉型方法,然後比較得到的字符串,確定如何排序,即使數組中的每一項都是數值,sort()方法比較的也是字符串。

那麼字符串又是怎麼排序的呢,是根據字符串的unicode編碼從小到大排序的。下面我們嘗試打印出數組每一項的unicode編碼看一下。

...
// 轉碼方法
function getUnicode (charCode) {
    return charCode.charCodeAt(0).toString(16);
}
// 打印轉碼
arr.forEach((n)=>{
  console.log(getUnicode(String(n)))
});

// 輸出: 31 31 31 33 34 35 39

驚奇地發現,1,101,11的字符串unicode編碼都是31

2、傳入比較函數以指定順序

  以上發現sort()方法不是按照我們想要的順序排序的,那麼,怎麼解決呢,sort()方法可以接收一個比較函數作為參數,以便指定哪個值位於哪個值前面

比較函數(compare)接收兩個參數,如果第一個參數位於第二個之前則返回一個負數,如果兩個參數相等則返回0,如果第一個參數位於第二個之後則返回一個整數。

function compare(value1,value2){
  if (value1 < value2){
    return -1;
  } else if (value1 > value2){
    return 1;
  } else{
    return 0;
  }
}

我們把比較函數傳遞給sort()方法,在對arr數組進行排列,打印結果如下:

var arr=[101,1,3,5,9,4,11];
console.log(arr.sort(compare));
// 輸出: [1, 3, 4, 5, 9, 11, 101];

可以發現排序從小到大沒有什麼問題。

3、對象數組的排序

  sort() 方法通過傳入一個比較函數來排序数字數組,但是在開發中,我們會對一個對象數組的某個屬性進行排序,例如id,年齡等等,那麼怎麼解決呢?

要解決這個問題:我們可以定義一個函數,讓它接收一個屬性名,然後根據這個屬性名來創建一個比較函數並作為返回值返回來(JS中函數可以作為值來使用,不僅可以像傳遞參數一樣把一個函數傳遞給另一個函數,也可以將一個函數作為另一個函數的結果返回,函數作為JS中的第一等公民不是沒有原因的,確實很靈活。),代碼如下。

function compareFunc(prop){
  return function (obj1,obj2){
    var value1=obj1[prop];
    var value2=obj2[prop];
    if (value1 < value2){
        return -1;
    } else if (value1 > value2){
        return 1;
    } else{
        return 0;
    }
  }
}

定義一個數組users,調用sort()方法傳入compareFunc(prop)打印輸出結果:

var users=[
    {name:'tom',age:18},
    {name:'lucy',age:24},
    {name:'jhon',age:17},
];
console.log(users.sort(compareFunc('age')));
// 輸出結果
[{name: "jhon", age: 17},
{name: "tom", age: 18},
{name: "lucy", age: 24}]

在默認情況下,調用sort()方法不傳入比較函數時,sort()方法會調用每個對象的toString()方法來確定他們的次序,當我們調用compareFunc(‘age’)方法創建一個比較函數,排序是按照對象的age屬性排序的。

4、XML節點的排序

  儘管現在很多後台返回數據就是JSON格式的,很輕量又方便解析。但是之前有個項目因為後台返回的都是XML字符串,前端拿到數據后還得進行序列化,有些需要排序,之前的排序都是把XML轉換成數組對象進行排序的,這樣做沒有什麼問題,只不過感覺代碼寫的很冗餘麻煩。後來就突發奇想,xml獲取得到也是類數組對象,把類數組對象轉換成數組不就可以直接排序了么。

// 1.模擬後端返回的XML字符串
var str=`
<root>
  <user>
    <name>tom</name>
    <age>18</age>
  </user>
  <user>
    <name>lucy</name>
    <age>24</age>
  </user>
  <user>
    <name>jhon</name>
    <age>17</age>
  </user>
<root>
`   
// 2.定義比較函數
function compareFunction(prop){
  return function (a, b) {
      var value1= a.getElementsByTagName(prop)[0].textContent;
      var value2= b.getElementsByTagName(prop)[0].textContent;
      if (value1 < value2){
        return -1;
      } else if (value1 > value2){
        return 1;
      } else{
        return 0;
    }
  }
}
// 3.xml字符串轉換成xml對象
var domParser = new DOMParser();
var xmlDoc = domParser.parseFromString(str, 'text/xml');
var userElements=xmlDoc.getElementsByTagName('user'));
// 4.userElements類數組對象轉換成數組再排序
var userElements=Array.prototype.slice.call(xmlDoc.getElementsByTagName('user'));
var _userElements=userElements.sort(compareFunction('age'));
// 5.打印排序后的結果
_userElements.forEach((user)=>{
  console.log(user.innerHTML);
});

打印排序后的結果

可以發現,XML節點已經按照age從小到大排序了。

5、總結

  JS數組的sort方法因為有了傳入比較函數使得排序靈活了許多,還有根據時間,漢字拼音首字母排序等等,我們只要牢記通過傳入比較函數明確比較兩個對象屬性值,通過比較屬性值來決定對象的排序順序即可。自己也是在工作中遇到問題從而發現解決問題的新思路,以上就簡單總結這麼多了,如有不足,多多指正。

參考資料:
《JavaScript高級教程》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

[ASP.NET Core 3框架揭秘] 文件系統[4]:程序集內嵌文件系統

一個物理文件可以直接作為資源內嵌到編譯生成的程序集中。藉助於EmbeddedFileProvider,我們可以採用統一的編程方式來讀取內嵌的資源文件,該類型定義在 “Microsoft.Extensions.FileProviders.Embedded”這個NuGet包中。在正式介紹EmbeddedFileProvider之前,我們必須知道如何將一個項目文件作為資源內嵌入到編譯生成的程序集中。

一、將項目文件變成內嵌資源

在默認情況下,我們添加到一個.NET Core項目中的靜態文件並不會成為目標程序集的內嵌資源文件。如果需要將靜態文件作為目標程序集的內嵌文件,我們需要修改當前項目對應的.csproj文件。具體來說,我們需要按照前面實例演示的方式在.csproj文件中添加<ItemGroup>/<EmbeddedResource>元素,並利用Include屬性顯式地將對應的資源文件包含進來。當我們直接利用Visual Studio將資源文件的Build Action屬性設置為“Embedded resource”,IDE會自動幫助我們修改項目文件。

<EmbeddedResource>的Include屬性可以設置多個路徑,路徑之間採用分號(“;”)作為分隔符。以上圖所示的目錄結構為例,如果我們需要將root目錄下的四個文件作為程序集的內嵌文件,我們可以修改.csproj文件並按照如下的形式將四個文件的路徑包含進來。

<Project Sdk="Microsoft.NET.Sdk">
    ...
    <ItemGroup>
        <EmbeddedResource  
            Include="root/dir1/foobar/foo.txt;root/dir1/foobar/bar.txt;root/dir1/baz.txt;root/dir2/qux.txt"></EmbeddedResource> 
    </ItemGroup>
</Project>

除了指定每個需要內嵌的資源文件的路徑之外,我們還可以採用基於通配符“*”和“**”的Globbing Pattern表達式將一組匹配的文件批量包含進來。同樣是將root目錄下的所有文件作為程序集的內嵌文件,如下的定義方式就會簡潔得多。

<Project Sdk="Microsoft.NET.Sdk">
    ...
    <ItemGroup>
        <EmbeddedResource  Include="root/**"></EmbeddedResource> 
    </ItemGroup>
</Project>

<EmbeddedResource>除了具有一個Include屬性用來添加內嵌資源文件之外,它還具有另一個Exclude屬性負責將不符合要求的文件排除出去。還是以前面這個項目為例,對於root目錄下的四個文件,如果我們不希望文件baz.txt作為內嵌資源文件,我們可以按照如下的方式將它排除。

<Project Sdk="Microsoft.NET.Sdk">
    ...
    <ItemGroup>
        <EmbeddedResource  Include="root/**" Exclude="root/dir1/baz.txt"></EmbeddedResource> 
    </ItemGroup>
</Project>

二、讀取資源文件

每個程序集都有一個清單文件(Manifest),它的一個重要作用就是記錄組成程序集的所有文件成員。總的來說,一個程序集主要由兩種類型的文件構成,它們分別是承載IL代碼的託管模塊文件和編譯時內嵌的資源文件。針對上圖所示的項目結構,如果我們將四個文本文件以資源文件的形式內嵌到生成的程序集(App.dll)中,程序集的清單文件將會採用如下所示的形式來記錄它們。

.mresource public App.root.dir1.baz.txt
{
  // Offset: 0x00000000 Length: 0x0000000C
}
.mresource public App.root.dir1.foobar.bar.txt
{
  // Offset: 0x00000010 Length: 0x0000000C
}
.mresource public App.root.dir1.foobar.foo.txt
{
  // Offset: 0x00000020 Length: 0x0000000C
}
.mresource public App.root.dir2.qgux.txt
{
  // Offset: 0x00000030 Length: 0x0000000C
}

雖然文件在原始的項目中具有層次化的目錄結構,但是當它們成功轉移到編譯生成的程序集中之後,目錄結構將不復存在,所有的內嵌文件將統一存放在同一個容器中。如果我們通過Reflector打開程序集,資源文件的扁平化存儲將會一目瞭然。為了避免命名衝突,編譯器將會根據原始文件所在的路徑來對資源文件重新命名,具體的規則是“{BaseNamespace}.{Path}”,目錄分隔符將統一轉換成“.”。值得強調的是資源文件名稱的前綴不是程序集的名稱,而是我們為項目設置的基礎命名空間的名稱。

表示程序集的Assembly對象定義了如下幾個方法來提取內嵌資源的文件的相關信息和讀取指定資源文件的內容。GetManifestResourceNames方法幫助我們獲取記錄在程序集清單文件中的資源文件名,而另一個方法GetManifestResourceInfo則用於獲取指定資源文件的描述信息。如果我們需要讀取某個資源文件的內容,我們可以將資源文件名稱作為參數調用GetManifestResourceStream方法,該方法會返回一個讀取文件內容的Stream對象。

public abstract class Assembly
{   
    public virtual string[] GetManifestResourceNames();
    public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName);
    public virtual Stream GetManifestResourceStream(string name);
}

同樣是針對前面這個演示項目對應的目錄結構,當四個文件作為內嵌文件被成功轉移到編譯生成的程序集中后,我們可以調用程序集對象的GetManifestResourceNames方法獲取這四個內嵌文件的資源名稱。如果以資源名稱(“App.root.dir1.foobar.foo.txt”)作為參數調用GetManifestResourceStream方法,我們可以讀取資源文件的內容,具體的演示如下所示。

class Program
{
    static void Main()
    {
        var assembly = typeof(Program).Assembly;
        var resourceNames = assembly.GetManifestResourceNames();
        Debug.Assert(resourceNames.Contains("App.root.dir1.foobar.foo.txt"));
        Debug.Assert(resourceNames.Contains("App.root.dir1.foobar.bar.txt"));
        Debug.Assert(resourceNames.Contains("App.root.dir1.baz.txt"));
        Debug.Assert(resourceNames.Contains("App.root.dir2.qgux.txt")); 

        var stream = assembly.GetManifestResourceStream("App.root.dir1.foobar.foo.txt");
        var buffer = new byte[stream.Length];
        stream.Read(buffer, 0, buffer.Length);
        var content = Encoding.Default.GetString(buffer);  
        Debug.Assert(content == File.ReadAllText("App/root/dir1/foobar/foo.txt"));
    }
}

三、EmbeddedFileProvider

在對內嵌於程序集的資源文件有了大致的了解之後,針對EmbeddedFileProvider的實現原理就很好理解了。由於內嵌於程序集的資源文件採用扁平化存儲形式,所以在通過 EmbeddedFileProvider構建的文件系統中並沒有目錄層級的概念。我們可以認為所有的資源文件都保存在程序集的“根目錄”下。對於EmbeddedFileProvider構建的文件系統來說,它提供的IFileInfo對象總是對一個具體資源文件的描述,這是一個具有如下定義的EmbeddedResourceFileInfo對象。

public class EmbeddedResourceFileInfo : IFileInfo
{
    private readonly Assembly     _assembly;
    private long? _length;
    private readonly string  _resourcePath;

    public EmbeddedResourceFileInfo(Assembly assembly, string resourcePath, string name, DateTimeOffset lastModified)
    {
        _assembly = assembly;
        _resourcePath = resourcePath;
        this.Name = name;
        this.LastModified = lastModified;
    }

    public Stream CreateReadStream()
    {
        Stream stream = _assembly.GetManifestResourceStream(_resourcePath);
        if (!this._length.HasValue)
        {
            this._length = new long?(stream.Length);
        }
        return stream;
    }
    
    public bool Exists => true;
    public bool IsDirectory => false;
    public DateTimeOffset LastModified { get; }    

    public string Name { get; }
    public string PhysicalPath => null;
    public long Length
    {
        get
        {
            if (!_length.HasValue)
            {
                using (Stream stream =_assembly.GetManifestResourceStream(this._resourcePath))
                {
                    _length = stream.Length;
                }
            }
            rReturn _length.Value;
        }
    }
}

如上面的代碼片段所示,我們在創建一個EmbeddedResourceFileInfo對象的時候需要指定內嵌資源文件在清單文件的中的路徑(resourcePath)、所在的程序集、資源文件的名稱(name)和作為文件最後修改時間的DateTimeOffset對象。由於一個EmbeddedResourceFileInfo對象總是對應着一個具體的內嵌資源文件,所以它的Exists屬性總是返回True,IsDirectory屬性則返回False。由於資源文件系統並不具有層次化的目錄結構,它所謂的物理路徑毫無意義,所以PhysicalPath屬性直接返回Null。CreateReadStream方法返回的是調用程序集的GetManifestResourceStream方法返回的輸出流,而表示文件長度的Length返回的是這個Stream對象的長度。

如下所示的是 EmbeddedFileProvider的定義。當我們在創建一個EmbeddedFileProvider對象的時候,除了指定資源文件所在的程序集之外,還可以指定一個基礎命名空間。如果該命名空間沒作顯式設置,默認情況下會將程序集的名稱作為命名空間,也就是說如果我們為項目指定了一個不同於程序集名稱的基礎命名空間,那麼當創建這個EmbeddedFileProvider對象的時候必須指定這個命名空間。

public class EmbeddedFileProvider : IFileProvider
{   
    public EmbeddedFileProvider(Assembly assembly);
    public EmbeddedFileProvider(Assembly assembly, string baseNamespace);

    public IDirectoryContents GetDirectoryContents(string subpath);
    public IFileInfo GetFileInfo(string subpath);
    public IChangeToken Watch(string pattern);
}

當我們調用EmbeddedFileProvider的GetFileInfo方法並指定資源文件的邏輯名稱時,該方法會將它與命名空間一起組成資源文件在程序集清單的名稱(路徑分隔符會被替換成“.”)。如果對應的資源文件存在,那麼一個EmbeddedResourceFileInfo會被創建並返回,否則返回的將是一個NotFoundFileInfo對象。對於內嵌資源文件系統來說,根本就不存在所謂的文件更新的問題,所以它的Watch方法會返回一個HasChanged屬性總是False的IChangeToken對象。

由於內嵌於程序集的資源文件總是只讀的,它所謂的最後修改時間實際上是程序集的生成日期,所以EmbeddedFileProvider在提供EmbeddedResourceFileInfo對象的時候會採用程序集文件的最後更新時間作為資源文件的最後更新時間。如果不能正確地解析出這個時間,EmbeddedResourceFileInfo的LastModified屬性將被設置為當前UTC時間。

由於 EmbeddedFileProvider構建的內嵌資源文件系統不存在層次化的目錄結構,所有的資源文件可以視為統統存儲在程序集的“根目錄”下,所以它的GetDirectoryContents方法只有在我們指定一個空字符串或者“/”(空字符串和“/”都表示“根目錄”)時才會返回一個描述這個“根目錄”的DirectoryContents對象,該對象實際上是一組EmbeddedResourceFileInfo對象的集合。在其他情況下,EmbeddedFileProvider的GetDirectoryContents方法總是返回一個NotFoundDirectoryContents對象。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

就該這樣理解 OSI 七層參考模型、淺談不同局域網之間的通信

簡介

說到OSI參考模型,理解網絡與網絡之間的關係,不說太深入難以理解的東西,只求能最大程度上理解與使用。

參考模型是國際標準化組織(ISO)制定的一個用於計算機或通信系統間互聯的標準體系,一般稱為OSI參考模型或七層模型。

概念性的東西,先知道這些就夠了,我們先來聊一聊一個常見的一個模型。

 

局域網與互聯網

互聯網就是許許多多個局域網組成的,從我們最簡單的一個局域網入手,開始理解

這裏舉例兩個不同的局域網,計算機用網線接入交換機、交換機連接網關路由器,另一處

也是通過相同的方式進行連接。先來了解一下OSI參考模型是如何定義這七層的

OSI 參考模型

參考:

這裏定義的七層只是為了方便我們去理解,實際上是不存在的。

簡單的了解一下這七層是如何定義的,具體的功能還是得舉例子來理解說明。

從最上層的應用層開始說起:如何一步步的封裝數據,到最後進行發送。

應用層

應用層是直接面向用戶的最高層,但它卻不是應用程序,它只是為引用程序提供服務的

就好比,我們用的電腦版微信吧!它就是一個實實在在的應用程序,假設要與一個遠方的小姐姐進行聊天會話,這個時候呢,發送一個Hello給遠方的小姐姐。

當你點擊發送的時候,其實做了很多事情,我們就來梳理一下。

需要發送的數據就是:Hello ,當然,應用層首先給這個數據拼接一個AH,這裏就是應用層的報頭,就好比是微信的一個特有的數據,就這樣先理解。

 

表示層

當然,我們總不能發送明文吧,將發送的文本數據進行編碼,平常我們計算機使用的萬國碼UTF-8,肯定要進行一下加密吧

表示層更關心的是所傳送數據的語法和語義,主要包括數據格式變化、與解密、與解壓等

 

會話層

字面意思,就可以理解出這一層表示的意思,建立一個會話,就好比使用Http訪問web的時候,都會存在一個Session 作為標識

讓服務器來區別訪問的計算機。主要功能是負責維護兩個節點之間的傳輸聯接,確保點到點傳輸不中斷,以及管理數據交換等功能。

會話層在應用進程中建立、管理和終止會話。會話層還可以通過對話控制來決定使用何種通信方式,通信或通信。會話層通過自身協議對請求與應答進行協調

 

傳輸層 端到端

傳輸層作為OSI參考模型中,最重要的一層,這裏主要是以端口到端口來區分。這裏涉及到兩個特別重要的協議TCP 以及UDP

一太計算機上同時運行着QQ、微信、以及瀏覽器等。發送數據報,這個數據包到底是哪個程序發出去的呢?當然要從指定的一個端口發出去

計算機的端口範圍 0-65535

0-1023 就是1024個端口為系統佔用端口

了解到這些,就該先來說說UDP協議

UDP 協議

UDP協議定義了端口,同一個主機上的每個應用程序都需要指定唯一的端口號,並且規定網絡中傳輸的數據包必須加上端口信息,當數據包到達主機以後,就可以根據端口號找到對應的應用程序了。UDP協議比較簡單,實現容易,但它沒有確認機制,數據包一旦發出,無法知道對方是否收到,因此可靠性較差,為了解決這個問題,提高網絡可靠性,TCP協議就誕生了。

 

參考:

一個UDP報文包含首部與數據部分,UDP首部佔用8個字節,數據部分最長長度為65535B(字節) 即 64KB

UDP協議是無連接,不保證穩定傳輸的協議,但處理速度較快,通常的音頻、視頻在傳送時候使用UDP較多。

我們這裏的例子是微信,微信能保證數據百分百到達,所以我們採用TCP來具體說明數據的封裝

 

TCP 協議

TCP即傳輸控制協議,是一種面向連接的、可靠的、基於字節流的通信協議。簡單來說TCP就是有確認機制的UDP協議,每發出一個數據包都要求確認,如果有一個數據包丟失,就收不到確認,發送方就必須重發這個數據包。為了保證傳輸的可靠性,TCP協議在UDP基礎之上建立了三次對話的確認機制,即在正式收發數據前,必須和對方建立可靠的連接。TCP數據包和UDP一樣,都是由首部和數據兩部分組成,唯一不同的是,TCP數據包沒有長度限制,理論上可以無限長,但是為了保證網絡的效率,通常TCP數據包的長度不會超過IP數據包的長度,以確保單個TCP數據包不必再分割

 

參考:

 這裏只需要了解的是TCP的基本封裝過程,這裏只涉及到源端口以及目的端口,還未涉及到IP相關的內容。它和UDP協議一樣。就好像是一個改進版的

UDP協議,它能保證數據的可靠傳輸,這個特點記住即可。這裏模擬一下,我們數據的封裝過程。假設微信使用的端口是6666,目標端口就是遠方小姐姐微信

的端口,當然也是一樣的。這裏我為了理解只做簡寫

 

網絡層

從上面幾層來看,我們已經將微信的數據封裝成來一個TCP數據報,裡面包含來微信的端口 假設是6666,當然,就好比寫信一樣,我的信封

已經準備好勒,裏面要發送的內容我也已經準備好了,接下來就是地址了。肯定要指定這個報文我要發送到哪裡去。所以呢IP 網際協議,就誕生了。

IP 網際協議

網絡層引入了IP協議,制定了一套新地址,使得我們能夠區分兩台主機是否同屬一個網絡,這套地址就是網絡地址,也就是所謂的IP地址。IP協議將這個32位的地址分為兩部分,前面部分代表網絡地址,後面部分表示該主機在局域網中的地址。如果兩個IP地址在同一個子網內,則網絡地址一定相同。為了判斷IP地址中的網絡地址,IP協議還引入了子網掩碼,IP地址和子網掩碼通過按位與運算后就可以得到網絡地址

IP地址在這裏我們就比較好理解了。我們平時的生活中都會涉及到。一個IP指向的就是互聯網當中的一台機器或者就是一台路由器了。

我們來封裝數據。再把上面的圖拿下來,說明一下,我們要給E電腦的小姐姐發送消息。比如我是A電腦,小姐姐在另外一個網關下的E電腦

 

 

比較重要的兩個參數:

源地址:192.168.0.120

目標地址:192.168.1.135

進行封裝后的數據,這裏將源地址,告訴路由器(郵局) 發件人 就是源地址,以及收件人 也就是目標地址

 

ARP 協議

這裏暫時不細說這個ARP協議的內容。我們只需要知道 ARP協議是用來拿IP換MAC地址的,上面的IP協議也已經提過了,通過子網掩碼和IP地址的換算,可以得到

網絡號,網絡號就可以區別這兩個IP是否在同一個局域網內。 參考這個秒懂:

數據鏈路層

到這一層,就已經到網卡、網絡設備(交換機)的範疇了。數據鏈路層最重要的協議是以太網協議,數據鏈路層最重要的一點就是數據成幀。

以太網協議

接入以太網的設備必須包含一塊以太網網卡,也就是我們常用的網卡,一組電信號稱作是一個數據幀 、或者叫做一個數據包

網卡都包含一個全球唯一的MAC地址,發送端的和接收端的地址便是指網卡的地址,即Mac地址。 

每塊網卡出廠時都被燒錄上一個實際上唯一的Mac地址,長度為48位2進制,通常由12位16進制數表示,(前六位是廠商編碼,后六位是流水線號)

 進行數據鏈路層的封裝,將本機的MAC(源MAC地址) 和目標MAC地址封裝在頭部,在尾部加入DT報尾,這樣 一個數據幀算是封裝完成了!

等等。我們好像還不知道小姐姐那邊的目標MAC地址,這時候就需要用ARP協議了。我們知道ARP協議就是用來用IP來換MAC地址的。

 

ARP協議

上面已經簡單的了解過了,我們要和在B局域網下的E電腦進行通信,但是我們不知道它的MAC地址,於是我們發送一個ARP請求,來獲取目標的MAC地址

目標MAC 為FF:FF:FF:FF:FF:FF 表示的是廣播地址,這個數據包發出去后,所有的子網機器都會收到,收到的機器判斷目標MAC是否是自己,若不是,則直接丟棄

若是,收到報文的主機會通過單播的形式,將MAC地址回傳給我們。

通過路由協議我們可以得知,若不在一個一個子網內,則會交給路由器

 路由器返回的包裏面,目標MAC就會變成路由器的MAC地址,我們拿路由器的MAC地址組裝數據鏈路層報文即可。

物理層 

經過以上的每一層的層層包裝,這時候,我們已經包裝好了一個以太網數據幀,包含源MAC,目標MAC,源IP,目標IP等等一系列數據。

物理層就是將這個數據通過電信號、光信號的方式傳遞過去的,物理層一般都是我么所說的光纜以及網線這些硬件設備。

 

 

 不同子網間的通信

通過上面的知識,我們已經了解到如何封裝成一個數據幀,以及一些協議的相關內容。那麼這裏就會有一個問題,同一子網、

不同子網、以及相隔很遠的兩個子網是如何進行通信的呢?以及我們撥號上網后,公網IP與內網IP是怎麼一回事呢?

 

同一子網通信

我們先來看一個圖,計算機A要與計算機B進行通信,這時候他們是同處於一個子網內的,這個時候就很簡單了。

按照上面的七層進行封裝數據,這裏的具體參數需要說明一下:

源IP: 0.120(簡寫)

目標IP:0.113

源MAC : A電腦的MAC

目標MAC:B電腦MAC(這裏若不知道就先發送ARP請求)

 

 

A將數據報發送出去后,交換機直接查詢目標MAC所轉發的端口,將這個數據報準確的推送到B電腦連接的那個端口即可。

不同子網通信

A電腦需要與E電腦進行通信,這時候發現A與E不在一個子網內,這時候呢,就需要路由器來協助了

源IP: A的IP

目標IP: E的IP

源MAC:A的 MAC

目標MAC: 路由器C的MAC

 

 

因為不在一個子網內,需要路由器來進行路由這個數據包,送至D路由器后,D路由器拿出數據報中目標的IP,發送ARP請求,

請求E的MAC地址,知道后,將數據報裏面的目標MAC進行替換,然後發送給E即可。

 

公網IP與內網IP通信的方式理解

我們在使用路由器上網后,運營商就會給我們分配一個公網IP,按照圖上的指示,C路由器在進行撥號后,就會給C路由器分配一個公網IP

我這裏假設有這樣兩個。這時候需要封裝數據,該如何封裝呢,還是以A電腦與E電腦進行通信,大家肯定會很迷惑。

這裏就需要了解一個協議:網路地址轉換協議

以下簡稱NAT,NAT 在IPV4 之前起到很大的作用,我們現在也在用,因為IPV4 IP數量的限制,但接入互聯網的電腦又那麼多

該怎麼辦呢。就是給一個路由下分配一個公網IP,路由器下面的IP與公網IP進行一個轉換,這裏面說的轉換就是:NAT

圖中黑色的就是轉換部分,通過端口的轉換,將多個子網IP映射到公網的一個IP上面

 

網絡地址端口轉換(NAPT)

這種方式支持端口的映射,並允許多台主機共享一個公網IP地址。 支持端口轉換的NAT又可以分為兩類:源地址轉換和目的地址轉換。前一種情形下發起連接的計算機的IP地址將會被重寫,使得內網主機發出的數據包能夠到達外網主機。后一種情況下被連接計算機的IP地址將被重寫,使得外網主機發出的數據包能夠到達內網主機。實際上,以上兩種方式通常會一起被使用以支持雙向通信。 還是舉例,這時候,我們的A電腦需要與E電腦進行通信,E電腦在廣東省,他們撥號后,都會分配一個公網IP,並且已經在路由器裏面完成了NAT映射,
源IP: A電腦IP
目標IP: E電腦映射后的公網IP
源MAC :A電腦MAC
目標MAC :  本地路由器MAC地址   封裝完成后,將數據報送到C路由器,路由器通過映射表,將源IP進行一個替換

 

替換后,交給互聯網上的路由器進行數據報的轉發,這就好像發快遞時候一樣,經過一系列的中轉站,到達目的路由。

到達D路由后,D路由將數據報中的目標地址也進行一個轉換,這個地址是可以相互轉的。現在就是公網映射轉到本機IP

 

轉換后就輕鬆了。按照ARP請求到E機器的MAC地址,然後發報即可。

 

小結

以上內容皆是自己查看一些博主的總結,通過學習后,能夠加深自己對OIS模型、以及TCP、IP、ARP

這些非常重要的協議的一個認識。以及了解到不同層級下面。兩台電腦如何完成一個通行。這裏講的比較淺,

互聯網的奧妙不是那麼容易就可以理解透的。還是那句,不要停止學習的腳步。就好

 

參考:

  • ARP請求 
  • 不同子網內兩台機器的通信方式 
  • OSI 參考模型 
  • 內網端口與外網端口的理解 
  • 網絡地址轉換協議:

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

JS三座大山再學習(二、作用域和閉包)

作用域

JS中有兩種作用域:全局作用域|局部作用域

栗子1

console.log(name);      //undefined
var name = '波妞';
var like = '宗介'
console.log(name);      //波妞
function fun(){
    console.log(name);  //波妞
    console.log(eat)    //ReferenceError: eat is not defined
    (function(){
        console.log(like)   //宗介
        var eat = '肉'
    })()
}
fun();
  1. name定義在全局,在全局可以訪問到,所以 (2) 打印能夠正確打印;
  2. 在函數fun中,如果沒有定義name屬性,那麼會到它的父作用域去找,所以 (3) 也能正確打印。
  3. 內部環境可以通過作用域鏈訪問所有外部環境,但外部環境不能訪問內部環境的任何變量和函數。類似單向透明,這就是作用域鏈,所以 (4) 不行而 (5) 可以。

那麼問題來了,為什麼第一個打印是”undefined”,而不是”ReferenceError: name is not defined”。原理簡單的說就是JS的變量提升

變量提升:JS在解析代碼時,會將所有的聲明提前到所在作用域的最前面

栗子2

console.log(name);      //undefined
var name = '波妞';
console.log(name);      //波妞
function fun(){
    console.log(name)   //undefined
    console.log(like)   //undefined
    var name = '大西瓜';
    var like = '宗介'
}
fun();

相當於

var name;
console.log(name);      //undefined
name = '波妞';
console.log(name);      //波妞
function fun(){
    var name;
    var like;
    console.log(name)   //undefined
    console.log(like)   //undefined
    name = '大西瓜';
    like = '宗介'
    console.log(name)   //大西瓜
    console.log(like)   //宗介
}
fun();

注意:是提前到當前作用域的最前面

栗子3

printName();     //printName is not a function
var printName = function(){
    console.log('波妞')
}
printName();       //波妞

相當於

var printName;
printName();     //printName is not a function
printName = function(){
    console.log('波妞')
}
printName();       //波妞

這樣一來就好理解了,函數表達式在聲明的時候還只是個變量

栗子4

{
    var name = '波妞';
}
console.log(name)   //波妞

(function(){
    var name = '波妞';
})()
console.log(name)   //ReferenceError: name is not defined

{
    let name = '波妞';
}
console.log(name)   //ReferenceError: name is not defined

從上面的栗子可以看出,不可以草率的認為JS中var聲明的變量的作用範圍就是大括號的起止範圍,ES5並沒有塊級作用域,實質是函數作用域;ES6中有了let、const定義后,才有了塊級作用域。

栗子5

function p1() { 
    console.log(1);
}
function p2() { 
    console.log(2);
}
(function () { 
    if (false) {
        function p1() {
            console.log(3);
        }
    }else{
        function p2(){
            console.log(4)
        }
    }
    p2();
    p1()
})();       
//4
//TypeError: print is not a function

這是一個非常經典的栗子,聲明提前了,但是因為判斷條件為否,所以沒有執行函數體。所以會出現”TypeError: print is not a function”。while,switch,for同理

閉包

函數與對其狀態即詞法環境(lexical environment)的引用共同構成閉包(closure)。也就是說,閉包可以讓你從內部函數訪問外部函數作用域。在JavaScript中,函數在每次創建時生成閉包。

上面的定義來自,簡單講,閉包就是指有權訪問另一個函數作用域中變量的函數。

  • 閉包的關鍵在於:外部函數調用之後其變量對象本應該被銷毀,但閉包的存在使我們仍然可以訪問外部函數的變量對象.,
//舉個例子
function makeFunc() {
    var name = "波妞";
    function displayName() {
        console.log(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();

JavaScript中的函數會形成閉包。 閉包是由函數以及創建該函數的詞法環境組合而成。這個環境包含了這個閉包創建時所能訪問的所有局部變量

在例子中,myFunc 是執行 makeFunc 時創建的 displayName 函數實例的引用,而 displayName 實例仍可訪問其詞法作用域中的變量,即可以訪問到 name 。由此,當 myFunc 被調用時,name 仍可被訪問,其值 ‘波妞’ 就被傳遞到console.log中。創建閉包最常見方式,就是在一個函數內部創建另一個函數

  • 通常,函數的作用域及其所有變量都會在函數執行結束后被銷毀。但是,在創建了一個閉包以後,這個函數的作用域就會一直保存到閉包不存在為止
//例二
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

//釋放對閉包的引用
add5 = null;
add10 = null;

從本質上講,makeAdder 是一個函數工廠 — 他創建了將指定的值和它的參數相加求和的函數。在上面的示例中,我們使用函數工廠創建了兩個新函數 — 一個將其參數和 5 求和,另一個和 10 求和。

add5 和 add10 都是閉包。它們共享相同的函數定義,但是保存了不同的詞法環境。在 add5 的環境中,x 為 5。而在 add10 中,x 則為 10。

閉包的作用域鏈包含着它自己的作用域,以及包含它的函數的作用域和全局作用域。

  • 閉包只能取得包含函數中的任何變量的最後一個值
//栗子1
function arrFun1(){
    var arr = [];
    for(var i = 0 ; i < 10 ; i++){
        arr[i] = function(){
            return i
        }
    }
    return arr
}
console.log(arrFun1()[9]());     //10
console.log(arrFun1()[1]());     //10

//栗子2
function arrFun2(){
    var arr = [];
    for(var i = 0 ; i < 10 ; i++){
        arr[i] = function(num){
            return function(){
                return num
            };
        }(i)
    }
    return arr
}
console.log(arrFun2()[9]());     //9
console.log(arrFun2()[1]());     //1

栗子 1 中,arr數組中包含10個匿名函數,每個函數都可以訪問外部的變量 i , arrFun1 執行后,其作用域被銷毀,但它的變量依然存在內存中,能被循環中的匿名函數訪問,這是的 i 為 10;

栗子 2 中,arr數組中有是個匿名函數,其匿名函數內還有匿名函數,最內層匿名函數訪問的 num 被 上一級匿名函數保存在了內存中,所以可以訪問到每次的 i 的值。

如有錯誤,請斧正

以上

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享