重學ES6基礎語法(一)

本列博客爲ES6基礎語法的使用及總結,若有錯誤,歡迎指正。 重學ES6基礎語法(一)主要包括 var、let、const變量聲明箭頭函數 相關。面試

var/let/const 變量聲明

var的使用

1.基本用法數組

  • 1.1 定義變量格式:var 變量名稱 = 變量值,若是沒有給變量賦值,默認爲undefined
  • 1.2 用 var 聲明的變量的做用域是它當前的執行上下文,它能夠是嵌套的函數,也能夠是聲明在任何函數外的變量。
  • 1.3 經過var定義變量,能夠重複定義同名變量,不會報錯;後定義的會覆蓋先定義的。
  • 1.4 經過var定義在{}(塊級做用域)的變量,不區分全局變量和局部變量,後續仍可使用。
  • 1.5 經過var定義變量,能夠先使用後定義(預解析)
//定義一個變量
var a;

//重複定義同名變量
var a = 18;
var a = 16;
console.log(a); //輸出16
複製代碼

2.變量提高(hoisting)瀏覽器

  • 2.1 經過var聲明的變量能夠在聲明以前使用,輸出undefined(也就是咱們常說的能夠先使用後定義)
  • 2.2 這個行爲叫作「hoisting」,也就是咱們說的預解析。把全部的變量聲明移動到函數或者全局代碼的開頭位置。
console.log(a); //輸出undefined
var a = 18

//預解析以後
var a;
console.log(a); //輸出undefined
a = 18
複製代碼

3.注意點bash

3.1 經過var聲明的變量屬於函數做用域,若是當前沒有函數,則屬於全局做用域。在外界是能夠訪問到的。app

var price = 10;
 var count = 10;
 if(count > 5){
     var discount = price * 0.8; //discount是一個全局變量,在控制檯能夠訪問到
     console.log(`the discount is ${discount}`);
 }
複製代碼

若是換成let定義:經過let定義的discount是一個局部變量,在控制檯不能夠訪問函數

var price = 10;
var count = 10;
if(count > 5){
    let discount = price * 0.8; 
    console.log(`the discount is ${discount}`);
}
複製代碼

3.2 window對象上有一個叫作name的屬性,若是使用var定義一個叫作name的屬性,那麼就會覆蓋掉window的name屬性。ui

var name = "mss";
複製代碼

解決:能夠經過當即執行函數(IIFE (Immediately Invoked Function Expression) ),使變量私有化,避免屬性重寫的問題。此時輸出的name就是window上面的name屬性。this

可是,利用當即執行函數僅僅爲了聲明一個變量,避免變量重寫有點大材小用了...請避免這樣的用法spa

(function () {
   var name = 'mss';
   console.log(name);
})()
複製代碼

let的使用

1.基本用法prototype

  • 1.1 經過let不能夠重複定義同名變量。
  • 1.2 經過let定義變量,不能夠先使用再定義,瀏覽器不會對let定義的變量進行預解析。會輸出ReferenceError
  • 1.3 經過let定義在{}的變量區分全局變量和局部變量
  • 1.4 經過let定義在{}的局部變量,只能在{}中使用,後續不可使用,只在塊級做用域內有效。
//不能夠先使用再定義
console.log(a); //ReferenceError
let a = 18; 

//不可重複定義
let a = 18;
let a = 88;
console.log(a); //Identifier 'a' has already been declared

//塊級做用域內區分全局變量和局部變量
{
   let a = 18; //局部變量
   var person = 'jelly'; //全局變量
   console.log(a); //18
}
console.log(a); //ReferenceError
console.log(person); //jelly
複製代碼

2.注意點

2.1 let不會在全局聲明時(在最頂部的範圍)建立window 對象的屬性。 在程序和方法的最頂端,let不像 var 同樣,let不會在全局對象裏新建一個屬性。 位於函數或代碼頂部的var聲明會給全局對象新增屬性, 而let不會。例如:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(window.x); // "global"
console.log(this.y); // undefined
console.log(window.y); // undefined
複製代碼

