专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

第21节 检测数组、类数组及多维数组-Web前端开发之Javascript

ins518 2024-09-30 21:28:21 技术文章 17 ℃ 0 评论

本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。

检测数组:

数组是具有自己特殊行为的对象,因此,确定某个对象是不是数组就很重要;

typeof只能检测其是object的类型,而不能确定是否为Array类型;

对于一个页面或者一个全局作用域而言,可以使用instanceof操作就可以得到检测结果,如:

var colors=["red","blue","green"];
if(colors instanceof Array){
    console.log(colors instanceof Array);
}

或者检查对象的构造函数;对数组而言,该属性值总是“Array”;

var arr = [];
console.log(arr.__proto__.constructor === Array);
console.log(arr.constructor === Array);

注:实际上就是判断它的原型;

但instanceof与constructor有问题:如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,分别具有自己的全局对象,每个全局对象都有自己的Array构造函数,因此一个框架窗口中的对象不可能是另一个框架窗口的实例;比如:如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中创建的数组分别具有各自不同的构造函数,它们就不是同一类对象;如:

var frame = document.createElement('iframe');
document.body.appendChild(frame);
var FrameArray = window.frames[0].Array;  // 获取框架全局环境中的Array构造函数
var fa = new FrameArray();  // 在框架全局环境中创建数组
console.log(fa instanceof Array);   // false,在当前环境中判断fa是不是数组
// console.log(Array.isArray(fa));  // true

解决方案:使用Object.prototype上的原生toString()方法判断;如:Object.prototype.toString.call(arr);

该方法返回一个[object NativeConstructorName]格式的字符串;但该方法不能检测非原生构造函数的函数名,因此自定义的构造函数都将返回[object Object],如:

// 修改上例
console.log(Object.prototype.toString.call(fa) === "[object Array]");   // true
 
function Person(){}
var p = new Person();
console.log(Object.prototype.toString.call(p));  // [object Object]
// 或者:
<iframe src="a.html" name="myFrame"></iframe>
<script>
// a.html 中定义了 var aArr = [1,2];
window.onload = function(){
    var myFrame = window.frames["myFrame"];
    console.log(myFrame.aArr);
    console.log(myFrame.aArr instanceof Array);  // false
    console.log(Object.prototype.toString.call(myFrame.aArr) === "[object Array]"); // true
}
</script>

根据封装一个判断类型的函数:

console.log(Object.prototype.toString.call({}));  // [object Object]
console.log(Object.prototype.toString.call([]));  // [object Array]
console.log(Object.prototype.toString.call(new String()));  // [object String]
 
function getType(target){
    var types = {
        "[object Object]" : "Object",
        "[object Array]" : "Array",
        "[object Number]" : "Number",
        "[object Boolean]" : "Boolean",
        "[object String]" : "String",
    };
    if(target == null) return "null";
    if(typeof target === "object"){
        var toStr = Object.prototype.toString.call(target);
        return types[toStr];
    }else
        return typeof target;
}
console.log(getType({}));
console.log(getType([]));
console.log(getType(new Number()));
console.log(getType(new Date()));
console.log(getType(18));

在ES5中,新增了Array.isArray()静态方法,该方法的作用是确定某个值是不是数组,而不管它是在哪个全局环境中创建的;如:

var colors=["red","blue","green"];
console.log(Array.isArray(colors));

数组去重:

使用一个空对象作为临时数组不重复元素值的载体,即把数组不重复元素值保存为空对象属性,再把该属性名添加到一个返回的数组中;

最主要的原理:对象的属性名不可能重复,会覆盖,如:

var arr = [1,1,1,2,2,3,3,2,2,1];
var obj ={1:'a',2:'b'};
obj[1] = 'x';
obj[1] = 'y';   // 覆盖了上一条语句
console.log(obj);  // {1: "y", 2: "b"}
 
function getUnique(arr){
    var obj = {};
    var a = [];
    for(var i=0; i<arr.length; i++){
        if(!obj[arr[i]]){
            obj[arr[i]] = true;  // 随便赋值,但不能使用i或0或空,因为!obj[arr[i]]就是假值了
            console.log(arr[i]);
            a.push(arr[i]);
        }
    }
    return a;
}
console.log(getUnique(arr));

