比亞迪秦純電動版2016年Q1上市 售價25萬元起

5{icon} {views}

日前,有媒體從比亞迪汽車內部人士處瞭解到,秦EV300車型將於2016年一季度正式上市,新車的續航里程將達到300公里。此外,新車前臉造型有所改變,增加了藍色塗裝。

比亞迪秦EV300定位為緊湊級轎車,車身架構採用承載式車身,提供三種不同樣式輪轂,輪胎規格為205/50R17。整車尺寸相對同級車型顯得中規中矩,車身長寬分別為4740*1770*1490毫米,高度小幅調整相比現款增加10毫米,軸距為2670毫米。與秦雙冠版相比,秦EV300售價將會更低,預計售價為25萬元起。

比亞迪秦EV300外觀前臉有所改變,增加了前中網的藍色塗裝,並且將快充與慢充介面全部集成在了車頭處,改變了以前中網的線條。刀鋒狀前大燈造型加入了LED日間行車燈,進氣格柵採用鍍鉻裝飾條,流線型車身及上揚式腰線設計突顯運動感。輪轂造型提供三款樣式,其中一款採用了的七輻式設計,輪圈直徑為17英寸。

比亞迪秦雙冠版綜合最大輸出功率為217千瓦,峰值扭矩達到479牛•米。秦EV300與其相比,此次搭載一台自主研發電動機,最大輸出功率為160千瓦,峰值扭矩為310牛•米,作為純電動車的比亞迪秦EV300動力表現已經超越多數同級車。

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

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比南投搬家公司費用收費行情懶人包大公開

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

詳細講解 Redis 的兩種安裝部署方式

5{icon} {views}

Redis 是一款比較常用的 NoSQL 數據庫,我們通常使用 Redis 來做緩存,這是一篇關於 Redis 安裝的文章,所以不會涉及到 Redis 的高級特性和使用場景,Redis 能夠兼容絕大部分的 POSIX 系統,例如 Linux、OS X 等,但是很遺憾不支持在 Windows 上安裝,當然如果你需要在 windows 下安裝 redis 的話,也是可以的,微軟公司的開源技術組在 GitHub 上 維護一個 Redis 的分支,GitHub 地址為:https://github.com/microsoftarchive/redis,我看了一下這上面的版本比較舊,所以我個人不推薦使用這個來安裝 Redis ,Windows 用戶可以使用 Docker 容器來安裝,也是非常方便、簡單的,接下來我們就一起來看看 Redis 的安裝方式吧。

1、Linux 系統下安裝 redis

安裝

在 redis 官網中有安裝教程,鏈接:https://redis.io/download,安裝步驟我拷貝過來了,步驟如下:

$ wget http://download.redis.io/releases/redis-5.0.6.tar.gz
$ tar xzf redis-5.0.6.tar.gz
$ cd redis-5.0.6
$ make

我在 /usr/local 目錄下面操作的這些命令,也就是說 Redis 的安裝目錄為 /usr/local ,這些命令執行完之後你的機器上安裝好 Redis ,在安裝的過程中,如果你的機器上沒有安裝 gcc ,你安裝好 gcc 之後再 make 可能會報以下錯誤

jemalloc/jemalloc.h: No such file or directory

當時沒有截取詳細的錯誤信息,只把主要的一段截取出來了,這個錯誤的原因是我們上一次 make 報錯之後,有編譯后的文件,所以我們需要清除上一次的殘留文件再重新編譯,將 make 換成 make distclean && make 就可以了。

redis.conf 文件

redis.conf 是 Redis 的配置文件,redis 的所有配置有在這個文件裏面,這個文件挺大的有接近 1400 行,有關 redis 的操作、使用說明都在裏面,可以詳細的閱讀閱讀這個配置文件,大部分情況下我們使用默認配置就行,只需要設置少量配置就行。redis.conf 的存放位置在 Redis 的安裝目錄下,我這裡是 /usr/local/redis-5.0.5 目錄下,一起來看看我們可能會修改的幾個配置:

  • bind 127.0.0.1:允許訪問機器的IP,默認只有本機才能訪問,你可以修改 ip 來運行其他機器也能訪問,但是如果你想讓所有機器都可以訪問的話,直接設置為 bind 0.0.0.0 就行了。
  • port 6379:redis 實例啟動的端口,默認為 6379
  • daemonize no:是否以守護進程的方式運行,默認是 no,也就是說你把啟動窗口關閉了,redis 實例也就關閉了,一般這個選項我們設置為 yes,以守護進程的方式運行,說俗一點就是後台運行。
  • pidfile /var/run/redis_6379.pid:如果我們使用守護進程方式運行的話 ,就會產生一個後綴名為 .pid 的文件,這個使用默認的也行
  • dir ./:持久化文件存放位置,這個配置我們還是設置一下為好,我這裏設置為 dir /usr/local/redis_data
  • appendonly no:是否開啟 AOF 持久化方式,redis 默認只開啟了 RDB 模式,這裏我們設置為 yes,兩種方式都開啟,雙重保險,關於這兩種方式的區別,我們後面在學習

好像大概設置這幾個就好了,更多關於 redis.conf 的配置,你可以詳細閱讀 redis.conf 配置文件或者查閱相關手冊。

redis 的啟動

Redis 的啟動非常簡單,Redis 安裝完成之後,會在 /usr/local/redis-5.0.5/src 存放 Redis 的 shell 交互命令,其中有一個 redis-server ,這個就是 Redis 的啟動命令,執行:

./redis-server /usr/local/redis-5.0.5/redis.conf

後面跟的是 redis.conf 的文件路徑,不出意外的話我們將啟動成功,你會看到如下界面:

這裏我們使用的是守護進程的方式啟動,所以不會出現帶有 redis logo 的啟動界面,我們可以使用 shell 命令登錄到 Redis 中,還是在 src 目錄下面,執行下面這條命令:

./redis-cli

這命令你就進入了 shell 交互界面,./redis-cli 命令可以帶一些參數,例如 -h IP 這個就可以進入指定機器的 Redis 實例,進入之後你就可以進行一些操作了,如下圖所示:

redis 關閉

Redis 的關閉方式有兩種,一種是在 shell 交互界面關閉,另一種是 kill + 進程號關閉 Redis 實例的方式

shell 交互界面關閉

shutdown [nosave|save]

在 shell 交互界面輸入 shutdown 命令就可以關閉 Redis 實例,後面有一個可選參數,nosave 就是不將內存中的數據持久化,save 就是將內存中的數據持久化。shutdown 關閉方式是比較優雅的關閉方式,建議使用這種關閉方式