2.2 在同一做用域內經過let定義變量,毫不容許同名變量出現;包括用var聲明的變量。

{
  let name = "jelly";
  var name = "vicky"; //Identifier 'name' has already been declared
}


let name = "jelly";
{
  var name = "vicky"; //Identifier 'name' has already been declared
}

{
  let name = "jelly";
  {
    let name = "vicky"; //不報錯
  }
}
複製代碼

3.暫存死區(TDZ(Temporal dead zone))

與經過 var 聲明的有初始化值 undefined 的變量不一樣,經過 let 聲明的變量直到它們的定義被執行時才初始化。在變量初始化前訪問該變量會致使 ReferenceError。該變量處在一個自塊頂部到初始化處理的「暫存死區」中。

4.一道很切題的題

var a = 5; //var定義的a綁定到了window對象上面
let obj = {
    a: 10,
    foo: function () {
        console.log(this.a); 
    }
};
let bar = obj.foo;
obj.foo(); //10 obj調用了foo函數,this就是obj對象,輸出10
bar(); //5 bar函數是輸出當前對象上面的a,爲5

let a = 5; //let定義的a不會綁定到window對象上面
let obj = {
    a: 10,
    foo: function () {
        console.log(this.a);
    }
};
let bar = obj.foo;
obj.foo(); //10 obj調用了foo,this就是obj對象,輸出10
bar(); //undefined 當前的this是window對象,可是window上面沒有a屬性,輸出undefined 
複製代碼

一道奇怪寫法的筆試題

var a = 5;
function test() {
    a = 0;
    console.log(a); //0
    console.log(this.a); //undefined
    var a;
    console.log(a); //0
}
new test();
複製代碼

上例中,在函數內聲明變量a=0,在後面又使用var a,會進行變量提高,因此在函數內部就存在一個局部變量a了,因此兩次console.log(a)都輸出0;console.log(this.a)輸出undefined能夠理解爲沒有東西調用test函數,致使this的指向不明確,也就找不到this.a屬性。

const的使用

1.用法

  • 1.1 const 用於聲明一個或多個常量,常量的值不可改變。例如亙古不變的PI,能夠用const定義。
  • 1.2 const定義常量與使用let 定義的變量有着驚人的類似之處:
    • 兩者都是塊級做用域
    • 都不能和它所在做用域內的其餘變量或函數擁有相同的名稱
var x = 10;
{
    const x = 2;
    console.log(x); //2
}
console.log(x); //10
複製代碼
  • 1.3 二者還有如下兩點區別:
    • const聲明的常量必須初始化,而let聲明的變量能夠不用初始化
    • const 定義常量的值不能經過再賦值修改,也不能再次聲明。而 let 定義的變量值能夠修改。
// 錯誤寫法
const PI;
PI = 3.14159265359;

// 正確寫法
const PI = 3.14159265359;
複製代碼

2.const注意點之面試現場還原

使用 const 定義的對象或者數組,實際上是可變的。對象的引用(地址)不可更改,可是對象的屬性能夠改變;只是咱們不能對常量對象從新賦值

// 建立常量對象
const car = {type:"Fiat", model:"500", color:"white"};
 
// 修改屬性:
car.color = "red"; // 合法
 
// 添加屬性
car.owner = "Johnson"; // 合法

const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"};    // 錯誤
複製代碼

