函數(shù),數(shù)組的擴(kuò)展

ECMAScript 6 入門(mén)

函數(shù)參數(shù)的默認(rèn)值 § ?

基本用法 § ?

ES6 之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值锈拨,只能采用變通的方法。

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World

上面代碼檢查函數(shù)log的參數(shù)y有沒(méi)有賦值羹唠,如果沒(méi)有推励,則指定默認(rèn)值為World。這種寫(xiě)法的缺點(diǎn)在于肉迫,如果參數(shù)y賦值了,但是對(duì)應(yīng)的布爾值為false稿黄,則該賦值不起作用喊衫。就像上面代碼的最后一行,參數(shù)y等于空字符杆怕,結(jié)果被改為默認(rèn)值族购。

為了避免這個(gè)問(wèn)題,通常需要先判斷一下參數(shù)y是否被賦值陵珍,如果沒(méi)有寝杖,再等于默認(rèn)值。

if (typeof y === 'undefined') {
  y = 'World';
}

參數(shù)變量是默認(rèn)聲明的互纯,所以不能用let或const再次聲明瑟幕。

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

上面代碼中,參數(shù)變量x是默認(rèn)聲明的,在函數(shù)體中只盹,不能用let或const再次聲明辣往,否則會(huì)報(bào)錯(cuò)

使用參數(shù)默認(rèn)值時(shí),函數(shù)不能有同名參數(shù)殖卑。

// 不報(bào)錯(cuò)
function foo(x, x, y) {
  // ...
}

// 報(bào)錯(cuò)
function foo(x, x, y = 1) {
  // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

另外站削,一個(gè)容易忽略的地方是,參數(shù)默認(rèn)值不是傳值的孵稽,而是每次都重新計(jì)算默認(rèn)值表達(dá)式的值许起。也就是說(shuō),參數(shù)默認(rèn)值是惰性求值的菩鲜。

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101

上面代碼中园细,參數(shù)p的默認(rèn)值是x + 1。這時(shí)睦袖,每次調(diào)用函數(shù)foo珊肃,都會(huì)重新計(jì)算x + 1,而不是默認(rèn)p等于 100馅笙。

函數(shù)的 length 屬性 § ?

指定了默認(rèn)值以后伦乔,函數(shù)的length屬性,將返回沒(méi)有指定默認(rèn)值的參數(shù)個(gè)數(shù)董习。也就是說(shuō)烈和,指定了默認(rèn)值后,length屬性將失真皿淋。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

上面代碼中招刹,length屬性的返回值,等于函數(shù)的參數(shù)個(gè)數(shù)減去指定了默認(rèn)值的參數(shù)個(gè)數(shù)窝趣。比如疯暑,上面最后一個(gè)函數(shù),定義了 3 個(gè)參數(shù)哑舒,其中有一個(gè)參數(shù)c指定了默認(rèn)值妇拯,因此length屬性等于3減去1,最后得到2洗鸵。

這是因?yàn)閘ength屬性的含義是越锈,該函數(shù)預(yù)期傳入的參數(shù)個(gè)數(shù)。某個(gè)參數(shù)指定默認(rèn)值以后膘滨,預(yù)期傳入的參數(shù)個(gè)數(shù)就不包括這個(gè)參數(shù)了甘凭。同理,后文的 rest 參數(shù)也不會(huì)計(jì)入length屬性火邓。

(function(...args) {}).length // 0

應(yīng)用 § ?

利用參數(shù)默認(rèn)值丹弱,可以指定某一個(gè)參數(shù)不得省略德撬,如果省略就拋出一個(gè)錯(cuò)誤。

function throwIfMissing() {
  throw new Error('Missing parameter');
}

function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}

foo()
// Error: Missing parameter

上面代碼的foo函數(shù)蹈矮,如果調(diào)用的時(shí)候沒(méi)有參數(shù)砰逻,就會(huì)調(diào)用默認(rèn)值throwIfMissing函數(shù),從而拋出一個(gè)錯(cuò)誤泛鸟。

