深入理解ES6-函數

2020-07-08     segmentfault官方

原標題:深入理解ES6-函數

作者:一聲薔薇udVkP

來源:SegmentFault 思否社區

函數形參默認值

1、es5、es6對默認參數的處理區別:

es5:形參默認值是要先判斷,參數是否有值,如果沒有在賦值

es6:在定義函數時,可以直接給形參添加默認值,當函數調用實參為undefined則給形參賦值為默認值

注意:當傳進來的值為null,則不調用默認參數

2、默認參數對argument的影響

1、影響argument的length屬性

2、命名參數的變化不會跟新同步到argument對象中

es5非嚴格模式下。參數的變化會同步到argument中,嚴格模式下則不會

es6隻要函數使用默認參數,無論是否顯示定義嚴格模式都使得arguments對象保持與命名參數分離

例子-非嚴格模式:

functionminArgs(first,second = "b"){

console.log(argument.length)

console.log(first===argument[0])

console.log(second===argument[1])

first = "c";

second = "d";

console.log(first===argument[0])

console.log(second===argument[1])

}

minArgs( "a")

// 1

// true

// false

// false

// false

默認參數表達式

1、可以使用函數調用結果作為默認參數

2、可以使用先定義的參數作為函數調用的默認參數

3、先定義參數不能訪問後定義參數,這是因為後定義的參數臨時死區(TDZ)

例子-函數調用結果作為默認參數:

letvalue =5

functiongetValue{

returnvalue++

}

functionadd(a,b=getValue){

returna+b

}

console.log(add(1,2))//3

console.log(add(1))//6

不定參數

1、概述:

es5中無命名參數-argument:記錄所有傳進來的實參

es6中不定參數-(...):記錄自這個參數後傳進來的所有參數,使用上會更加方便

2、不定參數的使用限制:

a:每個函數只能聲明一個不定參數

b:不定參數只能放在末尾

c:不定參數不能用於對象字面量的setter中

3、不定參數對argument的影響:

無影響

展開運算符(...)

概念:

1、不定參數是可以讓你指定多個參數,整合成數組

2、展開運算符是讓你指定數組,將他們打散後作為各種獨立的參數傳入函數

增強Function構造函數

目的:用來動態創建新函數

參數:字符串形式的參數,分別為函數參數,函數體

支持創建函數時使用默認參數和不定參數

例子:

var add = new Function0( "first", "second", "return first+second")

console.log(add(1,2))//3

//默認參數

var add1 = new Function0( "first", "second=first", "return first+second")

console.log(add1(1))//2

//不定參數

var add2 = new Function0( "...args", "return args[0]")

console.log(add2(1,2,3))//1

name屬性

1、目的:用於辨別函數

2、如何選擇合適的名稱:

函數聲明:

function doSomething{}

doSomething.name = doSomething

匿名函數表達式

let doAnotherThing = function{}

doAnotherThing.name = doAnotherThing

3、name的特殊情況

情況1:

letdoSomethingElse = functiondoSomething{}

doSomethingElse.name =doSomething

情況2:

var person={

get firstName{

},

sayName: function{

}

}

person.firstName.name = "get firstName"

(getter函數和setter函數會再name屬性前加一個前綴"get"、"set")

person.sayName.name = "sayName"

情況3:

通過bind函數創建的,名稱帶有"bound"前綴

情況4:

通過mew Function創建的,名稱為"anonymous"

注意:不能使用name屬性值來獲取對函數的引用

函數的兩種用途

  1. 結合new使用,函數返回一個新對象,this指向實例
  2. 直接調用

判斷函數是通過什麼方式調用

js函數有兩個不同的內部方法[[Call]]和[[Construct]]

當通過new調用,執行[[Construct]],否則執行[[Call]]

es5判斷函數的調用方式:instanceof

當通過new調用的時候,可以檢查this的值是否為構造函數的實例

functionPerson(name){

if(this instanceof Person){

this.name = name;

} else{

throw new Error( "必須通過構造函數調用")

}

}

var person = new Person( "lisi")//有效

var p1 = Person.call(Person, "wangwu")//有效

var p2 = Person( "dd")//Error( "必須通過構造函數調用")

弊端:通過instanceof 不能準確判斷這個函數是不是new調用,因為通過call或者apply等方法也可以改變this的指向,讓this指向某個實例

es6判斷函數調用方式:元屬性->new.target

