以前變數的宣告,可以通過var或者直接使用
直接使用相當于往window全域物件上掛了一個屬性


let和var的主要區別:
let宣告的變數只在當前(塊級)作用域內有效
let宣告的變數不能被重復宣告
不存在變數提升
ES6之前的作用域:
全域作用域 函式作用域 eval作用域
ES6塊級作用域:
所有用花括號包裹的代碼塊,如:
if(){} for(){} switch(){} try{}catch(e){} {}
但是不包括物件,如下面這種情況不是塊級作用域
var obj={ }
{ var a=1;//不受塊級作用域影響 let b=2;//只作用在塊級作用域內 } console.log(a); console.log(b);

塊級作用域可以嵌套
內層可以訪問外層,外層不能訪問內層
{ let a=1; { let b=2; console.log(a); } console.log(b); }

使用let或者const宣告的變數,不能再被重新宣告
var a=1; var a; console.log(a);//1 不會是undefined var a=2; console.log(a);//2 let c=1; let c;//報錯
let不存在變數提升
var是存在變數提升的,比如下面這段代碼

由于變數提升會把宣告提前,相當于

因此結果是undefined

如果是let,不存在變數提升,因此會報錯
暫存死區:


ES6規定,如果在一個作用域中存在let或者const宣告的變數,那么會形成一個封閉作用域,因此會拿不到外面的變數


使用let實作面試常見小例子
生成10個按鈕,每次點擊彈出1-10
使用var來實作
for(var i=0;i<10;i++){ (function(i){ var btn=document.createElement("button"); btn.innerText=i; btn.onclick=function(){ alert(i); } document.body.appendChild(btn); })(i); }
使用let來實作(不再需要閉包來實作內部的作用域)
for(let i=0;i<10;i++){ let btn=document.createElement("button"); btn.innerText=i; btn.onclick=function(){ alert(i); } document.body.appendChild(btn); }
效果

const 宣告常量-不可改變的量
常量必須在宣告的時候賦值
跟let一樣,不能重復宣告,存在塊級作用域,不存在變數提升
但是,當常量為參考型別的時候,是可以被修改的(不能修改參考地址,但是可以修改地址中的值)
物件:
const cyy={ name:"cyy", age:18 } console.log(cyy); cyy.name="cyy2"; console.log(cyy);//可以修改參考地址里的值 cyy={}; console.log(cyy);//不能修改參考地址

陣列:
const ARR=[]; ARR.push(1); console.log(ARR);//可以修改參考里的值 ARR=[]; console.log(ARR);//不可以修改參考地址

怎么防止常量為參考型別的時候能被修改的情況:
Object.freeze()
const cyy={ name:"cyy", age:18 } console.log(cyy); Object.freeze(cyy);//禁止物件常量的值被修改 cyy.name="cyy2"; console.log(cyy);//可以修改參考地址里的值

const ARR=[]; Object.freeze(ARR); ARR.push(1); console.log(ARR);//可以修改參考里的值

const擴展
ES6之前怎么宣告常量
1、假裝是常量
var CYY="cyy";
2、給物件設定不可修改的屬性
Object.defineProperty
var cyy={}; Object.defineProperty(cyy,"BASE_NAME",{ value:"cyy", writable:false }) cyy.BASE_NAME="cyy2"; console.log(cyy.BASE_NAME);
可以看到BASE_NAME屬性并沒有被更改

使用Object.defineProperty,并給屬性設定writable:false,能讓屬性不可被修改;但是物件是可以新增屬性的
同理,如果是在全域范圍內定義一個常量,就可以掛載到window上
Object.defineProperty(window,"BASE_NAME",{ value:"cyy", writable:false }) BASE_NAME="cyy2"; console.log(BASE_NAME);

但是這樣定義的常量,依然可以新增屬性
下面代碼是給window物件的BASE_NAME屬性設定了不可修改,但是還是可以給window物件上新增屬性
Object.defineProperty(window,"BASE_NAME",{ value:"cyy", writable:false }) window.a=1; console.log(window.a);

可以使用Object.seal防止屬性被擴展
Object.defineProperty(window,"BASE_NAME",{ value:"cyy", writable:false }) Object.seal(BASE_NAME);//防止常量的屬性被修改 BASE_NAME.a=1; console.log(BASE_NAME); console.log(BASE_NAME.a);

Object.seal能夠防止屬性被擴展,但是已經存在的屬性,值是可以修改的
var cyy={a:1}; Object.seal(cyy); console.log(cyy); cyy.b=2; console.log(cyy);//不能擴展,沒有b屬性 cyy.a=3; console.log(cyy);//屬性能被修改,a被改變

如果想要禁止屬性被修改,還是使用Object.defineProperty
var cyy={a:1}; //禁止修改cyy物件的a屬性 Object.defineProperty(cyy,"a",{ writable:false }) Object.seal(cyy); console.log(cyy); cyy.a=3; console.log(cyy);//屬性不能被修改

也就是說,Object.defineProperty和Object.seal結合使用,可以達到Object.freeze的效果
//在ES6之前,封裝函式,使得常量不能被修改 //參考型別的常量,屬性也不能被修改和擴展 Object.defineProperty(Object,"myfreeze",{ value:function(obj){ for(var i in obj){ if(obj.hasOwnProperty(i)){ Object.defineProperty(obj,i,{ writable:false }) } } Object.seal(obj); } }) const cyy={a:1}; Object.myfreeze(cyy);

補充:里面每一行undefined是console.log()執行之后的回傳值,
log()本質上是一個函式,函式中如果沒有設定return回傳值,默認回傳undefined,
這個不用管,不是代碼的問題,忽略這個undefined,只看前面輸出的內容即可
hasOwnProperty 判斷屬性是原型上的屬性,還是自身的屬性
var obj1={ a:1, b:2 } var obj2=Object.create(obj1); obj2.c=3; obj2.d=4; //for in遍歷到了所有屬性,有原型上的a和b屬性,還有自身的c和d屬性 for(var i in obj2){ console.log(obj2[i]); } //hasOwnProperty 只會顯示自身的屬性(不包括原型屬性) for(var i in obj2){ if(obj2.hasOwnProperty(i)){ console.log(obj2[i]); } }

陣列使用自定義的仿freeze方法
//在ES6之前,封裝函式,使得常量不能被修改 //參考型別的常量,屬性也不能被修改和擴展 Object.defineProperty(Object,"myfreeze",{ value:function(obj){ for(var i in obj){ if(obj.hasOwnProperty(i)){ Object.defineProperty(obj,i,{ writable:false }) } } Object.seal(obj); } }) const cyy=[]; Object.myfreeze(cyy);

但是,如果物件的屬性值又是一個物件的話,那這個物件的屬性值還是可以被修改的
//在ES6之前,封裝函式,使得常量不能被修改 //參考型別的常量,屬性也不能被修改和擴展 Object.defineProperty(Object,"myfreeze",{ value:function(obj){ for(var i in obj){ if(obj.hasOwnProperty(i)){ Object.defineProperty(obj,i,{ writable:false }) } } Object.seal(obj); } }) var cyy={ name:"cyy", age:18, num:{ a:1, b:2 } } Object.myfreeze(cyy);

需要使用遞回來解決
//在ES6之前,封裝函式,使得常量不能被修改 //參考型別的常量,屬性也不能被修改和擴展 Object.defineProperty(Object,"myfreeze",{ value:function(obj){ for(var i in obj){ if(obj.hasOwnProperty(i)){ Object.defineProperty(obj,i,{ writable:false }) } //如果屬性值是物件,則再次進行遍歷 if(obj[i] instanceof Object){ Object.myfreeze(obj[i]); } } Object.seal(obj); } }) var cyy={ name:"cyy", age:18, num:{ a:1, b:2 } } Object.myfreeze(cyy);

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/140002.html
標籤:JavaScript
上一篇:js手寫日歷插件
下一篇:ES6 陣列的解構賦值
