day 81 Vue學習一之vue初識

 

Vue學習一之vue初識

 

 

本節目錄javascript

一 vue初識

  vue稱爲漸進式js框架,這個框架用來作先後端分離的項目,以前咱們學習django,知道django是一個MTV模式的web框架,urls--views--templates,模板渲染經過後端的代碼來實現數據的渲染,再加上前端一些簡單的dom操做來完成網頁的開發,當咱們作一個複雜的大型的網頁的時候,你會發現這種模式做起來會比較複雜,擴展起來也比較困難,由於先後端沒有分離開,耦合性過高,牽一髮而動全身,因此人們就開始想,若是能有專門的人來開發前端,專門的人來開發後端,前端頁面就是前端語言來寫,後端服務端代碼就是後端服務端代碼來寫,二者以前只有數據的交流,那麼之後頁面在進行拓展,進行功能的更新的時候就會變得比較簡單,所以vue就誕生了,以前咱們前端頁面拿到數據都是經過dom操做或者django的模板語言來進行數據的渲染的,有了前端框架vue,就不須要他們了,而且頻繁的dom操做,建立標籤添加標籤對頁面的性能是有影響的,那麼直接數據驅動視圖,將django的MTV中的T交給vue來寫,也就是那個templates裏面的內容,而且前端的vue拿到了T這部分的工做,MTV前身是MVC,能夠將vue拿到的T的工做稱爲view視圖,就是完成MVC的V視圖層工做,只不過V稱爲視圖函數,重點在函數,而vue咱們稱爲視圖,接到後端的數據(經過接口url,得到json數據),直接經過vue的視圖渲染在前端。css

        

   前端三大框架,Vue、Angular、React,vue是結合了angular和react的優勢開發出來的,是中國人尤雨溪開發的,angular不少公司也在用,是谷歌開發的,老項目通常是angular2.0,最新的是6.0的,可是它是基於另一個版本的js,叫作typescript,因此若是未來你工做用的是angular6.0,那麼要本身提早學一下typescript,也比較簡單,react是facebook開發的,其實越大型的項目react越好用,我的觀點昂,react裏面用的可能是高階函數,須要你對js特別熟,對初學者不是很友好,可是你越熟練,用起來越nb,未來若是須要,你們再學習吧,爭取哪天給你們整理出來,在githup上react比vue的星還多一些。html

    

  先後端分離項目:分工明確前端

    前端作前端的事情:頁面+交互+兼容+封裝+class+優化 (技術棧:vue+vue-router+vuex+axios+element-ui)vue

    後端作後端的事情:接口+表操做+業務邏輯+封裝+class+優化 (技術棧:restframework框架+django框架+mysql\redis等)java

  畫一個django和vue的對比圖吧python

    

 

 

二 ES6基本語法

    

    

    

  因爲後面學習vue你會發現有不少es6的語法,因此咱們先學一些es6的基本語法mysql

  1 let聲明變量

    1.1 基本用法

      ES6 新增了let命令,用來聲明變量。它的用法相似於var,可是所聲明的變量,只在let命令所在的代碼塊內有效,其實在js裏面{}大括號括起來的表示一個代碼塊。     react

複製代碼
{
  let a = 10;
  var b = 1;  //至關於將b的聲明放在了做用域的外面前面,var b;而後這裏只是賦值
}

a // ReferenceError: a is not defined.
b // 1
複製代碼

      上面代碼在代碼塊之中,分別用letvar聲明瞭兩個變量。而後在代碼塊以外調用這兩個變量,結果let聲明的變量報錯,var聲明的變量返回了正確的值。這代表,let聲明的變量只在它所在的代碼塊有效。ios

      再看一個例子:

複製代碼
<script>

    var l = [];
    l[1] = 'aa';
    console.log(l[0],l[1]);  //undefined "aa"  能夠給數組經過索引來賦值,若是你給索引1賦值了,那麼索引0的值爲undefined
    console.log(a);  //undefined  //由於var存在變量提高的問題,會在這個打印前面先聲明一個var a;而後後面在進行a=1的賦值,因此打印出來不報錯,而是打印的undefined,let不存在這個問題,let只在本身的代碼塊中生效
    {  //js裏面大括號表示一個代碼塊
        var a = 1;
        let b = 2;
    }
    console.log(a);  //1
    console.log(b);  // 報錯
</script>
複製代碼

        for循環的計數器,就很合適使用let命令。

for (let i = 0; i < 10; i++) {
  // ...
}

console.log(i);
// ReferenceError: i is not defined

      上面代碼中,計數器i只在for循環體內有效,在循環體外引用就會報錯。

      下面的代碼若是使用var,最後輸出的是10

複製代碼
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
複製代碼

      上面代碼中,變量ivar命令聲明的,在全局範圍內都有效,因此全局只有一個變量i。每一次循環,變量i的值都會發生改變,而循環內被賦給數組a的函數內部的console.log(i),裏面的i指向的就是全局的i。也就是說,全部數組a的成員裏面的i,指向的都是同一個i,致使運行時輸出的是最後一輪的i的值,也就是 10。

      若是使用let,聲明的變量僅在塊級做用域內有效,最後輸出的是 6。

複製代碼
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6
複製代碼

      上面代碼中,變量ilet聲明的,當前的i只在本輪循環有效,因此每一次循環的i其實都是一個新的變量,因此最後輸出的是6。你可能會問,若是每一輪循環的變量i都是從新聲明的,那它怎麼知道上一輪循環的值,從而計算出本輪循環的值?這是由於 JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。

      另外,for循環還有一個特別之處,就是設置循環變量的那部分是一個父做用域,而循環體內部是一個單獨的子做用域。

複製代碼
for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc
複製代碼

       上面代碼正確運行,輸出了 3 次abc。這代表函數內部的變量i與循環變量i不在同一個做用域,有各自單獨的做用域。

    1.2 不存在變量提高

      var命令會發生「變量提高」現象,即變量能夠在聲明以前使用,值爲undefined。這種現象多多少少是有些奇怪的,按照通常的邏輯,變量應該在聲明語句以後纔可使用。

      爲了糾正這種現象,let命令改變了語法行爲,它所聲明的變量必定要在聲明後使用,不然報錯。

複製代碼
// var 的狀況
console.log(foo); // 輸出undefined
var foo = 2;