元屬性:非對象的屬性,可以提供非對象目標的補充信息(例如new)

當通過new調用,new.target被賦值為新創建對象的實例,也就是函數體內this構造函數,否則值為undefined

例子:

functionPerson(name){

// if(new.target === Person)

if(typeof new.target !== "undefined"){

this.name = name;

} else{

throw new Error( "必須通過構造函數調用")

}

}

var person = new Person( "lisi")//有效

var p1 = Person.call(Person, "wangwu")//拋出錯誤

注意:new.target只能在函數體內使用,否則報錯

塊及函數

1、在es3之前版本在代碼塊中聲明一個函數,會被認定是一個錯誤

2、最好不要在代碼塊中使用函數聲明,推薦用函數表達式

3、由於不同瀏覽器兼容不同,所以es5嚴格模式下,代碼塊中聲明一個函數時程序會拋出錯誤

4、es6會將聲明的函數視為一個塊及函數

例子:

if( true){

console.log(typeof doSomething)// "function"

//塊級函數

functiondoSomething{

console.log( "ss");

}

doSomething

// let函數表達式

letdoSomethingElse = function{

}

}

console.log(doSomething) //undefined

講解:定義函數的代碼塊中,函數會被提升到頂部,一旦if語句代碼塊結束執行,doSomething不復存在。塊級函數和let函數表達式類似,一旦執行過程流出代碼塊,函數定義立即被移除,區別是,塊級函數會被提升到代碼塊頂部,let定義的函數表達式不會被提升。

es6非嚴格模式,塊級函數會被提升到全局作用域

箭頭函數

特點:

1、沒有this,super,arguments,new.target的綁定,箭頭函數的this、arguments,new.target這些值由外圍最近一層非箭頭函數決定

2、不能通過new關鍵字調用

3、沒有原型,所以不存在prototype屬性

4、不可改變this的綁定,函數體內部的this值不可被該變

5、不支持arguments對象,始終可以訪問外圍的arguments對象

6、不支持重複命名參數

語法:

參數、箭頭、函數體組成

情況一:當只有一個參數時:

let reflect = value=>value

相當於:

letreflect = function(value){

returnvalue

}

情況二:兩個參數時,參數用括號括起來

情況三:沒有參數時,直接一個括號後面跟箭頭和函數體

情況四:如果想讓箭頭函數向外返回一個對象字面量,將該字面量包裹在小括號里

letgetTempItem = =>({name: "zhangsan",age:17})

相當於

letgetTempItem = function{

return{

name: "zhangsan",

age:17

}

}

情況五:創建立即執行函數表達式:

letperson = ((name)=>{

return{

getName{

returnname

}

}

})( "kankan")

用小括號包裹箭頭函數定義,不包括調用實際參數部分

箭頭函數的this值:取決於外部非箭頭函數的this值

箭頭函數設計初衷:即用即棄。

functionpageHandle{

id:123,

init: function{

document.addEnventListener( "click",event=>this.doSomething(event.typ))

},

doSomethis( type){

console.log( type)

}

}

尾調用優化

尾調用:函數作為另一個函數的最後一條語句被調用

es5引擎中,尾調用實現和其他函數調用實現類似,創建一個棧幀,將其推入調用棧表示函數調用,每一個未用完的棧幀都會保存在內存中,當調用棧變得過大會造成程序問題

es6嚴格模式下滿足以下3個條件,尾調用不創建棧幀,而是清除並重用當前棧幀

1、尾調用不訪問當前棧幀的變量(不是閉包)

2、在函數內部,尾調用是最後一條語句

3、尾調用結果作為函數值返回

例:(可以被js引擎自動優化)

"use strict"

functiontest{

returndoSomething

}

遞歸函數是其最主要的應用場景,優化效果顯著

functionfactorial(n){

if(n<=1){

return1

} else{

returnn*factorial(n-1)

}

}

在遞歸調用前執行了乘法操作,所以當前階乘函數無法優化

functionfactorial(n,p=1){

if(n<=1){

return1*p

} else{

letresult = n*p

優化後

returnfactorial(n-1,result)

}

}

可以被優化,如果遞歸函數足夠大,可以大幅提升程序性能

SegmentFault 思否社區和文章作者展開更多互動和交流。

文章來源: https://twgreatdaily.com/zh-mo/IGonL3MBiuFnsJQVR4uc.html

Flutter 知識點

2020-08-10