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
,咱們還能在HTMLCollection
或NodeList
身上使用。那麼到底什麼算是類數組呢?瀏覽器
有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
對象還有不少其餘的方法,這些方法是否是也能用到對象身上呢?
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的數組。
這個方法比較好玩:
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
, shift
和unshift
這三個方法的行爲應該能想象得出來,就再也不贅述了。
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
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
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
同樣會接着造成一個類數組的對象.
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
產生影響,而且是咱們預計的樣子
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 }
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 }
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 }
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 }
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 }