// let 的狀況
console.log(bar); // 報錯ReferenceError
let bar = 2;
複製代碼

       上面代碼中,變量foovar命令聲明,會發生變量提高,即腳本開始運行時,變量foo已經存在了,可是沒有值,因此會輸出undefined。變量barlet命令聲明,不會發生變量提高。這表示在聲明它以前,變量bar是不存在的,這時若是用到它,就會拋出一個錯誤。

    1.3 暫時性死區

      只要塊級做用域內存在let命令,它所聲明的變量就「綁定」(binding)這個區域,再也不受外部的影響。

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

      上面代碼中,存在全局變量tmp,可是塊級做用域內let又聲明瞭一個局部變量tmp,致使後者綁定這個塊級做用域,因此在let聲明變量前,對tmp賦值會報錯。

      ES6 明確規定,若是區塊中存在letconst命令,這個區塊對這些命令聲明的變量,從一開始就造成了封閉做用域。凡是在聲明以前就使用這些變量,就會報錯。

      總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)。暫時性死區的本質就是,只要一進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。

    1.4 不容許重複聲明

      let不容許在相同做用域內,重複聲明同一個變量。

複製代碼
// 報錯
function func() {
  let a = 10;
  var a = 1;
}

// 報錯
function func() {
  let a = 10;
  let a = 1;
}
複製代碼

      所以,不能在函數內部從新聲明參數。

複製代碼
function func(arg) {
  let arg;
}
func() // 報錯

function func(arg) {
  {
    let arg;
  }
}
func() // 不報錯
複製代碼

 

  2. 做用域

    ES5 只有全局做用域和函數做用域,沒有塊級做用域,這帶來不少不合理的場景。

    第一種場景,內層變量可能會覆蓋外層變量。

複製代碼
var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined
複製代碼

    上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,內部使用內層的tmp變量。可是,函數f執行後,輸出結果爲undefined,緣由在於變量提高,致使內層的tmp變量覆蓋了外層的tmp變量。

    第二種場景,用來計數的循環變量泄露爲全局變量。

複製代碼
var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5
複製代碼

     上面代碼中,變量i只用來控制循環,可是循環結束後,它並無消失,泄露成了全局變量。

    ES6中的做用域:

    let實際上爲 JavaScript 新增了塊級做用域。

複製代碼
function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}
複製代碼

    上面的函數有兩個代碼塊,都聲明瞭變量n,運行後輸出 5。這表示外層代碼塊不受內層代碼塊的影響。若是兩次都使用var定義變量n,最後輸出的值纔是 10。

    ES6 容許塊級做用域的任意嵌套。

{{{{{let insane = 'Hello World'}}}}};

    上面代碼使用了一個五層的塊級做用域。外層做用域沒法讀取內層做用域的變量。

{{{{
  {let insane = 'Hello World'}
  console.log(insane); // 報錯
}}}};

     內層做用域能夠定義外層做用域的同名變量。

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};

 

  3.const聲明常量

    3.1 基本用法

      const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

      上面代碼代表改變常量的值會報錯。

        const聲明的變量不得改變值,這意味着,const一旦聲明變量,就必須當即初始化,不能留到之後賦值。

const foo;
// SyntaxError: Missing initializer in const declaration

       上面代碼表示,對於const來講,只聲明不賦值,就會報錯。

        const的做用域與let命令相同:只在聲明所在的塊級做用域內有效。

if (true) {
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined

       const命令聲明的常量也是不提高,一樣存在暫時性死區,只能在聲明的位置後面使用。

if (true) {
  console.log(MAX); // ReferenceError
  const MAX = 5;
}

       上面代碼在常量MAX聲明以前就調用,結果報錯。

        const聲明的常量,也與let同樣不可重複聲明。

var message = "Hello!";
let age = 25;

// 如下兩行都會報錯
const message = "Goodbye!";
const age = 30;

 

    3.2 本質

       const實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,所以等同於常量。但對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即老是指向另外一個固定的地址),至於它指向的數據結構是否是可變的,就徹底不能控制了。所以,將一個對象聲明爲常量必須很是當心。

複製代碼
const foo = {};

// 爲 foo 添加一個屬性,能夠成功
foo.prop = 123;
foo.prop // 123

// 將 foo 指向另外一個對象,就會報錯
foo = {}; // TypeError: "foo" is read-only
複製代碼

      上面代碼中,常量foo儲存的是一個地址,這個地址指向一個對象。不可變的只是這個地址,即不能把foo指向另外一個地址,但對象自己是可變的,因此依然能夠爲其添加新屬性。

      下面是另外一個例子。

const a = [];
a.push('Hello'); // 可執行
a.length = 0;    // 可執行
a = ['Dave'];    // 報錯

      上面代碼中,常量a是一個數組,這個數組自己是可寫的,可是若是將另外一個數組賦值給a,就會報錯。

    ES6 聲明變量的六種方法

    ES5 只有兩種聲明變量的方法:var命令和function命令。ES6 除了添加letconst命令,另外兩種聲明變量的方法:import命令和class命令。因此,ES6 一共有 6 種聲明變量的方法。  

  4.模板字符串

    模板字符串就是兩個反引號,也就是tab鍵上面的那個鍵,括起來的字符串,就是模板字符串,var或者let聲明的變量均可以被模板字符串直接經過${變量名}來使用,看例子

複製代碼
var aa = 'chao';
let bb = 'jj';
var ss = `你好${aa}`;

ss
"你好chao"
var ss2 = `你好${bb}`;

ss2
"你好jj"
let ss3 = `你好${aa}`;

ss3
"你好chao"
複製代碼

 

    總結:

      let      :特色:   1.a是局部做用域的  2.不存在變量提高  3.不能重複聲明(var能夠重複聲明),  

      const :特色:  1.局部做用域  2.不存在變量提高  3.不能重複聲明  4.通常聲明不可變的量

      模板字符串:tab鍵上面的反引號,${變量名}來插入值

  5.函數

    說到函數,咱們來看看es5和es6是怎麼聲明函數的

複製代碼
//ES5寫法
    function add(x){
        return x
    }
    add(5);
    //匿名函數
    var add = function (x) {
        return x
    };
    //ES6的匿名函數
    let add = function (x) {
        return x
    };
    add(5);
    //ES6的箭頭函數,就是上面方法的簡寫形式
    let add = (x) => {
        return x
    };
    console.log(add(20));
    //更簡單的寫法,但不是很易閱讀
    let add = x => x;
    console.log(add(5));
多個參數的時候必須加括號,函數返回值仍是隻能有一個,沒有參數的,必須寫一個()
let add = (x,y) => x+y;
複製代碼

 

    下面來學一下自定義對象中封裝函數的寫法,看例子:

複製代碼
//es5對象中封裝函數的方法
    var person1 = {
        name:'超',
        age:18,
        f1:function () {  //在自定義的對象中放函數的方法
            console.log(this);//this指向的是當前的對象,{name: "超", age: 18, f1: ƒ}
            console.log(this.name)  // '超'
        }
    };
    person1.f1();  //經過自定對象來使用函數

    //ES6中自定義對象中來封裝箭頭函數的寫法
    let person2 = {
        name:'超',
        age:18,
        f1: () => {  //在自定義的對象中放函數的方法
            console.log(this); //this指向的再也不是當前的對象了,而是指向了person的父級對象(稱爲上下文),而此時的父級對象是咱們的window對象,Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
            console.log(window);//還記得window對象嗎,全局瀏覽器對象,打印結果和上面同樣:Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
            console.log(this.name)  //啥也打印不出來
        }
    };
    person2.f1(); //經過自定對象來使用函數

    //而咱們使用this的時候,但願this是person對象,而不是window對象,因此還有下面這種寫法
    let person3 = {
        name:'超',
        age:18,
        f1(){  //至關於f1:function(){},只是一種簡寫方式,稱爲對象的單體模式寫法,寫起來也簡單,vue裏面會看用到這種方法
            console.log(this);//this指向的是當前的對象,{name: "超", age: 18, f1: ƒ}
            console.log(this.name)  //'超'
        }
    };
    person3.f1()
複製代碼

 

  6.類

    咱們看看es5和es6的類寫法對比

複製代碼
<script>
    //es5寫類的方式
    function Person(name,age) {
        //封裝屬性
        this.name = name;
        this.age = age;
    }
    //封裝方法,原型鏈
    Person.prototype.f1 = function () {
        console.log(this.name);//this指的是Person對象, 結果:'超'
    };
    //封裝方法,箭頭函數的形式寫匿名函數
    Person.prototype.f2 = ()=>{
        console.log(this); //Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}  this指向的是window對象
    };

    var p1 = new Person('超',18);
    p1.f1();
    p1.f2();
    //其實在es5咱們將js的基本語法的時候,沒有將類的繼承,可是也是能夠繼承的,還記得嗎,那麼你想,繼承以後,咱們是否是能夠經過子類實例化的對象調用父類的方法啊,固然是能夠的,知道一下就好了,咱們下面來看看es6裏面的類怎麼寫
    class Person2{
        constructor(name,age){ //對象裏面的單體模式,記得上面將函數的時候的單體模式嗎,這個方法相似於python的__init__()構造方法,寫參數的時候也能夠寫關鍵字參數 constructor(name='超2',age=18)
            //封裝屬性
            this.name = name;
            this.age = age;
        }  //注意這裏不能寫逗號
        showname(){  //封裝方法
            console.log(this.name);
        }  //不能寫逗號
        showage(){
            console.log(this.age);
        }
    }
    let p2 = new Person2('超2',18);
    p2.showname()  //調用方法  '超2'
    //es6的類也是能夠繼承的,這裏我們就不作細講了,未來你須要的時候,就去學一下吧,哈哈,我記得是用的extends和super


</script>
複製代碼

 

 

三 Vue的基本用法

 

1.下載安裝 

  官網:https://cn.vuejs.org/  

  咱們使用vue就要把人家下載下來安裝一下,就像你使用django框架同樣,須要下載安裝django才能使用,這個vue框架小而精,可是功能很強大,以前咱們的js或者jQuery操做基本均可以經過vue來完成,咱們下面是按照vue2.x學的,若是vue更新爲3.0了,那麼你們記得去學學裏面的新語法。

  方式1:

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>

  方式2:

    

     

   引用:

<script src="vue.js"></script>

     下載的三種方式:cdn,下載js  npm下載(後面再說這個),最好的引用方式是先cdn引入而後備選本地js文件引入,減輕本身服務器壓力,這些大家先做爲了解。

   引用了vue以後,咱們直接打開引用了vue的這個html文件,而後在瀏覽器調試窗口輸入Vue,你會發現它就是一個構造函數,也就是我們js裏面實例化一個類時的寫法:

    

 

2 vue的模板語法

  個人文件的目錄結構是這樣的:

    

 

   簡單看一個模板語法的例子:就是上面這個html文件

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板語法,和django的模板語法相似 -->
    <h2>{{ msg }}</h2>   <!-- 放的是變量,就會去下面的Vue對象中的data屬性中的鍵去找對應的數據,注意語法規範,中間的數據先後都有一個空格 -->
    <h2>{{ 'xxxxx' }}</h2>  <!-- 寫個字符串就直接顯示這個字符串 -->
</div> <div id="content">{{ msg }}</div> <!--不生效--> <!-- 1.引包 --> <script src="vue.js"></script> <script> //2.實例化對象 new Vue({ //實例化的時候要傳一個參數,options配置選項,是一個自定義對象,下面就看看這些配置選項都是什麼,是咱們必需要知道的東西,el和data是必需要寫的 el:'#app', //el是當前咱們實例化對象綁定的根元素(標籤),會到html文檔中找到這個id屬性爲app的標籤,在html裏面寫一個id屬性爲app的div標籤,意思就是說,我如今實例化的這個Vue對象和上面這個id爲app的div綁定到了一塊兒,在這個div裏面使用vue的語法才能生效,就像一個地主圈了一塊地同樣,那麼接下來就要種東西了 data:{ //data是數據屬性,就是咱們要在地裏面種的東西了,是一個自定義對象 msg:'黃瓜', //這些數據之後是經過數據庫裏面調出來,而後經過後端代碼的接口接收到的,如今寫死了,先看看效果,咱們在上面的div標籤裏面寫一個其餘的標籤,而後寫上{{ msg }},這樣就至關於咱們在id爲app的div標籤的這個地裏面種了一個種子,這個種子產的是黃瓜,那麼咱們打開頁面就直接看到黃瓜了 } }) </script> </body> </html>
複製代碼

 

  看頁面效果:

     

    其餘的模板語法的使用:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板語法,和django的模板語法相似 -->
    <h2>{{ msg }}</h2> <!-- 放一個變量,會到data屬性中去找對應的值 -->
    <!-- 有人說,咱們直接這樣寫數據不就行嗎,可是你注意,咱們未來的數據都是從後端動態取出來的,不能寫死這些數據啊,你說對不對 -->
    <h2>{{ 'hello beautiful girl!' }}</h2>  <!-- 直接放一個字符串 -->
    <h2>{{ 1+1 }}</h2>  <!-- 四則運算 -->
    <h2>{{ {'name':'chao'} }}</h2> <!-- 直接放一個自定義對象 -->
    <h2>{{ person.name }}</h2>  <!-- 下面data屬性裏面的person屬性中的name屬性的值 -->
    <h2>{{ 1>2?'真的':'假的' }}</h2>  <!-- js的三元運算 -->
    <h2>{{ msg2.split('').reverse().join('') }}</h2>  <!-- 字符串反轉 -->


