每日分享

1、數組的目錄索引

Array

Array.length 是數組的長度,每一個新建的數組對象都會有 length 對象,能夠經過 Array.prototype 修改原型,不過數組的基本使用和操做不是今天的重點,咱們來看數組方法。數組

通常狀況下,數組方法在最後都會帶有一個 thisArg 的參數,這個參數會指定內部 this 的指向。若是參數中有回掉函數,這個回掉函數通常接受 3 個參數,value、index 和 array,分別表明當前傳入的值,當前傳入值所在的索引和當前的處理的數組。app

目錄索引:函數

concat
every
filter
forEach
indexOf
join
lastIndexOf
map
reduce
reduceRight
push
pop
unshift
shift
reverse
slice
splice
some
sort
toString
copyWithin
find
findIndex
fill
keys
entries
includes測試

concat

這個方法能夠用於數組的拼接,參數是一個或多個數組,返回的結果是拼接數組。MDN array.concatthis

concat 方法將建立一個新數組,而後將調用它的對象(this 指向的對象,即原數組)中的元素以及全部參數中的數組類型的參數中的元素以及非數組類型的參數自己按照順序放入這個新數組,並返回該數組。concat 方法並不修改原數組和參數數組,並且對非數組對象一樣有效果。spa

  1. 返回拼接的新數組;prototype

  2. 不修改原數組和參數數組;code

  3. 參數能夠是非數組。對象

var a1 = [1, 2, 3],
  a2 = [4, 5, 6],
  a3 = [7, 8, 9];
var newarr = a1.concat(a2, a3);
newarr //[1, 2, 3, 4, 5, 6, 7, 8, 9]
a1 //[1, 2, 3]
newarr = a1.concat(4, a3);//[1, 2, 3, 4, 7, 8, 9]
newarr = a1.concat('hello');//[1, 2, 3, "hello"]

 

every

every() 方法測試數組的全部元素是否都經過了指定函數的測試。MDN array.everyblog

arr.every(callback) 會對每個元素都執行 callback 方法,直到 callback 返回 false。有時候 every 方法會和 forEach 方法相比較,由於 forEach 沒法中止,而 every 方法返回 flase 時能夠中途中止。

  1. 若所有經過測試,函數返回值 true,中途退出,返回 false;

  2. 不對原數組產生影響。

function isBigEnough(element, index, array) {
  console.log(index);
  return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
// 0
// 1
// passed is false
passed = [12, 54, 18, 130, 44].every(isBigEnough);
// 0 1 2 3 4
// passed is true

 

filter

filter() 方法使用指定的函數測試全部元素,並建立一個包含全部經過測試的元素的新數組。MDN array.filter

其實這個方法就是一個過濾方法,前面那個 every 方法,只判斷不過濾,filter 會過濾掉一些不符合條件的,並返回新數組。

  1. 返回一個知足過濾條件的新數組;

  2. 不會改變原數組。

function isBigEnough(element, index, array) {
  return (element >= 10);
}
var a1 = [19, 22, 6, 2, 44];
var a2 = a1.filter(isBigEnough);
a1 //[19, 22, 6, 2, 44]
a2 //[19, 22, 44]

 

forEach

forEach() 方法對數組的每一個元素執行一次提供的函數(回調函數)。MDN array.forEach

  1. 函數沒有返回值,即 underfined;

  2. 不對原數組產生影響。

function logArrayElements(element, index, array) {
    console.log("a[" + index + "] = " + element);
}

// 注意索引2被跳過了,由於在數組的這個位置沒有項
var result = [2, 5, 9].forEach(logArrayElements);
// a[0] = 2
// a[1] = 5
// a[2] = 9
result //underfined

 

indexOf

indexOf()方法返回給定元素能找在數組中找到的第一個索引值,不然返回-1。MDN array.indexOf

  1. 返回值是找到元素的索引值或 -1;

  2. 不對原數組產生影響。

var array = [1, 2, 5];
array.indexOf(5); // 2
array.indexOf(7); // -1

 

join

join() 方法將數組中的全部元素鏈接成一個字符串。MDN array.join

其實,對於 join 想到的第一個是字符串的 split 操做,這兩個常常搭配用來處理字符串。

  1. 返回拼接的字符串;

  2. 不對原數組產生影響。

var a1 = [1, 2, 3];
var a2 = a1.join();
a1 //[1, 2, 3]
a2 //"1,2,3"
a2 = a1.join("");//"123"
a2 = a1.join("-");//"1-2-3"

 

lastIndexOf

lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或變量)在數組中的最後一個的索引,若是不存在則返回 -1。從數組的後面向前查找,從 fromIndex 處開始。MDN array.lastIndexOf