從上面代碼還可以看到蝠咆,參數(shù)mustBeProvided的默認(rèn)值等于throwIfMissing函數(shù)的運(yùn)行結(jié)果(注意函數(shù)名throwIfMissing之后有一對(duì)圓括號(hào)),這表明參數(shù)的默認(rèn)值不是在定義時(shí)執(zhí)行北滥,而是在運(yùn)行時(shí)執(zhí)行刚操。如果參數(shù)已經(jīng)賦值,默認(rèn)值中的函數(shù)就不會(huì)運(yùn)行再芋。

另外菊霜,可以將參數(shù)默認(rèn)值設(shè)為undefined,表明這個(gè)參數(shù)是可以省略的济赎。

function foo(optional = undefined) { ··· }

rest 參數(shù) § ?

ES6 引入 rest 參數(shù)(形式為...變量名)鉴逞,用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對(duì)象了司训。rest 參數(shù)搭配的變量是一個(gè)數(shù)組构捡,該變量將多余的參數(shù)放入數(shù)組中。

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

上面代碼的add函數(shù)是一個(gè)求和函數(shù)壳猜,利用 rest 參數(shù)勾徽,可以向該函數(shù)傳入任意數(shù)目的參數(shù)。

箭頭函數(shù) § ?

ES6 允許使用“箭頭”(=>)定義函數(shù)统扳。

var f = v => v;

// 等同于
var f = function (v) {
  return v;
};

如果箭頭函數(shù)不需要參數(shù)或需要多個(gè)參數(shù)喘帚,就使用一個(gè)圓括號(hào)代表參數(shù)部分。

var f = () => 5;
// 等同于
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭頭函數(shù)的代碼塊部分多于一條語(yǔ)句咒钟,就要使用大括號(hào)將它們括起來(lái)吹由,并且使用return語(yǔ)句返回。

var sum = (num1, num2) => { return num1 + num2; }

由于大括號(hào)被解釋為代碼塊朱嘴,所以如果箭頭函數(shù)直接返回一個(gè)對(duì)象溉知,必須在對(duì)象外面加上括號(hào),否則會(huì)報(bào)錯(cuò)腕够。

// 報(bào)錯(cuò)
let getTempItem = id => { id: id, name: "Temp" };

// 不報(bào)錯(cuò)
let getTempItem = id => ({ id: id, name: "Temp" });

下面是一種特殊情況,雖然可以運(yùn)行舌劳,但會(huì)得到錯(cuò)誤的結(jié)果帚湘。

let foo = () => { a: 1 };
foo() // undefined

上面代碼中,原始意圖是返回一個(gè)對(duì)象{ a: 1 }甚淡,但是由于引擎認(rèn)為大括號(hào)是代碼塊大诸,所以執(zhí)行了一行語(yǔ)句a: 1捅厂。這時(shí),a可以被解釋為語(yǔ)句的標(biāo)簽资柔,因此實(shí)際執(zhí)行的語(yǔ)句是1;焙贷,然后函數(shù)就結(jié)束了,沒(méi)有返回值贿堰。

如果箭頭函數(shù)只有一行語(yǔ)句辙芍,且不需要返回值,可以采用下面的寫(xiě)法羹与,就不用寫(xiě)大括號(hào)了故硅。

let fn = () => void doesNotReturn();

箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用。

const full = ({ first, last }) => first + ' ' + last;

// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}

箭頭函數(shù)使得表達(dá)更加簡(jiǎn)潔纵搁。

const isEven = n => n % 2 == 0;
const square = n => n * n;

上面代碼只用了兩行吃衅,就定義了兩個(gè)簡(jiǎn)單的工具函數(shù)。如果不用箭頭函數(shù)腾誉,可能就要占用多行徘层,而且還不如現(xiàn)在這樣寫(xiě)醒目。

