1. 程式人生 > >let const關鍵字定義變數、塊作用域

let const關鍵字定義變數、塊作用域

let 宣告的變數只能在它所在的程式碼中有效

{
    let a=10;
    var b=1;
}
console.log(a);
console.log(b);

for迴圈 中let變數

var a=[];
for(let i=0; i< 10; i++){
    //每一次迴圈的變數i其實都是一個新的變數
    //for迴圈的一個特別之處,就是設定迴圈變數的那部分是一個父作用域,而迴圈體內部是一個單獨的子作用域。
    a[i] = function () {
        console.log(i);
    };
}

a[6]();

a[5]();
// console.dir(a[5]);
// console.dir(a[6]);

函式內部的變數i與迴圈變數i不在同一個作用域,有各自單獨的作用域

for(let i=0; i< 3;i++){
    let i = 'abc';
    console.log(i);
}
//上面的例子證明了: 函式內部的變數i與迴圈變數i不在同一個作用域,有各自單獨的作用域。
//let 宣告的變數不存在"變數提升"!
console.log(foo);
var foo = 2;

//let的情況
//console.log(bar);  //報錯 bar is not defined
let bar = 2;

暫時性死區TDZ  temporal dead zone 

好習慣:在使用變數之前一定要先宣告!


//暫時性死區TDZ  temporal dead zone  TDZ
var tmp = 123;
if(true){
    //TDZ開始
    //tmp = 'abc';  //ReferenceError 報錯
    //TDZ結束
    let tmp;
}
//ES6規定,如果區塊中存在let 和const命令,這個區塊對這些命令宣告的變數,一開始就形成了封閉作用域,凡是在宣告之前就使用這些變數,就會報錯。

//"暫時性死區"也意味著typeof不再是一個百分之百安全的操作
typeof x;
let x;
//在沒有let 之前,typeof運算子是百分之百安全的,現在不成立了。
//所以呢, 好的習慣是:在使用變數前一定要先宣告!!!

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

bar();

//ES6 規定暫時性死區; let, const語句不出現變數提升

不允許重複宣告


//不允許重複宣告; 不允許重複宣告
function func() {
    let a = 10;
    var a = 1;
}

func();

function func() {
    let a = 10;
    let a = 1;
}

func();  //呼叫就報錯

//不能在函式內部重新宣告引數
function func(arg) {
    let arg;   //報錯
}

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

塊級作用域

ES5 只有全域性作用域和函式作用域,沒有塊級作用域,會出現場景

一: 內層變數可能覆蓋外層變數

//一: 內層變數可能覆蓋外層變數
var tmp = new Date();

function f(){
    console.log(tmp);
    //if(false){
    if(true){
        console.log('b:',b);
        //var tmp = 'hello world';
        let tmp = 'hello world';
        var b=3;
    }
    console.log(tmp);
    console.log('b:',b);
}

f();
//用來計數的迴圈變數洩露為全域性變數。
var s = 'hello';
for(var i =0; i< s.length; i++){
    console.log(s[i]);
}
console.log(i);

ES6塊級作用域

function f1() {
    //let n = 5;
    var n = 5;
    if(true){
        let n = 10;
        //var n = 10;
    }
    console.log(n);  //5
}
f1();
ES6允許作用域任意巢狀
外層作用域無法讀取內層作用域的變數
內層作用域可以定義外層作用域的同名變數
{{{{
    let insane = 'Hello world';
    {let insane = 'Hello'}
}}}}
塊級作用域的出現,使得廣泛應用的立即執行函式表示式不再必要
(function () {
    var tmp = "...";
})();
    //塊級作用域寫法
{
    let tmp = "..."
}
ES6中規定塊級作用域中,可以宣告函式,函式的行為類似於let ,在塊級作用域外不可引用

實際上ES6瀏覽器中://node 中也是這樣
--允許在塊級作用域內宣告函式
---函式宣告類似於var ,即會提升到全域性作用域或函式作用域的頭部
---同時,函式宣告還會提升到所在的塊級作用域的頭部
//所以以下會報錯
function f() {
    console.log('I am outside!');
}

(function () {
    console.log(f);
    console.log(typeof f);
    console.dir(f);
    //if(false){
    if(true){
        //重複宣告一次函式f
        // function f() {  //註釋掉看看
        //     console.log('I am inside!')
        // }
    }
    f();
}());

 考慮到環境導致的行為差異太大,應該避免在塊級作用域內宣告函式,如果確實需要,應該寫成函式表示式。!

//小細節,如果想在塊級作用下宣告函式,必須有大括號;不重要的知識點

const  

宣告一個只讀的常量,一旦宣告,常量的值就不能改變

const 一旦宣告,就必須馬上初始化

const foo=123;

if(true){
    const MAX = 5;
}

//console.log(MAX)  // 報錯 MAX is  not defined

注意, const宣告的常量也是不提升,同樣存在暫時性死驅,只能在宣告的位置後面使用。
const 宣告的常量,也與let 一樣不可重複宣告
var message = "Hello!";
let age = 25;

//以下兩行都會報錯
//const message = "Goodbye!";
//const age = 30;
const 本質
只能保證 指向的地址是固定的, ;簡單的型別資料(數值,字串,布林值)就儲存在變數指向的那個記憶體地址,因此等於常量;
const foo = {};
foo.prop = 123;
foo.prop

//將foo指向另一個物件就會報錯
//foo = {}

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

確實想把物件凍結,怎麼做?

const foo = Object.freeze({name: 'zhaosi', age : 29, order :{}});

foo.prop = 123;  //不起作用; 嚴格模式下,該行會報錯。

console.log(foo);

foo.age = 40;   //不可修改; 不起作用,也不報錯
console.log(foo);

foo.order.num = 3;  //還是能修改的,還是不徹底的呀!

console.log(foo);  //{ name: 'zhaosi', age: 29, order: { num: 3 } }

徹底凍結該怎麼做?

//寫一個凍結函式
var constantize = (obj) =>{
    Object.freeze(obj);
    Object.keys(obj).forEach((key, i) => {
        if(typeof obj[key] === 'object'){
            console.log('free');
            constantize(obj[key]);
        }
    })
}

var dongjie = {name : 'liang', age : 28, info :{num : 3}}

dongjie.info.num =5;
constantize(dongjie);
//Object.freeze(dongjie);
dongjie.info.num = 8;   //怎麼沒凍住 info呢?
dongjie.age = 22;
console.log(dongjie);

 

ES6宣告變數的六種方式
//var  function   let  const   import class

頂層物件的屬性

在瀏覽器環境中指的是 window 物件; Node 指的是 global 物件;
ES5 中 頂層物件 ====  全域性變數

ES6中 var function 宣告的的全域性變數, 依舊是頂層物件的屬性; let , const , class 宣告的全部變數不屬於頂層物件的屬性。
//
var a = 1;
//console.log(global.a);  //undefined   node 中
console.log(window.a);    //1   瀏覽器

let b = 1;
//console.log(global.b);  //undefined   node中
console.log(window.b);   //undefined  瀏覽器
// //global 物件
var getGlobal = function () {
    if(typeof self !== 'undefined'){
        return self;
    }
    if(typeof window !== 'undefined'){
        return window;
    }
    if(typeof global !== 'undefined'){
        return global;
    }
}

console.log(getGlobal());

 

 

參考: http://es6.ruanyifeng.com/#docs/let