Kill + 進程號關閉 Redis 實例

使用 ps -ef|grep redis 查看 Redis 進程號,如下圖所示:

在這裏找到我們需要關閉 redis 實例的進程號,比如這裏我們的進程號為 27133,那麼我們就直接使用 kill 27133 關閉 Redis 實例服務,這種方式我們需要注意一個地方,那就是需要我們去把 pid 文件刪掉,pid 文件存放的位置我們在 redis.conf 里配置的 pidfile /var/run/redis_6379.pid,我們需要到 /var/run 目錄下把 redis_6379.pid 刪掉,這樣下一次才能正常重啟 Redis 服務。

上面兩種方式都可以關閉 Redis 服務,隨便選一種都行,但是切記不要使用 Kill 9 方式關閉 Redis 進程,這樣 Redis 不會進行持久化操作,除此之外,還會造成緩衝區等資源不能優雅關閉,極端情況下會造成 AOF 和複製丟失數據的情況

redis 開機自啟動

在服務器上我們可能需要將 Redis 設置為開機自啟動,其實這個也非常簡單,我們只需要做以下四步操作即可。

1、 編寫配置腳本 vim /etc/init.d/redis
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
#chkconfig: 2345 80 90
#description:auto_run
# 端口號
REDISPORT=6379
# 啟動命令
EXEC=/usr/local/redis-5.0.5/src/redis-server
# shell 交付命令
CLIEXEC=/usr/local/redis-5.0.5/src/redis-cli
# pid 存放位置
PIDFILE=/var/run/redis_${REDISPORT}.pid
# redis 配置文件
CONF="/usr/local/redis-5.0.5/redis.conf"

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF
        fi
        ;;
    stop)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                PID=$(cat $PIDFILE)
                echo "Stopping ..."
                $CLIEXEC -p $REDISPORT shutdown
                while [ -x /proc/${PID} ]
                do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
    *)
        echo "Please use start or stop as first argument"
        ;;
esac
2、修改 redis.conf,設置 redis 為守護進程方式運行
################################# GENERAL #####################################

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes
3、修改文件執行權限
chmod +x /etc/init.d/redis
4、設置開機啟動
# 啟動 redis
service redis start
# 停止 redis
service redis stop
# 開啟服務自啟動
chkconfig redis on

2、Docker 安裝 Redis

Docker 安裝 Redis 整體來說比較方便,我說的是非生產環境,就是自己搭着測試或者學習的環境,下面的步驟全部建立在你已經在你的電腦上安裝了 Docker 的基礎上,下面就來開啟安裝之旅。

1、拉取 redis 鏡像

docker pull redis

2、快速啟動

docker run -p 6379:6379 –name myredis -d redis redis-server –appendonly yes

這種方式啟動使用的默認的 redis.conf 配置,我們先來看看這幾個參數的意思

  • -p 6379:6379:映射端口,前面的 6379 是外部 redis 端口,後面的 6379 是容器內部的 redis 端口
  • –name myredis :容器對應的名稱
  • redis redis-server:redis 代表着 redis 鏡像 redis-server 表示的是執行的命令,也是就 redis 的啟動命令,跟我們 linux 下面的 ./redis-server 一樣
  • –appendonly yes:開啟 AOF 持久化
3、使用 redis

通過上面的步驟,我們已經在 Docker 中啟動了 Redis 服務,下面我們就來通過 redis-cli 訪問一下,使用下面這條命令就可以啟動 redis-cli

docker exec -it dockerRedis redis-cli

其中 dockerRedis 是你啟動 Redis 容器名稱,不出意外的話,你可以啟動一個 redis-cli 客戶端,如下圖所示:

上面就是使用 Docker 簡單的啟動 Redis ,整體來說比 linux 上面安裝啟動要方便不少,主要是你可以在 windows 系統上運行,雖然最終它還是運行在 linux 上面的,但是這個過程我們是無感知的。你可以能會問:我想在啟動的時候知道 redis.conf 可行不?答案是可行的,但是如果你對 Docker 不了解的話,可能會遇到一些坑,我就遇到了,因為我對 Docker 不是太了解,平時使用 docker 都是只需要傳入參數就好了,沒有傳過文件。關於啟動時指定配置文件,在 redis 鏡像那裡有說明,但是是 linux 下面的,並不是 windows 系統下的 Docker 配置方式,所以我就百度到了下面這段命令

docker run -v /d:/dockerdata/redis/config/redis.conf:/usr/local/etc/redis/redis.conf –name myredis redis redis-server /usr/local/etc/redis/redis.conf

這段命令就是一個坑,壓根就沒有,啟動這條命令,你將得到如下反饋:

顯然這條命令是沒有用的,當然這隻是我個人認為,也許是我操作失誤,也許是我知識面不夠,如果朋友們發現錯誤還請多多指教,這裏我就先當它是錯誤的,正確的做法是在 Docker 的宿主機上存放 redis.conf 文件,顯然 Docker 的宿主機並不是 windows 系統,而是啟動在 windows 系統上的虛擬機,所以我們需要進入到虛擬機裏面,Docker Quickstart Terminal 啟動默認界面並沒有正真的登錄到虛擬機,所以我們需要更改登錄方式,使用 docker-machine ssh 命令,如下圖所示:

這樣我們就進入到了真正的虛擬機裏面,我們就在一台虛擬機上操作了,跟我們在 linux 上的安裝一樣,我們先建立兩個目錄用來存放 Redis 配置:

/usr/local/redis:存放redis.conf
/usr/local/redis/data :存放持久化文件

建立好兩個目錄后, 我們把 redis.conf 放在 /usr/local/redis 目錄下,使用下面這條 Docker 命令啟動 Redis 鏡像:

docker run -p 6379:6379 -v /usr/local/redis/redis.conf:/usr/local/etc/redis/redis.conf -v /usr/local/redis/ –name dockerRedis -d redis redis-server /usr/local/etc/redis/redis.conf

這條 docker 啟動命令跟上面的有一點區別,有兩個參數我在這裏說明一下:

  • -v /usr/local/redis/redis.conf:/usr/local/etc/redis/redis.conf:這參數是將 /usr/local/redis/redis.conf 複製到 /usr/local/etc/redis/redis.conf 下
  • -v /usr/local/redis/ /usr/local/redis/data 也會存放持久化文件

到此,Docker 安裝 Redis 的複雜操作也做完了,如果沒什麼特別要求的話,使用簡單的 docker 啟動就好了,簡單方便,完全夠用了。

最後