其實這個就是 indexOf 的翻版。

  1. 返回找到的第一個元素的索引;

  2. 不對原數組產生影響。

var array = [2, 5, 9, 2];
var index = array.lastIndexOf(2);
// index is 3
index = array.lastIndexOf(7);
// index is -1
index = array.lastIndexOf(2, 3);
// index is 3
index = array.lastIndexOf(2, 2);

 

map

map() 方法返回一個由原數組中的每一個元素調用一個指定方法後的返回值組成的新數組。MDN array.map

map reduce 這兩個函數在處理數組上一直都是一把手,帶來很大的便捷性。

  1. 返回一個通過回掉函數處理的新數組;

  2. 不對原數組產生影響。

var a1 = [1, 4, 9];
var a2 = a1.map(Math.sqrt);
a1 //[1, 4, 9]
a2 //[1, 2, 3]

 

reduce

reduce() 方法接收一個函數做爲累加器(accumulator),數組中的每一個值(從左到右)開始合併,最終爲一個值。MDN array.reduce

reduce 是一個合併的過程,從左到右,直到把全部元素合併到一塊兒,並返回最終的結果。它接受兩個參數,第一個參數是一個回掉函數,第二個參數是一個初始值,表示處理第一個元素時的前一個值。這個回掉函數接受四個參數,依次是 accumulator(上次處理的結果),currentValue(當前元素的值),index(當前元素索引),array(調用 reduce 的數組)。

  1. 返回最終合併的結果,即回掉函數的輸出,能夠爲字符串,對象,數組等任意結果;

  2. 不對原數組產生影響。

var getAdd = (pre, cur) => pre + cur;
var a1 = [1, 2, 3];
var a2 = a1.reduce(getAdd, 0);
a1 //[1, 2, 3]
a2 //6

 

reduceRight

reduceRight() 方法接受一個函數做爲累加器(accumulator),讓每一個值(從右到左,亦即從尾到頭)縮減爲一個值。(與 reduce() 的執行方向相反)MDN array.reduceRight

var toStr = (pre, cur) => '' + pre + cur;
var a1 = [1, 2, 3];
var a2 = a1.reduce(toStr, '');
a2 //"123"
a2 = a1.reduceRight(toStr, '');
a2 //"321"

 

push

push() 方法添加一個或多個元素到數組的末尾,並返回數組新的長度(length 屬性值)。MDN array.push

若是把數組看成棧,push pop 操做是棧進和出,而每每不少人會忽略函數執行後的返回值。

  1. 返回 push 操做執行以後數組的長度;

  2. 確定改變。

var a1 = [1, 2, 3];
var a2 = a1.push(4);
a1 //[1, 2, 3, 4]
a2 //4

 

pop

pop() 方法刪除一個數組中的最後的一個元素,而且返回這個元素。MDN array.pop

  1. 返回刪除的這個元素;

  2. 確定改變。

var a1 = [1, 2, 3];
var a2 = a1.pop();
a1 //[1, 2]
a2 //3

 

unshift

unshift() 方法在數組的開頭添加一個或者多個元素,並返回數組新的 length 值。MDN array.unshift

  1. 返回 length 值;

  2. 確定改變。

var a1 = [1, 2, 3];
var a2 = a1.unshift(4);
a1 //[4, 1, 2, 3]
a2 //4

 

shift

shift() 方法刪除數組的 第一個 元素,並返回這個元素。該方法會改變數組的長度。MDN array.shift

shift 方法和 push 方法能夠組成一個隊列的操做啦。

  1. 返回刪除的這個元素;

  2. 確定改變。

reverse

reverse() 方法顛倒數組中元素的位置。第一個元素會成爲最後一個,最後一個會成爲第一個。MDN array.reverse

  1. 函數返回值是修改了的原數組;

  2. 原數組會修改。

var a1 = [1, 2, 3];
var a2 = a1.reverse();
a1 //[3, 2, 1]
a1 === a2; //true

 

slice

slice() 方法會淺複製(shallow copy)數組的一部分到一個新的數組,並返回這個新數組。MDN array.slice