箭頭函數(shù)的一個(gè)用處是簡(jiǎn)化回調(diào)函數(shù)利职。

// 正常函數(shù)寫(xiě)法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭頭函數(shù)寫(xiě)法
[1,2,3].map(x => x * x);
另一個(gè)例子是

// 正常函數(shù)寫(xiě)法
var result = values.sort(function (a, b) {
  return a - b;
});

// 箭頭函數(shù)寫(xiě)法
var result = values.sort((a, b) => a - b);
下面是 rest 參數(shù)與箭頭函數(shù)結(jié)合的例子趣效。

const numbers = (...nums) => nums;

numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]

const headAndTail = (head, ...tail) => [head, tail];

headAndTail(1, 2, 3, 4, 5)
// [1,[2,3,4,5]]

使用注意點(diǎn) § ?

箭頭函數(shù)有幾個(gè)使用注意點(diǎn)。

(1)函數(shù)體內(nèi)的this對(duì)象眼耀,就是定義時(shí)所在的對(duì)象英支,而不是使用時(shí)所在的對(duì)象。

(2)不可以當(dāng)作構(gòu)造函數(shù)哮伟,也就是說(shuō)干花,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤楞黄。

(3)不可以使用arguments對(duì)象池凄,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用鬼廓,可以用 rest 參數(shù)代替肿仑。

(4)不可以使用yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)碎税。

上面四點(diǎn)中尤慰,第一點(diǎn)尤其值得注意。this對(duì)象的指向是可變的雷蹂,但是在箭頭函數(shù)中伟端,它是固定的。

雙冒號(hào)運(yùn)算符 § ?

箭頭函數(shù)可以綁定this對(duì)象匪煌,大大減少了顯式綁定this對(duì)象的寫(xiě)法(call责蝠、apply党巾、bind)。但是霜医,箭頭函數(shù)并不適用于所有場(chǎng)合齿拂,所以現(xiàn)在有一個(gè)提案,提出了“函數(shù)綁定”(function bind)運(yùn)算符肴敛,用來(lái)取代call署海、applybind調(diào)用值朋。

函數(shù)綁定運(yùn)算符是并排的兩個(gè)冒號(hào)(::)叹侄,雙冒號(hào)左邊是一個(gè)對(duì)象,右邊是一個(gè)函數(shù)昨登。該運(yùn)算符會(huì)自動(dòng)將左邊的對(duì)象趾代,作為上下文環(huán)境(即this對(duì)象),綁定到右邊的函數(shù)上面丰辣。

foo::bar;
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
  return obj::hasOwnProperty(key);
}

如果雙冒號(hào)左邊為空撒强,右邊是一個(gè)對(duì)象的方法,則等于將該方法綁定在該對(duì)象上面笙什。

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

如果雙冒號(hào)運(yùn)算符的運(yùn)算結(jié)果飘哨,還是一個(gè)對(duì)象,就可以采用鏈?zhǔn)綄?xiě)法琐凭。

import { map, takeWhile, forEach } from "iterlib";

getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));

尾調(diào)用優(yōu)化 § ?

尾調(diào)用(Tail Call)是函數(shù)式編程的一個(gè)重要概念芽隆,本身非常簡(jiǎn)單,一句話就能說(shuō)清楚统屈,就是指某個(gè)函數(shù)的最后一步是調(diào)用另一個(gè)函數(shù)胚吁。

function f(x){
  return g(x);
}

上面代碼中,函數(shù)f的最后一步是調(diào)用函數(shù)g愁憔,這就叫尾調(diào)用腕扶。

以下三種情況,都不屬于尾調(diào)用吨掌。

// 情況一
function f(x){
  let y = g(x);
  return y;
}

// 情況二
function f(x){
  return g(x) + 1;
}

// 情況三
function f(x){
  g(x);
}

