JavaScript 类数组
类数组
1. 可以利用属性名模拟数组的特性
2. 可以动态的增长length属性
3. 如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
一、arguments是类数组
类数组的意思是长的像数组,也可以拿它当数组用,但它就不是数组
我们接触过一个类数组,arguments实参列表就是类数组
function test () { console.log(arguments); } test(1,2,3,4,5,6); // Arguments(6) [1, 2, 3, 4, 5, 6, callee: ƒ, Symbol(Symbol.iterator): ƒ]
其实arguments不是数组,看着像数组,但数组有的方法arguments全没有,比如push一个7进去
function test () { console.log(arguments); arguments.push(7); // Uncaught TypeError: arguments.push is not a function } test(1,2,3,4,5,6);
系统报错 arguments.push is not a function 意思是没有这个方法。
是数组就有push方法,说明arguments不是数组,那为什么arguments长得像数组呢?
我们管这种数组叫类数组,我们探究一下类数组是怎样构成的
二、类数组是怎样构成的
1、用对象模拟数组的特征
obj是一个对象,对象的属性可以是任何一种形式的,而且属性名可以用双引号(可以用也可以不用),下面obj对象属性名用双引号
var obj = { "0" : 'a', // 属性名"0"用双引号,当然直接写0也可以 "1" : 'b', "2" : 'c' } console.log(arr[0]); // a
obj[0] 对象obj这样就有点意思
arr[0] 和数组arr访问起来差不太多
var arr = ['a', 'b', 'c']; console.log(obj[0]); // a
2、可以动态的增长length属性
现在obj还是对象,我们改一改,给obj对象加个length属性,属性值是3,属性名都加双引号
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', "length" : 3 } console.log(obj); // {0: "a", 1: "b", 2: "c", length: 3}
再加一个push属性,push属性是Array.prototype.push上的方法
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', "length" : 3, "push" : Array.prototype.push } console.log(obj); // {0: "a", 1: "b", 2: "c", 3: "d", length: 4, push: ƒ}
这样一个类数组的基本形态就已经构建完了,然后怎么用呢?
obj对象有push方法,只不过拿的是别人的,obj.push( ) 现在obj调用push方法,push方法里面的this就是obj了
arr.push( "d" ) 增加一个d进去好使吗?
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', "length" : 3, "push" : Array.prototype.push } obj.push('d'); // push一个"d"进去 console.log(obj); // {0: "a", 1: "b", 2: "c", 3: "d", length: 4, push: ƒ}
1). 会发现返回结果加了一个 3 : d 不是我们写的
2). 并且length属性的值变成4了
这些都是一个对象不能具备的,为什么调用了一个push()方法,又加东西又变东西的?
这块我们探究一下,这样的就叫类数组
3、类数组必须有几个组成部分
1). 首先属性要为索引属性,
什么叫索引属性?数字( 第0位、第1位... )
2). 然后必须有length属性,最好加上push()方法
3). 如果不加push()方法也叫类数组,类数组最佳关注点就是要有length属性,其它的都好说,一定要有length没有length不叫类数组
这样的obj是对象,可以当做对象来用,只不过它用起来就跟数组一样,可这还不像数组,怎么样让它变成跟数组差不多呢?
4、再添加splice属性
再给obj对象加一个属性让它变的像数组splice()方法
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', "length" : 3, "push" : Array.prototype.push, "splice" : Array.prototype.splice // 再加一个splice方法 } console.log(obj); // Object(3) ["a", "b", "c", push: ƒ, splice: ƒ]
这就是一个定律了,
一旦给一个对象加上splice方法之后,这个对象自此之后,长的就完全是数组的样了
但是obj是不是对象呢?是对象
可不可以当数组一样用呢?可以当数组一样用
是对象可以当数组一样用,这就叫类数组,类数组有很多好处一会在说
三、强行让类数组调用push方法
如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
1、先看看Array.prototype.push 里面写的代码是什么?
不考虑多个参数的,就传一个target参数
Array.prototype.push = function(target){ this[this.length] = target; // 第一步:增加一个属性 this.length ++; // 第二部:length加加 }
对象obj有没有length属性,我们设置了length属性,对象有length就能经过一系列的处理了
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', "length" : 3, "push" : Array.prototype.push, "splice" : Array.prototype.splice } Array.prototype.push = function(target){ this[this.length] = target; this.length ++; } obj.push('d'); console.log(obj);
push方法
1). lenght位是第3位,应该是 d
2). 然后length加加,lenght变成4
2、阿里巴巴出的面试题
问对象obj打印出来是什么样?
var obj = { "2" : 'a', "3" : 'b', "length" : 2, "push" : Array.prototype.push } obj.push('c'); obj.push('d'); console.log(obj); // {2: "c", 3: "d", length: 4, push: ƒ} /*------------------------------------------------------------ 这种形式叫类数组,叫对象也行只不过换个名。也没给splice属性,没让它长的那么像。push进去两个字母,最后obj是什么样的? 这是错的 obj = { "2" : 'a', "3" : 'b', "4" : 'c', "5" : 'd', "length" : 4, } 这也是错的 obj = { "2" : 'a', "3" : 'c', "4" : 'd', } 看push方法内部原理 Array.prototype.push = function(target){ obj[obj.length] = target; // 第一步,增加一个属性 obj.length ++; // 第二部,length加1 } 分析push执行 1.因为obj.length=2,push"c",所以2 : c 2.然后length++变成3 3.再来push(d),d放3这,length++最后得4 这是对的 var obj = { "2" :"c", "3" : "d" "length" : 4 } ------------------------------------------------------------*/
这道题不知道push的内部原理,懂不了类数组。
类数组的关键点在length,length决定在那一位赋东西,
因为push方法里面读的是length,所以会把原来的"2:a"、"3 : b"覆盖掉的,走一下length就明白了。
重做一下这个道题,最后结果是什么?
var obj = { "1" : 'a', "2" : 'c', "3" : 'd', "length" : 3, "push" : Array.prototype.push } obj.push('b'); console.log(obj); // {1: "a", 2: "c", 3: "b", length: 4, push: ƒ}
这是类数组特殊的一个情况,我们学习怎么样往类数组里面push东西,是根据它length的改变而改变,
类数组有很多应用,可以把类数组当做一个数组来用,完全的没问题,而且类数组本身是对象,所以类数组具备数组和对象两种特性,它存储东西更强大有些
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', name : 'abc', age : 123, length : 3, // 类数组必须有lenght属性 push : Array.prototype.push, splice : Array.prototype.splice } console.log(obj); // 对象obj像数组一样 Object(3) ["a", "b", "c", name: "abc", age: 123, push: ƒ, splice: ƒ] console.log(obj.name); // abc console.log(obj.age); // 123 console.log(obj.length); // 有数组的长度 3
既能像数组一样用也能像对象一样用,怎么把所有属性全遍历出来?
"for in"循环
var obj = { "0" : 'a', "1" : 'b', "2" : 'c', name : 'abc', age : 123, length : 3, push : Array.prototype.push, splice : Array.prototype.splice } for(var prop in obj){ console.log(obj[prop]); } //循环结果: // a // b // c // abc // 123 // 3 // ƒ push() { [native code] } // ƒ splice() { [native code] }
类数组的好处就是把数组和对象的特性全拼到一起,但也有一点问题,它并不是所有的数组方法都能用,除非我们自己给添。
DOM方法所能生成的,所有类似于数组的东西全是类数组,
以后高级编程的时候全是类数组了,所以对类数组必须了解到这个深度,这已经是最深了,不用再深了。
四、数组作业
1、第一个作业,封装一个type方法
因为系统的typeof方法,返回结果不是很精准,比如null分辩不出来,null返回的是对象。
现在要完全分辩出来每一个要传进去的东西
typeof([]) 传进去数组,返回array
typeof({}) 传进去对象,返回object
typeof(function) 传进去function,返回function
typeof(new Number()) 传进去new Number()数字类型对象,
返回number object(number类型的包装类对象)
typeof(123) 传进去数字123,返回number
typeof(new Number()) 怎么样返回一个对象形式的数字?
Object.prototype.toString.call(new Number(123)); //返回"[object Number]" Object.prototype.toString.call(123); //call数字打印的也是"[object Number]"
call数字打印的也是"[object Number]",但是没关系,Number可以通过typeof过滤掉,然后过滤不掉的是对象。
对象在放到Object.prototype.toString.call()里面去,能分变出来是什么类型的对象,数组对象、对象对象、还是包装类对象。
2、第二个作业,数组去重
要求在原型链上编程。(unique独一无二的意思,这个人这个事独一无二,比如求婚可以这么说)
Array.prototype.unique = function(){ } var arr = [1,1,1,1,0,0,0,a,b,a,b]; arr.unique(); // 任意一个数组,数组调用完之后,返回的是 [1, 0, a, b]
这个数组去重非常重要。
提示,可以利用对象的一些特性来写数组去重,叫哈希(键值对的形式)的方法。利用对象是最快的,写完不会超过十行,代码非常少,其实很简单。