slice 的參數包括拷貝的初識位置,結束位置(左閉右開),與 splice 有區別。因爲不會改變原數組,這個數組能夠用於前拷貝,好比常常看別人使用:arr.slice(0),表示拷貝數組。

  1. 返回淺拷貝後的新數組;

  2. 不會改變原數組。

var a1 = [1, 2, 3, 4, 5];
var a2 = a1.slice(1, 3);
a1 //[1, 2, 3, 4, 5]
a2 //[2, 3]

 

splice

splice() 方法用新元素替換舊元素,以此修改數組的內容。MDN array.splice

如其名,分割,會修改原數組的內容,返回一個新數組,並且它的參數也比較多,第一個參數表示初始位置,第二個參數表示分割長度,第三個參數及之後表示分割後在分割處添加新元素。

  1. 返回分割的元素組成的數組;

  2. 會對數組進行修改,原數組會減去分割數組。

var a1 = [1, 2, 3, 4];
var a2 = a1.splice(1, 2);
a1 //[1, 4]
a2 //[2, 3]
a1 = [1, 2, 3, 4];
a2 = a1.splice(1, 2, 5, 6);
a1 //[1, 5, 6, 4]

 

some

some() 方法測試數組中的某些元素是否經過了指定函數的測試。MDN array.some

sort

sort() 方法對數組的元素作原地的排序,並返回這個數組。 sort 排序多是不穩定的。默認按照字符串的Unicode碼位點(code point)排序。MDN array.sort

sort 函數用於排序,比較經常使用,若沒有制定排序函數,則按照 unicode 位點進行排序,並且數字會被轉成字符串,因此 ‘123’ 要排在 ‘11’ 的後面。

咱們會用 sort 作一些有意思的排序,好比漢字按照拼音排序。

  1. 返回排序後的原數組;

  2. 會對數組進行修改。

var big = function(a, b){
  return a - b;
}
var a1 = [2, 4, 77, 1];
var a2 = a1.sort(big);
a1 //[1, 2, 4, 77]
a1 === a2; //true

 

localeCompare 能夠對漢字進行排序,當同時出現漢字和字母的時候會有 bug:

var sort_py = function(a, b){
  return a.localeCompare(b);
}
var a1 = ["北京", "上海", "南京", "合肥"];
a1.sort(sort_py);
//["北京", "合肥", "南京", "上海"]

 

toString

toString() 返回一個字符串,表示指定的數組及其元素。MDN array.toString

顯然,這個方法和 join 方法比較一下。

  1. 返回拼接的字符串;

  2. 不會改變原數組。

var a1 = [1, 2, 3];
var a2 = a1.toString();
a2 //"1,2,3"

 

ES6 中新添的數組方法

上面的這些方法都是 ES5 的,來看看 ES6 添加了哪些新方法。

copyWithin

copyWithin() 方法會淺拷貝數組的部分元素到同一數組的不一樣位置,且不改變數組的大小,返回該數組。MDN array.copyWithin

接受三個參數,分別是要拷貝到的位置 target,拷貝開始位置 start 和結束位置 end。

  1. 返回修改了的原數組;

  2. 會對數組進行修改,且是淺拷貝;

  3. 參數可負,負值時倒推,且 end 爲空表示數組長度。

var a1 = [1, 2, 3, 4, 5];
var a2 = a1.copyWithin(0, 2, 4);
a1 //[3, 4, 3, 4, 5]
a2 //[3, 4, 3, 4, 5]
a1 === a2; //true

 

find

若是數組中某個元素知足測試條件,find() 方法就會返回知足條件的第一個元素,若是沒有知足條件的元素,則返回 undefined。MDN array.find

  1. 返回找到的那個元素,若未找到,返回 underfined

  2. 不對原數組產生影響。

function isBigEnough(element, index, array) {
  return (element >= 10);
}
var a1 = [8, 18, 14];
var num = a1.find(isBigEnough); //18

 

findIndex

findIndex()方法用來查找數組中某指定元素的索引, 若是找不到指定的元素, 則返回 -1。MDN array.findIndex

這個方法能夠參考 find 方法,只是返回值是元素的索引而非元素自己。

fill

使用 fill() 方法,能夠將一個數組中指定區間的全部元素的值, 都替換成或者說填充成爲某個固定的值。MDN array.fill