上面代碼中半抱,情況一是調(diào)用函數(shù)g之后,還有賦值操作膜宋,所以不屬于尾調(diào)用窿侈,即使語(yǔ)義完全一樣。情況二也屬于調(diào)用后還有操作秋茫,即使寫(xiě)在一行內(nèi)史简。情況三等同于下面的代碼。

function f(x){
  g(x);
  return undefined;
}

尾調(diào)用不一定出現(xiàn)在函數(shù)尾部学辱,只要是最后一步操作即可乘瓤。

function f(x) {
  if (x > 0) {
    return m(x)
  }
  return n(x);
}

上面代碼中,函數(shù)m和n都屬于尾調(diào)用策泣,因?yàn)樗鼈兌际呛瘮?shù)f的最后一步操作衙傀。

尾調(diào)用優(yōu)化
尾調(diào)用之所以與其他調(diào)用不同,就在于它的特殊的調(diào)用位置萨咕。

我們知道统抬,函數(shù)調(diào)用會(huì)在內(nèi)存形成一個(gè)“調(diào)用記錄”,又稱(chēng)“調(diào)用幀”(call frame)危队,保存調(diào)用位置和內(nèi)部變量等信息聪建。如果在函數(shù)A的內(nèi)部調(diào)用函數(shù)B,那么在A的調(diào)用幀上方茫陆,還會(huì)形成一個(gè)B的調(diào)用幀金麸。等到B運(yùn)行結(jié)束,將結(jié)果返回到A簿盅,B的調(diào)用幀才會(huì)消失挥下。如果函數(shù)B內(nèi)部還調(diào)用函數(shù)C,那就還有一個(gè)C的調(diào)用幀桨醋,以此類(lèi)推棚瘟。所有的調(diào)用幀,就形成一個(gè)“調(diào)用椣沧睿”(call stack)偎蘸。

尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用幀瞬内,因?yàn)檎{(diào)用位置迷雪、內(nèi)部變量等信息都不會(huì)再用到了惹谐,只要直接用內(nèi)層函數(shù)的調(diào)用幀邓尤,取代外層函數(shù)的調(diào)用幀就可以了掂器。

function f() {
  let m = 1;
  let n = 2;
  return g(m + n);
}
f();

// 等同于
function f() {
  return g(3);
}
f();

// 等同于
g(3);

上面代碼中侧纯,如果函數(shù)g不是尾調(diào)用友雳,函數(shù)f就需要保存內(nèi)部變量m和n的值杏瞻、g的調(diào)用位置等信息晴叨。但由于調(diào)用g之后涂召,函數(shù)f就結(jié)束了舟陆,所以執(zhí)行到最后一步误澳,完全可以刪除f(x)的調(diào)用幀,只保留g(3)的調(diào)用幀秦躯。

這就叫做“尾調(diào)用優(yōu)化”(Tail call optimization)忆谓,即只保留內(nèi)層函數(shù)的調(diào)用幀。如果所有函數(shù)都是尾調(diào)用踱承,那么完全可以做到每次執(zhí)行時(shí)倡缠,調(diào)用幀只有一項(xiàng)哨免,這將大大節(jié)省內(nèi)存。這就是“尾調(diào)用優(yōu)化”的意義昙沦。

注意琢唾,只有不再用到外層函數(shù)的內(nèi)部變量,內(nèi)層函數(shù)的調(diào)用幀才會(huì)取代外層函數(shù)的調(diào)用幀盾饮,否則就無(wú)法進(jìn)行“尾調(diào)用優(yōu)化”采桃。

function addOne(a){
  var one = 1;
  function inner(b){
    return b + one;
  }
  return inner(a);
}

上面的函數(shù)不會(huì)進(jìn)行尾調(diào)用優(yōu)化,因?yàn)閮?nèi)層函數(shù)inner用到了外層函數(shù)addOne的內(nèi)部變量one丘损。

尾遞歸 § ?

