JS 学习笔记--5---对象和数组

1、Object类型(引用类型) 不具备多少功能,但是对于在ECMAScript中存储和传递数据确实,确是很理想的选择。

  创建方式:(1)、使用new Object();方式创建对象,然后对对象进行设置属性及其值 eg:var box=new Object(); box.name='abc'; box.age=21;这样就创建了一个对象,并且具有两个属性,名为name和age,其值为'abc'和21;

   (2)、按照一种的方法 也可以省去关键字 new;即var box=Object();

   (3)、按照字面量创建,即 var box={name:'abc',age:21};这种方法来创建对象,  注意:对象的属性和值是以键值对形式出现;每一个属性的键值对之间必须是以逗号隔开,最后一个属性后面可有可无逗号。 (4)、在(3)的创建方法中属性名可以用引号给括起来

   (5)、使用方法(3)的字面量方法来创建对象,然后用方法(1)的形式来对对象进行属性的设置

  (6)、调用属性的两种方法:a:用对象名点的形式:box.name;这种不需要给属性名加引号  b:中括号形式,(注意引号):box['name'] ;这种方法访问属性是属性名必须加引号,否则  浏览器会把其中的属性名当做变量来处理

  (7)、注册和访问方法:     注册:同样是box.run=..;来给属性run注册成方法,run后面不能添加括号。即前面和属性是一样的;等号后面可以写上具体的方法名(可以带括号也可以不带括号),也可以写一个匿名函数;字面量形式定义对象的时候同样是run:function()...;一般是用匿名函数  

  访问:如果写的是匿名函数或者是具体的方法名没有带(box.run=Run;Run是定义好的方法)括号,那么在调用的时候需要在run后面添加括号,即:box.run(); 否则返回的是以关键字function开头的整个的方法实体内容;如果注册的时候写的是方法名加括号【box.run=Run();】,那么在调用的时候是不需要加括号的(box.run;即可),否则会报错(box.run();//error)

  (8)、使用 delete 课以删除对象的属性(只是删除了属性的值): delete box.name;//删除属性

练习代码:

  1 /*
  2 //用new关键字来创建一个对象
  3     var box=new Object();
  4     box.name="qigang";
  5     box.age=22;
  6     box.height=175;
  7     alert(box);//object Object
  8     alert(typeof box);//object
  9     alert(box.name);//qigang
 10     alert(box.age);//22
 11     alert(box['height']);//175
 12     //alert(box[height]);//error  height not undefiened 
 13     var height='height';
 14     alert(box[height]);//175    说明上面的错误是因为  浏览器把height当成一个变量名
 15 
 16 //不使用 new 关键字,同样可以创建一个对象
 17     var box=Object();
 18     box.name='abc';
 19     alert(box["name"]);// abc
 20 
 21 //字面量创建对象的时,对象的属性和值是以键值对形式出现;里面不能够出现分号
 22     //每一个属性的键值对之间必须是以逗号隔开,最后一个属性后面可有可无逗号
 23     var box={
 24         name:"abcd",
 25         age:22,
 26         he:175
 27     }
 28     alert(box.name);
 29     alert(box.age);
 30     alert(box["he"]);
 31 
 32 //使用字面量和传统的赋值形式来创建对象
 33     var box ={};
 34     box.name="abcd";
 35     box.age="22";
 36     box.height="175";
 37     alert(box["name"]);
 38     alert(box['age']);
 39     alert(box.height);
 40 
 41 
 42 //给对象创建方法
 43     //由于赋值的时候可能是直接赋值的匿名函数,那么在调用的时候需要加括号,否则输出的是函数的实体
 44     //如果不是赋值的匿名函数 而是已经写好的函数,赋值的时候如果是写的函数名加括号,
 45     //调用的时候就不需要在属性名后面添加括号,如果只是将方法名赋给了属性,
 46     //那么调用的时候就需要添加括号,否则打印出来的是函数实体,因为这和匿名方法等价
 47     var box=new Object();
 48     box.name='abc';
 49     //其实run也是一个属性,故下面的第一个打印方法输出是方法体,
 50     box.run=function(){return '123'};
 51     alert(box.run);//打印出的是方法 方法体 function(){return '123'};
 52     alert(box.run());//123
 53 
 54     function obc(){return 123;};
 55     var box=new Object();
 56     box.name='abc';
 57     box.run=obc;    //是赋值的方法名  古仔调用的时候需要添加括号
 58     alert(box.run);//方法体  function obc(){return 123;};
 59     alert(box.run());//123
 60 
 61     function obc(){return 123;};
 62     var box=new Object();
 63     box.name='abc';
 64     box.run=obc();//外部写好的函数在赋给属性的时候添加了括号,
 65     alert(box.run);//123
 66     //alert(box.run());//error 上面给属性赋值方法的时候,方法已经添加了括号,故此处就不需要括号
 67 
 68 //下面的三个程序段  看出,给对象注册方法的时候,其实是属性指向方法,而不是真真的方法
 69 //注册方法是  是不能够在属性名后面添加一个显示的括号表明是方法
 70     function obc(){return 123;};
 71     var box=new Object();
 72     box.name='abc';
 73     box.run()=obc;//error    
 74     alert(box.run);//
 75     alert(box.run());//
 76 
 77     var box=new Object();
 78     box.name='abc';
 79     box.run()=function obc(){return 123;};//不能够够这样赋值
 80     alert(box.run);
 81     alert(box.run());
 82 
 83     function obc(){return 123;};
 84     var box=new Object();
 85     box.name='abc';
 86     box.run()=obc();//error    
 87     alert(box.run);//
 88     alert(box.run());//
 89 
 90 //删除属性        其实不是删除属性  只是删除了属性的值
 91     var box={
 92         name:123
 93     }
 94     alert(box.name);
 95     alert(typeof box.name);
 96     delete box.name;
 97     alert(box.name);
 98     alert(typeof box.name);
 99 
100 //一般开发中很多时候都是使用字面量创建方式  给人一种封装的感觉  而且代码少
101 //字面量也是传递大量参数的时候进行首选的方式
102     function Box(obj){
103         ////当进行大量参数的传递的时候需要判断的    健壮的程序也是需要进过很多的判断
104         if(obj.name!=undefined)alert(obj.name);//qigang
105         if(obj.age)alert(obj.age);//22
106         alert(obj.height);//174
107         //等等下面还有很多参数
108     };
109 
110     Box({    //定义一个字面量对象  直接传递大量的数据过去
111             name:'qignag',
112             age:22,
113             height:174,
114             weight:60,
115             love:'null'
116     });
117 */
View Code

 

2、Array类型(引用类型)   

   (1)、本身也是一个对象类型 数组中每一个元素可以存放任意类型的数据,而且长度大小是可变的     全部输出的时候是以逗号为分隔符的  alert(box);box是一个数组名  

   (2)、创建数组可以通过new Array()方式也可以通过字面量创建。(关键字 new 是可以省略的)  

    (a)、var box=new Array();//通过下标索引来赋值,因为数组的长度是可以改变的

    (b)、var box=new Array(10);//创建一个有十个元素的数组,  注意:上面的方式中括号里面有且只能有一个number类型数据,否则会被认为创建数组并赋值  

    (c)、var box=new Array("a","cd");//创建一个数组  并给数组初始化两个string元素  

    (d)、var box=[];//使用字面量方式创建一个空的数组  

    (e)、var box=[2,4,5,6];//创建一个数组,并给数组初始化  注意:不要写成 var box=[3,4,];//这种方式老版本的IE识别可能会出问题  

  (3)、一般可以通过数组的属性 box.length 来获得某一个数组的长度,同时也用这个属性来给 数组添加元素,这样能够避免存储空间的冗余浪费以及错误的修改了数组的值:   box[box.length]="addNew";  

  (4)、ECMAScript中规定数组的长度可以改变,并且其中的数据类型是任意的。如下面复杂数组  

 1 var box=[3,5,'abcd',//前面两个个元素是number类型,第三个元素是string类型
 2             {
 3                 name:'qigang',run:function(){return 'run';}
 4             },//第四个类型是Object类型
 5             34+22,//第五个是算术表达式
 6             [5,6,7,8],//第六个是字面量数组
 7             new Array("mayun",'liyanhong','mahuateng')//第七个是数组
 8         ];
 9     alert(box);//3,5,abcd,[object Object],56,5,6,7,8,mayun,liyanhong,mahuateng
10     alert(typeof box[0]);//number
11     alert(typeof box[2]);//string
12     alert(typeof box[3]);//object
13     alert(box[3].name);//qigang
14     alert(box[3].run());//run
15     alert(box[5][2]);//7 
16     alert(box[6][2]);//mahuateng
View Code

 

练习中的所有代码:

 1 /*
 2 //使用new关键字  创建数组  当然下面三种创建方法  关键字new也是可以省略的
 3 //创建数组  
 4     var box=new Array();    //创建一个数组
 5     box[0]='abc';            //给数组添加第一个元素
 6     box[2]='chd';            //给元素添加第三个元素,其中第二个元素为空
 7     alert(box);    //abc,,chd
 8 
 9     var box=new Array('qignag',22,'nan',175);//创建数组并对其进行初始化变量
10     alert(box);//qigang,22,nan,175
11 
12     //在创建的时候,指定数组有几个元素,
13     var box=new Array(10);
14     alert(box);//,,,,,,,,,
15     box[5]='abc';
16     alert(box);//,,,,,abc,,,,
17 
18 //在创建数组的时候可以指定长度,但是括号中有且只能够有一个number类型的数据,
19 //否则会认为是创建一个数组 并且给数组赋值
20     var box=new Array(3);//创建一个数组  有三个元素
21     alert(box);// ,,
22     var age=new Array(3,4);//创建一个数组  包含两个元素
23     var he=new Array('3');//创建一个数组 包含一个字符串元素
24     alert(age);// 3,4
25     alert(he);// 3
26 
27 //使用字面量创建数组  
28     var box=[2,4,5,'bhc','45'];    //和var box=new Array('qignag',22,'nan',175);创建方式差不多
29     alert(box);//2,4,5,bhc,45    //说明JS中的数组中的元素可以是不同类型的数据,
30 
31     var box=[];//创建一个空的数组  和var box=new Array();差不多
32     box[1]=24;
33     box[0]=12;
34     box[2]=89;
35     alert(box);//12,24,89
36 
37     //var box;
38     //box[0]=78;    //error 
39     //box[1]=88;
40     //alert(box);
41 
42     var box=[4,6,];//最好不要这样  老版本的IE会识别为3个元素,
43     alert(box.length);//2
44     alert(box);//4,6
45     var age=[,,,,,,];
46     alert(age.length);//6    //length是数组的一个属性  获得数组的长度,元素的个数
47     alert(age);// ,,,,,,
48 
49 //数组是通过索引来读取和改变元素的值
50     var box=[3,5];
51     box[1]=14;//通过索引来修改元素的值
52     alert(box.length);//2
53     box[4]=45;//虽然数组在定义的时候只是声明了两个元素的长度,但是在JS中可以动态改变长度
54     alert(box);//3,14,,,45
55     alert(box.length);//5 
56     
57 //为了不错误的修改元素的值 以及在增加的时候动态的添加  多数采用下面方式
58     box[box.length]='length';//数组的索引是从0开始  最后一个元素的索引是长度减1,故要添加的元素的索引就是现在的数组的长度
59     alert(box);
60 
61 //前面说个,在JS中的数组是可以动态的修改长度,同时里面存储的数据的类型是可以各种各样的
62     var box=[3,5,'abcd',//前面两个个元素是number类型,第三个元素是string类型
63                         {
64                             name:'qigang',run:function(){return 'run';}
65                         },//第四个类型是Object类型
66                         34+22,//第五个是算术表达式
67                         [5,6,7,8],//第六个是字面量数组
68                         new Array("mayun",'liyanhong','mahuateng')//第七个是数组
69             ];
70     alert(box);//3,5,abcd,[object Object],56,5,6,7,8,mayun,liyanhong,mahuateng
71     alert(typeof box[0]);//number
72     alert(typeof box[2]);//string
73     alert(typeof box[3]);//object
74     alert(box[3].name);//qigang
75     alert(box[3].run());//run
76     alert(box[5][2]);//7 
77     alert(box[6][2]);//mahuateng
78 */
View Code

 

3、对象中的方法

  (1)、转换方法    基本上每一个对象和数组都有三个方法:toString()、valueOf()、toLocaleString(),

    只 要toString()和valueOf()方法写了其中的一个,那么返回的值是一样的,同时直接输出对 象的时候内部也是隐试的调用了toString()方法;toLocaleString()方法是将对象或者数 组转换成本地字符串输出,和前面两个函数最明显的区别就是在于输出时间的时候 var  box=[new Date()];//获取当前的时间和日期, alert(box.toLocaleString());   

    对于数组,ECMASctipt还有一个方法 join(),传递一个字符串进去,将数组按照传递的字符串 进行连接(分割),然后返回一个新的字符串,数组本身不会有任何改变

  (2)、栈方法  push()、pop() 操作的是数组的引用 先进先出   

    对于数组,ECMAScript()提供了两个栈方法,x.push(),在使用的时候方法里面传递,要增加 到数组末尾的数据,可以传递多个进去,只能够添加到数组的末尾 box.push('23','hh');   

     x.pop() 方法出栈,就是讲数组的最后一位给移除,一次只是能够弹出一位。

  (3)、队列方法 push()、shift()、unshift() 操作的是数组的引用 先进后出   

    push()方法和栈方法中的push()方法一样,从数组的后面开始添加   

    x.shift()方法,从数组的开头移除元素,一次只是能够移除一位元素   

    x.unshift() 和shift()方法相反,从数组的开头添加元素,可以一次添加多个,参数谁在 前,添加后谁的索引也就越小

  (4)、重新排序方法  reverse()、sort()  操作的是引用  

    box.reverse() 没有参数,就只是简单的将数组的元素进行倒叙,操作的是引用  

    box.sort() 将数组按照从小到达的方式进行排序,注意,在对数值进行排序的时候需要传递一个方法进去,因为在对数值数组进行排序的时候同样会采用数值字符串方法进行排序,按照前面的元素的编码大小来进行排序,传递的这个方法就是做一件事,比较两个数的大小返回正负和0,

 1 //方法两个参数,如果第一个参数大于第二个返回整数,小于返回负数,等于返回0
 2 function compare(value1,value2){
 3     if(value1>value2){    //这个方法的返回值是规定的,如果想从大到小也最好不要修改返回值
 4         return 1;
 5     }else if(value1<value2){
 6         return -1;
 7     }else{
 8         return 0;
 9     }
10 }
11 var box=[23,56,12,2,1];
12 alert(box);
13 alert(box.sort(compare));//1,2,12,23,56
14 //如果要按照从大到小排序,则将从小到大排序的数组 在用一次reverse方法就OK
15 alert(box.reverse());
View Code

    //如果要按照从大到小排序,则将从小到大排序的数组 在用一次reverse方法就OK  alert(box.reverse());

  (5)、操作方法  concat()(非引用操作), slice()(非引用),splic()(引用操作)   

    box.concat() 在数组后面追加元素,方法中传递参数,这些参数就是要添加到数组box后面的 值,返回的一个新的数组,是原来的数组中的元素和最加的元素的组合,原数组不会改变   

    box.slice() 拷贝原数组中的一部分数据组成一个新的数组返回。一个或者两个参数,如果传 递一个参数X(数值)就将数组box中下标为X处开始一直到最后;如果两个参数X,Y(均为数 值),就从下标为X出开始一直到下标为Y结束,但是不包括下标为Y的元素。   

    box.splic() 和上面函数差不多,操作的是引用,还带有删除,增加,替换的功能  

      删除:两个数值型参数(X,Y),X 代表的是开始下标,Y代表的是删除的个数  

      增加:三个或者三个以上的参数,第一个参数和上面删除方法一样,第二个参数改为 0,就    代表的是增加(如果大于0就代表要删除元素),后面的参数就是要增加到数组中的元素  

      修改:上面两个方法的合体,其实原理就是从下标为X处开始,先删除Y个元素,然后再把后    面的参数填充(增加)到下标为X处,故事上面两个方法的合体

 练习中的所有代码:

  1 /* 对象中的方法
  2 //转换方法,对象和数组都有 toString(),valueOf(),toLocaleString()方法
  3 //toString(),valueOf()两种方法,无论重写了谁  都会返回相同的返回值,即等价的
  4 //toLocaleString()方法是转换成本地的字符串形式显示
  5 
  6     var box=['hexiong','hahha','abcd','erty'];
  7     alert(box);//hexiong,hahha,abcd,erty    隐试的调用了 toString() 方法
  8     alert(box.toString());//hexiong,hahha,abcd,erty
  9     alert(box.valueOf());//hexiong,hahha,abcd,erty
 10     alert(box.toLocaleString());//hexiong,hahha,abcd,erty
 11 
 12     var age=['hexiong','hahha',new Date()];//创建了一个新的时间
 13     alert(age);//hexiong,hahha,Wed Jan 01 2014 13:32:53 GMT+0800 (China Standard Time)
 14     alert(age.toString());//hexiong,hahha,Wed Jan 01 2014 13:32:53 GMT+0800 (China Standard Time)
 15     alert(age.valueOf());//hexiong,hahha,Wed Jan 01 2014 13:32:53 GMT+0800 (China Standard Time)
 16     alert(age.toLocaleString());//hexiong, hahha, ‎2014‎-‎01‎-‎01‎ ‎13‎:‎34‎:‎08 本地字符串形式显示
 17 
 18 //默认情况下  数组一般都是用逗号分隔输出数组还有一个方法 join()方法,使用不同分隔符构建输出字符串
 19     var box=["ooii","uiui","qeqe","abcd"];
 20     alert(box);//ooii,uiui,qeqe,abcd
 21     alert(box.join('|'));//ooii|uiui|qeqe|abcd        将数组的元素,按照|连接输出,返回字符串
 22     alert(typeof box.join('|'));//string    方法返回的是字符串
 23     alert(box);//ooii,uiui,qeqe,abcd    上述方法处理后  元数组是没有变化的,说明处理的是拷贝数据
 24 
 25 //栈方法    push(),pop()    先进后出,一次可以压栈多个元素  只能够出栈一个元素
 26     var box=["四川","巴中","自贡"];
 27     alert(box);//四川,巴中,自贡
 28     alert(box.push('绵阳'));//4        入栈,即在数组的最后添加一个元素,然后返回数组的现在长度
 29     alert(box);//四川,巴中,自贡,绵阳    看出push()方法是对数组引用进行操作
 30     alert(box.push('宜宾','内江'));//6        一次按照顺序插入多个
 31     alert(box.join(' '));//四川 巴中 自贡 绵阳 宜宾 内江
 32 
 33     var box=["四川","巴中","自贡",'宜宾','内江'];
 34     alert(box.join(' '));//四川 巴中 自贡 宜宾 内江
 35     alert(box.pop());//内江        将数组的最后一个元素弹出,也就是删除,然后返回删除的对象的值
 36     alert(typeof box.pop());//string
 37     alert(box.join(' '));////四川 巴中 自贡
 38     alert(box.push('宜宾','内江'));//5
 39     alert(box.pop(2));//内江    pop()方法没有重载  一次只能够弹出一个元素
 40     alert(box.length);//4
 41 
 42 //队列方法    先进先出  push()(从后插入多个元素)  shift()(开头移除一个元素) unshift()(开头插入多个元素) 
 43     var box=["四川","巴中","自贡"];
 44     alert(box.push('宜宾','内江'));//从数组后面添加两个元素,(队列的末尾添加元素),返回最新长度
 45     alert(box.join(' '));//四川 巴中 自贡 宜宾 内江
 46     alert(box.shift());//四川    弹出前面第一个元素
 47     alert(box.join(' '));//巴中 自贡 宜宾 内江
 48     alert(box.unshift('四川'));//从队列的开头(数组第0个元素)插入一个数据,返回数组的最新长度
 49     alert(box.join(' '));//四川 巴中 自贡 宜宾 内江
 50     alert(box.unshift('12','34'));//7    多个元素插入的时候是从后插入,和push()从前开始插入相反
 51     alert(box.join(' '));//12 34 四川 巴中 自贡 宜宾 内江
 52 
 53 //重新排序方法    reverse() 逆序输出    sort()从小到大排序  操作的是引用
 54     //reverse() 方法是操作的数组的引用  
 55     var box=[3,4,5,6,7,5];
 56     alert(box.join(' '));//3 4 5 6 7 5
 57     alert(typeof box.reverse());//object
 58     alert(box.reverse());//3 4 5 6 7 5    此处和上面一条语句进行了两次 reverse(),故原序输出
 59     alert(box.join(' '));//3 4 5 6 7 5
 60     var age=box.reverse();
 61     alert(age.join(' '));//5 7 6 5 4 3
 62 
 63     var box=[3,5,6,3,1];
 64     alert(box);//3,5,6,3,1
 65     alert(box.sort());//1,3,3,5,6
 66     alert(box);//1,3,3,5,6        原数组也被排序了 说明操作的是引用
 67 
 68 //之所以出现下面这种情况是 因为他是按照数值字符串形式来从小到大排序的
 69     var box=[23,56,12,2,1];
 70     alert(box);
 71     alert(box.sort());//1,12,2,23,56
 72 
 73 //比较数值的时候一般传递一个方法进去
 74 //方法两个参数,如果第一个参数大于第二个返回整数,小于返回负数,等于返回0
 75     function compare(value1,value2){
 76         if(value1>value2){    //这个方法的返回值是规定的,如果想从大到小也最好不要修改返回值
 77             return 1;
 78         }else if(value1<value2){
 79             return -1;
 80         }else{
 81             return 0;
 82         }
 83     }
 84     var box=[23,56,12,2,1];
 85     alert(box);
 86     alert(box.sort(compare));//1,2,12,23,56
 87     //如果要按照从大到小排序,则将从小到大排序的数组 在用一次reverse方法就OK
 88     alert(box.reverse());
 89 
 90 //操作方法  concat()基于当前数组创建一个新的数组  slice()基于当前数组获取自定区域并创建数组
 91         splice()主要用途是插入元素,可以有删除元素的功能
 92 
 93 //concat()函数时在当前数组后面插入元素,并且将得到的新的数组返回,是操作的原数组的数据
 94     var box=['四川','巴中','自贡','绵阳','德阳'];
 95     alert(box);//四川,巴中,自贡,绵阳,德阳
 96     var age=box.concat(2);    //非引用操作
 97     alert(age);//四川,巴中,自贡,绵阳,德阳,2
 98     alert(box);//四川,巴中,自贡,绵阳,德阳
 99     age=box.concat('4','3');
100     alert(age);//四川,巴中,自贡,绵阳,德阳,4,3
101 
102 //slice(x,y) 从原数组的下标为x的元素开始取,取到下标为y的元素位置,不包含y,
103     var box=['四川','巴中','自贡','绵阳','德阳'];
104     alert(box);//四川,巴中,自贡,绵阳,德阳
105     var age=box.slice(2,4);
106     alert(age);//自贡,绵阳
107     alert(box);//四川,巴中,自贡,绵阳,德阳
108     alert(box.slice(2));//自贡,绵阳,德阳
109     var age=box.slice(2,-3);//输出为空
110 
111 //splice() 方法操作的是引用    
112 //splice()中的删除功能    只是需要传递两个参数,但是第二个参数为索引的元素也被截取
113     var box=['四川','巴中','自贡','绵阳','德阳'];
114     alert(box);//四川,巴中,自贡,绵阳,德阳
115     var age=box.splice(1,3);
116     alert(age);//巴中,自贡,绵阳
117     alert(box);//四川,德阳
118     alert(box.splice(1,-1));//输出为空
119 
120 //插入功能        三个参数以上,第一次参数代表位置,第二个参数为0代表不截取,后面为插入的元素
121     var box=['四川','巴中','自贡','绵阳','德阳'];
122     alert(box);//四川,巴中,自贡,绵阳,德阳
123     var x=box.splice(1,0,'23','45');//第二个参数小于等于0(一般等于0),就是代表没有截取,然后从下标为1出插入两条数据
124     alert(box);//四川,23,45,巴中,自贡,绵阳,德阳
125     alert(x);//应该是返回的被截取的数据  但是此处是截取的0个元素 故为空
126     alert(typeof box.splice(1,0,'23','45'));//object
127 
128 //替换功能        第一个参数为位置,第二个参数为要替换几个数,后面为替换后的参数
129 //返回被替换的数据        替换掉的参数的个数和替换后的参数个数没有任何关系,完全是挖一个大坑,再装东西
130     var box=['四川','巴中','自贡','绵阳','德阳'];
131     alert(box);//四川,巴中,自贡,绵阳,德阳
132     alert(box.splice(1,2,'34','34','34'));//巴中,自贡    参数二小于等于0就是插入
133     alert(box);//四川,34,34,34,绵阳,德阳
134 
135 */
View Code

 

posted on 2014-01-02 10:23  恋那片海  阅读(364)  评论(1编辑  收藏  举报