类数组对象:

ES数组的有一些特性是其他对象所没有的:

  • 当有新的元素添加到列表中时,自动更新length属性;
  • 设置length为一个较小值时将截断数组;
  • 从Array.prototype中继承一些方法;
  • 其类属性为Array;

这些特点让数组和常规的对象有明显的区别;通常来说,把一个拥有数值length属性和对应非负整数属性的对象看作一种“类数组”对象;这些“类数组”对象不能直接调用数组方法或者其length属性也没有特殊的行为(如字符串的length属性就是只读的),但可以使用数据遍历的方法来遍历它们,就像操作真正的数组一样;


字符串是一个类数组对象:

Arguments对象是一个类数组对象:

在客户端JS中,一些DOM方法,如document.getElementsByTagName()也返回类数组对象,如:

// 结构为:<ul><li>...</li>...</ul>
var lis = document.getElementsByTagName('li');
for(var i=0;i<lis.length;i++){
    var li = lis[i];
    li.onclick = function(e){
        alert(this.innerText);
    }
}

自定义类数组对象:

如果让一个对象成为一个类数组对象,主要的做法是:利用属性名模拟数组元素索引;动态的length属性;甚至可以调用push等数组方法;

// 最简单的类数组对象,模拟了索引
// var obj = {0:'a',1:'b',2:'c'};
var obj = {'0':'a','1':'b','2':'c'};
console.log(obj);
console.log(obj[0]);
 
var arr = ['a','b','c'];
console.log(arr);
console.log(arr[0]);
 
for(var i in obj)
    console.log(i,obj[i]);
for(var i in arr)
    console.log(i,arr[i]);
 
console.log(obj.length);    // undefined
console.log(arr.length);    // 3

添加length属性:

// 添加length属性
var obj = {'0':'a','1':'b','2':'c',length:3};
console.log(obj.length);    // 3
// 动态length属性
var o = {};
var i = 0;
while(i < 10){
    o[i] = i * i;
    i++;
}
o.length = i;
// 把o当作数据一样遍历
var total = 0;
for(var j=0; j<o.length; j++)
    total += o[j];
console.log(total);
// 自定义类数组类及对象
function MyArray(){
    this.length = arguments.length;
    for(var i=0; i<this.length; i++)
        this[i] = arguments[i];
}
var arr = new MyArray(4,3,5,"wang");
for(var i=0; i<arr.length; i++)
    console.log(arr[i]);
 
function YourArray(size){
    this.length = size;
    for(var i=0; i<size; i++)
        this[i] = i;
}
var a = new YourArray(3);
for(var x in a)
    console.log(x,a[x]);

添加push方法:

// 添加push方法,同时更新length属性,push一个元素
var obj = {'0':'a','1':'b',length:2,push:function(){
    this[this.length] = arguments[0];
    this.length += arguments.length;
    return this;
}};
console.log(obj.length);    // 2
console.log(obj.push("c"));
// push多个元素
var obj = {length:0, push:function(){
    for(var i=0; i<arguments.length; i++)
        this[this.length + i] = arguments[i];
    this.length += arguments.length;
    return this;
}};
console.log(obj.push("c","d"));
console.log(obj.length);    // 2

可间接应用Array的方法:

// 间接使用Array的push等方法
var obj = {
    length: 0, 
    push: Array.prototype.push,
    splice: Array.prototype.splice};
console.log(obj.push("c","d"));
console.log(obj.length);    // 2
console.log(obj);

检测类数据对象:

封装一个函数,用来检测类数组对象,如:

var o = new String("wangwei");
// 判定o是否是一个类数组对象
// 字符串和函数也具有length属性,但可用typeof进行排除
// 客户端,DOM文本节点也具有length属性,需要用额外判断o.nodeType != 3将其排除
function isArrayLike(o){
    if(o &&                         // o非null、undefined等
        typeof o === "object" &&    // 为是对象
        isFinite(o.length) &&       // o.length是有限数
        o.length >= 0 &&            // o.length是非负数
        o.length === Math.floor(o.length) &&    // o.length是整数
        o.length < 4294967296)      // o.length < 2^32
        return true;
    else 
        return false;
}
console.log(isArrayLike(o));  // 当o为几种类数组对象时