函數(shù)調(diào)用自身普办,稱(chēng)為遞歸。如果尾調(diào)用自身徘钥,就稱(chēng)為尾遞歸衔蹲。

遞歸非常耗費(fèi)內(nèi)存,因?yàn)樾枰瑫r(shí)保存成千上百個(gè)調(diào)用幀吏饿,很容易發(fā)生“棧溢出”錯(cuò)誤(stack overflow)踪危。但對(duì)于尾遞歸來(lái)說(shuō),由于只存在一個(gè)調(diào)用幀猪落,所以永遠(yuǎn)不會(huì)發(fā)生“棧溢出”錯(cuò)誤贞远。

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

如果改寫(xiě)成尾遞歸,只保留一個(gè)調(diào)用記錄笨忌,復(fù)雜度 O(1) 蓝仲。

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

還有一個(gè)比較著名的例子,就是計(jì)算 Fibonacci 數(shù)列官疲,也能充分說(shuō)明尾遞歸優(yōu)化的重要性袱结。

非尾遞歸的 Fibonacci 數(shù)列實(shí)現(xiàn)如下。

function Fibonacci (n) {
  if ( n <= 1 ) {return 1};

  return Fibonacci(n - 1) + Fibonacci(n - 2);
}

Fibonacci(10) // 89
Fibonacci(100) // 堆棧溢出
Fibonacci(500) // 堆棧溢出

尾遞歸優(yōu)化過(guò)的 Fibonacci 數(shù)列實(shí)現(xiàn)如下途凫。

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};

  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}

Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity

由此可見(jiàn)垢夹,“尾調(diào)用優(yōu)化”對(duì)遞歸操作意義重大,所以一些函數(shù)式編程語(yǔ)言將其寫(xiě)入了語(yǔ)言規(guī)格维费。ES6 是如此果元,第一次明確規(guī)定,所有 ECMAScript 的實(shí)現(xiàn)犀盟,都必須部署“尾調(diào)用優(yōu)化”而晒。這就是說(shuō),ES6 中只要使用尾遞歸阅畴,就不會(huì)發(fā)生棧溢出倡怎,相對(duì)節(jié)省內(nèi)存。

數(shù)組的擴(kuò)展

擴(kuò)展運(yùn)算符 § ?

含義 § ?

擴(kuò)展運(yùn)算符(spread)是三個(gè)點(diǎn)(...)。它好比 rest 參數(shù)的逆運(yùn)算监署,將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分隔的參數(shù)序列颤专。

替代函數(shù)的 apply 方法 § ?

由于擴(kuò)展運(yùn)算符可以展開(kāi)數(shù)組,所以不再需要apply方法钠乏,將數(shù)組轉(zhuǎn)為函數(shù)的參數(shù)了血公。

擴(kuò)展運(yùn)算符的應(yīng)用 § ?

(1)復(fù)制數(shù)組

(2)合并數(shù)組

(3)與解構(gòu)賦值結(jié)合

(4)字符串

(5)實(shí)現(xiàn)了 Iterator 接口的對(duì)象

(6)Map 和 Set 結(jié)構(gòu),Generator 函數(shù)

Array.from() § ?

Array.from方法用于將兩類(lèi)對(duì)象轉(zhuǎn)為真正的數(shù)組:類(lèi)似數(shù)組的對(duì)象(array-like object)和可遍歷(iterable)的對(duì)象(包括 ES6 新增的數(shù)據(jù)結(jié)構(gòu) Set 和 Map)缓熟。

只要是部署了 Iterator 接口的數(shù)據(jù)結(jié)構(gòu),Array.from都能將其轉(zhuǎn)為數(shù)組摔笤。

值得提醒的是够滑,擴(kuò)展運(yùn)算符(...)也可以將某些數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為數(shù)組。

