资讯专栏INFORMATION COLUMN

Array.prototype.slice及其他Array方法

yibinnn / 2744人阅读

摘要:方法真是一个有意思的东西,它可以改变函数调用时的值。所以前面的说法其实不对,所有的对象都可以被视为类数组,有的视为长度为的数组,没有的,视为长度为的数组。

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属性的对象。

比如:

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

那如果没有length呢?

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

原来没有length属性的对象也会被转为数组,只不过认为它length=0而已。

那如果对象的属性没有按照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的数组。

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 }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82349.html

相关文章

  • underscore中的小技巧

    摘要:在阅读的过程中,发现了它的一些小技巧,对我们平时的编程很有用。事实上,在上也的确是可以被更改的而在或高版本的中,并不能更改全局的。但是,局部的仍然可以被改变。所以,建议在已知长度的情况下,使用第一种,而不知道长度的情况下,使用第二种。 在阅读 underscore 的过程中,发现了它的一些小技巧,对我们平时的编程很有用。在这里向大家介绍一二 void 0 代替 underfined 首...

    lifesimple 评论0 收藏0
  • JavaScript 特殊对象 Array-Like Objects 详解

    摘要:很简单,不是数组,但是有属性,且属性值为非负类型即可。至于属性的值,给出了一个上限值,其实是感谢同学指出,因为这是中能精确表示的最大数字。如何将函数的实际参数转换成数组 这篇文章拖了有两周,今天来跟大家聊聊 JavaScript 中一类特殊的对象 -> Array-Like Objects。 (本文节选自 underscore 源码解读系列文章,完整版请关注 https://githu...

    zhaofeihao 评论0 收藏0
  • JS中的call、apply、bind方法详解

    摘要:不能应用下的等方法。首先我们可以通过给目标函数指定作用域来简单实现方法保存,即调用方法的目标函数考虑到函数柯里化的情况,我们可以构建一个更加健壮的这次的方法可以绑定对象,也支持在绑定的时候传参。原因是,在中,多次是无效的。 bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。 apply、call 在 javascript 中,call 和 apply 都是...

    zombieda 评论0 收藏0
  • JS基础篇--call、apply、bind方法详解

    摘要:首先我们可以通过给目标函数指定作用域来简单实现方法保存,即调用方法的目标函数考虑到函数柯里化的情况,我们可以构建一个更加健壮的这次的方法可以绑定对象,也支持在绑定的时候传参。原因是,在中,多次是无效的。而则会立即执行函数。 bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。 apply、call 在 javascript 中,call 和 apply 都是...

    lastSeries 评论0 收藏0
  • JavaScript 类数组对象

    摘要:定义类数组对象的定义可以通过索引访问元素,并且拥有属性没有数组的其他方法,例如,,等。所以当后面的作用对象是一个类数组时,就会把这个类数组对象转换为了一个新的数组。 定义 JavaScript 类数组对象的定义: 可以通过索引访问元素,并且拥有 length 属性; 没有数组的其他方法,例如 push , forEach , indexOf 等。 举例说明 var foo = {...

    haobowd 评论0 收藏0

发表评论

0条评论

yibinnn

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<