ES的数组方法为了能应用在类数组对象上而特意定义为通用的,但是,由于类数组没有继承自Array.prototype,就不能直接调用某些数组方法,只有间接的使用Function.call方法调用,如:

var s1 = "zero";
console.log(s1.concat("network"));
 
var o = {0:"a","1":"b","2":"c",length:3};
o.length = 3;
console.log(Array.prototype.join.call(o,"+"));  // a+b+c
console.log(Array.prototype.slice.call(o,0)); // ["a", "b", "c"]
console.log(Array.prototype.map.call(o, function(x){
    return x.toUpperCase();
}));    // ["A", "B", "C"]

注:concat()方法是个特例,可直接应用在字符串上;

作为数组的字符串:

字符串的行为类似于只读的数组;除了用charAt()方法来访问单个字符外,还可以使用方括号;ES为可索引的字符串定义这些特性,主要为了更加简洁、可读和更高效的操作;

var s = "zeronetwork";
console.log(s.charAt(0));   // z
console.log(s[0]);          // z

字符串的这些类数组的行为,可以使用数组方法:

var str = "zeronetwork";
console.log(Array.prototype.join.call(str,","));
console.log(Array.prototype.filter.call(str,function(s){
    return s;
}).join(""));

字符串是不可变值,所以当把它当作数组看待时,它们是只读的;故如push、sort、reverse、splice等数组方法会修改数组,但在字符串上是无效的;

二维及多维数组:

二维或多维数组就是以数组作为数组元素的数组;多维数组的应用场景还是比较多的,比如一个班的学生成绩表;

但从本质上说,ES并不支持真正的二维或多维数组;

var a1 = [1,2,3];
var a2 = [4,5,6];
var a3 = [7,8,9];
var arr = [a1,a2,a3];
// 或者
var arr = [
    [1,2,3],
    [4,5,6],
    [7,8,9,10,12]
];
var arr = [
    [1,2,3],
    ['wangwei','wujing'],
    ['零点','网络']
];
var arr = new Array(
    new Array(1,2,3),
    ['wangwei','wujing'],
    new Array('零点','网络')
);

如果把二维数组当作另外一个数组的元素,那就是三维数组了,以此类推,就形成多维数组;

访问二维或多维数组,只要多次使用[ ]操作符即可,如:

var arr = [
    [1,2,3],
    ['wangwei','wujing'],
    ['零点','网络']
];
console.log(arr[1][1]); // 访问
arr[3] = new Array('one','two','three');  // 添加
arr[2][2] = "程序员";
console.log(arr);

二维或多维数组的遍历同一维数组一致,只不过使用了嵌套遍历:

// 求二维数组中最大值
var arr = [
    [88,67,44,98],
    [56,78,99,56],
    [79,95,78,92]
]
var max = arr[0][0];  // 先取出第一个来初始化max
for(var i=0; i<arr.length; i++){
    for(var j=0; j<arr.length; j++){
        max = max > arr[i][j] ? max : arr[i][j];
    }
}
console.log(max);
 
// 九九乘法表
var table = new Array(10);  // 10行
for(var i=0;i<table.length;i++)
    table[i] = new Array(10);   // 每行10列
for(var row = 0; row < table.length; row++){    // 初始化数组
    for(col = 0; col < table[row].length; col++){
        table[row][col] = row * col;
    }
}
console.log(table[2][9]);
function getTable(arr){
    var table = document.createElement('table');
    for(var i = 1, rows = arr.length; i<rows; i++){
        var tr = document.createElement("tr");
        for(var j = 1, cols = arr[i].length; j < cols; j++){
            var td = document.createElement('td');
            td.innerText = i * j;
            tr.appendChild(td);
        }
        table.appendChild(tr);
    }
    document.body.appendChild(table);
}
getTable(table);


Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表