擴(kuò)展運(yùn)算符背后調(diào)用的是遍歷器接口(Symbol.iterator)吕世,如果一個(gè)對(duì)象沒(méi)有部署這個(gè)接口彰触,就無(wú)法轉(zhuǎn)換。Array.from方法還支持類(lèi)似數(shù)組的對(duì)象命辖。所謂類(lèi)似數(shù)組的對(duì)象况毅,本質(zhì)特征只有一點(diǎn),即必須有l(wèi)ength屬性尔艇。因此尔许,任何有l(wèi)ength屬性的對(duì)象,都可以通過(guò)Array.from方法轉(zhuǎn)為數(shù)組终娃,而此時(shí)擴(kuò)展運(yùn)算符就無(wú)法轉(zhuǎn)換味廊。

3. Array.of() § ?

Array.of方法用于將一組值,轉(zhuǎn)換為數(shù)組棠耕。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

這個(gè)方法的主要目的余佛,是彌補(bǔ)數(shù)組構(gòu)造函數(shù)Array()的不足。因?yàn)閰?shù)個(gè)數(shù)的不同窍荧,會(huì)導(dǎo)致Array()的行為有差異辉巡。

4數(shù)組實(shí)例的 copyWithin() § ?

數(shù)組實(shí)例的copyWithin方法,在當(dāng)前數(shù)組內(nèi)部蕊退,將指定位置的成員復(fù)制到其他位置(會(huì)覆蓋原有成員)郊楣,然后返回當(dāng)前數(shù)組。也就是說(shuō)咕痛,使用這個(gè)方法痢甘,會(huì)修改當(dāng)前數(shù)組。

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三個(gè)參數(shù)茉贡。

target(必需):從該位置開(kāi)始替換數(shù)據(jù)塞栅。如果為負(fù)值,表示倒數(shù)。
start(可選):從該位置開(kāi)始讀取數(shù)據(jù)放椰,默認(rèn)為 0作烟。如果為負(fù)值,表示倒數(shù)砾医。
end(可選):到該位置前停止讀取數(shù)據(jù)拿撩,默認(rèn)等于數(shù)組長(zhǎng)度。如果為負(fù)值如蚜,表示倒數(shù)压恒。

這三個(gè)參數(shù)都應(yīng)該是數(shù)值,如果不是错邦,會(huì)自動(dòng)轉(zhuǎn)為數(shù)值探赫。

[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]

上面代碼表示將從 3 號(hào)位直到數(shù)組結(jié)束的成員(4 和 5),復(fù)制到從 0 號(hào)位開(kāi)始的位置撬呢,結(jié)果覆蓋了原來(lái)的 1 和 2伦吠。

5數(shù)組實(shí)例的 find() 和 findIndex() § ?

數(shù)組實(shí)例的find方法,用于找出第一個(gè)符合條件的數(shù)組成員魂拦。它的參數(shù)是一個(gè)回調(diào)函數(shù)毛仪,所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù),直到找出第一個(gè)返回值為true的成員芯勘,然后返回該成員箱靴。如果沒(méi)有符合條件的成員,則返回undefined荷愕。

[1, 4, -5, 10].find((n) => n < 0)
// -5

數(shù)組實(shí)例的findIndex方法的用法與find方法非常類(lèi)似刨晴,返回第一個(gè)符合條件的數(shù)組成員的位置,如果所有成員都不符合條件路翻,則返回-1狈癞。

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

6數(shù)組實(shí)例的 fill() § ?

fill方法使用給定值,填充一個(gè)數(shù)組茂契。

['a', 'b', 'c'].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]

上面代碼表明蝶桶,fill方法用于空數(shù)組的初始化非常方便。數(shù)組中已有的元素掉冶,會(huì)被全部抹去真竖。

fill方法還可以接受第二個(gè)和第三個(gè)參數(shù),用于指定填充的起始位置和結(jié)束位置厌小。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

注意恢共,如果填充的類(lèi)型為對(duì)象,那么被賦值的是同一個(gè)內(nèi)存地址的對(duì)象璧亚,而不是深拷貝對(duì)象讨韭。