目前互聯網上很多大佬都有 Redis 系列教程,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比南投搬家公司費用收費行情懶人包大公開

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

【從今天開始好好學數據結構02】棧與隊列

3{icon} {views}

目錄

我們今天主要來談談“棧”以及隊列這兩種數據結構。

回顧一下上一章中中,在數組中只要知道數據的下標,便可通過順序搜索很快查詢到數據,可以根據下標不同自由查找,然而今天要講的“棧”以及隊列這兩種數據結構訪問是受限制的,只允許在一端讀取、插入和刪除數據,這時候對它存在的意義產生了很大的疑惑。因為會覺得,相比數組和鏈表,棧帶給我的只有限制,並沒有任何優勢。那我直接使用數組或者鏈表不就好了嗎?為什麼還要用這個“操作受限”的“棧”呢?事實上,從功能上來說,數組或鏈表確實可以替代棧,但你要知道,特定的數據結構是對特定場景的抽象,而且,數組或鏈表暴露了太多的操作接口,操作上的確靈活自由,但使用時就比較不可控,自然也就更容易出錯。

@

1、理解棧與隊列

首先,如何理解“棧”?用現實一個通俗貼切的例子,我們平時放盤子的時候,都是從下往上一個一個放;取的時候,我們是從上往下一個一個地依次取,不能從中間任意抽出,先進后出,這就是典型的“”結構,當某個數據集合只涉及在一端插入和刪除數據,並且滿足後進先出、先進后出的特性,我們就應該首選“棧”這種數據結構。

其次如何理解隊列?同樣用現實一個通俗貼切的例子,平時在校的時候飯堂吃飯都是排隊,而且不能插隊,先進先出,這就是典型的隊列結構

2、用代碼談談棧

實際上,棧既可以用數組來實現,也可以用鏈表來實現。用數組實現的棧,我們叫作順序棧,用鏈表實現的棧,我們叫作鏈式棧。不管是順序棧還是鏈式棧,我們存儲數據只需要一個大小為n的數組就夠了。在入棧和出棧過程中,只需要一兩個臨時變量存儲空間,所以空間複雜度是O(1)。

注意,這裏存儲數據需要一個大小為n的數組,並不是說空間複雜度就是O(n)。因為,這n個空間是必須的,無法省掉。所以我們說空間複雜度的時候,是指除了原本的數據存儲空間外,算法運行還需要額外的存儲空間。

空間複雜度分析是不是很簡單?時間複雜度也不難。不管是順序棧還是鏈式棧,入棧、出棧只涉及棧頂個別數據的操作,所以時間複雜度都是O(1)。

還有一點,JVM內存管理中有個“堆棧”的概念。棧內存用來存儲局部變量和方法調用,堆內存用來存儲Java中的對象。那JVM裏面的“棧”跟我們這裏說的“棧”是不是一回事呢?如果不是,那它為什麼又叫作“棧”呢?知道的大牛請自覺評論區見面~

2.1、用數組實現的棧:順序棧

public class MyStack {
    
    //棧的底層我們使用數組來存儲數據
    int[] elements;

    public MyStack() {
        elements = new int[0];
    }
    
    //壓入元素
    public void push(int element) {
        // 創建一個新的數組
        int[] newArr = new int[elements.length + 1];
        // 把原數組中的元素複製到新數組中
        for (int i = 0; i < elements.length; i++) {
            newArr[i] = elements[i];
        }
        // 把添加的元素放入新數組中
        newArr[elements.length] = element;
        // 使用新數組替換舊數組
        elements = newArr;
    }
    
    //取出棧頂元素
    public int pop() {
        //棧中沒有元素
        if(elements.length==0) {
            throw new RuntimeException("stack is empty");
        }
        //取出數組的最後一個元素
        int element = elements[elements.length-1];
        //創建一個新的數組
        int[] newArr = new int[elements.length-1];
        //原數組中除了最後一個元素的其它元素都放入新的數組中
        for(int i=0;i<elements.length-1;i++) {
            newArr[i]=elements[i];
        }
        //替換數組
        elements=newArr;
        //返回棧頂元素
        return element;
    }
    
    //查看棧頂元素
    public int peek() {
        //棧中沒有元素
        if(elements.length==0) {
            throw new RuntimeException("stack is empty");
        }
        return elements[elements.length-1];
    }
    
    //判斷棧是否為空
    public boolean isEmpty() {
        return elements.length==0;
    }
    
}

2.2、測試數組實現的棧

import demo2.MyStack;

public class TestMyStack {

    public static void main(String[] args) {
        //創建一個棧
        MyStack ms = new MyStack();
        //壓入數組
        ms.push(9);
        ms.push(8);
        ms.push(7);
        //最出棧頂元素
        System.out.println(ms.pop());
        System.out.println(ms.pop());
        System.out.println(ms.pop());
        //查看棧頂元素
//      System.out.println(ms.peek());
        System.out.println(ms.isEmpty());
    }

}

2.3、基於鏈表實現的棧:鏈式棧

package stack;
/**
 * 基於鏈表實現的棧。
 */
public class StackBasedOnLinkedList {
  private Node top = null;

  public void push(int value) {
    Node newNode = new Node(value, null);
    // 判斷是否棧空
    if (top == null) {
      top = newNode;
    } else {
      newNode.next = top;
      top = newNode;
    }
  }

  /**
   * 我用-1表示棧中沒有數據。
   */
  public int pop() {
    if (top == null) return -1;
    int value = top.data;
    top = top.next;
    return value;
  }

  public void printAll() {
    Node p = top;
    while (p != null) {
      System.out.print(p.data + " ");
      p = p.next;
    }
    System.out.println();
  }

  private static class Node {
    private int data;
    private Node next;

    public Node(int data, Node next) {
      this.data = data;
      this.next = next;
    }

    public int getData() {
      return data;
    }
  }
}

3、用代碼談談隊列

棧只支持兩個基本操作:入棧push()和出棧pop()。隊列跟棧非常相似,支持的操作也很有限,最基本的操作也是兩個:入隊enqueue(),放一個數據到隊列尾部;出隊dequeue(),從隊列頭部取一個元素。所以,隊列跟棧一樣,也是一種操作受限的線性表數據結構。隊列的概念很好理解,基本操作也很容易掌握。作為一種非常基礎的數據結構,隊列的應用也非常廣泛,特別是一些具有某些額外特性的隊列,比如循環隊列、阻塞隊列、併發隊列。它們在很多偏底層系統、框架、中間件的開發中,起着關鍵性的作用。比如高性能隊列Disruptor、Linux環形緩存,都用到了循環併發隊列;Java concurrent併發包利用ArrayBlockingQueue來實現公平鎖等。