varletconst的使用場景

  • 默認變量儘可能使用const來定義(use const by default)
  • 須要從新綁定時使用let定義(only use let if rebinding is needed)
  • 在ES6中儘可能避免使用var(var shouldn't be used in ES6)

箭頭函數(Arrow Function)

1.箭頭函數的寫法

//沒有參數
() => {函數聲明}

//一個參數
單一參數 => {函數聲明}

//多個參數
(參數1, 參數2, …, 參數N) => { 函數聲明 }
複製代碼

2.箭頭函數特色

2.1 隱式返回

什麼是顯示返回?
顯示返回格式:return 「返回的內容」

2.1.1 箭頭函數的隱式返回:

const arr = [2,4,6,8,10];
const double = arr.map(function (number) {
    return number *2;
});
console.log(double);
//等價於
const double2 = arr.map((number) => number*2);
console.log(double2);
複製代碼

2.1.2 注意:箭頭函數都是匿名函數,若是想要經過命名函數的方式使用箭頭函數,通常把箭頭函數賦值給一個變量便可

const greet = name => console.log(`hello ${name}`);
greet("mss");
複製代碼

2.2 不綁定this

const person = {
    name: 'mss',
    hobbies: ['money','sleeping','eating'],
    output: function () {
        //console.log(this); //this是person這個對象
        this.hobbies.map(function (hobby) {
            //console.log(this);//this是window
            console.log(`${this.name} loves ${hobby}`);
        })
    }
};
person.output();
/*   輸出結果:
     loves money
     loves sleeping
     loves eating
*/
複製代碼

上述例子中能夠看出,並無輸出name屬性。在output的函數中,this指向person這個對象,而map方法的回調函數是一個獨立的函數,一個獨立函數運行的時候,沒有做爲對象的方法調用或者沒有使用call等修改this的值時,則此時this指向window; 過去的作法是:在外面保存一下this

const person = {
    name: 'mss',
    hobbies: ['money','sleeping','eating'],
    output: function () {
        let that = this;
        this.hobbies.map(function (hobby) {
            console.log(`${that.name} loves ${hobby}`);
        })
    }
};
person.output();
/*  輸出結果
    mss loves money
    mss loves sleeping
    mss loves eating
*/
複製代碼

2.2.1 解決:在ES6中能夠用箭頭函數來解決這種問題

2.2.2 緣由:

  • 箭頭函數不會建立本身的this,它只會從本身的做用域鏈的上一層繼承this。也就是他的父做用域的this
  • 與過去的this值是在運行時動態指定的不一樣,箭頭函數的this是詞法做用域,在定義時就被指定了,在後續也不會隨着調用方法的改變進行改變。
const person = {
    name: 'mss',
    hobbies: ['money','sleeping','eating'],
    output: function () {
        this.hobbies.map(hobby => {
            console.log(`${this.name} loves ${hobby}`);
        })
    }
};
person.output();
/*  輸出結果
    mss loves money
    mss loves sleeping
    mss loves eating
*/
複製代碼

2.2.3 應用:函數節流/防抖中使用箭頭函數簡化代碼

//防抖
function debounce(fn,delay) {
   let timerId = null;
   return function (...args) {
       timerId && clearTimeout(timerId);
       timerId = setTimeout(()=>{
           fn.apply(this,args);
       },delay || 3000);
   }
}

//節流
function throttle(fn,delay) {
   let timerId = null;
   let flag = true;
      return function (...args) {
         if(!flag) return;
         flag = false;
         timerId && clearTimeout(timerId);
         timerId = setTimeout(()=>{
             flag = true;
             fn.apply(this,args);
         },delay || 3000);
     }
}
複製代碼

2.3 不綁定Arguments

箭頭函數不綁定Arguments 對象,即箭頭函數沒有Arguments對象。

3.不推薦使用箭頭函數的場景

3.1 做爲構造函數或者是一個對象的方法或者給原型綁定方法的時候,是不容許使用箭頭函數的

箭頭函數沒有prototype屬性。

let Foo = (name,age) => {
    this.name = name;
    this.age = age;
};
let foo = new Foo('ghk',22); // TypeError: Foo is not a constructor
Foo.prototype.say = () => {
    console.log(`hello ${this.name}`); // TypeError: Foo is not a constructor
}
複製代碼

3.1.1 緣由: 經過new生成一個實例的時候,在內部會完成四個步驟

(1)建立一個新的對象

(2)將構造函數中的做用域賦值給新對象(構造函數的this就指向了該新生成的對象)

(3)執行構造函數中的代碼(爲新對象添加屬性)

(4)返回新對象

箭頭函數用做構造器時,並未完成把this值綁定到新生成的對象上面去這個步驟,因此會報錯,所以只能使用原始函數做爲構造函數。 正確寫法:

let Foo = function(name,age){
    this.name = name;
    this.age = age;
};
let foo = new Foo('ghk',22); 
Foo.prototype.say = function (){
    console.log(`hello ${this.name}`);
};
foo.say();
複製代碼

3.2 須要使用this的時候(交互的時候),不推薦使用箭頭函數

給元素綁定事件時,不可用箭頭函數。由於此時箭頭函數的this是window,而觸發事件this應該指向到觸發事件的該元素上。 錯誤寫法:

let oBtn = document.querySelector(".btn");
oBtn.addEventListener("click",() => {
  console.log(this); //this是window,因此會報錯
  this.classList.add("in");
  setTimeout(() => {
      console.log(this); //this是oBtn
      this.classList.remove("in");
  },2000)
})
複製代碼

正確寫法:

let oBtn = document.querySelector(".btn");
oBtn.addEventListener("click",function () {
    console.log(this); //this是oBtn
    this.classList.add("in");
    setTimeout(() => {
        console.log(this); //this是oBtn
        this.classList.remove("in");
    },2000)
})
複製代碼

3.3 須要使用Arguments對象的時候,不推薦使用箭頭函數

箭頭函數中沒有Arguments對象 錯誤寫法:

let sum = () =>{
    return Array.from(arguments)
        .reduce((prevSum,curValue) => prevSum + curValue);
};
sum(1,2,3); //arguments is not defined
複製代碼

正確寫法:

let sum = function(){
    return Array.from(arguments)
        .reduce((prevSum,curValue) => prevSum + curValue);
};
console.log(sum(1, 2, 3)); //6
複製代碼

或者

let sum = (...args) => {
    return args.reduce((prevSum,curValue) => prevSum + curValue);
};
console.log(sum(1, 2, 3));
複製代碼

3.4 不可使用 yield命令,所以箭頭函數不能用做Generate函數。


補充知識

Array.from()做用是把類數組對象轉換爲真數組

  • 語法:Array.from(arrayLike[, mapFn[, thisArg]])
  • 參數:①arrayLike:想要轉換成數組的僞數組對象或可迭代對象;
    ②mapFn (可選參數):若是指定了該參數,新數組中的每一個元素會執行該回調函數;
    ③thisArg (可選參數):可選參數,執行回調函數 mapFn 時 this 對象。
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]

