作者:一聲薔薇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屬性值來獲取對函數的引用
函數的兩種用途
- 結合new使用,函數返回一個新對象,this指向實例
- 直接調用
判斷函數是通過什麼方式調用
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 思否社區和文章作者展開更多互動和交流。