摘要:否則調(diào)用時(shí)依然需要傳參報(bào)錯(cuò)注意這里不能用觸發(fā)默認(rèn)值這里我們還需要多帶帶討論一下默認(rèn)參數(shù)對的影響很明顯,默認(rèn)參數(shù)并不能加到中。關(guān)于作用域集中在函數(shù)擴(kuò)展的最后討論。那如果函數(shù)的默認(rèn)參數(shù)是函數(shù)呢?zé)X的要來了如果基礎(chǔ)好那就根本談不上不燒腦。
參數(shù)默認(rèn)值
ES5中設(shè)置默認(rèn)值非常不方便, 我們這樣寫:
function fun(a){ a = a || 2; console.log(a); } fun(); //2 fun(0); //2 fun(1); //1
以上寫法, 如果傳入了參數(shù), 但這個(gè)參數(shù)對應(yīng)值的布爾型是 false, 就不起作用了。當(dāng)然你也可以判斷 arguments.length 是否為0來避免這個(gè)問題, 但每個(gè)函數(shù)這樣寫就太啰嗦了, 尤其參數(shù)比較多的時(shí)候。在 ES6 中我們可以直接寫在參數(shù)表中, 如果實(shí)際調(diào)用傳遞了參數(shù), 就用這個(gè)傳過來的參數(shù), 否則用默認(rèn)參數(shù)。像這樣:
function fun(a=2){ console.log(a); } fun(); //2 fun(0); //0 fun(1); //1
其實(shí)函數(shù)默認(rèn)參數(shù)這一點(diǎn)最強(qiáng)大的地方在于可以和解構(gòu)賦值結(jié)合使用:
//參數(shù)傳遞 function f([x, y, z=4]){ return [x+1, y+2, z+3]; } var [a, b, c] = f([1, 2]); //a=2, b=4, c=7 [[1, 2], [3, 4]].map(([a, b]) => a + b); //返回 [3, 7]
通過上面這個(gè)例子不難發(fā)現(xiàn), 不僅可以用解構(gòu)的方法設(shè)置初始值, 還可以進(jìn)行參數(shù)傳遞。當(dāng)然, 這里也可以是對象形式的解構(gòu)賦值。如果傳入的參數(shù)無法解構(gòu), 就會(huì)報(bào)錯(cuò):
function fun1({a=1, b=5, c="A"}){ console.log(c + (a + b)); } fun1({}); //"A6" fun1(); //TypeError, 因?yàn)闊o法解構(gòu) //但這樣設(shè)計(jì)函數(shù)對使用函數(shù)的碼農(nóng)很不友好 //所以, 技巧: function fun2({a=1, b=5, c="A"}={}){ console.log(c + (a + b)); } fun2(); //"A6"
注意, 其實(shí)還有一種方法, 但不如這個(gè)好, 我們比較如下:
//fun1 比 fun2 好, 不會(huì)產(chǎn)生以外的 undefined function fun1({a=1, b=5, c="A"}={}){ console.log(c + (a + b)); } function fun2({a, b, c}={a: 1, b: 5, c: "A"}){ console.log(c + (a + b)); } //傳了參數(shù), 但沒傳全部參數(shù)就會(huì)出問題 fun1({a: 8}); //"A13" fun2({a: 8}); //NaN
不過這里強(qiáng)烈建議, 將具有默認(rèn)值的參數(shù)排在參數(shù)列表的后面。否則調(diào)用時(shí)依然需要傳參:
function f1(a=1, b){ console.log(a + b); } function f2(a, b=1){ console.log(a + b); } f2(2); //3 f1(, 2); //報(bào)錯(cuò) f1(undefined, 2); //3, 注意這里不能用 null 觸發(fā)默認(rèn)值
這里我們還需要多帶帶討論一下默認(rèn)參數(shù)對 arguments 的影響:
function foo(a = 1){ console.log(a, arguments[0]); } foo(); //1 undefined foo(undefined); //1 undefined foo(2); //2 2 foo(null); //null null
很明顯,默認(rèn)參數(shù)并不能加到 arguments 中。
函數(shù)的 length 屬性
這個(gè)屬性ES6 之前就是存在的, 記得length表示預(yù)計(jì)傳入的形參個(gè)數(shù), 也就是沒有默認(rèn)值的形參個(gè)數(shù):
(function(a){}).length; //1 (function(a = 5){}).length; //0 (function(a, b, c=5){}).length; //2 (function(...args){}).length; //0, rest參數(shù)也不計(jì)入 lengthrest 參數(shù)
rest 參數(shù)形式為 ...變量名, 它會(huì)將對應(yīng)的全部實(shí)際傳遞的變量放入數(shù)組中, 可以用它來替代 arguments:
function f(...val){ console.log(val.join()); } f(1, 2); //[1, 2] f(1, 2, 3, 4); //[1, 2, 3, 4] function g(a, ...val){ console.log(val.join()); } g(1, 2); //[2] g(1, 2, 3, 4); //[2, 3, 4]
否則這個(gè)函數(shù) g 你的這樣定義函數(shù), 比較麻煩:
function g(a){ console.log([].slice.call(arguments, 1).join()); }
這里需要注意2點(diǎn):
rest參數(shù)必須是函數(shù)的最后一個(gè)參數(shù), 它的后面不能再定義參數(shù), 否則會(huì)報(bào)錯(cuò)。
rest參數(shù)不計(jì)入函數(shù)的 length 屬性中
建議:
所有配置項(xiàng)都應(yīng)該集中在一個(gè)對象,放在最后一個(gè)參數(shù),布爾值不可以直接作為參數(shù)。這樣方便調(diào)用者以任何順序傳遞參數(shù)。
不要在函數(shù)體內(nèi)使用arguments變量,使用rest運(yùn)算符(...)代替。因?yàn)閞est運(yùn)算符顯式表明你想要獲取參數(shù),而且arguments是一個(gè)類似數(shù)組的對象,而rest運(yùn)算符可以提供一個(gè)真正的數(shù)組。
使用默認(rèn)值語法設(shè)置函數(shù)參數(shù)的默認(rèn)值。
擴(kuò)展運(yùn)算符擴(kuò)展運(yùn)算符類似 rest運(yùn)算符的逆運(yùn)算, 用 ... 表示, 放在一個(gè)(類)數(shù)組前, 將該數(shù)組展開成獨(dú)立的元素序列:
console.log(1, ...[2, 3, 4], 5); //輸出1, 2, 3, 4, 5
擴(kuò)展運(yùn)算符的用處很多:
可以用于快速改變類數(shù)組對象為數(shù)組對象, 也是用于其他可遍歷對象:
[...document.querySelectorAll("li")]; //[
結(jié)合 rest 參數(shù)使函數(shù)事半功倍:
function push(arr, ...val){ return arr.push(...val); //調(diào)用函數(shù)時(shí), 將數(shù)組變?yōu)樾蛄?}
替代 apply 寫法
var arr = [1, 2, 3]; var max = Math.max(...arr); //3 var arr2 = [4, 5, 6]; arr.push(...arr2); //[1, 2, 3, 4, 5, 6] new Date(...[2013, 1, 1]); //ri Feb 01 2013 00: 00: 00 GMT+0800 (CST)
連接, 合并數(shù)組
var more = [4, 5]; var arr = [1, 2, 3, ...more]; //[1, 2, 3, 4, 5] var a1 = [1, 2]; var a2 = [3, 4]; var a3 = [5, 6]; var a = [...a1, ...a2, ...a3]; //[1, 2, 3, 4, 5, 6]
解構(gòu)賦值
var a = [1, 2, 3, 4, 5]; var [a1, ...more] = a; //a1 = 1, more = [2, 3, 4, 5] //注意, 擴(kuò)展運(yùn)算符必須放在解構(gòu)賦值的結(jié)尾, 否則報(bào)錯(cuò)
字符串拆分
var str = "hello"; var alpha = [...str]; //alpha = ["h", "e", "l", "l", "o"] [..."xuD83DuDE80y"].length; //3, 正確處理32位 unicode 字符
建議:使用擴(kuò)展運(yùn)算符(...)拷貝數(shù)組。
name 屬性name 屬性返回函數(shù)的名字, 對于匿名函數(shù)返回空字符串。不過對于表達(dá)式法定義的函數(shù), ES5 和 ES6有差別:
var fun = function(){} fun.name; //ES5: "", ES6: "fun" (function(){}).name; //""
對于有2個(gè)名字的函數(shù), 返回后者, ES5 和 ES6沒有差別:
var fun = function baz(){} fun.name; //baz
對于 Function 構(gòu)造函數(shù)得到的函數(shù), 返回 anonymous:
new Function("fun").name; //"anonymous" new Function().name; //"anonymous" (new Function).name; //"anonymous"
對于 bind 返回的函數(shù), 加上 bound 前綴
function f(){} f.bind({}).name; //"bound f" (function(){}).bind({}).name; //"bound " (new Function).bind({}).name; //"bound anonymous"箭頭函數(shù)
箭頭函數(shù)的形式如下:
var fun = (參數(shù)列表) => {函數(shù)體};
如果只有一個(gè)參數(shù)(且不指定默認(rèn)值), 參數(shù)列表的圓括號可以省略; (如果沒有參數(shù), 圓括號不能省略)
如果只有一個(gè) return 語句, 那么函數(shù)體的花括號也可以省略, 同時(shí)省略 return 關(guān)鍵字。
var fun = value => value + 1; //等同于 var fun = function(value){ return value + 1; }
var fun = () => 5; //等同于 var fun = function(){ return 5; }
如果箭頭函數(shù)的參數(shù)或返回值有對象, 應(yīng)該用 () 括起來:
var fun = n => ({name: n}); var fun = ({num1=1, num2=3}={}) => num1 + num2;
看完之前的部分, 箭頭函數(shù)應(yīng)該不陌生了:
var warp = (...val) => val; var arr1 = warp(2, 1, 3); //[2, 1, 3] var arr2 = arr1.map(x => x * x); //[4, 1, 9] arr2.sort((a, b) => a - b); //[1, 4, 9]
使用箭頭函數(shù)應(yīng)注意以下幾點(diǎn):
不可以將函數(shù)當(dāng)做構(gòu)造函數(shù)調(diào)用, 即不能使用 new 命令;
不可以在箭頭函數(shù)中使用 yield 返回值, 所以不能用過 Generator 函數(shù);
函數(shù)體內(nèi)不存在 arguments 參數(shù);
函數(shù)體內(nèi)部不構(gòu)成獨(dú)立的作用域, 內(nèi)部的 this 和定義時(shí)候的上下文一致; 但可以通過 call, apply, bind 改變函數(shù)中的 this。關(guān)于作用域, 集中在ES6函數(shù)擴(kuò)展的最后討論。
舉幾個(gè)箭頭函數(shù)的實(shí)例:
實(shí)例1: 實(shí)現(xiàn)功能如: insert(2).into([1, 3]).after(1)或insert(2).into([1, 3]).before(3)這樣的函數(shù):
var insert = value => ({ into: arr => ({ before: val => { arr.splice(arr.indexOf(val), 0, value); return arr; }, after: val => { arr.splice(arr.indexOf(val) + 1, 0, value); return arr; } }) }); console.log(insert(2).into([1, 3]).after(1)); console.log(insert(2).into([1, 3]).before(3));
實(shí)例2: 構(gòu)建一個(gè)管道(前一個(gè)函數(shù)的輸出是后一個(gè)函數(shù)的輸入):
var pipe = (...funcs) => (init_val) => funcs.reduce((a, b) => b(a), init_val); //實(shí)現(xiàn) 2 的 (3+2) 次方 var plus = a => a + 2; pipe(plus, Math.pow.bind(null, 2))(3); //32
實(shí)例3: 實(shí)現(xiàn) λ 演算
//fix = λf.(λx.f(λv.x(x)(v)))(λx.f(λv.x(x)(v))) var fix = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v)));
建議:箭頭函數(shù)取代 Function.prototype.bind,不應(yīng)再用 self / _this / that 綁定 this。其次,簡單的、不會(huì)復(fù)用的函數(shù),建議采用箭頭函數(shù)。如果函數(shù)體較為復(fù)雜,行數(shù)較多,還是應(yīng)該采用傳統(tǒng)的函數(shù)寫法。
這里需要強(qiáng)調(diào),以下情況不能使用箭頭函數(shù):
定義字面量方法
let calculator = { array: [1, 2, 3], sum: () => { return this.array.reduce((result, item) => result + item); //這里的 this 成了 window } }; calculator.sum(); //"TypeError: Cannot read property "reduce" of undefined"
定義原型方法
function Cat(name) { this.name = name; } Cat.prototype.sayCatName = () => { return this.name; //和上一個(gè)問題一樣:這里的 this 成了 window }; let cat = new Cat("Mew"); cat.sayCatName(); //undefined
綁定事件
const button = document.getElementById("myButton"); button.addEventListener("click", () => { this.innerHTML = "Clicked button"; //這里的 this 本應(yīng)該是 button, 但不幸的成了 window });
定義構(gòu)造函數(shù)
let Message = (text) => { this.text = text; }; let helloMessage = new Message("Hello World!"); //TypeError: Message is not a constructor
不要為了追求代碼的簡短喪失可讀性
let multiply = (a, b) => b === undefined ? b => a * b : a * b; //這個(gè)太難讀了,太費(fèi)時(shí)間 let double = multiply(2); double(3); //6 multiply(2, 3); //6函數(shù)綁定
ES7 中提出了函數(shù)綁定運(yùn)算, 免去我們使用 call, bind, apply 的各種不方便, 形式如下:
objName::funcName
以下幾組語句兩兩等同
var newFunc = obj::func; //相當(dāng)于 var newFunc = func.bind(obj); var result = obj::func(...arguments); //相當(dāng)于 var result = func.apply(obj, arguments);
如果 :: 左邊的對象原本就是右邊方法中的 this, 左邊可以省略
var fun = obj::obj.func; //相當(dāng)于 var fun = ::obj.func; //相當(dāng)于 var fun = obj.func.bind(obj);
:: 運(yùn)算返回的還是對象, 可以進(jìn)行鏈?zhǔn)秸{(diào)用:
$(".my-class")::find("p")::text("new text"); //相當(dāng)于 $(".my-class").find("p").text("new text");尾調(diào)用優(yōu)化
尾調(diào)用是函數(shù)式編程的概念, 指在函數(shù)最后調(diào)用另一個(gè)函數(shù)。
//是尾調(diào)用 function a(){ return g(); } function b(p){ if(p>0){ return m(); } return n(); } function c(){ return c(); } //以下不是尾調(diào)用 function d(){ var b1 = g(); return b1; } function e(){ g(); } function f(){ return g() + 1; }
尾調(diào)用的一個(gè)顯著特點(diǎn)就是, 我們可以將函數(shù)尾部調(diào)用的函數(shù)放在該函數(shù)外面(后面), 而不改變程序?qū)崿F(xiàn)結(jié)果。這樣可以減少函數(shù)調(diào)用棧的開銷。
這樣的優(yōu)化在 ES6 的嚴(yán)格模式中被強(qiáng)制實(shí)現(xiàn)了, 我們需要做的僅僅是在使用時(shí)候利用好這個(gè)優(yōu)化特性, 比如下面這個(gè)階乘函數(shù):
function factorial(n){ if(n <= 1) return 1; return n * factorial(n - 1); } factorial(5); //120
這個(gè)函數(shù)計(jì)算 n 的階乘, 就要在內(nèi)存保留 n 個(gè)函數(shù)調(diào)用記錄, 空間復(fù)雜度 O(n), 如果 n 很大可能會(huì)溢出。所以進(jìn)行優(yōu)化如下:
"use strict"; function factorial(n, result = 1){ if(n <= 1) return result; return factorial(n - 1, n * result); } factorial(5); //120
當(dāng)然也可以使用柯里化:
var factorial = (function factor(result, n){ if(n <= 1) return result; return factor(n * result, n - 1); }).bind(null, 1); factorial(5); //120函數(shù)的尾逗號
這個(gè)僅僅是一個(gè)提案: 為了更好地進(jìn)行版本控制, 在函數(shù)參數(shù)尾部加一個(gè)逗號, 表示該函數(shù)日后會(huì)被修改, 便于版本控制器跟蹤。目前并未實(shí)現(xiàn)。
作用域這里僅僅討論 ES6 中的變量作用域。除了 let 和 const 定義的的變量具有塊級作用域以外, var 和 function 依舊遵守詞法作用域, 詞法作用域可以參考博主的另一篇文章javascript函數(shù)、作用域鏈與閉包
首先看一個(gè)例子:
var x = 1; function f(x, y=x){ console.log(y); } f(2); //2
這個(gè)例子輸出了2, 因?yàn)?y 在初始化的時(shí)候, 函數(shù)內(nèi)部的 x 已經(jīng)定義并完成賦值了, 所以, y = x 中的 x 已經(jīng)是函數(shù)的局部變量 x 了, 而不是全局的 x。當(dāng)然, 如果局部 x 變量在 y 聲明之后聲明就沒問題了。
var x = 1; function f(y=x){ let x = 2 console.log(y); } f(); //1
那如果函數(shù)的默認(rèn)參數(shù)是函數(shù)呢?燒腦的要來了:
var foo = "outer"; function f(x){ return foo; } function fun(foo, func = f){ console.log(func()); } fun("inner"); //"outer"
如果基礎(chǔ)好, 那就根本談不上不燒腦。因?yàn)? 函數(shù)中的作用域取決于函數(shù)定義的地方, 函數(shù)中的 this 取決于函數(shù)調(diào)用的方式。(敲黑板)
但如果這樣寫, 就是 inner 了, 因?yàn)閒unc默認(rèn)函數(shù)定義的時(shí)候 fun內(nèi)的 foo 已經(jīng)存在了。
var foo = "outer"; function fun(foo, func = function(x){ return foo; }){ console.log(func()); } fun("inner"); //"inner"
技巧: 利用默認(rèn)值保證必需的參數(shù)被傳入, 而減少對參數(shù)存在性的驗(yàn)證:
function throwErr(){ throw new Error("Missing Parameter"); } function fun(necessary = throwErr()){ //...如果參數(shù)necessary沒有收到就使用參數(shù), 從而執(zhí)行函數(shù)拋出錯(cuò)誤 } //當(dāng)然也可以這樣表示一個(gè)參數(shù)是可選的 function fun(optional = undefined){ //... }
箭頭函數(shù)的作用域和定義時(shí)的上下文一致, 但可以通過調(diào)用方式改變:
window && (window.name = "global") || (global.name = "global"); var o = { name: "obj-o", foo: function (){ setTimeout(() => {console.log(this.name); }, 500); } } var p = { name: "obj-p", foo: function (){ setTimeout(function(){console.log(this.name); }, 1000); } } o.foo(); //"obj-o" p.foo(); //"global" var temp = { name: "obj-temp" } o.foo.bind(temp)(); //"obj-temp" o.foo.call(temp); //"obj-temp" o.foo.apply(temp); //"obj-temp" p.foo.bind(temp)(); //"global" p.foo.call(temp); //"global" p.foo.apply(temp); //"global"
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97422.html
摘要:數(shù)組的擴(kuò)展將類數(shù)組對象和可遍歷對象轉(zhuǎn)化為真正的數(shù)組。這兩個(gè)函數(shù)的參數(shù)都是回調(diào)函數(shù)。遍歷數(shù)組找到符合條件回調(diào)函數(shù)返回為的第一個(gè)值返回其值返回其下標(biāo)。這三個(gè)方法用來遍歷數(shù)組返回一個(gè)遍歷器供使用其中是對鍵的遍歷是對值的遍歷是對鍵值對的遍歷。 數(shù)組的擴(kuò)展 Array, from() 將類數(shù)組對象和可遍歷對象轉(zhuǎn)化為真正的數(shù)組。 var arrayLike = { 0 : a, 1 : b...
摘要:原來的也被修改了數(shù)組實(shí)例的喝方法,用于找出第一個(gè)符合條件的數(shù)組成員。它的參數(shù)是一個(gè)回調(diào)函數(shù),所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù),直到找出第一個(gè)返回值為的成員,然后返回該成員。數(shù)組實(shí)例的方法使用給定值,填充一個(gè)數(shù)組。 1 Array.from() Array.from方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括...
摘要:返回空字符串,返回將一個(gè)具名函數(shù)賦值給一個(gè)變量,則和的屬性都返回這個(gè)具名函數(shù)原本的名字。不可以使用對象,該對象在函數(shù)體內(nèi)不存在。等到運(yùn)行結(jié)束,將結(jié)果返回到,的調(diào)用幀才會(huì)消失。 1 函數(shù)參數(shù)的默認(rèn)值 ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面: function log(x = message.,y = duration infomation.) { consol...
摘要:第二個(gè)參數(shù)指定修飾符,如果存在則使用指定的修飾符。屬性表示是否設(shè)置了修飾符屬性的屬性返回正則表達(dá)式的正文的屬性返回正則表達(dá)式的修飾符字符串必須轉(zhuǎn)義,才能作為正則模式。 1 RegExp構(gòu)造函數(shù) ES6 允許RegExp構(gòu)造函數(shù)接受正則表達(dá)式作為參數(shù)。第二個(gè)參數(shù)指定修飾符,如果存在則使用指定的修飾符。 var regexp = new RegExp(/xyz/i, ig); consol...
摘要:屬性的簡潔表示法允許直接寫入變量和函數(shù)作為對象的屬性和方法。,中有返回一個(gè)數(shù)組,成員是參數(shù)對象自身的不含繼承的所有可遍歷屬性的鍵名。對象的擴(kuò)展運(yùn)算符目前,有一個(gè)提案,將解構(gòu)賦值擴(kuò)展運(yùn)算符引入對象。 1 屬性的簡潔表示法 ES6允許直接寫入變量和函數(shù)作為對象的屬性和方法。 寫入屬性 var name = value; var obj = { name }; console.log...
摘要:正則表達(dá)式擴(kuò)展構(gòu)造函數(shù)支持傳入正則得到拷貝,同時(shí)可以用第二參修改修飾符引入新的修飾符中的修飾符有個(gè)加上的修飾符,一共個(gè)修飾符描述描述多行模式忽略大小寫模式全局匹配模式模式粘連模式模式為了兼容自己我們需要在一下情況使用該模式情況很明顯這個(gè)是不 正則表達(dá)式擴(kuò)展 構(gòu)造函數(shù)支持傳入正則得到拷貝,同時(shí)可以用第二參修改修飾符 var reg = /^abc/ig; var newReg_ig = ...
閱讀 2855·2023-04-25 17:59
閱讀 685·2023-04-25 15:05
閱讀 675·2021-11-25 09:43
閱讀 3038·2021-10-12 10:13
閱讀 3545·2021-09-27 13:59
閱讀 3589·2021-09-23 11:21
閱讀 3888·2021-09-08 09:35
閱讀 571·2019-08-29 17:12