跟棧一樣,隊列可以用數組來實現,也可以用鏈表來實現。用數組實現的棧叫作順序棧,用鏈表實現的棧叫作鏈式棧。同樣,用數組實現的隊列叫作順序隊列,用鏈表實現的隊列叫作鏈式隊列

3.1、數組實現隊列:順序隊列

package queue;

// 用數組實現的隊列
public class ArrayQueue {
  // 數組:items,數組大小:n
  private String[] items;
  private int n = 0;
  // head表示隊頭下標,tail表示隊尾下標
  private int head = 0;
  private int tail = 0;

  // 申請一個大小為capacity的數組
  public ArrayQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入隊
  public boolean enqueue(String item) {
    // 如果tail == n 表示隊列已經滿了
    if (tail == n) return false;
    items[tail] = item;
    ++tail;
    return true;
  }

  // 出隊
  public String dequeue() {
    // 如果head == tail 表示隊列為空
    if (head == tail) return null;
    // 為了讓其他語言的同學看的更加明確,把--操作放到單獨一行來寫了
    String ret = items[head];
    ++head;
    return ret;
  }

  public void printAll() {
    for (int i = head; i < tail; ++i) {
      System.out.print(items[i] + " ");
    }
    System.out.println();
  }
}

3.2、鏈表實現的隊列:鏈式隊列

package queue;

/**
 * 基於鏈表實現的隊列
 */
public class QueueBasedOnLinkedList {

  // 隊列的隊首和隊尾
  private Node head = null;
  private Node tail = null;

  // 入隊
  public void enqueue(String value) {
    if (tail == null) {
      Node newNode = new Node(value, null);
      head = newNode;
      tail = newNode;
    } else {
      tail.next = new Node(value, null);
      tail = tail.next;
    }
  }

  // 出隊
  public String dequeue() {
    if (head == null) return null;

    String value = head.data;
    head = head.next;
    if (head == null) {
      tail = null;
    }
    return value;
  }

  public void printAll() {
    Node p = head;
    while (p != null) {
      System.out.print(p.data + " ");
      p = p.next;
    }
    System.out.println();
  }

  private static class Node {
    private String data;
    private Node next;

    public Node(String data, Node next) {
      this.data = data;
      this.next = next;
    }

    public String getData() {
      return data;
    }
  }

}

3.2、循環隊列

用數組來實現隊列的時候會有數據搬移操作,這樣入隊操作性能就會受到影響。那有沒有辦法能夠避免數據搬移呢?我們來看看循環隊列的解決思路。

循環隊列,顧名思義,它長得像一個環。原本數組是有頭有尾的,是一條直線。現在我們把首尾相連,扳成了一個環。

package queue;
public class CircularQueue {
  // 數組:items,數組大小:n
  private String[] items;
  private int n = 0;
  // head表示隊頭下標,tail表示隊尾下標
  private int head = 0;
  private int tail = 0;

  // 申請一個大小為capacity的數組
  public CircularQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入隊
  public boolean enqueue(String item) {
    // 隊列滿了
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // 出隊
  public String dequeue() {
    // 如果head == tail 表示隊列為空
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
  }

  public void printAll() {
    if (0 == n) return;
    for (int i = head; i % n != tail; ++i) {
      System.out.print(items[i] + " ");
    }
    System.out.println();
  }
}

好了,到這裏,總結一下隊列,隊列最大的特點就是先進先出,主要的兩個操作是入隊和出隊。跟棧一樣,它既可以用數組來實現,也可以用鏈表來實現。用數組實現的叫順序隊列,用鏈表實現的叫鏈式隊列。特別是長得像一個環的循環隊列。在數組實現隊列的時候,會有數據搬移操作,要想解決數據搬移的問題,我們就需要像環一樣的循環隊列。

如果本文章對你有幫助,哪怕是一點點,請點個讚唄,謝謝~

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術…說好了來了就是盆友喔…

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比南投搬家公司費用收費行情懶人包大公開

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

特斯拉4月將推出無線充電裝置 10小時充滿Model S

4{icon} {views}

月下旬,特斯拉汽車發佈聲明稱,從今年4月開始,其無線充電裝置“免插充電系統”(Plugless Systems)開始發售。

據特斯拉官方說法,無線充電裝置可用於所有特斯拉品牌車型。該系統包含一個充電板,對接50安培線路,另外一個主要部件則是適配器。該系統需要專業技術人員進行安裝。充電時車輛停在充電板上,無論室內室外都可操作,適用溫度為0至122華氏度(-18至50攝氏度)。

從充電效果看,該系統相當於7.2千瓦二級線圈式充電樁,每充入一小時可以支援電動車續航20英里(32公里)。對特斯拉Model S60而言,充滿電量需要8小時,Model S70需要10小時,Model S85需要12小時。此外,用於四驅D車型的無線充電裝置也很快將上市。

特斯拉尚未公佈該系統的造價,不過參考其他電動車無線充電裝置,日產聆風為1,540美元,雪佛蘭沃藍達為1,260美元,凱迪拉克ELR為1,940美元,因此特斯拉裝置價格可能接近2,000美元。

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

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比南投搬家公司費用收費行情懶人包大公開

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

Class文件結構全面解析(上)

4{icon} {views}

什麼是Class文件?

在Java剛剛誕生的時候就提出了一個非常著名的口號:“一次編寫,到處運行。(Write Once,Run Anywhere)”。為了實現平台無關性,各種不同平台的虛擬機都統一使用一種程序儲存格式,就是字節碼(ByteCode)。它就以二進制字節流的方式被存放在Class文件中,其中包含了Java虛擬機指令集和符號表以及其他輔助信息。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

為什麼需要了解Class文件結構?

一般對於數據結構的分享難免比較枯燥,但是了解Class文件結構是了解Java虛擬機的重要基礎之一。如果想比較深入地了解Java虛擬機,那麼Class文件結構是不能不接觸的。我會力求在保證邏輯準確的基礎上,盡量通俗易懂地分享,並結合實際案例。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

Class文件結構簡介

Class文件是一組以8位字節為基礎單位的二進制流,各個數據項目嚴格按照順序準確地排列在Class文件中,中間沒有任何分隔符。當遇到8位字節以上的數據時,就按照高位在前的方式(最高位字節在地址最低位、最低位字節在地址最高位的順序儲存)分割成多個8位字節儲存。

Class文件格式採用一種類似於C語言結構體的偽結構來儲存數據的,這種偽結構有兩種數據類型:無符號數

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

無符號數用u1、u2、u4、u8分別代表1個字節、2個字節、4個字節和8個字節的無符號數,可以用來描述数字、索引引用、數量值或者UTF-8編碼構成的字符串值。

是由多個無符號數或其他表作為數據項構成的複合數據類型,所有的表都習慣地以“_info”結尾。的數據結構和樹很類似,無符號數相當於它的恭弘=叶 恭弘子節點,其他的表相當於它的子節點。整個Class文件就本質上也是一個表,具體結構如下:

類型 名稱 數量 描述
u4 magic 1 魔數
u2 minor_version 1 次版本號
u2 major_version 1 主版本號
u2 constant_pool_count 1 常量池容量計數值
cp_info constant_pool constant_pool_count – 1 常量池
u2 access_flags 1 訪問標誌
u2 this_class 1 類索引
u2 super_class 1 父類索引
u2 interfaces_count 1 接口索引計數值
u2 interfaces interface_count 接口索引
u2 fields_count 1 字段計數值
field_info fields fields_count 字段
u2 methods_count 1 方法計數值
method_info fields methods_count 方法
u2 attributes_count 1 屬性計數值
attribute_info attributes attributes_count 屬性

可以發現,無論是無符號數還是,當需要描述同一種類型又數量不定的多條數據時,就會用一個前置的計數器加幾個連續的數據項的方式,這個時候我們就把這種一系列連續的某種類型的數據叫做這個類型的集合

在Class文件中,無論是順序還是數量,甚至是數據存儲的字節序,都必須嚴格按照上面表格進行設定,哪個字節代表什麼含義,長度是多少,先後順序怎麼樣,都不允許改變。接下來看一下各個數據項的具體含義。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

魔數

魔數(Magic Number)是每個Class文件的前4個字節,它用來確定當前文件是否是一個被Java虛擬機所接受的Class文件。很多文件存儲標準中都使用了魔數進行身份識別,比如gif、jpeg等圖片文件中都有魔數。使用魔數而不使用擴展名是出於安全考慮,因為擴展名更容易被修改。文件格式制定者可以自主選擇魔數,只要這個魔數沒有被廣泛使用又不和其他文件混淆就可以。

Class文件的魔數是:0xCAFEBABE(咖啡寶貝?),這個魔數在Java還被稱為“Oak”語言的時候(大概是1991年)就確定下來了,據Java開發小組最初的關鍵成員Patrick Naughton說:“我們一直在尋找一些好玩的、容易記憶的東西,選擇0xCAFEBABE是因為它象徵著著名咖啡品牌Peet’s Coffee中深受歡迎的Baristas咖啡”,他們是真的很喜歡喝咖啡啊,可能也預示着日後“Java”這個名字的出現。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

為了更快的理解,我準備了一個實際案例,一段非常簡單的Java代碼:

public class OneMoreStudy {
    private int number;

