title: es6-函數
date: 2018-02-01 21:47:46
tags: es6
前言
杭州這周溫度達到-5度了,溫度下降的蠻快始腾,年會排舞感覺好懸啊州刽,不知到能拍出啥道道來,現在唯一的心愿就是,早點回家浪箭,冷也阻止不了我回家的腳步穗椅,嗯哼。
我的博客地址 :http://www.aymfx.cn/
引子
ES6函數的改變不算太大奶栖,都是一些其他語言早就有的功能匹表,而Javascript一直比較欠缺的,比如函數參數默認值宣鄙,任意參數的表示法袍镀,最大的變化應該是支持箭頭函數(其他語言稱之為LAMBDA表達式),一種對匿名函數的一種簡寫方式
函數形參的默認值
es5 模擬默認參數
function sendAjax(url,timeout,callback){
timeout = timeout || 2000;
callback = callback || $.noop(); //默認參數
$.ajax(url).done(function(){
setTimeout(callback,timeout)
})
}
上面的賦值操作會存在問題冻晤,你懂的苇羡,所以引入了es6的默認參數
function add(a=200,b=2){
return a+b;
}
console.log(add()); //202
console.log(add(2)); //4
console.log(add(null,2)); //2 null是合法值 被當成0
console.log(add(undefined,2)); //202
默認參數對arguments對象的影響
非嚴格模式下
function temp(first,second){
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
first = 'c';
second = 'd'
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
}
temp('a','b')
//true
//VM264:3 true
//VM264:6 true
//VM264:7 true
嚴格模式下
function temp(first,second){
'use strict'
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
first = 'c';
second = 'd'
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
}
temp('a','b')
//true
//VM264:3 true
//VM264:6 false
//VM264:7 false
非嚴格模式下,參數與argument的值保持一致,嚴格模式下arguments與傳進來的初始參數保持一致鼻弧,看看es6默認值存在的話
非嚴格模式下
function temp(first,second='b'){
console.log(arguments.length)
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
first = 'c';
second = 'b'
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
}
temp('a')
//1
//true
//VM264:3 false
//VM264:6 false
//VM264:7 false
嚴格模式下
function temp(first,second='b'){
'use strict'
console.log(arguments.length)
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
first = 'c';
second = 'd'
console.log(first=== arguments[0]);
console.log(second=== arguments[1]);
}
temp('a')
// Illegal 'use strict' directive in function with non-simple parameter list
//hhhhhhhhhhhh,不能用设江,尷尬
默認參數表達式,可以傳函數
function getValue(){
return 5;
}
function add(first,second=getValue()){
return first+second;
}
console.log(add(1,2)); //3
console.log(add(10)); //15
可以將先前定義好的形參當默認值
function add(first,second=first){
return first+second;
}
console.log(add(1,2)); //3
console.log(add(10)); //20
反之不行哈
function add(first=second,second){
return first+second;
}
console.log(add(1,2)); //3
console.log(add(undefined,10)); //second is not defined
//這就是所謂的臨時死區(qū)TDZ,未初始化之前不可被引用
不定參數 (...keys)
//模仿Underscore.js pick()方法
function pick(object,...keys){
console.log(arguments.length); //3
let result = Object.create(null);
for(let i=0,len=keys.length;i<len;i++){
result[keys[i]]= object[keys[i]]
}
return result;
}
let person = pick({name:'ly',age:'18',sex:'mael'},'age','sex');
console.log(person.name); //undefind
console.log(person.age); //18
console.log(person.sex); //mael
不定參數的要求
function pick(obj,...keys,last){} //報錯攘轩,不定參數必須放在最后后面
//不定參數不能用于對象字面量setter之中
let object = {
set name(...values){
//執(zhí)行邏輯
}
}
Function 構造函數功能增強 可以使用默認參數和不定參數
const add = new Function('first','second = first','return first+second')
console.log(add(1,2),add(1)) //3 2
var pick = new Function("...args","return args[0]")
console.log(pick(1,2)); //1
展開運算符
//之前求最大值的時候
let values = [25,100,75,56];
console.log(Math.max.apply(this,values)); //100
//有點麻煩叉存,但是用展開運算符的話
console.log(Math.max(...values)); //100
//開不開心,我們可以拿其他值和數組值比較
console.log(Math.max(...values,200)); //200
函數中可以獲取函數名稱的的屬性 name
var func1 = function () {};
// ES5
func1.name // ""
// ES6
func1.name // "func1"
//上面代碼中撑刺,變量func1等于一個匿名函數鹉胖, ES5 和 ES6 的name屬性返回的值不一樣。
//如果將一個具名函數賦值給一個變量够傍,則 ES5 和 ES6 的name屬性都返回這個具名函數原本的名字甫菠。
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
//Function構造函數返回的函數實例,name屬性的值為 “anonymous” 冕屯。
(new Function).name // "anonymous"
//bind返回的函數寂诱,name屬性值會加上 “bound ” 前綴。
function foo() {};
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
//還有幾個es6的情況
var dosomething = function(){}
console.log(dosomething.bind().name); //bound dosomething
console.log((new Function()).name); //anonymous
明確函數的多重用途
js函數有兩種內部方式 [[Call]]和[[Construct]],當通過new關鍵字調用函數時安聘,執(zhí)行的是[[Construct]]函數,他負責創(chuàng)建一個通常被稱作實例的新對象痰洒,然后再執(zhí)行函數體,將this綁定到實例上;如果不同過new則執(zhí)行[[Call]]函數,從而直接執(zhí)行代碼中的函數體
es5判斷函數被調用的方法
function Person(name){
if(this instanceof Person){
this.name = name;
} else{
throw new Error('必須new才行,嘿嘿')
}
}
var person = new Person("ly"); //
var notperson =Person("ll"); // 必須new才行浴韭,嘿嘿
//但是可以蒙混過關
var notperson =Person.call(peson,"ll"); //
new.target 精準判斷
function Person(name){
if(typeof new.target !== 'undefined'){
this.name = name;
} else{
throw new Error('必須new才行丘喻,嘿嘿')
}
}
var person = new Person("ly"); //
var notperson =Person.call(person,"ll"); //
塊級作用域
在es5的時代,當啟用嚴格模式時念颈,下列代碼會報錯,es6則不會,因為產生了塊級作用域泉粉,該函數可以在這個if條件語句內部使用,外部依舊是undefined
"use strict"
if(true){
console.log(typeof add); //function
function add(a,b){
return a+b;
}
}
console.log(typeof add); //undefined
但是在非嚴格模式下,該函數還是會被提升到全局作用域頂部
if(true){
console.log(typeof add); //function
function add(a,b){
return a+b;
}
}
console.log(typeof add); //function
重要改變 箭頭函數
一些好玩的改變(興奮狀)
- 沒有this,spuer,arguments和new.target綁定
- 不能通過new關鍵字調用
- 沒有原型
- 不可以改變this的綁定
- 不支持arguments對象
- 不支持重復的命名參數
箭頭函數的語法
let add = (a,b) => a+b;
//實際類似于
let add = function(a,b){return a+b}
//當箭頭函數只有一個參數時嗡靡,不需要括號
let reflrct = value => value
//類似于
let reflrct = function(value){return value}
//不寫參數時要加括號
let name = () => 'ly';
//類似于
let name = function(){return 'ly'}
//如果需要寫復雜的函數體跺撼,則必須這樣寫
let getName = (fisrtName,secondName) => {
return firstName+' '+ secondName;
}
//類似于
let getName = function(fisrtName,secondName){
return firstName+' '+ secondName;
}
//如果想反回一個字面量對象則需要這樣寫
let person = () => ({
name:'ly',
age:18
})
//類似于
let person = function(){
return {
name:'ly',
age:18
}
}
//創(chuàng)建一個立即表達函數
let person = ((name) => ({getName:() => name}))('ly')
//自己還原下看看,嘿嘿
箭頭函數沒有this綁定
let PageHandler = {
id:'13579',
init:function(){
document.addEventListener('click',function(event){
this.doSomething(event.type) //會報錯
})
},
doSomething:function(type){
console.log(type);
}
}
es5的做法讨彼,將會這么做
let PageHandler = {
id:'13579',
init:function(){
document.addEventListener('click',(function(event){
this.doSomething(event.type)
}).bind(this))
},
doSomething:function(type){
console.log(type);
}
}
但是有了箭頭函數的話歉井,就帥多了
let PageHandler = {
id:'13579',
init:function(){
document.addEventListener('click',event => this.doSomething(event.type)
)
},
doSomething:function(type){
console.log(type);
}
}
因為箭頭函數是沒有this的,所以在處理的過程中,它里面的this取決函數外部非箭頭函數的this值
箭頭函數不存在arguments綁定所以可以這么操作
function outer(){
return () => arguments[0];
}
let inner = outer(18)
console.log(inner()); //18 就是這么騷氣哈误,直接訪問箭頭函數體外函數的arguments
call(),bind(),apply() 都是可以用的哩至,但是改變不了this的值的哈
尾調用的優(yōu)化(Tail Call)
尾調用指的是函數做為另一個函數最后一條語句被調用,它不會在調用棧上增加新的堆棧幀,而是直接更新調用棧黑滴,調用棧所占空間始終是常量憨募,節(jié)省了內存,避免了爆棧的可能性,但是es5存在調用棧變得過大則會造成程序問題
尾調用實例
function add(a,b){
}
function max(a,b,c,d){
//......
return add(a,c);
}
優(yōu)化需要滿足以下條件,尾調用才不會創(chuàng)建新棧幀,而是清除并重用當前棧幀
- 尾調用不訪問當前棧的變量(不形成閉包)
- 尾調用是函數內部的最后一句
- 尾調用的結果將作為函數值返回 (必須有return fn())
使用場景 尾遞歸
錯誤方式
console.time('testForEach');
function factorial(n) {
if(n<=1){
return 1
} else {
return n*factorial(n-1) //如果n很大袁辈,在不斷遞歸的情況下菜谣,會棧溢出,這也不是尾遞歸
}
}
var a =factorial(5000);
console.log(a);
console.timeEnd('testForEach'); // 1.55322265625ms
正確寫法
console.time('testForEach');
function factorial(n,p=1) {
if(n<=1){
return 1*p;
} else {
let result = p*n;
return factorial(n-1,result)
}
}
factorial(5000);
console.timeEnd('testForEach'); //0.492919921875ms 測 了10000 居然棧溢出 搞不懂