7數(shù)組實(shí)例的 entries(),keys() 和 values() § ?

ES6 提供三個(gè)新的方法——entries(),keys()和values()——用于遍歷數(shù)組透硝。它們都返回一個(gè)遍歷器對(duì)象(詳見(jiàn)《Iterator》一章)狰闪,可以用for...of循環(huán)進(jìn)行遍歷,唯一的區(qū)別是keys()是對(duì)鍵名的遍歷濒生、values()是對(duì)鍵值的遍歷埋泵,entries()是對(duì)鍵值對(duì)的遍歷。

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

如果不使用for...of循環(huán)罪治,可以手動(dòng)調(diào)用遍歷器對(duì)象的next方法丽声,進(jìn)行遍歷。

let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

8數(shù)組實(shí)例的 includes() § ?

Array.prototype.includes方法返回一個(gè)布爾值觉义,表示某個(gè)數(shù)組是否包含給定的值恒序,與字符串的includes方法類(lèi)似。ES2016 引入了該方法谁撼。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

該方法的第二個(gè)參數(shù)表示搜索的起始位置,默認(rèn)為0滋饲。如果第二個(gè)參數(shù)為負(fù)數(shù)厉碟,則表示倒數(shù)的位置,如果這時(shí)它大于數(shù)組長(zhǎng)度(比如第二個(gè)參數(shù)為-4屠缭,但數(shù)組長(zhǎng)度為3)箍鼓,則會(huì)重置為從0開(kāi)始。

indexOf方法有兩個(gè)缺點(diǎn)呵曹,一是不夠語(yǔ)義化款咖,它的含義是找到參數(shù)值的第一個(gè)出現(xiàn)位置,所以要去比較是否不等于-1奄喂,表達(dá)起來(lái)不夠直觀铐殃。二是,它內(nèi)部使用嚴(yán)格相等運(yùn)算符(===)進(jìn)行判斷跨新,這會(huì)導(dǎo)致對(duì)NaN的誤判富腊。

另外,Map 和 Set 數(shù)據(jù)結(jié)構(gòu)有一個(gè)has方法域帐,需要注意與includes區(qū)分赘被。

Map 結(jié)構(gòu)的has方法,是用來(lái)查找鍵名的肖揣,比如Map.prototype.has(key)民假、WeakMap.prototype.has(key)、Reflect.has(target, propertyKey)龙优。
Set 結(jié)構(gòu)的has方法羊异,是用來(lái)查找值的,比如Set.prototype.has(value)、WeakSet.prototype.has(value)球化。

9數(shù)組的空位 § ?

數(shù)組的空位指秽晚,數(shù)組的某一個(gè)位置沒(méi)有任何值。比如筒愚,Array構(gòu)造函數(shù)返回的數(shù)組都是空位赴蝇。

Array(3) // [, , ,]

ES5 對(duì)空位的處理,已經(jīng)很不一致了巢掺,大多數(shù)情況下會(huì)忽略空位句伶。

forEach(), filter(), reduce(), every() 和some()都會(huì)跳過(guò)空位。
map()會(huì)跳過(guò)空位陆淀,但會(huì)保留這個(gè)值
join()和toString()會(huì)將空位視為undefined考余,而undefined和null會(huì)被處理成空字符串。


// forEach方法
orEach [,'a'].forEach((x,i) => console.log(i)); // 1

// filter方法
['a',,'b'].filter(x => true) // ['a','b']

// every方法
[,'a'].every(x => x==='a') // true

// reduce方法
[1,,2].reduce((x,y) => x+y) // 3

// some方法
[,'a'].some(x => x !== 'a') // false

// map方法
[,'a'].map(x => 1) // [,1]

// join方法
[,'a',undefined,null].join('#') // "#a##"

// toString方法
[,'a',undefined,null].toString() // ",a,,"
ES6 則是明確將空位轉(zhuǎn)為undefined轧苫。