</div>

<!-- 1.引包 -->
<script src="vue.js"></script>
<script>
//2.實例化對象
    new Vue({
        el:'#app',
        data:{
            msg:'黃瓜',
            person:{
                name:'超',
            },
            msg2:'hello Vue'
        }
    })

</script>
</body>
</html>
複製代碼

 

       看效果:

        

 

3 vue的指令系統

  vue裏面全部的指令系統都是v開頭的,v-text和v-html(重點是v-html),使用指令系統就可以立馬幫你作dom操做了,不須要我們本身再寫dom操做了,因此咱們以後就學習vue的指令系統語法就能夠了。

  3.1 v-text和v-html

    v-text至關於innerText,至關於咱們上面說的模板語法,直接在html中插值了,插的就是文本,若是data裏面寫了個標籤,那麼經過模板語法渲染的是文本內容,這個你們不陌生,這個v-text就是輔助咱們使用模板語法的

    v-html至關於innerHtml

    模板語法data中寫個標籤的例子:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板語法 -->
    <div>{{ text }}</div>


</div>

<!-- 1.引包 -->
<script src="vue.js"></script>
<script>
//2.實例化對象
    new Vue({
        el:'#app',
        data:{
            text:'<h2>只要vue學得好</h2>' //這裏放個標籤
        }
    })

</script>
</body>
</html>
複製代碼

     看效果:

      

      上面咱們使用data屬性的時候,都是用的data:{}對應一個自定義對象,可是在咱們後學的學習中,這個data咱們通常都是對應一個函數,而且這個函數裏面必須return {}一個對象,看例子:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板語法 -->
    <div>{{ msg }}</div>


</div>
<hr>
<div id="content">
    {{ msg }}
</div>

<script src="vue.js"></script>
<script>
    //每個Vue對象均可以綁定一個根元素,好比上面咱們有兩個div標籤,一個id爲app一個id爲content,咱們就能夠寫兩個Vue對象,意思是告訴你們是能夠綁定多個根元素的,使用多個Vue對象就能夠了,可是通常咱們一個就夠用了,充當body的角色,那麼說在body標籤給個id爲app行不行啊,固然行,可是通常不這麼幹
    new Vue({
        el:'#content',
        data:function () {  
            return{
                msg:'哈哈'
            }
        }
    });
//因此重點看下面這個就好了 new Vue({ el:'#app', // data:function () { //你們記住一點,未來凡是涉及到data數據屬性的,牽扯到組件的概念的時候,data都對應一個函數,寫法就是這樣的,由於後面咱們要學習的組件中明確規定,組件中的data必須對應函數,因此你們就用函數來寫data。 // // return{ //函數裏面必須return一個對象{} // msg:'超', //這個return的對象裏面再玩咱們的數據 // } // } //咱們對於這個函數就能夠簡寫,按照單體模式的寫法,你們還記得單體模式嗎 data(){ //單體模式 return{ msg:'超', } } }) </script> </body> </html>
複製代碼

 

      下面看一看v-text和v-html:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板語法 -->
    <div>{{ msg }}</div>
    <div v-text="msg"></div>
    <div v-html="msg"></div>

</div>

<script src="vue.js"></script>
<script>

    new Vue({
        el:'#app',
        data(){
            //記着data中是一個函數,函數中return一個對象,能夠是一個空對象,但必須return
            return{
                msg:'<h2>超</h2>', //後端返回的是標籤,那麼咱們就能夠直接經過v-html渲染出來標籤效果
            }
        }
    })

</script>
</body>
</html>
複製代碼

      看頁面效果:

        

 

     3.2 v-if和v-show

      在模板語法裏面{{ 屬性或者函數 }},雙大括號裏面能夠是data裏面的屬性,也能夠是一個函數,看寫法:  

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="content">
    <p>{{ msg }}</p>
    <p>{{ add(5,6) }}</p> <!-- 使用函數,若是函數沒有返回值,這裏啥也不顯示-->


</div>


<script src="vue.js"></script>
<script>

    new Vue({
        el:'#content',
        data(){
            //記着data中是一個函數,函數中return一個對象,能夠是一個空對象,但必須return
            return{
                msg:'<h2>超</h2>', //後端返回的是標籤,那麼咱們就能夠直接經過v-html渲染出來標籤效果
            }
        },
        methods:{  //在模板語法中使用函數的時候,不是在data屬性裏面寫了,而是在這個methods裏面寫,看寫法
            add(x,y){
                return x+y;
            }

        }
    })

</script>
</body>
</html>
複製代碼

 

      而後咱們接着來看一下v-if和v-show以及v-on的簡單用法

      v-show的例子:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
    <style>
        .box{
            height: 200px;
            width: 200px;
            background-color: red;
        }
    </style>
</head>
<body>

<div id="content">
    <p>{{ msg }}</p>
    <p>{{ add(5,6) }}</p> <!-- 使用函數,若是函數沒有返回值,這裏啥也不顯示-->

    <!-- 注意,使用指令系統的時候,v-xxx=字符串,必須是個字符串,並且這個字符串必須是Vue對象裏面聲明的屬性或者方法,否則在瀏覽器上會報錯,並且使用模板語法{{}}的時候,只能寫在標籤的裏面 -->
    <div class="box" v-show="isShow"></div> <!-- 根據isShow的值來顯示或者隱藏標籤 --> 
</div>

<script src="vue.js"></script>
<script>

    new Vue({
        el:'#content',
        data(){
            return{
                msg:'<h2>超</h2>',
                // isShow:true, //true顯示標籤,false隱藏標籤,就是給標籤加一個css屬性爲display:'none'
                // isShow:1===1,
                isShow:1===2,
            }
        },
        methods:{
            add(x,y){
                return x+y;
            }

        }
    })

</script>
</body>
</html>
複製代碼

      v-show結合v-on的例子2:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
    <style>
        .box{
            height: 200px;
            width: 200px;
            background-color: red;
        }
    </style>
</head>
<body>

