let独郎、const和塊級作用域
let
- let沒有變量提升走贪、可以重復(fù)定義檢查大渤,同一個作用域中重復(fù)定義就會報錯、可以被用于塊級作用域
- 使用let形成的塊級作用域可以在大部分具有{...}的語句中使用祠锣,例如for(){} , do{}while() , while{} 和switch(){}等酷窥。
if(true){ //塊級作用域
let a=10;
}
//console.log(a); //報錯 a is not defined
---------------------------------------------
let b=10;
b=20;
console.log(b) //20 可以被重新賦值
---------------------------------------------
let x1=1;
{
console.log(x1); //1
}
//以上證明 作用域鏈?zhǔn)谴嬖诘?
const
- 定義常量,對常量賦值會報錯
- const定義對象不能完全鎖死對象
- 棧內(nèi)存中存放的只是該對象的訪問地址伴网,在堆內(nèi)存中為這個值分配空間
- const的原理便是在變量名與內(nèi)存地址之間建立不可變的綁定,當(dāng)后面的程序嘗試申請新的內(nèi)存空間時,引擎便會拋出錯誤蓬推。
const person={'name':'aaa'};
person.name='bbb'
console.log(person.name);//不是完全鎖死
const a=10; //const必須給一個初值 否則會報錯 并且不能重復(fù)聲明
a=20; //報錯 不可修改
console.log(a)
- 完全鎖死對象要用Object.freeze();多層屬性對象可以利用遞歸鎖死。
const obj1=Object.freeze({
a:1,
b:2
})
內(nèi)存與變量之間的關(guān)系
- Ecmascript在對變量的引用進(jìn)行讀取時,會從該變量當(dāng)前所對應(yīng)的內(nèi)存地址所指向的內(nèi)存空間中讀取內(nèi)容澡腾。而當(dāng)用戶改變變量的值時,引擎會重新從內(nèi)存中分配一個新的內(nèi)存空間以存儲新的值,并將新的內(nèi)存地址與變量進(jìn)行綁定拳氢。
- 關(guān)于堆內(nèi)存和棧內(nèi)存
棧內(nèi)存存放的是該對象的訪問地址,在堆內(nèi)存中為這個值分配空間
let const的使用原則
- 1.一般情況下蛋铆,使用const來定義值的存儲容器(常量)
- 2.只有在值容器明確被確定將會被改變時才使用let來定義(變量)
- 3.不再使用var
let const與循環(huán)語句for...of...
for...of..循環(huán)不能直接遍歷對象(但是支持return)
const zootop=[
{ name: 'nick', gender: 1,species:'fox'},
{name: 'judy', gender: 0,species: 'Bunny'}
];
for(const {name, species} of Zootopia) {
console. log(`Hi, I am (name), and I am a (species)`)
}
//Hi, I am nick, and I am a fox
//Hi, I am judy, and I am a Bunny
- 數(shù)組類型再次被賦予了一個名為entries的方法,它可以返回對應(yīng)數(shù)組中每一個元素與其下標(biāo)配對的一個新數(shù)組
const arr=['a','b','c'];
console.log(arr.entries())//[[0,'a'],[1,'b'],['2','c']];
模板字符串
把變量放在${ }中放接,字符串放在反引號中刺啦,即可渲染,代替字符串拼接
const zootop=[
{ name: 'nick', gender: 1,species:'fox'},
{name: 'judy', gender: 0,species:'Bunny'}
];
for(const [index,{name,species}] of zootop.entries()){
console.log(`${index} i am ${name} `)
}
簡單實(shí)現(xiàn):
function templatea(str,arr){
var reg=/\$\{([^\{\}]+)\}/g;//匹配一個${...};
var i=0;
var str=str.replace(reg,function(){
return arr[i++];
})
return str;
}
console.log(templatea('ajlj${aaa}sdkl${bbb}fjs${ccc}d',[111,222,333]))
//ajlj111sdkl222fjs333d
原理:
let aaa=111,bbb=222,ccc=333;
desc`ajlj${aaa}sdkl${bbb}fjs${ccc}d`;
function desc(raw,...Ary){
let res=''
for(let i=0;i<raw.length-1;i++){
res+=raw[i]+Ary[i]
}
res+=raw[raw.length-1];
return res;
}
默認(rèn)參數(shù) 和 拓展運(yùn)算符
默認(rèn)參數(shù)
function fn(arg='foo'){
console.log(arg);
}
fn();//foo
fn('bar')//bar
類數(shù)組轉(zhuǎn)數(shù)組
//類數(shù)組轉(zhuǎn)數(shù)組的一般方式
args=[].slice.call(arguments);
//Array的新方法
args=Array.from(arguments)
//剩余參數(shù)
function fn(foo,...rest){
//rest是arguments除去foo的剩余數(shù)組[2,3,4,4]
}
fn(1,2,3,4,4)
--------------------
arr1=[...arr2];//數(shù)組拷貝
剩余參數(shù)注意事項(xiàng)
- 使用剩余參數(shù)后纠脾,后面不可以再添加任何參數(shù)玛瘸,否則會報錯
- 推薦使用 ...arg 代替 arguments
解構(gòu)賦值
- 只要等號兩邊的模式相同,左邊的變量就會被賦予對應(yīng)的值 這種寫法叫做‘模式匹配’
var [a,b,c]=[12,5,'aaa']
console.log(a,b,c)//12,5,'aaa'
- 跟順序無關(guān) 鍵值對對應(yīng)的名字 a,b,c 必須一致 否則解析不出來
var {a,b,c}={b:5,a:10,c:'aa'}
console.log(a,b,c)//10,5,'aa'
- 模式匹配 左右兩邊的格式一樣
var [a,[b,c],d]=[10,[1,2],100]
console.log(a,b,c,d)//10,1,2,100
- 給默認(rèn)值 可用于函數(shù)參數(shù)的默認(rèn)值
var {time=12,a}={time,a:'111'}
console.log(time,a)//12,'111'
- 對象的解構(gòu)賦值
let {foo,bar}={foo:'aaa',bar:'bbb'};
console.log(foo) //aaa
//對象的解構(gòu)于數(shù)組有一個重要的不同苟蹈。數(shù)組的元素是按次序排列的糊渊,變量的取值由他的位置決定;而對象的屬性沒有次序慧脱,變量必須與屬性同名
- 匹配的模式與真正的變量
let { foo3: foo2, bar: bar2 } = { foo3: "aaa", bar: "bbb" };
let { foo2: baz2 } = { foo2: "aaa", bar: "bbb" };
console.log(baz2 +'---------baz2') // "aaa"
console.log(foo2 +'---------foo2')//'aaa'
console.log(foo3) // error: foo3 is not defined
//上面代碼中渺绒,foo3是匹配的模式,baz才是變量菱鸥。真正被賦值的是變量baz宗兼,而不是模式foo3。
- 屬性的簡潔表示法,屬性名和屬性值一樣的情況下可以簡寫
var foo='bar';
var baz={foo};
baz //{foo:'bar'}
//等同于
var baz={boo:foo}
//上面代碼表明氮采,ES6允許在對象之中殷绍,直接寫變量。這時鹊漠,屬性名為變量名主到,屬性值為變量值
function f(x,y){
return {x,y}
}
//等同于
function f(x,y){
return {x:x,y:y}
}
- 方法簡寫
var o={
method(){
return 'Hello';
}
}
//等同于
var o={
method:function(){
return 'Hello'
}
}
9. 變量值轉(zhuǎn)換(三個杯子調(diào)換兩杯水的問題)
傳統(tǒng)方法
function swap(a,b){
var temp=a;
a=b;
b=temp;
}
現(xiàn)在的方法(利用解構(gòu)賦值)
[a,b]=[b,a]
注:
- 如果解構(gòu)不成功,變量的值就等于undefined
- 如果等號右邊不是數(shù)組(或者嚴(yán)格的說躯概,不是可遍歷對象)登钥,那么將會報錯
箭頭函數(shù)
- 單行
- 單行可以省略 return,
- 只能包含一條語句
- 返回對象時要用()包裹返回的對象
const fn=foo=>`${foo} world`//意思是return `${foo} world`
相當(dāng)于
var fn=function (foo){
return foo+' world';
}
- 多行(需要寫return,需要 { } 包裹函數(shù)體)
foo=>{
return `${foo} world`;
}
- 有無參數(shù)
1.如果一個箭頭函數(shù)無參數(shù)傳入楞陷,則需要一個空括號占位
const fn=()=>{
........
}
2. 如果有多個參數(shù)(需要括號)
const fn=(arg1,arg2,...args)=>{
........
}
3. 如果有一個參數(shù)(不需要括號)
const fn=foo=>{
return `${foo} world`
}
- this指針
- 箭頭函數(shù)中this會延伸至上一層作用域中怔鳖,即上一層的上下文會穿透到內(nèi)層的箭頭函數(shù)中
const obj={
a:111,
foo(){
const bar = ()=>this.a;
return bar;
}
}
window.a=222
window.bar=obj.foo();
window.bar();//111 不關(guān)心函數(shù)執(zhí)行時前邊是誰,this最開始指定誰,是不會再改變的
--------------------------
以上foo函數(shù)代碼類似于
foo(){
var that=this;
var bar=function(){
return that.a
}
}
//可以說:在當(dāng)前作用域的上層作用域定義that=this结执,函數(shù)內(nèi)使用that
- apply和call無法改變this的指向度陆,bind是可以改變this的
- 不要隨意在頂層作用域使用this,防止指向window 報錯
- 箭頭函數(shù)中沒有arguments献幔,callee和caller對象懂傀,想要使用arguments的話,用 ...args代替
const fn=(...args){
console.log(args[0])
}
fn(1,2,3)//1
對象蜡感、數(shù)組新特性
- 對象支持proto注入:相當(dāng)于開放原型鏈蹬蚁,可以改變所屬類的原型
繼承的又一種方式
obj.__proto__=new Array();//對象obj繼承數(shù)組的屬性和方法;
- 可動態(tài)計(jì)算的屬性名:可以使用一個表達(dá)式作為屬性名
const obj={};
const key='foo';
obj[key+'abc']='aaa';
- 變量名和屬性名相同的可以省略
const userInfor='aaa';
const obj={
userInfor
}
相當(dāng)于
const obj={
'userInfor':userInfor
}
- Array.from(arg)//類數(shù)組轉(zhuǎn)數(shù)組
- 對象拷貝
let obj={a:{c:{d:1}}}
let newObj=Object.assign({},obj)
newObj.a==obj.a//true
Object.assign實(shí)現(xiàn)了對象的淺拷貝
let newObj2=JSON.parse(JSON.stringify(obj))
newObj2.a==obj.a//false JSON.parse(JSON.stringify(obj))實(shí)現(xiàn)了對象的深拷貝
class類
基本定義
- 函數(shù)原型實(shí)現(xiàn)
function Person(name,age){
this.name=name;//私有屬性
this.age=age
}
Person.prototype.sayName=function(){//公有屬性
console.log(this.name);
}
var person1=new Person('jack',19);
person1.sayName();//jack
- class實(shí)現(xiàn)
class Person{
constructor(name,age){//私有屬性
this.name=name;
this.age=age
}
sayName(){//公有屬性
console.log(this.name)
}
}
//原本的構(gòu)造函數(shù)被類的constructor方法代替郑兴,其余原本需要在constructor中的方法則可以直接定義在class內(nèi)
- 注意:在類中的方法犀斋,都是帶有作用域的普通函數(shù),而不是箭頭函數(shù)情连,方法第一層所引用的this都是指向當(dāng)前實(shí)例
繼承語法extends 和 super(原理是this繼承:把父類的私有屬性和方法叽粹,克隆一份一模一樣的作為子類的私有屬性)
class Woker extends Person{
constructor(name,age,job){
super(name);//調(diào)用父級的構(gòu)造的屬性 , 此處沒有繼承父類的age屬性
this.job=job;//添加自己的屬性
}
sayWork(){//添加自己的方法
console.log(this.job+' '+this.name+' '+this.age)
}
}
var p1=new Person('');
var w1=new Worker('mmm',59,"worker");
w1.sayWork()//worker mmm undefined 所以此處沒有age屬性,即使傳值也無效
w1.sayName()//繼承了父類的公有屬性
- 如果一個子類繼承了父類却舀,那么在子類的constructor構(gòu)造函數(shù)中必須使用super函數(shù)調(diào)用父類的構(gòu)造函數(shù)后才能在子類的constructor構(gòu)造函數(shù)中使用this虫几,否則會報錯
Getter、Setter在類中的使用
//還是這個例子
class Person{
constructor(name,age,banzhaun){//私有屬性
this.name=name;
this.age=age;
this.banzhaun=banzhaun
}
sayName(){//公有屬性
console.log(this.name)
}
get gongqian(){
console.log(10*this.banzhaun+'塊錢')
}
}
var person1=new Person('jack',19,100);
person1.gongqian//1000塊錢
靜態(tài)方法
- 如果在一個方法前加上static關(guān)鍵字挽拔,就表示該方法不會被實(shí)例繼承辆脸,而是通過類來調(diào)用,實(shí)例不能調(diào)用螃诅,屬于靜態(tài)方法
class Person{
constructor(name,age,banzhaun){//私有屬性
this.name=name;
this.age=age;
this.banzhaun=banzhaun
}
sayName(){//公有屬性
console.log(this.name)
}
get gongqian(){
console.log(10*this.banzhaun+'塊錢')
}
static own(){
console.log(this+'這是一個實(shí)例不能調(diào)用的方法啡氢,只有類能調(diào)用')
}
}
Person.own()//函數(shù)中zhis指向的是類不是實(shí)例,沒有實(shí)例的屬性
var person1=new Person('jack',19,100);
person1.gongqian//1000塊錢
ES6 module
導(dǎo)出模塊和引入模塊需要相對應(yīng) 否則有時會報錯
導(dǎo)出頁面 多個export 相當(dāng)于導(dǎo)出一個對象 引入是也需要一個對象解構(gòu)
導(dǎo)出
export let a=1;
export let b=2;
導(dǎo)入
import {a,b} from 'modulePath'
或者
import * as c from 'modulePath'//*代表所有的州刽,取時候可以用c.a空执,c.b
導(dǎo)出的是一個對象的情況下
導(dǎo)出
export default {xxx:xxx,xxx:xxx}
導(dǎo)入
import xxx from 'modulePath'
引入模塊(import有預(yù)解釋的功能 )
1. import name from 'module-name'//從module-name引入所有接口對象
2. import * as name from 'module-name'//給引入的所有接口對象起別名
3. import {member1,member2} from 'module-name'//引入部分接口
4. import {member as alias} from 'module-name'//給引入的部分接口起別名
5. import from 'module-name'//不引入接口,只執(zhí)行內(nèi)部代碼
導(dǎo)出模塊
1. export 除了輸出變量穗椅,還可以輸出函數(shù)和類
export var sex="boy";
export var echo=function(value){
console.log(value)
}
export class Foo{
...
}
2. 導(dǎo)出的語句必須要有聲明和賦值兩部分
const foo ='bar';
export foo//報錯
3.加 { } 才代表是接口不是值
var sex="boy";
var echo=function(value){
console.log(value)
}
export {sex,echo}
4.讓一個值直接成為模塊的內(nèi)容辨绊,而無需聲明
var sex="boy";
export default sex(sex不能加大括號)
模塊的示例
- 1.導(dǎo)出一個對象
// math.js
export default math = {
PI: 3.14,
foo: function(){}
}
// app.js
import math from "./math";
math.PI
- 導(dǎo)出函數(shù)、變量匹表、對象
// export Declaration
export function foo(){
console.log('I am not bar.');
}
// export VariableStatement;
export var PI = 3.14;
export var bar = foo; // function expression
// export { ExportsList }
var PI = 3.14;
var foo = function(){};
export { PI, foo };//對象簡寫(導(dǎo)出對象)
- 引入
// import { ImportsList } from "module-name"
import { PI } from "./math";//引入單個接口
import { PI, foo } from "module-name";//引入兩個接口
// import IdentifierName as ImportedBinding
import { foo as bar } from "./math";//引入foo函數(shù)并起別名叫bar
bar(); // use alias bar
// import NameSpaceImport
import * as math from "./math";//引入math文件中所有接口
math.PI
math.foo()
- 把引入的接口再導(dǎo)出
// components.js
import Button from './Button';
import Header from './Header';
// export { ExportsList }
// Not ES6 Destructing. Not object property shorthand
export {
Button,
Header
}
// app.js
// import { ImportList }
import { Button, Header } from "./components";
Set和Map
set無序集合(無序门坷,元素不可重復(fù)的集合對象)
//Set本身是一個構(gòu)造函數(shù),用來生成Set數(shù)據(jù)結(jié)構(gòu)
const s1 = new Set();
- api:
- set.add(value):添加元素到集合內(nèi)
- set.delete(value):刪除集合內(nèi)指定元素
- set.clear(value):清空集合內(nèi)元素
- set.forEach(callback):遍歷集合內(nèi)所有元素袍镀,并調(diào)用callback
- set.has(value):檢查集合內(nèi)是否含有某元素
[2, 3, 5, 4, 5, 2, 2].forEach(x => s1.add(x));
for (let i of s1) {
console.log(i);
}
//上面代碼通過add方法向set結(jié)構(gòu)加入成員默蚌,結(jié)果表明Set結(jié)構(gòu)不會重復(fù)添加相同的值。
- 實(shí)例
// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 例三
function divs () {
//return [...document.querySelectorAll('div')];
}
//const set = new Set(divs());
set.size // 56
// 類似于
//divs().forEach(div => set.add(div));
set.size // 56
數(shù)組去重
2 種去除數(shù)組重復(fù)成員的方法苇羡。
[...new Set(array)];
function dedupe(array) {
return Array.from(new Set(array));
}
- 向Set加入值的時候绸吸,不會發(fā)生類型轉(zhuǎn)換,所以5和"5"是兩個不同的值。Set內(nèi)部判斷兩個值是否不同锦茁,使用的算法叫做“Same-value equality”攘轩,它類似于精確相等運(yùn)算符(===),主要的區(qū)別是NaN等于自身码俩,而精確相等運(yùn)算符認(rèn)為NaN不等于自身度帮。
只能添加一個NaN
let set1 = new Set();
let a = NaN;
let b = NaN;
set1.add(a);
set1.add(b);
set1 // Set {NaN}
- 遍歷操作
- keys():返回鍵名的遍歷器
- values():返回鍵值的遍歷器
- entries():返回鍵值對的遍歷器
- forEach():使用回調(diào)函數(shù)遍歷每個成員
//由于 Set 結(jié)構(gòu)沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值)稿存,所以keys方法和values方法的行為完全一致笨篷。
let set2 = new Set(['red', 'green', 'blue']);
for (let item of set2.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Map( 值--對--值 的映射關(guān)系 )
- 含義和基本用法
- js的對象,本質(zhì)上是鍵值對的集合瓣履,但是傳統(tǒng)上只能用字符串當(dāng)做鍵 這給他的使用的帶來很大的限制
- 它類似于對象率翅,也是鍵值對的集合,但是 鍵 的范圍不限于字符串 各種類型的值(包括對象)都可以當(dāng)做鍵袖迎。
- 也就是說安聘,Object結(jié)構(gòu)提供了‘字符串---值’的對應(yīng),Map提供了‘值---值’的對應(yīng)瓢棒,是一種更完善的hash結(jié)構(gòu)形式。如果你需要“鍵值對”的形式map比Object更合適
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
- 遍歷
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
console.log([...map.keys()])
// [1, 2, 3]
console.log([...map.values()])
// ['one', 'two', 'three']
console.log([...map.entries()])
// [[1,'one'], [2, 'two'], [3, 'three']]
console.log([...map])
// [[1,'one'], [2, 'two'], [3, 'three']]