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]); // aobj[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]这个数组去重非常重要。
提示,可以利用对象的一些特性来写数组去重,叫哈希(键值对的形式)的方法。利用对象是最快的,写完不会超过十行,代码非常少,其实很简单。