    private int plusOne() {
        return number + 1;
    }
}

使用JDK 1.7把這段代碼編譯成Class文件,用打開,就可以到魔數了,如下圖:

在接下來的分享中,也會經常使用這個Class文件。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

次版本號和主版本號

緊跟着魔數的第5和第6個字節是次版本號(Minor Version),第7和第8個字節是主版本號(Major Version)。Java的主版本號是從45開始的,從JDK 1.1以後每個JDK大版本發布主版本號都加1,高版本的JDK向下兼容低版本的Class文件,但不能運行更高版本的Class文件,即使Class文件的格式沒有發生任何變化,Java虛擬機也會拒絕運行超過其版本號的Class文件。

再來看一下之前的Class文件例子:

表示次版本號的第5和第6個字節值為0x0000,表示主版本號的第7和第8個字節值為0x0033,也就是十進制的51,說明這個Class文件可以被JDK 1.7及其以上版本的Java虛擬機運行。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

常量池

緊跟着主版本號的就是常量池,它可以理解為Class文件的資源倉庫,也是Class文件結構中與其他數據項關聯最多的數據類型。因為在常量池中的常量數量是不固定的,所以首先有一個u2類型的數據,表示常量池容量大小(constant_pool_count)。

常量池的容量計數不是從0開始的,而是從1開始的,這是因為0有它的特殊用用途,那就是為了表達在特殊情況下需要表達“不引用任何一個常量池項目”的含義。在Class文件結構中只有常量池的容量計數是從1開始的,對於其他集合,包括接口索引集合、字段集合、方法集合等的容量計數都是從0開始的。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

再來看一下之前的Class文件例子:

常量池容器計數值為0x0013,也就是十進制的19,它表示常量池中有18個常量,索引值範圍從1到18。

常量池中主要存儲兩種常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近Java語言層面的常量,比如文本字符串、聲明為final的常量值。符號引用則是編譯原理層次的概念,它包括以下三種:

  • 類和接口的全限定名
  • 字段的名稱和描述符
  • 方法的名稱和描述符

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

常量池中每一個常量都是一個表,共有14種不同的常量類型(JDK1.7及之前版本),每一種類型的表在第一位都有一個u1類型的標誌位,具體如下錶:

類型 標誌位 描述
CONSTANT_Utf8_info 1 UTF-8編碼的字符串
CONSTANT_Integer_info 3 整形字面量
CONSTANT_Float_info 4 浮點型字面量
CONSTANT_Long_info 5 長整型字面量
CONSTANT_Double_info 6 雙精度浮點型字面量
CONSTANT_Class_info 7 類或接口的符號引用
CONSTANT_String_info 8 字符串類型字面量
CONSTANT_Fieldref_info 9 字段的符號引用
CONSTANT_Methodref_info 10 類中方法的符號引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符號引用
CONSTANT_NameAndType_info 12 字段或方法的部分符號引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 標識方法類型
CONSTANT_InvokeDynamic_info 18 表示一個動態方法調用點

有個一個專門分析Class文件字節碼的工具javap,我們用它直接看一下之前的Class文件例子里的18個常量(常量池以外的信息已省略):

E:\>javap -verbose OneMoreStudy
  Compiled from "OneMoreStudy.java"
  minor version: 0
  major version: 51
Constant pool:
   #1 = Methodref     #4.#15     // java/lang/Object."<init>":()V
   #2 = Fieldref      #3.#16     // OneMoreStudy.number:I
   #3 = Class         #17        // OneMoreStudy
   #4 = Class         #18        // java/lang/Object
   #5 = Utf8          number
   #6 = Utf8          I
   #7 = Utf8          <init>
   #8 = Utf8          ()V
   #9 = Utf8          Code
  #10 = Utf8          LineNumberTable
  #11 = Utf8          plusOne
  #12 = Utf8          ()I
  #13 = Utf8          SourceFile
  #14 = Utf8          OneMoreStudy.java
  #15 = NameAndType   #7:#8       // "<init>":()V
  #16 = NameAndType   #5:#6       // number:I
  #17 = Utf8          OneMoreStudy
  #18 = Utf8          java/lang/Object

其中,有一些常量好像在代碼里沒有出現過,如“I”、“ ”、“Code”、“LineNumberTable”、“SourceFile”。它們其實自動生成的,是後面要分享的字段表、方法表、屬性表引用到的,用於描述一些不方便使用“固定字節”進行表達的內容。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

訪問標誌

緊跟着常量池的2個字節表示訪問標誌(access_flags),它用於識別一些類或接口層次的訪問信息,具體見下錶:

標誌名稱 標誌值 含義
ACC_PUBLIC 0x0001 是否為public類型
ACC_FINAL 0x0010 是否被聲明為final
ACC_SUPER 0x0020 是否允許使用invokespecial字節碼指令
ACC_INTERFACE 0x0200 是否是接口
ACC_ABSTRACT 0x0400 是否為abstract類型
ACC_SYNTHETIC 0x1000 標誌這個類並非由用戶代碼產生的
ACC_ANNOTATION 0x2000 是否是註解
ACC_ENUM 0x4000 是否是枚舉

其中,ACC_SUPER在JDK 1.0.2之後編譯出來的Class文件必須為true;ACC_ABSTRACT對於接口或抽象類來說為true,其他類為false。

之前的例子OneMoreStudy是一個普通的類,不是接口、註解或枚舉,只被public修飾,沒有被聲明為final或abstract,而且是JDK 1.7編譯的,所以只有ACC_PUBLIC和ACC_SUPER為true,所以它的訪問標誌應該是0x0001 | 0x0020 = 0x0021,如下圖:

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

下回分解

由於篇幅限制,這次的分享先暫時到這裏,希望大家更好地消化吸收。欲知後事如何,請聽下回分解!敬請期待!

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比南投搬家公司費用收費行情懶人包大公開

南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

從「河」而來的亞馬遜汞污染? 研究追「毒黃金」非法來源 籲國際重視

5{icon} {views}

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比彰化搬家公司費用,南投搬家公司費用收費行情懶人包大公開

彰化搬家費用,南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

Vue項目性能優化整理

5{icon} {views}

 以下方式基於 @vue/cli 快速搭建的交互式項目腳手架

1. 路由懶加載

當打包構建應用時,JavaScript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應的組件分割成不同的代碼塊,然後當路由被訪問的時候才加載對應組件,這樣就更加高效了。

結合 Vue 的和 Webpack 的,輕鬆實現路由組件的懶加載。

 1 import Vue from 'vue'
 2 import Router from 'vue-router'
 3 import store from './store'
 4 import Home from './views/Home.vue'
 5 
 6 Vue.use(Router)
 7 
 8 const router = new Router({
 9   routes: [
10     {
11       path: '/',
12       name: 'home',
13       component: Home,
14     },
15     {
16       path: '/make',
17       name: 'make',
18       component: () => import(/* webpackChunkName: "make" */ './views/Make.vue'),
19     }
20   ],
21 })

 

2. webpack動態導入

將statically import(靜態導入) 改為 dynamic import(動態導入)進行代碼拆分

 1 import(/* webpackChunkName: "html2canvas" */ 'html2canvas').then(
 2   ({ default: html2canvas }) => {
 3     html2canvas(document.querySelector('.container'), {
 4       scale: window.devicePixelRatio,
 5       allowTaint: false,
 6       useCORS: true,
 7     }).then(canvas => {
 8       console.log(canvas.toDataURL('image/jpeg', 0.9))
 9     })
10   }
11 )

 

3. 預取/預加載模塊(prefetch/preload module)

在聲明 import 時,使用下面這些內置指令,可以讓 webpack 輸出 “resource hint(資源提示)”,來告知瀏覽器:

  prefetch(預取):將來某些導航下可能需要的資源
  preload(預加載):當前導航下可能需要資源

import(/* webpackPrefetch: true */ 'LoginModal');

import(/* webpackPreload: true */ 'ChartingLibrary');

這會生成 <link rel="prefetch" href="login-modal-chunk.js"> 並追加到頁面頭部,指示着瀏覽器在閑置時間預取 login-modal-chunk.js 文件。

只要父 chunk 完成加載,webpack 就會添加 prefetch hint(預取提示)。

與 prefetch 指令相比,preload 指令有許多不同之處:

  • preload chunk 會在父 chunk 加載時,以并行方式開始加載。prefetch chunk 會在父 chunk 加載結束后開始加載。
  • preload chunk 具有中等優先級,並立即下載。prefetch chunk 在瀏覽器閑置時下載。
  • preload chunk 會在父 chunk 中立即請求,用於當下時刻。prefetch chunk 會用於未來的某個時刻。
  • 瀏覽器支持程度不同。

vue默認開啟,可在vue.config.js中全局禁用prefetch,再針對指定模塊開啟。

chainWebpack: config => {
  config.plugins.delete('prefetch')
},

 

4. 添加Gzip打包配置()

  yarn add compression-webpack-plugin -D

configureWebpack: config => {
  const CompressionPlugin = require('compression-webpack-plugin')
  config.plugins.push(new CompressionPlugin())
}

 

5. 添加頁面預渲染()

在單頁應用程序中預呈現靜態HTML,可以極大的提高網頁訪問速度,而且配合一些meat插件,基本可以滿足SEO需求。

預渲染基本上是在啟動無頭瀏覽器,加載應用程序的路由並將結果保存到靜態HTML文件中。然後將其與以前使用的任何靜態文件服務解決方案一起使用,是無需更改代碼或添加服務器端渲染的解決方法。

不過,它僅適用於HTML5 history,因為每個預渲染的路由都會生成一個對應的HTML,在hash模式下使用只會有一個HTML。

  yarn add prerender-spa-plugin -D

