博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Array.prototype.slice及其他Array方法
阅读量:6471 次
发布时间:2019-06-23

本文共 6145 字,大约阅读时间需要 20 分钟。

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);// 1console.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);// falseconsole.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);// undefinedconsole.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 }

转载地址:http://gwjko.baihongyu.com/

你可能感兴趣的文章
MySQL日期 专题
查看>>
C#中禁止程序多开
查看>>
分布式缓存Redis使用以及原理
查看>>
[LeetCode] Number of 1 Bits 位操作
查看>>
练习二:结对练习
查看>>
JSON中JObject和JArray,JValue序列化(Linq)
查看>>
杂七杂八
查看>>
Activity竟然有两个onCreate方法,可别用错了
查看>>
Linux经常使用命令(十六) - whereis
查看>>
Tomcat
查看>>
插件编译 版本问题
查看>>
android中TextView的阴影设置
查看>>
core dump相关
查看>>
MySQL如何导出带日期格式的文件
查看>>
Linux五种IO模型
查看>>
Bootstrap技术: 模式对话框的使用
查看>>
小知识,用myeclipes找jar
查看>>
[LintCode] Longest Substring Without Repeating Characters
查看>>
in-list expansion
查看>>
设计原则(四):接口隔离原则
查看>>