<div id="content">
    <p>{{ msg }}</p>
    <p>{{ add(5,6) }}</p> <!-- 使用函數,若是函數沒有返回值,這裏啥也不顯示-->

    <!-- 注意,使用指令系統的時候,v-xxx=字符串,必須是個字符串,並且這個字符串必須是Vue對象裏面聲明的屬性或者方法,否則在瀏覽器上會報錯,並且使用模板語法{{}}的時候,只能寫在標籤的裏面 -->
    <div class="box" v-show="isShow"></div> <!-- 根據isShow的值來顯示或者隱藏標籤 -->
    <div>
        <!-- 點擊隱藏按鈕讓上面的class值爲box的標籤隱藏或者顯示,以前咱們經過dom操做來完成事件驅動的,這裏咱們經過vue的數據驅動來搞 -->
        <!--<button v-on:click="函數名,找Vue對象中methods裏面的函數">隱藏或者顯示</button>,經過v-on指令來實現事件的綁定和驅動-->
        <button v-on:click="handlerClick">隱藏或者顯示</button>

    </div>
</div>

<script src="vue.js"></script>
<script>

    new Vue({
        el:'#content',
        data(){
            return{
                msg:'<h2>超</h2>',
                isShow:true,
            }
        },
        methods:{
            add(x,y){
                return x+y;
            },
            handlerClick(){
                //數據驅動來控制標籤的顯示隱藏
                // console.log(this) //this是當前Vue對象,當咱們打印這個對象的時候,在瀏覽器控制檯你會發現Vue對象自帶的屬性(el\data\methods等,會在屬性名前面加一個$符號,以便和咱們自定義的屬性(msg\isShow\add等等)區分)
                this.isShow = !this.isShow;  //更改isShow的數據,你會發現數據一邊,上面的標籤就根據數據來顯示或者隱藏,這就是vue的思想,數據驅動,徹底不須要咱們本身寫dom操做來完成標籤的顯示或者隱藏了

            }

        }
    })

</script>
</body>
</html>
複製代碼

      效果:

        

      接下來看一下v-show和v-if的區別

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
    <style>
        .box{
            height: 200px;
            width: 200px;
            background-color: red;
        }
        .box2{
            height: 200px;
            width: 200px;
            background-color: green;
        }
    </style>
</head>
<body>

<div id="content">
    <p>{{ msg }}</p>
    <p>{{ add(5,6) }}</p>
    <div class="box" v-show="isShow"></div>
    <!-- v-if也是經過值來判斷顯示或者隱藏,可是這個隱藏不是加diaplay屬性爲none了,而是直接將這個標籤刪除了,經過瀏覽器控制檯你會發現,一隱藏,下面這個標籤就沒有了,一顯示,這個標籤又從新添加回來了,這就是v-if和v-show的區別 -->
    <div class="box2" v-if="isShow"></div>
    <div>
        <button v-on:click="handlerClick">隱藏或者顯示</button>

    </div>
</div>

<script src="vue.js"></script>
<script>

    new Vue({
        el:'#content',
        data(){
            return{
                msg:'<h2>超</h2>',
                isShow:true,
            }
        },
        methods:{
            add(x,y){
                return x+y;
            },
            handlerClick(){
                //數據驅動來控制標籤的顯示隱藏
                // console.log(this) //this是當前Vue對象,當咱們打印這個對象的時候,在瀏覽器控制檯你會發現Vue對象自帶的屬性(el\data\methods等,會在屬性名前面加一個$符號,以便和咱們自定義的屬性(msg\isShow\add等等)區分)
                this.isShow = !this.isShow;  //更改isShow的數據,你會發現數據一邊,上面的標籤就根據數據來顯示或者隱藏,這就是vue的思想,數據驅動,徹底不須要咱們本身寫dom操做來完成標籤的顯示或者隱藏了

            }

        }
    })

</script>
</body>
</html>
複製代碼

      看效果,v-if是標籤的添加和刪除,v-show是標籤的顯示和隱藏,v-if的渲染效率開銷比較大,v-if叫作條件渲染,還有個v-else,一會咱們測試一下。

        

      v-if和v-show的區別,官網解釋:

複製代碼
v-if 是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。

v-if 也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。

相比之下,v-show 就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。

通常來講,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用 v-show 較好;若是在運行時條件不多改變,則使用 v-if 較好。
複製代碼

      接下來咱們簡單看一下v-if和v-else

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
    <style>
        .box{
            height: 200px;
            width: 200px;
            background-color: red;
        }
        .box2{
            height: 200px;
            width: 200px;
            background-color: green;
        }
    </style>
</head>
<body>

<div id="content">
    <p>{{ msg }}</p>
    <p>{{ add(5,6) }}</p>
    <div class="box" v-show="isShow"></div>
    <div class="box2" v-if="isShow"></div>
    <div>
        <button v-on:click="handlerClick">隱藏或者顯示</button>
    </div>
    <div>
        <!-- 首先你會發現其實指令系統的值和咱們模板語法同樣,也支持各類操做,這裏咱們使用了js的一個隨機數方法,而且和0.5進行比較,大於0.5的時候結果爲true,那麼就會顯示'有了',若是爲false就會顯示'沒了',那麼咱們每次刷新頁面的時候,顯示的內容可能會發生變化,這就是v-if和v-else的用法 -->
        <div v-if="Math.random() > 0.5">
          有了
        </div>
        <div v-else>
          沒了
        </div>
    </div>
</div>

<script src="vue.js"></script>
<script>

    new Vue({
        el:'#content',
        data(){
            return{
                msg:'<h2>超</h2>',
                isShow:true,
            }
        },
        methods:{
            add(x,y){
                return x+y;
            },
            handlerClick(){
                this.isShow = !this.isShow;
            }
        }
    })

</script>
</body>
</html>
複製代碼

      在vue2.1.0版本以後,又添加了v-else-if,v-else-if,顧名思義,充當 v-if 的「else-if 塊」,能夠連續使用:

複製代碼
<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>
複製代碼

      相似於 v-elsev-else-if 也必須緊跟在帶 v-if 或者 v-else-if 的元素以後。

      

     3.3 v-bind和v-on

         直接看例子吧:  

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
    <style>
        .box{
            height: 200px;
            width: 200px;
            background-color: red;
        }
        .box2{
            height: 200px;
            width: 200px;
            background-color: green;
        }
        .active{
            background-color: green;
            height: 100px;
            width: 100px;
        }
    </style>
</head>
<body>