 1 configureWebpack: config => {
 2   const path = require('path')
 3   const PrerenderSPAPlugin = require('prerender-spa-plugin')
 4   config.plugins.push(
 5     new PrerenderSPAPlugin({
 6       staticDir: path.join(__dirname, 'dist'),
 7       routes: ['/'],
 8       minify: {
 9         collapseBooleanAttributes: true,
10         collapseWhitespace: true,
11         keepClosingSlash: true,
12         decodeEntities: true,
13         sortAttributes: true,
14       },
15       renderer: new PrerenderSPAPlugin.PuppeteerRenderer({
16         renderAfterDocumentEvent: 'render-event',
17         renderAfterTime: 5000,
18         // headless: false,
19       }),
20     })
21   )
22 }
23 
24 new Vue({
25   router,
26   store,
27   render: h => h(App),
28   mounted() {
29     // 預渲染 renderAfterDocumentEvent.
30     document.dispatchEvent(new Event('render-event'))
31   },
32 }).$mount('#app')

prerender-spa-plugin 利用了 Puppeteer 的爬取頁面的功能。 Puppeteer 是一個 Chrome官方出品的 headlessChromenode 庫。它提供了一系列的 API, 可以在無 UI 的情況下調用 Chrome 的功能, 適用於爬蟲、自動化處理等各種場景。它很強大,所以很簡單就能將運行時的 HTML 打包到文件中。原理是在 Webpack 構建階段的最後,在本地啟動一個 Puppeteer 的服務,訪問配置了預渲染的路由,然後將 Puppeteer 中渲染的頁面輸出到 HTML 文件中,並建立路由對應的目錄。

 

6. 壓縮js,刪除console()

  yarn add terser-webpack-plugin -D

1 configureWebpack: config => {
2   const TerserPlugin = require('terser-webpack-plugin')
3   config.optimization.minimizer.push(
4     new TerserPlugin({
5       extractComments: false,
6       terserOptions: { compress: { drop_console: true } },
7     })
8   )
9 }

 

7. bundle 分析()

將 bundle 內容展示為便捷的、交互式、可縮放的樹狀圖形式。

  yarn add -D webpack-bundle-analyzer

1 configureWebpack: config => {
2   const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
3     .BundleAnalyzerPlugin
4   config.plugins.push(new BundleAnalyzerPlugin())
5 }

 

8. WebP

WebP(發音 weppy),是一種支持有損壓縮和無損壓縮的圖片文件格式,派生自圖像編碼格式 VP8。根據 Google 的測試,無損壓縮后的 WebP 比 PNG 文件少了 45% 的文件大小,即使這些 PNG 文件經過其他壓縮工具壓縮之後,WebP 還是可以減少 28% 的文件大小。

不過WebP目前在IOS上兼容性不好,可以使用JavaScript進行檢測,對支持 WebP 的用戶輸出 WebP 圖片。

 1 created() {
 2   const htmlClass = document.documentElement.classList
 3   this.checkWebpSupport() ? htmlClass.add('webps') : htmlClass.remove('webps')
 4 }
 5 
 6 checkWebpSupport() {
 7   try {
 8     return (
 9       document
10         .createElement('canvas')
11         .toDataURL('image/webp')
12         .indexOf('data:image/webp') === 0
13     )
14   } catch (err) {
15     return false
16   }
17 }

記一次BUG:

  在默認情況下,頁面加載完成執行 this.checkWebpSupport() && document.documentElement.classList.add(‘webps’),沒有問題。

  但使用了prerender-spa-plugin進行預渲染后,因為執行預渲染的瀏覽器是支持WebP的,所有會直接在頁面中加上’webps’類,所以即使瀏覽器不支持WebP,不執行此方法也會有該類名。

 

9. 網頁性能優化測試()

進行網頁測試,根據優化建議針對性的修改,提高網頁加載速度。

  https://www.googlespeed.cn/

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比彰化搬家公司費用,南投搬家公司費用收費行情懶人包大公開

彰化搬家費用,南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

衛星監測曝光 義大利全境封鎖後空污大幅改善

2{icon} {views}

摘錄自2020年03月15日自由時報報導

中國日前因新型冠狀病毒肺炎(武漢肺炎,COVID-19)影響,封省限制民眾外出、工業活動暫停,意外讓中國碳排放量大減,同樣的情形也出現在義大利,由於政府宣布全國封鎖,許多民眾被迫留在家中,義大利半島上空空污大幅減少,尤其是疫情重災區義大利北部地區更是明顯。

綜合外媒報導,可監測大氣中空污情形的歐洲太空總署(ESA)「哨兵五號」(Sentinel-5P)衛星發現,整個義大利上空的空氣污染,特別是二氧化氮排放量大幅減少,在半島北部地區尤為明顯,ESA「哨兵五號」團隊成員澤納(Claus Zehner)指出,受雲層覆蓋和天氣變化影響,數據可能略有出入,「但我們非常有信心,交通和工業活動大幅縮減的情況下,排放量降低與義大利封鎖有密切關聯。」

ESA特地製作義大利上空自今年1月1日起到2020年3月11日的二氧化氮排放量波動,可以明顯看出,義大利半島北部的波河流域二氧化氮含量急遽減少,波河中上游為義大利重要的人口密集工業地帶。

公害污染
空氣污染
國際新聞
義大利
武漢肺炎

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比彰化搬家公司費用,南投搬家公司費用收費行情懶人包大公開

彰化搬家費用,南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

你不知道的JS系列【1】- 什麼是作用域

3{icon} {views}

  幾乎所有的編程語言都能夠儲存變量,並且能在之後對這個變量值進行訪問或修改,正是儲存和訪問變量的能力將狀態帶給了程序,那麼,這些變量儲存在哪裡呢?程序需要時又是如何找到他們?這些問題說明需要一套設計良好的規則來儲存變量,並且之後可以方便的找到這些變量,這套規則被稱為作用域

1、了解編譯原理

  儘管將JS歸類為“動態”或“解釋執行”腳本語言,但事實上它是一門編譯語言。但是與傳統編譯語言不同的是,它不是提前編譯的,編譯結果也不能在分佈式系統中進行移植。JS引擎進行編譯的步驟與傳統的語言非常相似,程序中一段源代碼在執行之前會經歷三個步驟,統稱為“編譯”。

  • 分詞/詞法分析

這個過程會將由字符組成的字符串分解成有意義的代碼塊,這些代碼塊被稱為詞法單元。例如,考慮程序 var a = 2;。這段程序通常會被分解成 為下面這些詞法單元:var、a、=、2 、;

  • 解析/語法分析

這個過程是將詞法單元流(數組)轉換成一個由元素逐級嵌套所組成的代表了程序語法結構的樹。這個樹被稱為“抽象語法樹”(Abstract Syntax Tree,AST)。var a = 2;的抽象語法樹中可能會有一個叫作VariableDeclaration的頂級節點,接下來是一個叫作 Identifier(它的值是 a)的子節點,以及一個叫作 AssignmentExpression 的子節點。AssignmentExpression 節點有一個叫作 NumericLiteral(它的值是 2)的子節點。

