ECMAScript6(ES6)基礎(chǔ)知識(shí)及核心原理
使用Babel編譯ES6
一、下載安裝Babel
環(huán)境:需要電腦上安裝node(node中一般都會(huì)自帶npm包管理器)
npm install babel-cli -g
把模塊安裝在全局環(huán)境下(在任何的項(xiàng)目中,都可以使用命令來(lái)編譯我們的代碼了)
npm uninstall babel-cli -g
把全局下安裝的babel模塊卸載掉
[圖片上傳失敗...(image-be07d3-1570785377246)]
觀看安裝目錄發(fā)現(xiàn)一些細(xì)節(jié)需要了解的知識(shí)點(diǎn):
1、我們后期之所以可以使用babel的命令,是因?yàn)榘惭b在全局環(huán)境下之后,會(huì)生成一些 xxx.cmd 的文件,而這里的xxx就是可以在DOC窗口中執(zhí)行的命令
babel.cmd
以后可以使用babel命令了
babel-node.cmd
...2、執(zhí)行babel命令后我們可以完成一些編譯或者其它的任務(wù),主要原因是執(zhí)行babel命令后,會(huì)自動(dòng)加載一些處理任務(wù)的文件
[圖片上傳失敗...(image-b51a8a-1570785377246)]
二、配置.babelrc文件,安裝一些語(yǔ)言解析包
1、我們需要把.babelrc文件配置在當(dāng)前項(xiàng)目的根目錄下(這個(gè)文件沒(méi)有文件名,后綴名是babelrc)
a:在電腦上不能直接創(chuàng)建沒(méi)有文件名的文件,我們需要使用WS中的 new -> file 來(lái)創(chuàng)建,或者使用命令創(chuàng)建
b:babelrc這個(gè)后綴名在某些ws中是不識(shí)別的,它其實(shí)是一個(gè)json文件,我們需要在ws中配置一下(讓他隸屬于json文件)
[圖片上傳失敗...(image-34afb4-1570785377246)]
2、在文件中編寫一些內(nèi)容
{
"presets": [], //=>存放的是,我們編譯代碼時(shí)候需要依賴的語(yǔ)言解析包
"plugins": [] //=>存放的是,我們編譯代碼時(shí)候需要依賴的插件信息
}
3、安裝依賴的語(yǔ)言解析包
在當(dāng)前項(xiàng)目的根目錄下安裝(不是安裝在全局),需要特殊注意的是:要在當(dāng)前項(xiàng)目根目錄中打開DOC命令才可以
npm install babel-preset-latest
安裝最新已經(jīng)發(fā)布的語(yǔ)言標(biāo)準(zhǔn)解析模塊
npm install babel-preset-stage-2
安裝當(dāng)前還沒(méi)有發(fā)布但是已經(jīng)進(jìn)入草案的語(yǔ)言解析模塊(如果你的代碼中用到了發(fā)布非標(biāo)準(zhǔn)的語(yǔ)法,我們需要安裝他)
...
安裝成功后在自己的當(dāng)前項(xiàng)目根目錄下,會(huì)存在一個(gè)node_modules
文件夾,在這個(gè)文件夾中有我們安裝的模塊4、完成最后.babelrc文件的配置
{
"presets": [
"latest",
"stage-2"
],
"plugins": []
}
三、使用命令編譯JS代碼
基本上所有支持命令操作的模塊都有一個(gè)命令
babel --help / babel -h
查看幫助
babel --version / babel -V
查看版本號(hào)
babel --out-file / babel -o
把某一個(gè)JS文件中的ES6代碼進(jìn)行編譯
babel --out-dir / babel -d
把某一個(gè)文件夾中所有的JS文件中的ES6代碼進(jìn)行編譯
babel --watch / babel -w
監(jiān)聽文件中代碼的改變,當(dāng)代碼改變后,會(huì)自動(dòng)進(jìn)行編譯結(jié)束監(jiān)聽的程序:
ctrl+c 按兩遍
[圖片上傳失敗...(image-90e7f0-1570785377246)]
1.ES6
ES6 的模塊自動(dòng)采用嚴(yán)格模式,不管你有沒(méi)有在模塊頭部加上"use strict"; 嚴(yán)格模式主要有以下限制。
- 變量必須聲明后再使用
- 函數(shù)的參數(shù)不能有同名屬性,否則報(bào)錯(cuò)
- 不能使用with語(yǔ)句
- 不能對(duì)只讀屬性賦值,否則報(bào)錯(cuò)
- 不能使用前綴 0 表示八進(jìn)制數(shù),否則報(bào)錯(cuò)
- 不能刪除不可刪除的屬性,否則報(bào)錯(cuò)
- 不能刪除變量delete prop,會(huì)報(bào)錯(cuò),只能刪除屬性delete global[prop]
- eval不會(huì)在它的外層作用域引入變量
- eval和arguments不能被重新賦值
- arguments不會(huì)自動(dòng)反映函數(shù)參數(shù)的變化
- 不能使用arguments.callee
- 禁止this指向全局對(duì)象
- 不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
- 增加了保留字(比如protected、static和interface);
1.數(shù)組遍歷方法
- forEach:遍歷
- map:遍歷,可以修改返回值
- find: 數(shù)組實(shí)例的
find
方法,用于找出第一個(gè)符合條件的數(shù)組成員。它的參數(shù)是一個(gè)回調(diào)函數(shù),所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù),直到找出第一個(gè)返回值為true
的成員,然后返回該成員。如果沒(méi)有符合條件的成員,則返回undefined
。find
方法的回調(diào)函數(shù)可以接受三個(gè)參數(shù),依次為當(dāng)前的值、當(dāng)前的位置和原數(shù)組。 - findIndex: 數(shù)組實(shí)例的
findIndex
方法的用法與find
方法非常類似,返回第一個(gè)符合條件的數(shù)組成員的位置,如果所有成員都不符合條件,則返回-1
。 - filter:篩選,返回一個(gè)新數(shù)組,原數(shù)組不變
- some::返回結(jié)果只要有一個(gè)true,就返回true
- every:返回結(jié)果只要有一個(gè)false,就返回false
上面都可以改this
- reduce:迭代,比如數(shù)組求和,consloe.log(ary.reduce((prev,item)=>{return prev+item}))
- reduceRight:從數(shù)組的最后一項(xiàng)往前迭代.
2.set &&map
- set可以數(shù)組去重,返回的是一個(gè)類數(shù)組.只有value沒(méi)有key.遍歷只能遍歷他的value值,比如forEach((value,set)=>{})
- keys():返回鍵名的遍歷器
- values():返回鍵值的遍歷器
- entries():返回鍵值對(duì)的遍歷器
- forEach():使用回調(diào)函數(shù)遍歷每個(gè)成員
//數(shù)組去重
let ary=[1,1,1,1,2,2,2];
console.log([...new Set(ary)]);//1,2
console.log(Object.is(NaN,NaN));解決NAN問(wèn)題
Array.from方法可以將 Set 結(jié)構(gòu)轉(zhuǎn)為數(shù)組。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
//這就提供了去除數(shù)組重復(fù)成員的另一種方法。
function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
keys方法、values方法、entries方法返回的都是遍歷器對(duì)象,由于 Set 結(jié)構(gòu)沒(méi)有鍵名,只有鍵值(或者說(shuō)鍵名和鍵值是同一個(gè)值),所以keys方法和values方法的行為完全一致。
這意味著,可以省略values方法,直接用for...of循環(huán)遍歷 Set。
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
set
add 增加一項(xiàng),重復(fù)的加不上,返回當(dāng)前set
set.add((value):添加某個(gè)值,返回 Set 結(jié)構(gòu)本身。
delete(value):刪除某個(gè)值,返回一個(gè)布爾值,表示刪除是否成功。刪除已有的返回true.
has(value):返回一個(gè)布爾值,表示該值是否為Set的成員。 返回true或者false
clear 清空沒(méi)有返回值 undefined
size 相當(dāng)于數(shù)組中的length.
map
它類似于對(duì)象,也是鍵值對(duì)的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對(duì)象)都可以當(dāng)作鍵。也就是說(shuō),Object 結(jié)構(gòu)提供了“字符串—值”的對(duì)應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對(duì)應(yīng),是一種更完善的 Hash 結(jié)構(gòu)實(shí)現(xiàn)。如果你需要“鍵值對(duì)”的數(shù)據(jù)結(jié)構(gòu),Map 比 Object 更合適。
- 對(duì)象的屬性名一定是字符串,如果寫的不是字符串是其他類型,默認(rèn)將其變?yōu)樽址?
- map的key可以是任意數(shù)據(jù)類型.
- map.set(key,value)返回值是這個(gè)MAP
- map.get(key);返回的是對(duì)應(yīng)的value;
- map.has();返回的值是布爾類型的
- clear delete同set類似
- foreach key values entries
let map=new Map([[true,true],[{},{a:"a"}],[[],[]],[null,null]]);
//key可以是任意數(shù)據(jù)類型的
console.log(map);
//set(key,value) 返回值是這個(gè) map
console.log(map.set(NaN, NaN));
//get(key)返回對(duì)應(yīng)的value
console.log(map.get(true));
//has(key)->布爾值
console.log(map.has(NaN));//true
//clear delete
//foEach keys values entries
map.forEach((value,key,c)=>{});
for (let key of map.keys()){
//console.log(key);
}
for (let value of map.values()){
//console.log(value);
}
for (let [key,value] of map.entries()) {
console.log(value, key);
}
map.foreach((value,key,c)=>{
for(let key of map.keys()){consloe.log(key)};
for(let key of map.values()){consloe.log(value)};
for(let [key,value] of map.entries()){console.log(value,key)};
})
3.Symbol
- 基本數(shù)據(jù)類型 通過(guò)函數(shù)執(zhí)行得到 不能使用new執(zhí)行
- 唯一值
- 不能進(jìn)行運(yùn)算 因?yàn)椴豢梢赞D(zhuǎn)數(shù)字 也不可以進(jìn)行字符串拼接 這些都會(huì)報(bào)錯(cuò)
- 可以轉(zhuǎn)為布爾值
- 當(dāng)做屬性名的時(shí)候只能用[""]的形式
- Object.getOwnPropertySymbols()
- Reflect.ownKeys方法可以返回所有類型的鍵名
4.proxy
- get(target, propKey, receiver):攔截對(duì)象屬性的讀取,比如proxy.foo和proxy['foo']。
- set(target, propKey, value, receiver):攔截對(duì)象屬性的設(shè)置,比如proxy.foo = v或proxy['foo'] = v,返回一個(gè)布爾值。
- has(target, propKey):攔截propKey in proxy的操作,返回一個(gè)布爾值。
- deleteProperty(target, propKey):攔截delete proxy[propKey]的操作,返回一個(gè)布爾值。
- ownKeys(target):攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循環(huán),返回一個(gè)數(shù)組。該方法返回目標(biāo)對(duì)象所有自身的屬性的屬性名,而Object.keys()的返回結(jié)果僅包括目標(biāo)對(duì)象自身的可遍歷屬性。
- getOwnPropertyDescriptor(target, propKey):攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對(duì)象。
- defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一個(gè)布爾值。
- preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個(gè)布爾值。
- getPrototypeOf(target):攔截Object.getPrototypeOf(proxy),返回一個(gè)對(duì)象。
- isExtensible(target):攔截Object.isExtensible(proxy),返回一個(gè)布爾值。
- setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個(gè)布爾值。如果目標(biāo)對(duì)象是函數(shù),那么還有兩種額外操作可以攔截。
- apply(target, object, args):攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
- construct(target, args):攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作,比如new proxy(...args)。
5.Iterator接口 和for of
一個(gè)數(shù)據(jù)結(jié)構(gòu)只要部署了Symbol.iterator屬性,就被視為具有 iterator 接口,就可以用for...of循環(huán)遍歷它的成員。也就是說(shuō),for...of循環(huán)內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator方法。
for...of循環(huán)可以使用的范圍包括數(shù)組、Set 和 Map 結(jié)構(gòu)、某些類似數(shù)組的對(duì)象(比如arguments對(duì)象、DOM NodeList 對(duì)象)、后文的 Generator 對(duì)象,以及字符串。
Iterator 的遍歷過(guò)程是這樣的。
(1)創(chuàng)建一個(gè)指針對(duì)象,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說(shuō),遍歷器對(duì)象本質(zhì)上,就是一個(gè)指針對(duì)象。
(2)第一次調(diào)用指針對(duì)象的next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個(gè)成員。
(3)第二次調(diào)用指針對(duì)象的next方法,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個(gè)成員。
(4)不斷調(diào)用指針對(duì)象的next方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置。
每一次調(diào)用next方法,都會(huì)返回?cái)?shù)據(jù)結(jié)構(gòu)的當(dāng)前成員的信息。具體來(lái)說(shuō),就是返回一個(gè)包含value和done兩個(gè)屬性的對(duì)象。其中,value屬性是當(dāng)前成員的值,done屬性是一個(gè)布爾值,表示遍歷是否結(jié)束。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
next方法返回一個(gè)對(duì)象,表示當(dāng)前數(shù)據(jù)成員的信息。這個(gè)對(duì)象具有value和done兩個(gè)屬性,value屬性返回當(dāng)前位置的成員,done屬性是一個(gè)布爾值,表示遍歷是否結(jié)束,即是否還有必要再一次調(diào)用next方法。
總之,調(diào)用指針對(duì)象的next方法,就可以遍歷事先給定的數(shù)據(jù)結(jié)構(gòu)。
Iterator 接口的目的,就是為所有數(shù)據(jù)結(jié)構(gòu),提供了一種統(tǒng)一的訪問(wèn)機(jī)制,即for...of循環(huán)。當(dāng)使用for...of循環(huán)遍歷某種數(shù)據(jù)結(jié)構(gòu)時(shí),該循環(huán)會(huì)自動(dòng)去尋找 Iterator 接口。
ES6 規(guī)定,默認(rèn)的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性,或者說(shuō),一個(gè)數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性,就可以認(rèn)為是“可遍歷的”(iterable)。Symbol.iterator屬性本身是一個(gè)函數(shù),就是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的遍歷器生成函數(shù)。執(zhí)行這個(gè)函數(shù),就會(huì)返回一個(gè)遍歷器。至于屬性名Symbol.iterator,它是一個(gè)表達(dá)式,返回Symbol對(duì)象的iterator屬性,這是一個(gè)預(yù)定義好的、類型為 Symbol 的特殊值,所以要放在方括號(hào)內(nèi)
調(diào)用 Iterator 接口的場(chǎng)合
- 解構(gòu)賦值
- 擴(kuò)展運(yùn)算符
for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
Promise.all()
Promise.race()
字符串是一個(gè)類似數(shù)組的對(duì)象,也原生具有 Iterator 接口。
原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)如下。
- Array
- Map
- Set
- String
- TypedArray
- 函數(shù)的 arguments 對(duì)象
- NodeList 對(duì)象
6.async await
- async 將一個(gè)函數(shù)變成promise對(duì)象
- await后面跟一個(gè)promise對(duì)象 如果不是默認(rèn)將其變成一個(gè)resolve的promise 值作為resolve 的參數(shù)
- await的結(jié)果就是resolve或者reject的參數(shù)
- await異步執(zhí)行完成之后再去執(zhí)行后面的代碼
7.Object.getOwnPropertyDescriptor
- configurable:true 是否可配置 是否可以刪除這個(gè)屬性
- enumerable:true 是否可枚舉 是否可遍歷
- value:"dali"
- writable:true 是否可修改
8.Object.defineProperty
let obj={name:"dali",age:10};
//new Proxy(target目標(biāo)對(duì)象,{代理的方法})
let proxy1=new Proxy(obj,{
get(target,prop){
//獲取屬性名的屬性值的時(shí)候就會(huì)觸發(fā)這個(gè)get
return target[prop];
},
set(target,prop,value){
target[prop]=value;
}
});
Object.defineProperty(obj,"name",{
value:"mq",
enumerable:false,
writable:false,
configurable:false
});
ES6中的let和const
let基礎(chǔ)語(yǔ)法
let 變量名 = 變量值
使用let創(chuàng)建變量和使用var創(chuàng)建變量的區(qū)別
1、let不存在變量提升機(jī)制
console.log(str);//=>undefined
console.log(fn);//=>FN本身
console.log(avg);//=>undefined
console.log(sum);//=>Uncaught ReferenceError: sum is not defined
console.log(num);//=>Uncaught ReferenceError: num is not defined
var str = '珠峰培訓(xùn)';
let num = 12;
function fn() {}
var avg = function () {};
let sum = function () {};
//=>ES6中只提供了創(chuàng)建變量的新語(yǔ)法標(biāo)準(zhǔn)(let),創(chuàng)建函數(shù)還是沿用ES5中的function(還會(huì)存在變量提升),如果想讓函數(shù)也不存在變量提升,都使用函數(shù)表達(dá)式賦值的方式操作:let fn=function(){}
//=>創(chuàng)建變量
let xxx=xxx;
//=>創(chuàng)建函數(shù)
let xxx=function(){}
//=>自執(zhí)行函數(shù)
;(function(){
})();
//=>好處:此時(shí)代碼中就不要在考慮變量提升了,只要這樣處理,沒(méi)有所謂的變量提升
2、使用let定義的變量不允許在
同一個(gè)作用域中
重復(fù)聲明
var num2 = 12;
var num2 = 13;
console.log(num2);//=>13
let str = '珠峰';
let str = '培訓(xùn)';
console.log(str);//=>Uncaught SyntaxError: Identifier 'str' has already been declared 當(dāng)前報(bào)錯(cuò),上面代碼也不會(huì)執(zhí)行(在JS代碼執(zhí)行之前就已經(jīng)知道有重復(fù)聲明的了,也就是瀏覽器依然存在類似于變量提升的機(jī)制:在JS代碼之前先把所有LET聲明的變量過(guò)一遍,發(fā)現(xiàn)有重復(fù)的直接報(bào)錯(cuò))
let num = 12;
num = 13;
console.log(num);//=>13 LET不允許重復(fù)被聲明,但是允許重新賦值
var att=200;
let att=100;//=>Uncaught SyntaxError: Identifier 'att' has already been declared 不管你之前使用什么方式在當(dāng)前作用域中聲明的變量,再使用let聲明的時(shí)候都會(huì)報(bào)錯(cuò)
let num = 12,
fn = function () {
let num = 13;
};
console.log(num);//=>12 當(dāng)前作用域下別重復(fù)聲明即可(不同作用域中的變量是自己私有的,名字重復(fù)沒(méi)有關(guān)系)
let att = 13,
sum = function () {
att = 14;
};
sum();
console.log(att);//=>let也存在私有變量和作用域鏈的概念,和ES5中基本上差不多 =>14
3、關(guān)于暫時(shí)性死區(qū):使用typeof檢測(cè)一個(gè)未被聲明過(guò)的變量
ES5中返回的結(jié)果是undefined但是不報(bào)錯(cuò)
ES6中直接報(bào)錯(cuò)
"use strict";
console.log(typeof num);//=>undefined 當(dāng)前變量不存在,但是使用typeof檢測(cè)的時(shí)候,不會(huì)提示錯(cuò)誤,而是返回undefined
console.log(typeof num);//=>Uncaught ReferenceError: num is not defined ES6中檢測(cè)一個(gè)沒(méi)有被聲明過(guò)的變量直接報(bào)錯(cuò),不像之前ES5中的值是UNDEFINED一樣了
let num;
let num;
console.log(typeof num);//=>undefined 只聲明沒(méi)有定義(賦值),默認(rèn)值是UNDEFINED
4、ES6語(yǔ)法創(chuàng)建的變量(let)存在塊級(jí)作用域,ES5語(yǔ)法創(chuàng)建變量(var/function)沒(méi)有塊級(jí)作用域
[ES5]
window全局作用域
函數(shù)執(zhí)行形成的私有作用域[ES6]
除了有ES5中的兩個(gè)作用域,ES6中新增加塊級(jí)作用域(我們可以把塊級(jí)作用域理解為之前學(xué)習(xí)的私有作用域:存在私有變量和作用域鏈的一些機(jī)制)ES6語(yǔ)法中把大部分用大括號(hào)包起來(lái)都稱之為塊級(jí)作用域
let num = 12,
str = '';
let fn = function (str) {
str = 'HELLO';
//console.log(arguments[0]);//=>"HELLO" 當(dāng)前JS并沒(méi)有開啟嚴(yán)格模式,所以形參變量和ARG存在映射機(jī)制(但是我們以后盡量不要這樣處理:因?yàn)榘袳S6編譯為ES5之后,會(huì)默認(rèn)的開啟嚴(yán)格模式,映射機(jī)制會(huì)中斷,此處的值依然是'珠峰',這樣導(dǎo)致我們的ES6結(jié)果和ES5結(jié)果不一致)
// console.log(num);//=>Uncaught ReferenceError: num is not defined
let num = 13;
console.log(num, str);//=>13 "HELLO"
};
fn('珠峰');
console.log(num, str);//=>12 ''
大部分我們遇到的大括號(hào)操作都是塊級(jí)作用域
/*
if (10 >= 10) {
let total = 100;
}
console.log(total);//=>Uncaught ReferenceError: total is not defined 判斷體也是一個(gè)塊級(jí)私有作用域,在這個(gè)作用域中聲明的變量是私有變量,在塊級(jí)作用域之外是無(wú)法使用的
*/
/*
for (let i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);//=>Uncaught ReferenceError: i is not defined 循環(huán)體也是一個(gè)塊級(jí)作用域(每一次循環(huán)都會(huì)形成一個(gè)新的塊級(jí)作用域:當(dāng)前案例形成五個(gè)塊級(jí)作用域,每一個(gè)塊級(jí)作用域中都有一個(gè)私有變量i,分別存儲(chǔ)的是0~4)
*/
/*{
let i=12;
}
console.log(i);//=>Uncaught ReferenceError: i is not defined 這是一個(gè)ES6中標(biāo)準(zhǔn)的塊級(jí)作用域*/
/*
let i=10;
{
let i=20;
{
{
console.log(i);//=>Uncaught ReferenceError: i is not defined 雖然ES6沒(méi)有變量提升,但是每一次進(jìn)入當(dāng)前作用域都會(huì)把LET定義的變量找一遍(不提升但是找了,找到了說(shuō)明當(dāng)前作用域中是有這個(gè)變量的,提前用都會(huì)報(bào)錯(cuò))
}
let i=30;
}
}
*/
/*
try{
let i=100;
}catch (e){
let k=200;
}
console.log(k);//=>Uncaught ReferenceError: k is not defined
console.log(i);//=>Uncaught ReferenceError: i is not defined TRY CATCH中的任何大括號(hào)都是塊級(jí)作用域
*/
/*
switch (10){
case 10:
let i=20;
break;
}
console.log(i);//=>Uncaught ReferenceError: i is not defined
*/
/*
let obj={name:'珠峰'};
for (let key in obj) {
}
console.log(key);//=>Uncaught ReferenceError: key is not defined
*/
塊級(jí)作用域的增加有什么用?
let tempList = document.getElementsByName('TEMP');
// for (var i = 0; i < tempList.length; i++) {
// tempList[i].onclick = function () {
// console.log(i);//=>5 怎么點(diǎn)擊都是5 異步操作以及作用域鏈的查找,找到的都是全局下最后一次循環(huán)的結(jié)束值
// }
// }
//=>自定義屬性解決
// for (var i = 0; i < tempList.length; i++) {
// tempList[i].index = i;
// tempList[i].onclick = function () {
// console.log(this.index);
// }
// }
//=>閉包解決
// for (var i = 0; i < tempList.length; i++) {
// ~function (i) {
// tempList[i].onclick = function () {
// console.log(i);
// }
// }(i);
// }
//=>使用ES6的塊級(jí)作用域
for (let i = 0; i < tempList.length; i++) {
tempList[i].onclick = function () {
console.log(i);
}
}
const的基礎(chǔ)語(yǔ)法
const的細(xì)節(jié)知識(shí)點(diǎn)和let一樣,和let的主要區(qū)別在于:let是創(chuàng)建變量,const是創(chuàng)建常量
變量:值是可以修改的
常量:值不能被修改
let num = 12;
num = 13;
console.log(num);//=>13
const str = '珠峰';
str = '培訓(xùn)';//=>而且使用BABEL如果遇到了CONST設(shè)置的常量在進(jìn)行修改,就無(wú)法進(jìn)行編譯了
console.log(str);//=>Uncaught TypeError: Assignment to constant variable.
JS中創(chuàng)建變量方式匯總
var
:ES5中創(chuàng)建變量
function
:ES5中創(chuàng)建函數(shù)
ES5中創(chuàng)建變量或者函數(shù)存在:變量提升、重復(fù)聲明等特征,但是沒(méi)有塊級(jí)作用域的機(jī)制
let
:ES6中創(chuàng)建變量
const
:ES6創(chuàng)建常量
ES6中創(chuàng)建的變量或者常量都不可以變量提升,也不可以重復(fù)聲明,而且還存在塊級(jí)作用域
class
:ES6中創(chuàng)建類的方式
import
:ES6中模塊導(dǎo)入的方式
class Parent{
constructor(){
//=>this.xxx=xxx
}
//=>Parent.prototype
aa(){}
//=>Parent own property
static bb(){}
}
ES6中的解構(gòu)賦值
按照原有值的結(jié)構(gòu),把原有值中的某一部分內(nèi)容快速獲取到(快速賦值給一個(gè)變量)
數(shù)組的解構(gòu)賦值
結(jié)構(gòu)賦值本身是ES6的語(yǔ)法規(guī)范,使用什么關(guān)鍵字來(lái)聲明這些變量是無(wú)所謂的
let [a,b,c] = [12,23,34];
//=>a:12 b:23 c:34
var [d,e,f] = [12,23,34];
//=>d:12 e:23 f:34
[g,h,i] = [12,23,34];
//=>此處相當(dāng)于給window增加的全局屬性
//g:12 h:23 i:34
//=>但是這個(gè)操作在JS的嚴(yán)格模式下是不允許的,因?yàn)閲?yán)格模式下不允許出現(xiàn)非使用var/let等聲明的變量
多維數(shù)組的結(jié)構(gòu)賦值,可以讓我們快速獲取到需要的結(jié)果
let [,[,A],[,B,[,C]]] = [12, [23, 34], [45, 56, [67, 78]]];
console.log(A, B, C);//=>34 56 78
如果只想獲取數(shù)組中前面的某幾項(xiàng)內(nèi)容,后面的結(jié)構(gòu)不需要不全
let [D]=[12, 23, 34];
console.log(D);//=>12
let [,E]=[12, 23, 34];
console.log(E);//=>23
在解構(gòu)賦值中,我們可以給某一項(xiàng)設(shè)置默認(rèn)值
let [,,,A]=[12, 23, 34];
console.log(A);//=>undefined
let [,,,B = 0]=[12, 23, 34];
console.log(B);//=>0
在解構(gòu)賦值中,支持
...xxx
的拓展運(yùn)算符
let [A,...B]=[12, 23, 34, 45, 56];
console.log(A, B);//=>12 [23,34...]
let [...C]=[12, 23, 34, 45, 56];
console.log(C);//=>[12,23...] 數(shù)組克隆
let [D,...E,F]=[12, 23, 34, 45, 56];
console.log(D, E, F);//=>Uncaught SyntaxError: Rest element must be last element 拓展運(yùn)算符只能出現(xiàn)在解構(gòu)賦值中的結(jié)構(gòu)末尾的位置
let [G,,,...H]=[12, 23, 34, 45, 56];
console.log(G, H);//=>12 [45,56]
對(duì)象的解構(gòu)賦值
let {name, age}={name: '珠峰培訓(xùn)', age: 9};
console.log(name, age);//=>'珠峰培訓(xùn)' 9
let {A, B}={name: '珠峰培訓(xùn)', age: 9};
console.log(A, B);//=>在對(duì)象的解構(gòu)賦值中需要注意的是:賦值的變量需要和對(duì)象中的屬性名吻合,否則無(wú)法獲取對(duì)應(yīng)的屬性值 undefined*2
let {C = 0}={name: '珠峰培訓(xùn)', age: 9};
console.log(C);//=>0 可以給當(dāng)前的變量設(shè)置默認(rèn)值
let {name}={name: '珠峰培訓(xùn)', age: 9};
console.log(name);//=>'珠峰培訓(xùn)' 和數(shù)組的解構(gòu)賦值一樣,我們可以把后面不需要獲取的結(jié)構(gòu)省略掉
let {,age}={name: '珠峰培訓(xùn)', age: 9};//=>Uncaught SyntaxError: Unexpected token , 和數(shù)組的解構(gòu)賦值不一樣的地方在于,對(duì)象前面不允許出現(xiàn)空來(lái)占位(因?yàn)閷?duì)象獲取需要通過(guò)具體的屬性名獲取,寫成空的話,瀏覽器不知道怎么識(shí)別)
let {age}={name:'xxx',age:'xxx'};//=>但是我們可以把逗號(hào)去掉,這樣就是只獲取其中一個(gè)
let {name, ...arg}={name: '珠峰培訓(xùn)', age: 9, teacher: '周嘯天'};
console.log(name, arg);//=>'珠峰培訓(xùn)' {age:9...} 支持拓展運(yùn)算符的
//=>把對(duì)象進(jìn)行淺克隆(只把第一級(jí)克隆了)
let obj = {name: 'xxx', age: 10, score: [100, 90, 80]};
let {...arg}=obj;
console.log(arg, obj);
console.log(arg === obj);//=>false
console.log(arg.score === obj.score);//=>true
let {name:A, age:B}={name: '珠峰培訓(xùn)', age: 9};
console.log(A, B);//=>'珠峰培訓(xùn)' 9 在對(duì)象的結(jié)構(gòu)賦值中,我們可以把對(duì)象的屬性名起一個(gè)小名(A和B相當(dāng)于小名或者叫做別名)
let data = [
{
"name": "張三",
"age": 25,
"score": {
"english": [100, 90, 95],
"math": [100, 100, 100],
"chinese": [98, 99, 100]
}
},
{
"name": "李四",
"age": 24,
"score": {
"english": [8, 9, 3],
"math": [149, 150, 148],
"chinese": [138, 140, 145]
}
}
];
let [{score:{english:[A], math:[,B], chinese:[,,C]}}]=data;
console.log(A, B, C);//=>100 100 100
解構(gòu)賦值的作用
快速交換兩個(gè)變量的值
let a = 12;
let b = 13;
[a, b] = [b, a];
console.log(a, b);//=>13 12
接收函數(shù)返回的多個(gè)值
let fn = function () {
let a = 12,
b = 13,
c = 14;
return [a, b, c];
};
let [a,b,c] = fn();
console.log(a, b, c);//=>12 13 14
可以快速接收到傳遞的多個(gè)值(我傳遞的是一個(gè)數(shù)組或者對(duì)象)
let fn = function ([A,B,C,D = 0]) {
console.log(A, B, C, D);//=>12 23 34 0
};
fn([12, 23, 34]);
//console.log(A);//=>Uncaught ReferenceError: A is not defined 函數(shù)中的ABC是私有的變量
//=>快速處理options參數(shù)的初始化操作
let animate = function ({curEle = null, target = {}, duration = 1000, callBack = null}={}) {
console.log(curEle, target, duration, callBack);//=>body {opacity: 1} 1000 null
};
animate({
curEle: document.body,
target: {opacity: 1}
});
在ES6中支持給函數(shù)設(shè)置默認(rèn)值
let fn = function (x) {
console.log(x);//=>undefined
x = x || 0;
console.log(x);//=>0
};
fn();
let fn2 = function (x = 0) {
console.log(x);//=>0
};
fn2();
按照一個(gè)數(shù)據(jù)值得結(jié)構(gòu),快速解析獲取到其中的內(nèi)容
1.真實(shí)項(xiàng)目中一般都是針對(duì)于數(shù)組或者對(duì)象進(jìn)行解構(gòu)賦值。
`數(shù)組解構(gòu)賦值`
let ary=[1,2,3]
let[a,b,c]=ary;//讓等號(hào)左邊出現(xiàn)和右邊相同的數(shù)據(jù)結(jié)構(gòu),左邊可以創(chuàng)建一些變量快速獲取到右側(cè)對(duì)應(yīng)位置的值
let[,,c]=ary;//=>c=3
獲取第一項(xiàng),把剩下的項(xiàng)作為一個(gè)數(shù)組返回
let[a,...b]=ary;//...在此處被稱為剩余運(yùn)算符
console.log(a,b);//=>1,[2,3]
let[a,...b,c]=ary;//會(huì)報(bào)錯(cuò),剩余運(yùn)算符只能處于解構(gòu)中最后的位置
let ary=[12];
let [a,b=0]=ary;//解構(gòu)的時(shí)候可以給變量設(shè)置默認(rèn)值,如果當(dāng)前變量對(duì)應(yīng)結(jié)構(gòu)中的這一項(xiàng)沒(méi)有值,變量用默認(rèn)值。
console.log(a,b);//12,0
如果對(duì)應(yīng)的位置有值,會(huì)把默認(rèn)的值覆蓋。
let a=12,b=13;//讓a,b互換位置
[a,b]=[b,a]
----------
對(duì)象解構(gòu)賦值
let obj={name:"dali",age:25,high:184};
let {name,age}=obj;//左側(cè)變量名和對(duì)象中的屬性名保持一致才可以
let {high}=obj;//=>184;
let {age:ageAA}=obj;
console.log(ageAA);//=>25,給解構(gòu)的屬性名起別名作為我們使用的變量
let {friend=0}=obj;
console.log(friend);//=>0給不存在的屬性設(shè)置默認(rèn)值
let fn=function({}={}){//把傳遞的對(duì)象解構(gòu)了,不傳遞值默認(rèn)賦值為空對(duì)象:現(xiàn)在傳遞對(duì)象或者不傳遞,形參接收到的都是對(duì)象,結(jié)構(gòu)的時(shí)候可以把傳遞進(jìn)來(lái)的對(duì)象中,如果某個(gè)屬性不存在我們賦值默認(rèn)值
};
fn({name:"xxx",age:25})
let value={}
2."..."有三個(gè)含義:
1.剩余運(yùn)算符
2.拓展運(yùn)算符
3.展開運(yùn)算符:把數(shù)組(對(duì)象/類數(shù)組)中的每一項(xiàng)展開,分別傳遞給一個(gè)函數(shù)
let ary=[1,2,3];
let[...arg]=ary;//=>ary.slice(0);
function fn(context,...arg)
//獲取傳遞值中的第一個(gè)和剩下的{console.log(context,arg)//arg是一個(gè)數(shù)組、arguments是類數(shù)組。
}
let={}
fn(obj,1,2,3)
function sun(...arg){//類似于arguments但是arg是數(shù)組,arguments是類數(shù)組
}
let obj={name:"dali",age:25}
let newObj={...obj,sex:0}//{name:"dali",age:25,sex:0}
let ary=[12,23];
let Newary=[...ary,100,...ary];//=>[12,23,100,12,13];
ES6中的箭頭函數(shù)
let fn=(x,y)=>{};
fn(10,20);
let fn=x=>{};只有一個(gè)形參我們可以省略小括號(hào)。
fn(10);
let fn=(x,y)=>x+y;
fn(10,20);//如果函數(shù)體只有一句操作并且是return的我們可以省略大括號(hào)(給形參設(shè)置默認(rèn)值)
let fn=x=>y=>x+y;等同于
var fn=function fn(x){
return function(y){
return x+y;}}
箭頭函數(shù)沒(méi)有arguments,
let fn=(...arg)=>{};//可以使用剩余運(yùn)算符而且arg是一個(gè)數(shù)組
fn(1,2,3,4);
箭頭函數(shù)中沒(méi)有自己的執(zhí)行主體(this),他的this都是繼承上下文中的this.
let obj={
fn:(function(){
return function (){console.log(this);}
})()
}
obj.fn();//this:obj//如何把this指向window,可以用call,也可以在自執(zhí)行那里自定義一個(gè)變量,在return后的輸出這個(gè)變量
用箭頭函數(shù):
let obj ={fn:(function(){return ()=>{console.log(this);}
})()
};
obj.fn();//this:window箭頭函數(shù)執(zhí)行和是否有點(diǎn),點(diǎn)前面是誰(shuí)都沒(méi)有關(guān)系了,因?yàn)樗麤](méi)有自己的執(zhí)行主體,在箭頭函數(shù)中使用到的this都是直接找上下文中的this來(lái)使用
自執(zhí)行函數(shù)用箭頭函數(shù)表示:
(()=>{})();
箭頭函數(shù)的基礎(chǔ)語(yǔ)法
let fn = function (x, y) {
return x + y;
};
console.log(fn(10, 20));//=>30
//=>改寫成箭頭函數(shù)
let fn = (x, y)=> x + y;
let fn = function (n = 0) {
let x = 10,
y = 20;
return x + y + n;
};
//=>改寫成箭頭函數(shù)
let arrowFn = (n = 0)=> {
let x = 10,
y = 20;
return x + y + n;
};
箭頭函數(shù)中不支持arguments
//=>傳統(tǒng)函數(shù)支持ARGUMENTS
// let fn = function () {
// let arg = Array.prototype.slice.call(arguments);
// return eval(arg.join('+'));
// };
let fn = (...arg)=> {
//console.log(arguments);//=>Uncaught ReferenceError: arguments is not defined
//=>不支持ARGUMENTS沒(méi)事,我們使用ES6中的剩余運(yùn)算符...來(lái)獲取傳遞的進(jìn)來(lái)的所有參數(shù)值(優(yōu)勢(shì):使用剩余運(yùn)算符接收到的結(jié)果本身就是一個(gè)數(shù)組,不需要再轉(zhuǎn)換了)
//console.log(arg instanceof Array);//=>true
return eval(arg.join('+'));
};
//=>也可以把FN簡(jiǎn)寫成以下方式
//let fn = (...arg)=> eval(arg.join('+'));
console.log(fn(10, 20, 30, 40));
箭頭函數(shù)中的this問(wèn)題
復(fù)習(xí)普通函數(shù)中this指向的問(wèn)題
let obj = {
name: 'obj',
fn(){
//=>這樣處理和下面SUM的處理是一樣的
console.log(this);
},
sum: function () {
}
};
obj.fn();//=>this:obj 普通函數(shù)執(zhí)行THIS的指向:看函數(shù)執(zhí)行前面是否有點(diǎn),有點(diǎn),點(diǎn)前面是誰(shuí)THIS就是誰(shuí),沒(méi)有點(diǎn)THIS指向WINDOW或者UNDEFINED(嚴(yán)格模式下)
document.body.onclick = obj.fn;//=>this:body
setTimeout(obj.fn, 1000);//=>this:window
obj.fn.call(12);//=>this:12
箭頭函數(shù)中沒(méi)有自己的THIS指向,用到的THIS都是所在宿主環(huán)境(它的上級(jí)作用域)中的THIS
let obj = {
name: 'obj',
fn: ()=> {
console.log(this);
//=>不管我們?cè)趺慈ゲ僮?最后THIS都指向WINDOW:箭頭函數(shù)中沒(méi)有自己的THIS指向,用到的THIS都是所在宿主環(huán)境(它的上級(jí)作用域)中的THIS
}
};
obj.fn();
document.body.onclick = obj.fn;
setTimeout(obj.fn, 1000);
obj.fn.call(12);
以后實(shí)戰(zhàn)項(xiàng)目中,不是要把所有的函數(shù)都改為箭頭函數(shù),根據(jù)自身需要來(lái)修改即可(例如:我們需要讓函數(shù)中的this是宿主環(huán)境中的this,我們才來(lái)使用箭頭函數(shù);或者不涉及this問(wèn)題,我們想讓代碼寫起來(lái)簡(jiǎn)單一些也可以使用箭頭函數(shù);)
let obj = {
name: 'obj',
fn(){
//=>this:obj
// setTimeout(function () {
// //=>this:window
// }, 1000);
// setTimeout(function () {
// //=>this:obj
// }.bind(this), 1000);
// var _this = this;
// setTimeout(function () {
// //=>_this:obj
// }, 1000);
setTimeout(()=> {
//=>this:obj
}, 1000);
}
};
obj.fn();
箭頭函數(shù)的一點(diǎn)擴(kuò)充
宿主環(huán)境:不是執(zhí)行的環(huán)境而是定義的環(huán)境
let fn = ()=> {
console.log(this);
};
let obj = {
name: 'obj',
sum: function () {
//=>this:obj
fn();//=>this:window
//宿主環(huán)境:不是執(zhí)行的環(huán)境而是定義的環(huán)境,F(xiàn)N雖然是在這執(zhí)行的,但是它是在WINDOW下定義的,所以它的宿主環(huán)境還是WINDOW
}
};
obj.sum();
層級(jí)嵌套的箭頭函數(shù)
// let fn = function (i) {
// return function (n) {
// return n + (++i);
// }
// };
let fn = (i)=> (n)=> n + (++i);
ES6中的類和繼承
ES5中創(chuàng)建類和實(shí)例,以及如何禁止用戶把類當(dāng)做普通函數(shù)執(zhí)行:
new.target
function Person(name, age) {
//console.log(new.target);//=>ES6新增加的語(yǔ)法,如果是通過(guò)NEW執(zhí)行的,返回的結(jié)果是當(dāng)前創(chuàng)建的類,如果是當(dāng)做普通函數(shù)執(zhí)行的,返回的是UNDEFINED
if (typeof new.target === 'undefined') {
throw new SyntaxError(`當(dāng)前Person不能作為一個(gè)普通函數(shù)執(zhí)行,請(qǐng)使用new Person來(lái)執(zhí)行~~`);
}
//=>NEW執(zhí)行的時(shí)候,THIS是當(dāng)前類的實(shí)例,THIS.XXX=XXX是給當(dāng)前實(shí)例增加的私有屬性
this.name = name;
this.age = age;
}
//=>原型上存放的是公有的屬性和方法:給創(chuàng)建的實(shí)例使用
Person.prototype = {
constructor: Person,
say: function () {
console.log(`my name is ${this.name},i am ${this.age} years old~`);
}
};
//=>把PERSON當(dāng)做一個(gè)普通的對(duì)象,給對(duì)象設(shè)置的私有屬性
Person.study = function () {
console.log(`good good study,day day up~`);
};
var p1 = new Person('王雪超', '80');
Person('王雪超', '80');
ES6中創(chuàng)建類
//console.log(Person);//=>Uncaught ReferenceError: Person is not defined 不存在變量提升
class Person {
constructor(name = '珠峰培訓(xùn)', age = 9) {
//=>給實(shí)例設(shè)置的私有屬性
this.name = name;
this.age = age;
}
//=>直接在大括號(hào)中編寫的方法都設(shè)置在類的原型上:ES6默認(rèn)把CONSTRUCTOR的問(wèn)題解決了,此時(shí)原型上的CONSTRUCTOR指向的就是PERSON
say() {
console.log(`my name is ${this.name},i am ${this.age} years old~`);
}
//=>把PERSON當(dāng)做普通對(duì)象設(shè)置屬性和方法,只需要在設(shè)置的方法前面加STATIC即可
static study() {
console.log(`good good study,day day up~`);
}
}
let p1 = new Person('王雪超');
//Person();//=>Uncaught TypeError: Class constructor Person cannot be invoked without 'new' =>ES6中使用CLASS創(chuàng)建的類,天生自帶NEW.TARGET的驗(yàn)證,不允許把創(chuàng)建的類當(dāng)做普通函數(shù)執(zhí)行
ES6中的繼承
class Person {
constructor(...ARG) {
let [x = 0,y = 0]=ARG;
this.x = x;
this.y = y;
}
sum() {
return this.x + this.y;
}
}
class Child extends Person {
//=>創(chuàng)建CHILD類,并且讓CHILD類繼承了PERSON類:
//1、把PERSON中的私有屬性繼承過(guò)來(lái)設(shè)置給了子類實(shí)例的私有屬性
//2、讓子類實(shí)例的原型鏈上能夠找到PERSON父類的原型(這樣子類的實(shí)例就可以調(diào)用父類原型上的方法了)
//-------------
//=>我們可以不寫CONSTRUCTOR,瀏覽器默認(rèn)會(huì)創(chuàng)建它,而且默認(rèn)就把父類私有的屬性繼承過(guò)來(lái)了(而且把傳給子類的參數(shù)值也傳遞給父類了)
// constructor(...arg) {
// //=>ARG:傳遞給子類的參數(shù)(數(shù)組) [剩余運(yùn)算符]
// super(...arg);//=>[展開運(yùn)算符] 把ARG中每一項(xiàng)值展開,分別傳遞給父類方法 SUPPER(10,20,30)
// }
//------------
//=>很多時(shí)候我們不僅要繼承父類私有的,還需要給子類增加一些而外私有的,此時(shí)就必須寫CONSTRUCTOR,但是一定要在CONSTRUCTOR中的第一行寫上SUPPER,否則會(huì)報(bào)錯(cuò)
// constructor(...arg) {
// super(...arg);
//
// let [,,z]=arg;
// this.z = z;
// }
constructor(x, y, z) {
super();//<=>Person.prototype.constructor.call(this)
this.z = z;
}
fn() {
}
}
let c = new Child(10, 20, 30);
ES6:let創(chuàng)建的變量不存在變量提升
在es6中基于let或者const等方式創(chuàng)建變量或者函數(shù)
- 不存在變量提升機(jī)制,
- 切斷了全局變量和window屬性的映射機(jī)制。
- 在相同作用域中,基于let不能聲明相同名字的變量( 不管用什么方式在當(dāng)前作用域下聲明了變量再次使用let創(chuàng)建都會(huì)報(bào)錯(cuò))
- 雖然沒(méi)有變量提升機(jī)制,但是在當(dāng)前作用于代碼自上而下執(zhí)行之前,瀏覽器會(huì)做一個(gè)重復(fù)性檢測(cè)(語(yǔ)法檢測(cè)):自上而下查找當(dāng)前作用域下所有變量,一旦發(fā)現(xiàn)有重復(fù)的直接拋出異常代碼也不會(huì)在執(zhí)行了(雖然沒(méi)有把變量提前聲明定義但是瀏覽器記住了當(dāng)前作用域有哪些變量)
- 基于let和基于var創(chuàng)建變量,在私有變量和作用域鏈機(jī)制上是一樣的
- 現(xiàn)有項(xiàng)目當(dāng)中是es5和es6混合開發(fā)模式,如果當(dāng)前變量是基于let創(chuàng)建的,就按照es6的語(yǔ)法機(jī)制渲染的,否則按照es5的老語(yǔ)法機(jī)制渲染
基于let創(chuàng)建的變量存儲(chǔ)塊級(jí)作用域(類似于函數(shù)執(zhí)行形成的私有作用域=》一般來(lái)說(shuō)基本上所有的{}都會(huì)形成塊級(jí)作用域。
包括:let,循環(huán)體,判斷體,{}。
console.log(a);//報(bào)錯(cuò)語(yǔ)法錯(cuò)誤:a is not undefined
let a=12;
console.log(window.a);//undefined
let a = 10,
b = 20;
let fn = function () {
console.log(a, b);//a會(huì)報(bào)語(yǔ)法錯(cuò)誤,not defined.沒(méi)有變量提升
let a = b = 30;//let a=20,b=20
console.log(a, b);
};
console.log(a, b);
var a=12;
if(true){
consloe.log(a);//會(huì)報(bào)錯(cuò)a is not defined
let a=13;//基于let創(chuàng)建變量會(huì)把大部分{}當(dāng)做一個(gè)私有的塊級(jí)作用域(類似函數(shù)的私有作用域),在這里也是重新檢測(cè)語(yǔ)法規(guī)范,看一下是否是基于新語(yǔ)法創(chuàng)建的變量,如果是按照新語(yǔ)法規(guī)范來(lái)解析
}
console.log(typeof a);//""undefined",在原有瀏覽器渲染機(jī)制下,基于typeof 等邏輯運(yùn)算符檢測(cè)一個(gè)未被聲明的變量,不會(huì)報(bào)錯(cuò)返回undefined
console.log(typeof a);//報(bào)錯(cuò),a is not undefined
let a=13;//如果當(dāng)前變量是基于es6語(yǔ)法處理,在沒(méi)有聲明這個(gè)變量的時(shí)候使用typeof檢測(cè)會(huì)直接報(bào)錯(cuò)不會(huì)是undefined,解決了原有的js死區(qū)。
全局變量和私有變量
var a=12,b=13,c=14;
function fn(a){//a是形參,所以a是私有變量
console.log(a,b,c);//12,undefined,14
var b=c=a=20;//var b=20;c=20;a=20
console.log(a,b,c);}//20,20,20
fn(a);
conslole.log(a,b,c);//12,13,20
var ary=[12,23];
function fn(ary){
console.log(ary);//[12,23]
ary[0]=100;
ary=[100];
ary[0]=0;
console.log(ary);//[0]
}
fn(ary);
console.log(ary);//[100,23]
在私有變量當(dāng)中,只有當(dāng)前以下兩種是私有變量“
1.帶var的
2.形參也是私有變量
- 剩下的都不是私有變量,需要基于作用域鏈向上查找
查找上級(jí)作用域
- 當(dāng)前函數(shù)執(zhí)行形成一個(gè)私有作用域a,a的上級(jí)作用域是誰(shuí),和他在哪執(zhí)行沒(méi)有關(guān)系,和他在哪創(chuàng)建定義的有關(guān)系,在哪創(chuàng)建的他的上級(jí)作用域就是誰(shuí)
var a=12;
function fn(){
//arguments:實(shí)參集合;
//arguments.callee:函數(shù)本身fn;
//arguments.callee.caller:當(dāng)前函數(shù)在哪執(zhí)行的,caller就是誰(shuí)(記錄的是他執(zhí)行的宿主環(huán)境),在全局下執(zhí)行caller的結(jié)果是null
}