console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
複製代碼

reduce()爲數組中的每個元素依次執行callback函數,不包括數組中被刪除或從未被賦值的元素

  • 語法:arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

  • 參數: 第一個參數:callback函數

    callback函數裏面的參數有:①accumulator 累計器;②currentValue 當前值;③currentIndex 當前索引(可選);④array 數組(可選)

    第二個參數:initialValue(可選)

    做爲第一次調用 callback函數時的第一個參數的值。 若是沒有提供初始值,則將使用數組中的第一個元素。 在沒有初始值的空數組上調用 reduce 將報錯。

let arr = [1,2,3,4];
let sum = (accumulator,currentValue) => accumulator + currentValue;
console.log(arr.reduce(sum)); //10
console.log(arr.reduce(sum, 5)); //15
複製代碼

上面的代碼中,sum函數的參數accumulator是累積變量,參數currentValue是當前的數組成員。每次執行時,currentValue會加到accumulator,最後輸出accumulator。 reduce方法中的第二個參數不傳的時候,初始值默認使用數組中的第一個元素。即1+2+3+4輸出10;當初始值爲5時,即5+1+2+3+4輸出15.


本文章參考到的連接: developer.mozilla.org/en-US/docs/…

www.ruanyifeng.com/blog/develo…

www.runoob.com/

相關文章
相關標籤/搜索