  • 代碼生成

AST轉換為可執行代碼的過程稱被稱為代碼生成,簡單來說就是有某種方法可以將 var a = 2;的AST轉化為一組機器指 令,用來創建一個叫作a的變量(包括分配內存等),並將一個值儲存在a中。

編譯流程如下圖所示:

JS引擎比傳統的編譯語言編譯器複雜很多,在語法分析和代碼生成階段有特定的步驟來對性能進行優化,大部分情況下編譯發生在代碼之前的前幾微秒,在討論作用域背後,js引擎用了各種辦法來保證性能最佳。

Tips:我們平時在寫JS代碼的時候,一個語句結尾要加分號(;),便於JS編譯器編譯。

2、理解作用域

  我們先了解JS編譯過程中幾個名詞,JS引擎,編譯器,作用域。

2.1.名詞介紹

  • JS引擎:從頭到尾負責整個JS程序編譯過程。
  • 編譯器:負責語法分析及代碼生成等。
  • JS引擎:負責收集並維護由所有聲明的標識符(變量)組成的一系列查 詢,並實施一套非常嚴格的規則,確定當前執行的代碼對這些標識符的訪問權限。

2.2.變量賦值

對於var a=2;這段代碼,我們認為這就是申明一個為變量a且初始值為2,實際上,JS引擎認為這裡有兩個完全不同的申明,一個由編譯器在編譯時處理,另一個則由引擎在運行時處理。

處理過程分為兩步:

1.遇到var a,編譯器會詢問作用域是否已經有一個該名稱的變量存在於同一個作用域的集合中。如果是,編譯器會忽略該聲明,繼續進行編譯;否則它會要求作用域在當前作用域的集合中聲明一個新的變量,並命名為a。

2.接下來編譯器會為引擎生成運行時所需的代碼,這些代碼被用來處理a = 2這個賦值操作。引擎運行時會首先詢問作用域,在當前的作用域集合中是否存在一個叫作a的變量。如果是,引擎就會使用這個變量;如果否,引擎會繼續查找該變量。

如果引擎最終找到a變量,就會將2賦值給它。否則就拋出異常。

Tips:聲明提前(hoist)-JS引擎在創建變量時,會將該變量提升到當前作用域的最前面。

總結:變量的賦值操作會執行兩個動作,首先編譯器會在當前作用域中聲明一個變量(如果之前沒有聲明過),然後在運行時引擎會在作用域中查找該變量,如果能夠找到就會對它賦值。

2.3.LHS查詢&RHS查詢

編譯器在編譯過程中的第二步生成了代碼,引擎在執行時,會通過查找變量a來判斷它是否已經聲明過。當變量出現在賦值操作的左側時進行LHS查詢,當變量出現在右側時進行RHS查詢。

console.log(a); //對a的引用時RHS引用,這裏沒有對a賦予任何值,需要查找a的值。

a=2; //對a的引用是LHS引用,因為這裏不關心a的值等於多少,只想為 =2 這個賦值操作找到一個目標(變量a);

LHS和RHS的含義是“賦值操作的左側或右側”並不一定意味着就是“= 賦值操作符的左側或右側”。賦值操作還有其他幾種形式,因此在概念上最好將其理解為“賦值操作的目標是誰(LHS)”以及“去找到XX變量的值,誰是賦值操作的源頭(RHS)”。

3、作用域嵌套

  作用域是根據名稱查找變量的一套規則。實際情況中,通常需要同時顧及幾個作用域。 當一個塊或函數嵌套在另一個塊或函數中時,就發生了作用域的嵌套。因此,在當前作用 域中無法找到某個變量時,引擎就會在外層嵌套的作用域中繼續查找,直到找到該變量, 或抵達最外層的作用域(也就是全局作用域)為止。

參考以下代碼:

var name='peer';
function sayHello(){
  alert('hello '+ name)
}
sayHello();
// 對name的RHS引用無法在函數sayHello完成,但是可以在上一級作用域中完成。

把作用域比喻成一個建築如下圖所示:

LHS和RHS引用都會在當前樓層進行查找,如果沒有找到,就會坐電梯前往上一層樓,如果還是沒有找到就繼續向上,以此類推。一旦抵達頂層(全局作用域),可能找到了你所需的變量,也可能沒找到,但無論如何查找過程都將停止。

4、總結

  作用域是一套規則,用於確定在何處以及如何查找變量(標識符)。LHS和RHS查詢都會在當前執行作用域中開始,如果有需要就會向上級作用域繼續查找目標標識符,這樣每次上升一級作用域,最後抵達全局作用域,無論找到或沒找到都將停止。不成功的RHS 引用會導致拋出ReferenceError異常。不成功的LHS引用會導致自動隱式地創建一個全局變量(非嚴格模式下),掌握這些基本作用域知識能使我們更深入理解JS引擎的編譯過程來編寫更高性能的代碼。

參考資料:
《你不知道的JavaScript》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比彰化搬家公司費用,南投搬家公司費用收費行情懶人包大公開

彰化搬家費用,南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

新冠疫情打亂中國貿易 肯亞漁民收入好轉

7{icon} {views}

摘錄自2020年3月24日中時報導

武漢肺炎全球大流行,造成國際物流與貿易的停滯,不過這肯亞漁民來說,卻帶來了意想不到的好處。由於現在中國大陸的冷凍魚片無法進口,因此當地轉而購買肯亞漁民在維多利亞湖捕獲的鮮魚。

路透社報導,中國大陸貿易力量的強勢,使得肯亞當地漁民處境辛苦,根據國際貿易中心(International Trade Center)的數據,以2018年為例,中國大陸銷售給肯亞價值2320萬美元的冷凍魚,佔領了肯亞的魚獲市場,幾乎所有的魚類進口都來自中國大陸。長期以來,肯亞漁民一直抱怨,廉價進口的冷凍漁,幾乎扼殺了當地漁民生計。

然而,武漢肺炎的蔓延,中止了中國大陸的進口業,很快魚肉供應緊缺,使得肯亞消費者轉向購買當地的魚類,對當地漁民而言是個好消息。但是,從較大的經濟角度來看,接下來肯亞會遇到供不應求的麻煩,因為當地漁獲量僅能提供消費量的三分之一。

農林漁牧業
環境經濟
國際新聞
肯亞
武漢肺炎
漁民

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※專營大陸空運台灣貨物推薦

台灣空運大陸一條龍服務

※評比彰化搬家公司費用,南投搬家公司費用收費行情懶人包大公開

彰化搬家費用,南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!