Array.prototype.slice及其餘Array方法

call方法真是一個有意思的東西,它能夠改變函數調用時this的值。而咱們知道,在函數裏,this指向了調用這個函數的環境對象,好比一道經典面試題:面試

var num = 2;
var obj = {
  num: 1,
  show: function () {
    console.log(this.num)
  }
};
var foo = obj.show;
obj.show();/* 顯示1;show是被obj調用的,因此this指向obj */
foo();/* 顯示2;至關於global.foo(),因此this指向global,若是在瀏覽器裏global就是window */

換句話說,若是一個對象obj上有方法foo,你能夠經過obj.foo()調用;若是沒有obj上沒有方法foo,obj.foo()是會報錯的,可是,使用foo.call(obj),能夠強行達到obj.foo()的效果,好比:數組

function foo(){
    console.log(this.num);
}
var obj = {
    num: 1
}
foo.call(obj);// 1

Array.prototype.slice.call的用處就是這樣,能夠在array-like(類數組,就是長得像數組,但不是數組)的對象上強行使用slice方法,好比:Array.prototype.slice.call(arguments)就是把arguments對象轉化爲數組。固然,除了arguments,咱們還能在HTMLCollectionNodeList身上使用。那麼到底什麼算是類數組呢?瀏覽器

有length屬性的對象。函數

好比:this

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason',
  length: 3
}
var arr = [].slice.call(obj1);
console.log('arr: ', arr);/* [ 'Tom', 'Jack', 'Jason' ] */

那若是沒有length呢?prototype

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason'
}
var arr = [].slice.call(obj1);//* [] */

原來沒有length屬性的對象也會被轉爲數組,只不過認爲它length=0而已。code

那若是對象的屬性沒有按照0-n順序乖乖排好呢?對象

var obj1 = {
  1: 'Tom',
  3: 'Jack',
  5: 'Jason',
  7: 'Dave',
  foo: 'bar',
  length: 6
}
var arr = [].slice.call(obj1);/* [ , 'Tom', , 'Jack', , 'Jason' ] */

原來轉化的時候,會以length爲基礎,生成一個長度爲length的數組,obj的屬性是數組的有效index的話,就會把對應值填入到對應位置,其餘的位置找不到值,就會填入undefined字符串

因此前面的說法其實不對,全部的對象均可以被視爲類數組,有length的視爲長度爲length的數組,沒有的,視爲長度爲0的數組。io

length屬性爲基礎

這句話很重要。

另外,call方法的參數若是是原始值類型,會傳入它的自動包裝對象

var arr = [].slice.call('hello');

等價於:

var arr = [].slice.call(new String('hello'));/* [ 'h', 'e', 'l', 'l', 'o' ] */

由於new String('hello')就是
{
    0: "h",
    1: "e",
    2: "l",
    3: "l",
    4: "o",
    length: 5
}

以上就是Array.prototype.slice.call的一些細節,那麼除了slice以外,Array對象還有不少其餘的方法,這些方法是否是也能用到對象身上呢?

Array.prototype.join

join方法是把數組轉化爲字符串的方法,具體表現再也不贅述,看兩個例子:

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason',
  length: 6
}
var arr = [].join.call(obj1, '-');// Tom-Jack-Jason---

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason',
}
var arr = [].join.call(obj1, '-'); // ''

仍是那句話,length爲基礎,沒有length屬性的,視爲長度爲0的數組。

Array.prototype.push

這個方法比較好玩:

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason',
  length: 6
}
var arr = [].push.call(obj1, 'Dave');
console.log('arr: ', arr);// 7,由於push方法返回的是push以後array的操做數
console.log('obj: ', obj1);// { '0': 'Tom', '1': 'Jack', '2': 'Jason', '6': 'Dave', length: 7 }

能夠看到obj1裏新增屬性6,值爲'Dave',而且length也更新爲7,這說明調用push時會對原有對象進行修改。
咱們能夠利用這個特性,好比當咱們須要一個obj1的類數組副本時:

var obj = {
  foo: 'foo',
  bar: 'bar',
  cei: 'cei'
};
var copy = {};
for (var i in obj) {
  [].push.call(copy, obj[i])
}
console.log(copy);// { '0': 'foo', '1': 'bar', '2': 'cei', length: 3 }

若是,沒有傳入length呢?

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason'
}
var arr = [].push.call(obj1, 'Dave');
console.log('arr: ', arr);// 1
console.log('obj: ', obj1);// { '0': 'Dave', '1': 'Jack', '2': 'Jason', length: 1 }

這裏的行爲有些詭異,不過也更好地解釋了以length爲基礎這句話:
沒有length的時候,認爲數組長度爲0,而且會對obj進行修改,把屬性0的值改成Dave.

那麼,會觸類旁通的話,對於pop, shiftunshift這三個方法的行爲應該能想象得出來,就再也不贅述了。

Array.prototype.reverse

var obj1 = {
  0: 'Tom',
  1: 'Jack',
  2: 'Jason',
  length: 6
}
var arr = [].reverse.call(obj1);
console.log('arr: ', arr);// { '3': 'Jason', '4': 'Jack', '5': 'Tom', length: 6 }
console.log('obj: ', obj1);// { '3': 'Jason', '4': 'Jack', '5': 'Tom', length: 6 }

reverse的話,arr === obj1

Array.prototype.sort

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].sort.call(obj1);
console.log('arr: ', arr);// { '0': 'a', '1': 'b', '2': 'c', length: 6 }
console.log('obj: ', obj1);// { '0': 'a', '1': 'b', '2': 'c', length: 6 }

sort也同樣,arr === obj1

Array.prototype.concat

concat的表現就不是咱們意料之中的了:

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}

var add = {
  foo: 'foo',
  bar: 'bar'
}
var arr = [].concat.call(obj1, add);
console.log('arr: ', arr);// [ { '0': 'c', '1': 'b', '2': 'a', length: 6 }, 'foo', 'bar' ]
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }
var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].concat.call(obj1, 'foo', 'bar');
console.log('arr: ', arr);// [ { '0': 'c', '1': 'b', '2': 'a', length: 6 }, 'foo', 'bar' ]
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }

能夠看到obj1並不會改變,不會像push同樣會接着造成一個類數組的對象.

Array.prototype.splice

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].splice.call(obj1, 0, 1);
console.log('arr: ', arr);// [ 'c' ]
console.log('obj: ', obj1);// { '0': 'b', '1': 'a', length: 5 }
var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].splice.call(obj1, 1, 0, 'foo','bar');
console.log('arr: ', arr);// []
console.log('obj: ', obj1);// { '0': 'c', '1': 'foo', '2': 'bar', '3': 'b', '4': 'a', length: 8 }
var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].splice.call(obj1, 1, 1, 'foo','bar');
console.log('arr: ', arr);// [ 'b' ]
console.log('obj: ', obj1);// { '0': 'c', '1': 'foo', '2': 'bar', '3': 'a', length: 7 }

splice的行爲迴歸了,它如今對obj1產生影響,而且是咱們預計的樣子

Array.prototype.every

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].every.call(obj1, function (val) {
  return val === 'a' || val === 'c'
});
console.log('arr: ', arr);// false
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }

Array.prototype.filter

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].filter.call(obj1, function (val) {
  return val === 'a' || val === 'c'
});
console.log('arr: ', arr);// [ 'c', 'a' ]
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }

Array.prototype.forEach

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].forEach.call(obj1, function (val) {
  return val + ' add';
});
console.log('arr: ', arr);// undefined
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }

Array.prototype.map

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].map.call(obj1, function (val) {
  return val + ' add';
});
console.log('arr: ', arr);// [ 'c add', 'b add', 'a add', , ,  ]
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }

Array.prototype.reduce

var obj1 = {
  0: 'c',
  1: 'b',
  2: 'a',
  length: 6
}
var arr = [].reduce.call(obj1, function (pre, cur) {
  return pre + ' ' + cur
});
console.log('arr: ', arr);// 'c b a'
console.log('obj: ', obj1);// { '0': 'c', '1': 'b', '2': 'a', length: 6 }
相關文章
相關標籤/搜索