<div id="app">
    <!--1. v-bind 可以綁定標籤全部的屬性 好比img標籤的src alt等,啊標籤的href id class title等 -->
    <!-- 用一個img標籤的src和alt屬性來試一下,寫法,v-bind:屬性='字符串(data裏面return裏面的屬性)' -->
    <!--<img v-bind:src="imgSrc" v-bind:alt="imgAlt">-->
    <!--2. 在來一個經過控制class屬性的值來讓標籤的css樣式動態變化 -->
    <!-- 注意寫法,v-bind:class='{class屬性值:Vuew對象的data裏面return中的屬性}',如今的意思是若是isActive的值爲true,那麼會將active添加的前面的class屬性的值中,變成class='box active' ,若是isActive的值爲false則不添加,無論是否已經寫了class屬性,寫了就是添加操做,沒寫就是建立屬性操做-->
    <!--<div v-bind:class="{active:isActive}"></div>-->
    <!--<div class="box"  v-bind:class="{active:isActive}"></div>-->
    <!--3. 咱們再來一個button標籤來點擊讓下面的div標籤動態的增減class值來變化css樣式效果,還要注意,o-on來綁定事件函數,都在Vue對象的methods屬性裏面聲明-->
    <!--<button v-on:click="handlerChange">點擊</button>-->
    <!--<div class="box"  v-bind:class="{active:isActive}"></div>-->

    <!--4. v-bind和v-on有簡單的寫法,v-bind:(簡寫就寫一個冒號:)  v-on:(簡寫就寫一個@) -->
    <img :src="imgSrc" :alt="imgAlt">
    <!--<button @click="handlerChange">點擊</button>-->
    <!-- 綁定多個事件 -->
    <button @click="handlerChange" @mouseenter="handlerEnter" @mouseleave="handlerLeave">點擊</button>
    <div class="box"  :class="{active:isActive}"></div>
</div>

<script src="vue.js"></script>
<script>
    // 數據驅動視圖,設計模式:MVVM:Model-->View-->ViewModel
    //vue稱爲聲明式的JavaScript框架,聲明瞭什麼屬性,HTML裏面就是用什麼屬性,你在頁面上一看到@就知道後面綁定了一個方法,一看到:就知道後面綁定了一個屬性,這就叫作聲明式,以前咱們經過原生的js或者jQuery都是命令式的,讓它作什麼它就作什麼,瞭解一下就行啦
    new Vue({
        el:'#app',
        data(){
            return{
                //之後經過後端獲取到數據,就可以經過更改這些數據來動態的顯示圖片等信息了
                imgSrc:'timg.jpg', //圖片路徑
                // imgSrc:'timg2.jpg',
                imgAlt:'美女',  //圖片未加載成功時的描述
                isActive:true,
            }
        },
        methods:{
            //鼠標點擊時的觸發效果
            handlerChange(){
                this.isActive=!this.isActive;
            },
            //鼠標進入事件的觸發效果
            handlerEnter(){
                this.isActive=false;
            },
            //鼠標離開時的觸發效果
            handlerLeave(){
                this.isActive=true;
            }
        }

    })

</script>
</body>
</html>
複製代碼

 

        總結:

          v-bind能夠綁定標籤中的任意屬性

          v-on能夠監聽js的全部事件

 

       MVVM框架模型圖解:(注意:之後工做的時候,先後端應該先商量好後端給什麼樣子的數據結構,前端再怎麼處理)

        

 

      

    3.4 v-for

      循環,直接看例子:

複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>

</head>
<body>

<div id="app">
    <ul v-if="data.status === 'ok'">
        <!-- 遍歷數組,item表示下面列表中的每一個字典 -->
        <!--<li v-for="item in data.users"></li> -->
        <!-- item和index表示下面列表中的每一個字典中的鍵和值 -->
        <!--<li v-for="(item,index) in data.users">-->
            <!--<h3>{{ item.id }}&#45;&#45;{{ item.name }}&#45;&#45;{{ item.age }}</h3>-->
        <!--</li>-->
        <!-- v-for不只能夠遍歷數組,還能夠遍歷對象,這裏你們記住v-for裏面的一個東西 :key, 就是v-bind:key,這個key是幹什麼的呢,就是爲了給如今已經渲染好的li標籤作個標記,之後即使是有數據更新了,也能夠在這個li標籤裏面進行數據的更新,不須要再讓Vue作從新生成li標籤的dom操做,提升頁面渲染的性能,由於咱們知道頻繁的添加刪除dom操做對性能是有影響的,我如今將數據中的id綁定到這裏,若是數據裏面有id,通常都用id,若是沒有id,就綁定v-for裏面那個index(固然你看你給這個索引取的變量名是什麼,我這裏給索引取的名字是index),這裏面它用的是diff算法,回頭再說這個算法 -->
        <!--  <li v-for="(item,index) in data.users" :key="item.id" @click> 還能夠綁定事件 -->
        <li v-for="(item,index) in data.users" :key="item.id"> <!-- v-for的優先級最高,先把v-for遍歷完,而後給:key加數據,還有,若是沒有bind這個key,有可能你的頁面都後期用動態數據渲染的時候,會出現問題,因此之後你們記着,必定寫上v-bind:key -->
            <h3>{{ item.id }}--{{ item.name }}--{{ item.age }}</h3>
        </li>
    </ul>

    <!-- 2. 循環對象,循環對象的時候,注意寫法,值在前,key在後,若是隻寫一個變量,那麼這個變量循環出來的是值 -->
    <!--<div v-for="value in person">-->
        <!--{{ value }}-->
    <!--</div>-->
    <div v-for="(value,key) in person">
        {{ value }} -- {{ key }}
    </div>

</div>

<script src="vue.js"></script>
<script>

    new Vue({
        el:'#app',
        data(){
            return{
                //通常後端返回的數據都叫作data
                data:{
                    status:'ok', //返回ok表示成功
                    users:[
                        {id:1,name:'chao',age:18},
                        {id:2,name:'jaden',age:38},
                        {id:3,name:'yue',age:28},
                    ]
                },
                //變臉這個對象的屬性
                person:{
                    name:'超',
                }
            }
        },
        methods:{

        }

    })

</script>
</body>
</html>
複製代碼

 

       看效果:

        

 

 

 

 

四 xxx

 

 

 

 

 

五 xxx

 

 

 

 

 

 

六 xxx

 

 

 

 

七 xxx

 

 

 

 

八 xxx

  

 

 

 

 

 

回到頂部

基本用法

ES6 新增了let命令,用來聲明變量。它的用法相似於var,可是所聲明的變量,只在let命令所在的代碼塊內有效。