fill 方法接受三個參數,第一個參數 value 表示要填充到值,後面兩個 start 和 end 表示開始和結束位置,可選,且左閉右開。

  1. 函數返回值是修改了的原數組;

  2. 可對數組產生影響。

var a1 = [1, 2, 3, 4, 5];
var a2 = a1.fill(6, 1, 4);
a1 //[1, 6, 6, 6, 5]
a2 //[1, 6, 6, 6, 5]
a1 === a2; //true

 

keys

數組的 keys() 方法返回一個數組索引的迭代器。MDN array.keys

這個方法會返回一個數組索引的迭代器,迭代器在 ES6 中有特殊的用途。

  1. 函數返回一個迭代器對象;

  2. 不會改變原數組。

var arr = ["a", "b", "c"];
var iterator = arr.keys();

console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

 

entries

entries() 方法返回一個 Array Iterator 對象,該對象包含數組中每個索引的鍵值對。MDN array.entries

var arr = ["a", "b", "c"];
var eArr = arr.entries();

console.log(eArr.next().value); // [0, "a"]
console.log(eArr.next().value); // [1, "b"]
console.log(eArr.next().value); // [2, "c"]

 

includes

includes() 方法用來判斷當前數組是否包含某指定的值,若是是,則返回 true,不然返回 false。MDN array.includes

該函數接受兩個參數,第二個參數表示開始查找位置,起始位置爲 0。這個方法與 indexOf 方法最大的區別不只在於返回值一個是索引,一個是布爾值,indexOf 方法使用的是 === 來判斷,沒法判斷 NaN 狀況,而 includes 能夠判斷。

  1. 返回 true 或 false;

  2. 不會改變原數組。

var a1 = [1, NaN];
a1.indexOf(NaN);//-1
a1.includes(NaN);//true

2、原型

一. 原型與構造函數

  Js全部的函數都有一個prototype屬性,這個屬性引用了一個對象,即原型對象,也簡稱原型。這個函數包括構造函數和普通函數,咱們講的更可能是構造函數的原型,可是也不可否定普通函數也有原型。譬如普通函數:

function F(){
  ;
}
alert(F.prototype instanceof Object) //true

 

  構造函數,也即構造對象。首先了解下經過構造函數實例化對象的過程。

function A(x){
  this.x=x;
}
var obj=new A(1);

實例化obj對象有三步:

  1. 建立obj對象:obj=new Object();

  2. 將obj的內部__proto__指向構造他的函數A的prototype,同時,obj.constructor===A.prototype.constructor(這個是永遠成立的,即便A.prototype再也不指向原來的A原型,也就是說:類的實例對象的constructor屬性永遠指向"構造函數"的prototype.constructor),從而使得obj.constructor.prototype指向A.prototype(obj.constructor.prototype===A.prototype,當A.prototype改變時則不成立,下文有遇到)。obj.constructor.prototype與的內部_proto_是兩碼事,實例化對象時用的是_proto_,obj是沒有prototype屬性的,可是有內部的__proto__,經過__proto__來取得原型鏈上的原型屬性和原型方法,FireFox公開了__proto__,能夠在FireFox中alert(obj.__proto__);

  3. 將obj做爲this去調用構造函數A,從而設置成員(即對象屬性和對象方法)並初始化。

  當這3步完成,這個obj對象就與構造函數A再無聯繫,這個時候即便構造函數A再加任何成員,都再也不影響已經實例化的obj對象了。此時,obj對象具備了x屬性,同時具備了構造函數A的原型對象的全部成員,固然,此時該原型對象是沒有成員的。

  原型對象初始是空的,也就是沒有一個成員(即原型屬性和原型方法)。能夠經過以下方法驗證原型對象具備多少成員。

 

var num=0;
for(o in A.prototype) {
  alert(o);//alert出原型屬性名字
  num++;
}
alert("member: " + num);//alert出原型全部成員個數。

 

  可是,一旦定義了原型屬性或原型方法,則全部經過該構造函數實例化出來的全部對象,都繼承了這些原型屬性和原型方法,這是經過內部的_proto_鏈來實現的。

  譬如

  A.prototype.say=function(){alert("Hi")};

  那全部的A的對象都具備了say方法,這個原型對象的say方法是惟一的副本給你們共享的,而不是每個對象都有關於say方法的一個副本。

 