Array.from方法會(huì)將數(shù)組的空位楚堤,轉(zhuǎn)為undefined,也就是說(shuō)含懊,這個(gè)方法不會(huì)忽略空位身冬。

Array.from(['a',,'b'])
// [ "a", undefined, "b" ]
擴(kuò)展運(yùn)算符(...)也會(huì)將空位轉(zhuǎn)為undefined。

[...['a',,'b']]
// [ "a", undefined, "b" ]
copyWithin()會(huì)連空位一起拷貝岔乔。

[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
fill()會(huì)將空位視為正常的數(shù)組位置酥筝。

new Array(3).fill('a') // ["a","a","a"]
for...of循環(huán)也會(huì)遍歷空位。

let arr = [, ,];
for (let i of arr) {
  console.log(1);
}
// 1
// 1
上面代碼中雏门,數(shù)組arr有兩個(gè)空位嘿歌,for...of并沒(méi)有忽略它們。如果改成map方法遍歷茁影,空位是會(huì)跳過(guò)的宙帝。

entries()、keys()募闲、values()茄唐、find()和findIndex()會(huì)將空位處理成undefined。

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0

由于空位的處理規(guī)則非常不統(tǒng)一蝇更,所以建議避免出現(xiàn)空位沪编。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市年扩,隨后出現(xiàn)的幾起案子蚁廓,更是在濱河造成了極大的恐慌,老刑警劉巖厨幻,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件相嵌,死亡現(xiàn)場(chǎng)離奇詭異腿时,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)饭宾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)批糟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人看铆,你說(shuō)我怎么就攤上這事徽鼎。” “怎么了弹惦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵否淤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我棠隐,道長(zhǎng)石抡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任助泽,我火速辦了婚禮啰扛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嗡贺。我一直安慰自己隐解,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布暑刃。 她就那樣靜靜地躺著,像睡著了一般膜眠。 火紅的嫁衣襯著肌膚如雪岩臣。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天宵膨,我揣著相機(jī)與錄音架谎,去河邊找鬼。 笑死辟躏,一個(gè)胖子當(dāng)著我的面吹牛谷扣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捎琐,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼会涎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了瑞凑?” 一聲冷哼從身側(cè)響起末秃,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎籽御,沒(méi)想到半個(gè)月后练慕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體惰匙,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年铃将,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了项鬼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劲阎,死狀恐怖绘盟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哪工,我是刑警寧澤奥此,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站雁比,受9級(jí)特大地震影響稚虎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜偎捎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一蠢终、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茴她,春花似錦寻拂、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至己沛,卻和暖如春慌核,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背申尼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工垮卓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人师幕。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓粟按,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親霹粥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灭将,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 函數(shù)參數(shù)的默認(rèn)值 基本用法 在ES6之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值后控,只能采用變通的方法宗侦。 上面代碼檢查函數(shù)l...
    陳老板_閱讀 449評(píng)論 0 1
  • 函數(shù)參數(shù)的默認(rèn)值 基本用法 在ES6之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值忆蚀,只能采用變通的方法矾利。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,384評(píng)論 0 1
  • 1.函數(shù)參數(shù)的默認(rèn)值 (1).基本用法 在ES6之前姑裂,不能直接為函數(shù)的參數(shù)指定默認(rèn)值,只能采用變通的方法男旗。
    趙然228閱讀 688評(píng)論 0 0
  • 近日察皇,北京市教委發(fā)布了新中考改革方案茴厉。從2018年開(kāi)始,北京市中考科目將分為必考與選考科目什荣,學(xué)生較為擅長(zhǎng)的科目可以...
    搖錢(qián)草閱讀 238評(píng)論 0 1
  • RunLoop 的概念一般來(lái)講矾缓,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完成后線程就會(huì)退出稻爬。如果我們需要一個(gè)機(jī)制嗜闻,讓線程...
    南方_H閱讀 157評(píng)論 0 1