{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1 

上面代碼在代碼塊之中,分別用letvar聲明瞭兩個變量。而後在代碼塊以外調用這兩個變量,結果let聲明的變量報錯,var聲明的變量返回了正確的值。這代表,let聲明的變量只在它所在的代碼塊有效。

for循環的計數器,就很合適使用let命令。

for (let i = 0; i < 10; i++) {  // ... } console.log(i); // ReferenceError: i is not defined 

上面代碼中,計數器i只在for循環體內有效,在循環體外引用就會報錯。

下面的代碼若是使用var,最後輸出的是10

var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10 

上面代碼中,變量ivar命令聲明的,在全局範圍內都有效,因此全局只有一個變量i。每一次循環,變量i的值都會發生改變,而循環內被賦給數組a的函數內部的console.log(i),裏面的i指向的就是全局的i。也就是說,全部數組a的成員裏面的i,指向的都是同一個i,致使運行時輸出的是最後一輪的i的值,也就是 10。

若是使用let,聲明的變量僅在塊級做用域內有效,最後輸出的是 6。

var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6 

上面代碼中,變量ilet聲明的,當前的i只在本輪循環有效,因此每一次循環的i其實都是一個新的變量,因此最後輸出的是6。你可能會問,若是每一輪循環的變量i都是從新聲明的,那它怎麼知道上一輪循環的值,從而計算出本輪循環的值?這是由於 JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。

另外,for循環還有一個特別之處,就是設置循環變量的那部分是一個父做用域,而循環體內部是一個單獨的子做用域。

for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc 

上面代碼正確運行,輸出了 3 次abc。這代表函數內部的變量i與循環變量i不在同一個做用域,有各自單獨的做用域。

不存在變量提高

var命令會發生「變量提高」現象,即變量能夠在聲明以前使用,值爲undefined。這種現象多多少少是有些奇怪的,按照通常的邏輯,變量應該在聲明語句以後纔可使用。

爲了糾正這種現象,let命令改變了語法行爲,它所聲明的變量必定要在聲明後使用,不然報錯。

// var 的狀況 console.log(foo); // 輸出undefined var foo = 2;  // let 的狀況 console.log(bar); // 報錯ReferenceError let bar = 2; 

上面代碼中,變量foovar命令聲明,會發生變量提高,即腳本開始運行時,變量foo已經存在了,可是沒有值,因此會輸出undefined。變量barlet命令聲明,不會發生變量提高。這表示在聲明它以前,變量bar是不存在的,這時若是用到它,就會拋出一個錯誤。

暫時性死區

只要塊級做用域內存在let命令,它所聲明的變量就「綁定」(binding)這個區域,再也不受外部的影響。

var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; } 

上面代碼中,存在全局變量tmp,可是塊級做用域內let又聲明瞭一個局部變量tmp,致使後者綁定這個塊級做用域,因此在let聲明變量前,對tmp賦值會報錯。

ES6 明確規定,若是區塊中存在letconst命令,這個區塊對這些命令聲明的變量,從一開始就造成了封閉做用域。凡是在聲明以前就使用這些變量,就會報錯。

總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)。

if (true) {  // TDZ開始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ結束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 } 

上面代碼中,在let命令聲明變量tmp以前,都屬於變量tmp的「死區」。

「暫時性死區」也意味着typeof再也不是一個百分之百安全的操做。

typeof x; // ReferenceError let x; 

上面代碼中,變量x使用let命令聲明,因此在聲明以前,都屬於x的「死區」,只要用到該變量就會報錯。所以,typeof運行時就會拋出一個ReferenceError

做爲比較,若是一個變量根本沒有被聲明,使用typeof反而不會報錯。

typeof undeclared_variable // "undefined" 

上面代碼中,undeclared_variable是一個不存在的變量名,結果返回「undefined」。因此,在沒有let以前,typeof運算符是百分之百安全的,永遠不會報錯。如今這一點不成立了。這樣的設計是爲了讓你們養成良好的編程習慣,變量必定要在聲明以後使用,不然就報錯。

有些「死區」比較隱蔽,不太容易發現。

function bar(x = y, y = 2) { return [x, y]; } bar(); // 報錯 

上面代碼中,調用bar函數之因此報錯(某些實現可能不報錯),是由於參數x默認值等於另外一個參數y,而此時y尚未聲明,屬於「死區」。若是y的默認值是x,就不會報錯,由於此時x已經聲明瞭。

function bar(x = 2, y = x) { return [x, y]; } bar(); // [2, 2] 

另外,下面的代碼也會報錯,與var的行爲不一樣。

// 不報錯 var x = x;  // 報錯 let x = x; // ReferenceError: x is not defined 

上面代碼報錯,也是由於暫時性死區。使用let聲明變量時,只要變量在尚未聲明完成前使用,就會報錯。上面這行就屬於這個狀況,在變量x的聲明語句尚未執行完成前,就去取x的值,致使報錯」x 未定義「。

ES6 規定暫時性死區和letconst語句不出現變量提高,主要是爲了減小運行時錯誤,防止在變量聲明前就使用這個變量,從而致使意料以外的行爲。這樣的錯誤在 ES5 是很常見的,如今有了這種規定,避免此類錯誤就很容易了。

總之,暫時性死區的本質就是,只要一進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。

不容許重複聲明

let不容許在相同做用域內,重複聲明同一個變量。

// 報錯 function func() { let a = 10; var a = 1; }  // 報錯 function func() { let a = 10; let a = 1; } 

所以,不能在函數內部從新聲明參數。

function func(arg) { let arg; } func() // 報錯 function func(arg) { { let arg; } } func() // 不報錯 

塊級做用域

爲何須要塊級做用域?

ES5 只有全局做用域和函數做用域,沒有塊級做用域,這帶來不少不合理的場景。

第一種場景,內層變量可能會覆蓋外層變量。

var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined 

上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,內部使用內層的tmp變量。可是,函數f執行後,輸出結果爲undefined,緣由在於變量提高,致使內層的tmp變量覆蓋了外層的tmp變量。

第二種場景,用來計數的循環變量泄露爲全局變量。

var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5 

上面代碼中,變量i只用來控制循環,可是循環結束後,它並無消失,泄露成了全局變量。

ES6 的塊級做用域

let實際上爲 JavaScript 新增了塊級做用域。

function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 } 

上面的函數有兩個代碼塊,都聲明瞭變量n,運行後輸出 5。這表示外層代碼塊不受內層代碼塊的影響。若是兩次都使用var定義變量n,最後輸出的值纔是 10。