二. 原型與繼承

  首先,看個簡單的繼承實現。

複製代碼
1 function A(x){
2   this.x=x;
3 }
4  function B(x,y){
5   this.tmpObj=A;
6   this.tmpObj(x);
7   delete this.tmpObj;
8   this.y=y;
9 }
複製代碼

 

  第五、六、7行:建立臨時屬性tmpObj引用構造函數A,而後在B內部執行,執行完後刪除。當在B內部執行了this.x=x後(這裏的this是B的對象),B固然就擁有了x屬性,固然B的x屬性和A的x屬性二者是獨立,因此並不能算嚴格的繼承。第五、六、7行有更簡單的實現,就是經過call(apply)方法:A.call(this,x);

這兩種方法都有將this傳遞到A的執行裏,this指向的是B的對象,這就是爲何不直接A(x)的緣由。這種繼承方式便是類繼承(js沒有類,這裏只是指構造函數),雖然繼承了A構造對象的全部屬性方法,可是不能繼承A的原型對象的成員。而要實現這個目的,就是在此基礎上再添加原型繼承。

 

  經過下面的例子,就能很深刻地瞭解原型,以及原型參與實現的完美繼承。(本文核心在此^_^)

複製代碼
1 function A(x){
2   this.x = x;
3 }
4 A.prototype.a = "a";
5 function B(x,y){
6   this.y = y;
7   A.call(this,x);
8 }
9 B.prototype.b1 = function(){
10   alert("b1");
11 }
12 B.prototype = new A();
13 B.prototype.b2 = function(){
14   alert("b2");
15 }
16 B.prototype.constructor = B;
17 var obj = new B(1,3);
複製代碼

  這個例子講的就是B繼承A。第7行類繼承:A.call(this.x);上面已講過。實現原型繼承的是第12行:B.prototype = new A();

  就是說把B的原型指向了A的1個實例對象,這個實例對象具備x屬性,爲undefined,還具備a屬性,值爲"a"。因此B原型也具備了這2個屬性(或者說,B和A創建了原型鏈,B是A的下級)。而由於方纔的類繼承,B的實例對象也具備了x屬性,也就是說obj對象有2個同名的x屬性,此時原型屬性x要讓位於實例對象屬性x,因此obj.x是1,而非undefined。第13行又定義了原型方法b2,因此B原型也具備了b2。雖然第9~11行設置了原型方法b1,可是你會發現第12行執行後,B原型再也不具備b1方法,也就是obj.b1是undefined。由於第12行使得B原型指向改變,原來具備b1的原型對象被拋棄,天然就沒有b1了。

 

  第12行執行完後,B原型(B.prototype)指向了A的實例對象,而A的實例對象的構造器是構造函數A,因此B.prototype.constructor就是構造對象A了(換句話說,A構造了B的原型)。

alert(B.prototype.constructor)出來後就是"function A(x){...}" 。一樣地,obj.constructor也是A構造對象,alert(obj.constructor)出來後就是"function A(x){...}" ,也就是說B.prototype.constructor===obj.constructor(true),可是B.prototype===obj.constructor.prototype(false),由於前者是B的原型,具備成員:x,a,b2,後者是A的原型,具備成員:a。如何修正這個問題呢,就在第16行,將B原型的構造器從新指向了B構造函數,那麼B.prototype===obj.constructor.prototype(true),都具備成員:x,a,b2。

 

  若是沒有第16行,那是否是obj = new B(1,3)會去調用A構造函數實例化呢?答案是否認的,你會發現obj.y=3,因此仍然是調用的B構造函數實例化的。雖然obj.constructor===A(true),可是對於new B()的行爲來講,執行了上面所說的經過構造函數建立實例對象的3個步驟,第一步,建立空對象;第二步,obj.__proto__ === B.prototype,B.prototype是具備x,a,b2成員的,obj.constructor指向了B.prototype.constructor,即構造函數A;第三步,調用的構造函數B去設置和初始化成員,具備了屬性x,y。雖然不加16行不影響obj的屬性,但如上一段說,卻影響obj.constructor和obj.constructor.prototype。因此在使用了原型繼承後,要進行修正的操做。

  關於第十二、16行,總言之,第12行使得B原型繼承了A的原型對象的全部成員,可是也使得B的實例對象的構造器的原型指向了A原型,因此要經過第16行修正這個缺陷。

相關文章
相關標籤/搜索