ES6 容許塊級做用域的任意嵌套。

{{{{{let insane = 'Hello World'}}}}}; 

上面代碼使用了一個五層的塊級做用域。外層做用域沒法讀取內層做用域的變量。

{{{{ {let insane = 'Hello World'} console.log(insane); // 報錯 }}}}; 

內層做用域能夠定義外層做用域的同名變量。

{{{{ let insane = 'Hello World'; {let insane = 'Hello World'} }}}}; 

塊級做用域的出現,實際上使得得到普遍應用的當即執行函數表達式(IIFE)再也不必要了。

// IIFE 寫法 (function () { var tmp = ...; ... }());  // 塊級做用域寫法 { let tmp = ...; ... } 

塊級做用域與函數聲明

函數能不能在塊級做用域之中聲明?這是一個至關使人混淆的問題。

ES5 規定,函數只能在頂層做用域和函數做用域之中聲明,不能在塊級做用域聲明。

// 狀況一 if (true) { function f() {} }  // 狀況二 try { function f() {} } catch(e) {  // ... } 

上面兩種函數聲明,根據 ES5 的規定都是非法的。

可是,瀏覽器沒有遵照這個規定,爲了兼容之前的舊代碼,仍是支持在塊級做用域之中聲明函數,所以上面兩種狀況實際都能運行,不會報錯。

ES6 引入了塊級做用域,明確容許在塊級做用域之中聲明函數。ES6 規定,塊級做用域之中,函數聲明語句的行爲相似於let,在塊級做用域以外不可引用。

function f() { console.log('I am outside!'); } (function () { if (false) {  // 重複聲明一次函數f function f() { console.log('I am inside!'); } } f(); }()); 

上面代碼在 ES5 中運行,會獲得「I am inside!」,由於在if內聲明的函數f會被提高到函數頭部,實際運行的代碼以下。

// ES5 環境 function f() { console.log('I am outside!'); } (function () { function f() { console.log('I am inside!'); } if (false) { } f(); }()); 

ES6 就徹底不同了,理論上會獲得「I am outside!」。由於塊級做用域內聲明的函數相似於let,對做用域以外沒有影響。可是,若是你真的在 ES6 瀏覽器中運行一下上面的代碼,是會報錯的,這是爲何呢?

原來,若是改變了塊級做用域內聲明的函數的處理規則,顯然會對老代碼產生很大影響。爲了減輕所以產生的不兼容問題,ES6 在附錄 B裏面規定,瀏覽器的實現能夠不遵照上面的規定,有本身的行爲方式

  • 容許在塊級做用域內聲明函數。
  • 函數聲明相似於var,即會提高到全局做用域或函數做用域的頭部。
  • 同時,函數聲明還會提高到所在的塊級做用域的頭部。

注意,上面三條規則只對 ES6 的瀏覽器實現有效,其餘環境的實現不用遵照,仍是將塊級做用域的函數聲明看成let處理。

根據這三條規則,在瀏覽器的 ES6 環境中,塊級做用域內聲明的函數,行爲相似於var聲明的變量。

// 瀏覽器的 ES6 環境 function f() { console.log('I am outside!'); } (function () { if (false) {  // 重複聲明一次函數f function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function 

上面的代碼在符合 ES6 的瀏覽器中,都會報錯,由於實際運行的是下面的代碼。

// 瀏覽器的 ES6 環境 function f() { console.log('I am outside!'); } (function () { var f = undefined; if (false) { function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function 

考慮到環境致使的行爲差別太大,應該避免在塊級做用域內聲明函數。若是確實須要,也應該寫成函數表達式,而不是函數聲明語句。

// 函數聲明語句 { let a = 'secret'; function f() { return a; } }  // 函數表達式 { let a = 'secret'; let f = function () { return a; }; } 

另外,還有一個須要注意的地方。ES6 的塊級做用域容許聲明函數的規則,只在使用大括號的狀況下成立,若是沒有使用大括號,就會報錯。

// 不報錯 'use strict'; if (true) { function f() {} }  // 報錯 'use strict'; if (true) function f() {} 

const 命令

基本用法

const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。

const PI = 3.1415; PI // 3.1415 PI = 3; // TypeError: Assignment to constant variable. 

上面代碼代表改變常量的值會報錯。

const聲明的變量不得改變值,這意味着,const一旦聲明變量,就必須當即初始化,不能留到之後賦值。

const foo; // SyntaxError: Missing initializer in const declaration 

上面代碼表示,對於const來講,只聲明不賦值,就會報錯。

const的做用域與let命令相同:只在聲明所在的塊級做用域內有效。

if (true) { const MAX = 5; } MAX // Uncaught ReferenceError: MAX is not defined 

const命令聲明的常量也是不提高,一樣存在暫時性死區,只能在聲明的位置後面使用。

if (true) { console.log(MAX); // ReferenceError const MAX = 5; } 

上面代碼在常量MAX聲明以前就調用,結果報錯。

const聲明的常量,也與let同樣不可重複聲明。

var message = "Hello!"; let age = 25;  // 如下兩行都會報錯 const message = "Goodbye!"; const age = 30; 

本質

const實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,所以等同於常量。但對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即老是指向另外一個固定的地址),至於它指向的數據結構是否是可變的,就徹底不能控制了。所以,將一個對象聲明爲常量必須很是當心。

const foo = {};  // 爲 foo 添加一個屬性,能夠成功 foo.prop = 123; foo.prop // 123  // 將 foo 指向另外一個對象,就會報錯 foo = {}; // TypeError: "foo" is read-only 

上面代碼中,常量foo儲存的是一個地址,這個地址指向一個對象。不可變的只是這個地址,即不能把foo指向另外一個地址,但對象自己是可變的,因此依然能夠爲其添加新屬性。

下面是另外一個例子。

const a = []; a.push('Hello'); // 可執行 a.length = 0;  // 可執行 a = ['Dave'];  // 報錯 

上面代碼中,常量a是一個數組,這個數組自己是可寫的,可是若是將另外一個數組賦值給a,就會報錯。

若是真的想將對象凍結,應該使用Object.freeze方法。

const foo = Object.freeze({});  // 常規模式時,下面一行不起做用; // 嚴格模式時,該行會報錯 foo.prop = 123; 

上面代碼中,常量foo指向一個凍結的對象,因此添加新屬性不起做用,嚴格模式時還會報錯。

除了將對象自己凍結,對象的屬性也應該凍結。下面是一個將對象完全凍結的函數。

var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); }; 
相關文章
相關標籤/搜索