JavaScript谁,字符类型,布尔值,null,数组,运算符,流程控制,序列化和反序列化,日期对象,正则,函数

JavaScipt

JavaScipt 是脚本语言

JavaScipt 是一种轻量级的编程语言

JavaScipt 是可插入 HTML 页面的编程代码

JavaScipt 插入HTML后,可有所有的现代浏览器执行

注:ES6就是指ECMAScript 6。

目录

JavaScipt 引入方式

Scipt标签内写代码

<script>
  // 左斜杠代表注释
</script>

引入额外的JS文件

<script src="myscript.js"></script>

谷歌浏览器提供的js编辑器console可直接编写JavaScript

2608117-20220209204453861-602485933

在使用浏览器书写js代码的时候,左上方点击🚫键时,只是清空了当前页面,旧代码还是存在。

想要重新定义变量,需要重新开启一个网页或者重载页面。

JavaScipt语言规范

注释是代码的灵魂

// 这是单行注释

/*
这是多行注释
*/

结束符

JavaScript 中的语句要以分号( ; )为结束符。

JavaScript 语言基础

变量声明>>>var / let

  1. JavaScript 的变量名可以使用_,数字,字母,$组成,不能以数字开头
  2. 声明变量需要使用var 变量名;的格式来进行声明

补充:ES6新增了let命令,用于声明变量。其用法类似于var,但是所声明的变量只在let命令所在的代码块内容有效。

老版本:var(声明全局变量)

新版本:let (声明局部变量)

var name = 'junjie01';
let name = 'junjie02';

var 和 let 的区别

声明常量>>>const

2608117-20220209204710316-1546129363

编写代码js代码

  1. pycharm提供的js文件
  2. 直接使用浏览器提供的编程环境

pycharm在使用上述关键字的时候如果出现了报错说明js版本没有选择6:自定义设置 settings>>>l f>>>js

JavaScript数据类型

Python JavaScript
Int,float number
str string
boolean bool
None,null undefined
dict,list... object
list,tuple array
True true
Flase flase

JvavScriptPython都是动态类型

var x;
undefined
x
undefined //此时x是undefined
var x = 1;
undefined
x
1					//此时x是数字
var x = 'junjie';
undefined
x
'junjie'  //此时x是字符串

数值(number)

typeof查看数据类型

JavaScript 不区分整型和浮点型,就只有一种数字类型

var a = 9
var b = 10.9
typeof a; //number类型
typeof b; //number类型

NaN属于数值类型:不是数字(Not a Number),在转换类型时提现

parseInt   : 整型
parseFloat : 浮点型

parseInt('122aa')
122
parseInt('a122aa')
NaN
parseFloat('1.1')
1.1
parseFloat('aa11a.1')
NaN

2608117-20220209205014757-1941818579

字符类型(string)

单行文本:单引号:' ' 双引号:""

多行文本:模板字符串``,tab上方按键,英文状态下。

JS 中字符串拼接推荐使用+号

var a = 'hello'
var b = 'world'
console.log(a+b)
//输出:helloworld

字符类型(string)>>>:格式化输出:必须添加$符号

let name = 'junjie';
let age = 18;

var data = `
my name is {name} and mt age is {age}`
data
'\nmy name is {name} and mt age is {age}' //输出

var data = `
my name is ${name} and mt age is ${age}` 
data
'\nmy name is junjie and mt age is 18' //输出

字符类型string内置方法:

JS方法 说明 对比python
.length 返回长度 len( )
.trim( ) 移除空白 strip( )
.trimLeft 移除左边空白 lstrip( )
.trimRight 移除右边空白 rstrip( )
.charAt(n) 返回第n个字符 [索引]
.concat(value, ...) 拼接 join( )
.indexOf(substring, start) 子序列位置 str.find( )
.substring(from, to) 根据索引获取子序列 索引取值:[索引]
.slice(start, end) 切片 [start,end]
.toLowerCase() 小写 lower( )
.toUpperCase() 大写 upper( )
.split(delimiter, limit) 分割 split

实际代码:

  1. 返回长度.lenth

var name = 'jie';
name
'jie'
name.length
3 //输出
  1. 移除空白.trim( ) / .trimLeft( ) / .trimRight( )

var name = '     junjie  ';
name.trim()
'junjie'

name.trimLeft()
'junjie  '

name.trimRight()
'     junjie'
  1. 返回索引所对应的值charAT( )

var name = 'junjie';
name
'junjie'
name.charAt(0)
'j' //索引从0开始
  1. 返回值所对应的索引.indexOf()

var name = 'junjie';
name
'junjie'
name.indexOf('j')
0
  1. 切片操作.substring() / .slice()

name
'junjie'
name.substring(0,3) //索引取值
'jun'
name.slice(0,3) //切片
'jun'
name.substring(0,-1) //substring不能负数索引
''
name.slice(1,-1)
'unji'
  1. 转换大小写.toUpperCase() / .toLowerCase()

var name = 'JUNjie';
name
'JUNjie'
name.toUpperCase()
'JUNJIE'
name.toLocaleLowerCase()
'junjie'
  1. 字符串切割.split(delimiter, limit)

var name = 'junjie|junjun|jiejie|jun|jie'
name
'junjie|junjun|jiejie|jun|jie'
name.split('|')
(5) ['junjie', 'junjun', 'jiejie', 'jun', 'jie']
typeof(name.split('|'))
'object'
name.split('|',2)
(2) ['junjie', 'junjun'] //2表示切割后输出几个元素
  1. 字符串拼接.concat()
name
'junjie|junjun|jiejie|jun|jie'
name.concat('junjiedada')
'junjie|junjun|jiejie|jun|jiejunjiedada' //默认拼接至尾部,并且无分割符
name.concat('|','junjiedada')
'junjie|junjun|jiejie|jun|jie|junjiedada'

JS是弱类型(内部会自动转换成相同的数据类型做操作)

布尔值boolean

JS中布尔值都是小写:trueflase

在JS中布尔值为flase的:' '(空字符串),0,null,undefined,NaN

2608117-20220211160138056-2043616870

null和undefined

null : 表示值为空,一般为指定或者清空一个变量时使用

undefined:表示声明一个变量,但是没有初始化操作即没有赋值

函数在没有返回值的时候,返回的是undefined

null抽象比喻图片:

2608117-20220211160143380-1946819110

undefined抽象比喻图片

2608117-20220211160215233-1049940386

数组

使用单独的变量名存储一系列的值,类似于python中的列表

> var l = ['junjie',18,'play']
< undefined
> l
< ["junjie", 18, "play"] (3)
> console.log(l[0]) //通过索引取值
junjie
< undefined
> console.log(l[1]) //通过索引取值
18
< undefined
> typeof(l)
< "object"

常用方法:

方法 描述
.lenght 数组的大小
.push(ele) 尾部追加元素
.pop( ) 获取尾部元素
.unshift(ele) 头部插入元素
.shift( ) 头部移除元素
.slice(start,end) 切片
.reverse( ) 反转
.join(seq) 将数组元素链接成字符串
.concat(val,...) 链接数组
.sort( ) 排序
.forEach( ) 将数组的每个元素传递给回调函数
.splice( ) 删除元素,并向数组添加新元素
.map( ) 返回一个数组元素调用函数处理后的值的新数组

实际代码:

准备一个数字

var l = ['junjie',18,'play']
.length>>>数组大小
> l.length
< 3
.push()>>>尾部追加元素
> l.push('black_silk')
< 4
> l
["junjie", 18, "play", "black_silk"] (4) = $1
.pop()>>>获取尾部元素并删除
> l.pop()
< "black_silk"
> l
< ["junjie", 18, "play"] (3)
.unshift()>>>头部增加元素
> l
< ["junjie", 18, "play"] (3)
> l.unshift('tom')
< 4
> l
< ["tom", "junjie", 18, "play"] (4)
.shift()>>>头部移除元素
> l
< ["tom", "junjie", 18, "play"] (4)
> l.shift()
< "tom"
> l
< ["junjie", 18, "play"] (3)
.slice()>>>切片
> var number = [11,22,33,44,55,66,77]
< undefined
> number
< [11, 22, 33, 44, 55, 66, 77] (7)
> number.slice(0,3) //从零开始,步长顾头不顾尾,切到第二位。
< [11, 22, 33] (3)
.reverse()>>>反转
> number
< [11, 22, 33, 44, 55, 66, 77] (7)
> number.reverse()
< [77, 66, 55, 44, 33, 22, 11] (7)
.join()>>>拼接成字符串
> number
< [11, 22, 33, 44, 55, 66, 77] (7)
> number.join('|')
< "11|22|33|44|55|66|77"
.concat()>>>链接数组
> number
< [11, 22, 33, 44, 55, 66, 77] (7)
var number_two = [999,888,777]; //新定义一个数组
> var a = number.concat(number_two)
< undefined
> a
< [11, 22, 33, 44, 55, 66, 77, 999, 888, 777] (10)
.sort()>>>排序
> a
< [11, 22, 33, 44, 55, 66, 77, 999, 888, 777] (10)
> a.sort()
< [11, 22, 33, 44, 55, 66, 77, 777, 888, 999] (10)
.forEach()>>>将数组的每个元素传递给回调函数

语法a.forEach(function(形参){console.log(实参)})

function()表示函数

> a
< [11, 22, 33, 44, 55, 66, 77, 999, 888, 777] (10)
> a.forEach(function(n){console.log(n)}) //传一个形参
[Log] 11
[Log] 22
[Log] 33
[Log] 44
[Log] 55
[Log] 66
[Log] 77
[Log] 777
[Log] 888
[Log] 999
# 或者
> a.forEach(function(n,m){console.log(n,m)}) // 传两个形参,第一个for循环元素,第二个索引
[Log] 11 – 0
[Log] 22 – 1
[Log] 33 – 2
[Log] 44 – 3
[Log] 55 – 4
[Log] 66 – 5
[Log] 77 – 6
[Log] 777 – 7
[Log] 888 – 8
[Log] 999 – 9
# 或者
> a.forEach(function(p,l,m){console.log(p,l,m)}) 
// 传三个形参,第一个for循环元素,第二个索引,第三个元素来自哪里
[Log] 11 – 0 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 22 – 1 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 33 – 2 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 44 – 3 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 55 – 4 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 66 – 5 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 77 – 6 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 777 – 7 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 888 – 8 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
[Log] 999 – 9 – [11, 22, 33, …] (10)
[11, 22, 33, 44, 55, 66, 77, 777, 888, 999]Array (10)
.splice()>>>删除元素,并向数组添加新元素

语法splice(index,howmany,item1,.....,itemX)

参数:

参数 描述
index 必需,规定从何处添加/删除元素
howmany 必需。规定应该删除多少元素即为索引。必需是数字,但可以为0.
如果未规定此参数,则删除从index开始到原数组结尾的所有元素。
item1, ..., itemX 可选。要添加到数组的新元素
> a //删除元素
< [11, 22, 33, 44, 55, 66, 77, 777, 888, 999] (10)
> a.splice(0,3)
< [11, 22, 33] (3)
> a
< [44, 55, 66, 77, 777, 888, 999] (7)
//删除第一位数字,并添加指定添加某数字
> a
< [44, 55, 66, 77, 777, 888, 999] (7)
> a.splice(0,1,1)
< [44] (1)
> a
< [1, 55, 66, 77, 777, 888, 999] (7)
.map( )>>>返回一个数组元素调用函数处理后的值得新数组

语法map(function(currentValue,index,arr), thisValue)

参数

参数 描述


function(currentValue, index,arr)
必需。函数,数组中的每个元素都会执行这个函数
函数参数:
currentValue:必需。当前元素的值
index :可选。当前元素的索引值。
arr :可选。当前元素属于的数值对象。
thisValue 可选。对象作为该执行回调时使用,传递给函数,用作"this"的值
如果省略了 thisValue,"this" 的值为 "undefined"

1342004-20180711095244157-693529215

自定义对象object

JS中所有事物都是对象:字符串,数值,数组,函数...此外,JavaScript 允许自定义对象。

JavaScript 提供多个内建对象,比如 String、Date、Array 等等。

对象只是带有属性和方法的特殊数据类型。

对比于python可以看做是字典

var n = {'name':'junjie','age':18}
n 
{name: 'junjie', age: 18}
n.name //字典取值可以通过(.)的方式取值
'junjie'
n.age //字典取值
18
n['like'] = play //新增k:v键值对
'play'
n
{name: 'junjie', age: 18, like: 'play'}

同样支持for循环,暴露给外界的也是key键

> for (var i in a){console.log(a[i])}
[Log] junjie
[Log] 18
[Log] paly

第二种创建自定义对象的方法,使用关键字:new

> a
< undefined
> a = new Object()
< {}
> a.name = 'junjie'
< "junjie"
> a
< {name: "junjie"}
> a.age = 18
< 18
> a
< {name: "junjie", age: 18}
> a.city = 'play'
< "play"
> a
< {name: "junjie", age: 18, city: "play"}

JS之运算符

算数运算符:

加号在后面,先赋值后自增
加号在前面,先增加加后赋值

== :弱等于(会自动转换数据类型至相同状态)

=== :强等于(不会自动转换数据类型)

> a = 10
< 10
> res = a++ //此时res=10,a=11
< 10
> a
< 11
> res1=++a //此时先给a+1=12,再对res1进行赋值
< 12

比较运算符

> >= < <= != == === !==

1 == "1"  // true  弱等于
1 === "1"  // false 强等于
//上面这张情况出现的原因在于JS是一门弱类型语言(会自动转换数据类型),所以当你用两个等号进行比较时,JS内部会自动先将
//数值类型的1转换成字符串类型的1再进行比较,所以我们以后写JS涉及到比较时尽量用三等号来强制限制类型,防止判断错误

逻辑运算符

与:&&   // 两边都为真才为真 结果取右边的值
或:||   // 一边为true则为真 左边为真就取左边 右边为真就取右边
非:!	   // 取反

eg:  5 && '5';   // 结果就为'5'
     0 || '5';   // 结果就为'5'
	!5 && '5';  // 无结果 false

辅助运算符

= += -+ *= /=

流程控制

if-else

语法:if(条件){条件成立后执行的代码块}

var a = 10;
if (a > 5){
  console.log("yes");
}else {
  console.log("no");
}

if-else if-else

var age = 18
> if(age<=18){
console.log('我喜欢')
}else if(age>18){
console.log('我不喜欢'
)}else{
console.log('cao')
}
[Log] 我喜欢

switch

switch中的case子句通常都会加break语句,否则程序会继续执行后续case中的语句。

> a
< 3
switch(a){
case 0:console.log('吃饭');break;
case 1:console.log('打架');break;
case 2:console.log('跳舞');break;
case 3:console.log('散步');break;
defaultstatus:console.log('啥也不是')}
散步 //输出

for循环

语法:for(起始条件;循环条件;每次循环后的操作){循环执行代码块}

> a
< [11, 22, 33, 44, 55, 66, 77] (7)
> for(var i=0;i<a.length;i++){}
< undefined
> for(var i=0;i<a.length;i++){console.log(a[i])}
[Log] 11
[Log] 22
[Log] 33
[Log] 44
[Log] 55
[Log] 66
[Log] 77

while循环

语法:while(条件){条件成立执行的代码块}

> while(a<10){
console.log(a)
a++;};
[Log] 0
[Log] 1
[Log] 2
[Log] 3
[Log] 4
[Log] 5
[Log] 6
[Log] 7
[Log] 8
[Log] 9
< 9

三元运算符

语法格式:var res=条件?条件成立执行代码:条件不成立执行代码

> var res = 1>2?console.log('成立'):console.log(console.log('不成立'))
[Log] 不成立

与python基本没有差别

语法格式:res = 条件成立执行if条件else条件不成立执行

res = 1 if 1 < 2 else 3
print(res)

JavaScript 对象 序列化与反序列化

Python中的序列化与反序列化

dumps 返序列化,loads 序列化

Python中能够被序列化对象:如图所示
2608117-20220209210331385-2124720050

JavaScript中的序列化与反序列化

JSON.stringify()序列化

JSON.parse()反序列化

> a = ['junjie','nn','jun']
< ["junjie", "nn", "jun"] (3)
> var b=JSON.stringify(a) //序列化
< undefined
> b
< "[\"junjie\",\"nn\",\"jun\"]"
> JSON.parse(b) //反序列化
< ["junjie", "nn", "jun"] (3)

假设现在JS中有一个布尔值ture需要基于网络发送给python程序并且让python转换成布尔值,该如何操作?

1.在JS中使用JSON.stringify()序列化成json格式字符串

2.基于网络发送给python程序(自动编码)

3.python接收 解码并反序列化

JS之日期对象

> var a = new Date()
< undefined
> a
< Sat Feb 12 2022 19:45:15 GMT+0800 (CST)
> a.toLocaleString() //格式化时间
< "2022/2/12 下午7:45:15"

并且JS支持手动输入格式化和结构化时间并且手动输入支持格式化

//  也支持自己手动输入格式化和结构化时间 也同样支持格式化。
格式化:
var d4 = new Date('2022/2/2 11.22.33');
d4.toLocaleString();
'2022/2/2 上午11:22:33'

结构化:
var d5 = new Date(2022,2,2 11,22,33);
d5.toLocaleString();
'2022/3/2 上午11:22:33'   // 为什么写的2月变成了三月:因为结构化为0-11月从0开始

2608117-20220209210445982-1703204114

> a = new Date
< Sat Feb 12 2022 19:54:00 GMT+0800 (CST)
> a.toLocaleString()
< "2022/2/12 下午7:54:00"
> a.toLocaleTimeString()
< "下午7:54:00"
> a.toUTCString()
< "Sat, 12 Feb 2022 11:54:00 GMT" //UTC时区
> a = new Date(5000) //5000毫秒=5分钟
< Thu Jan 01 1970 08:00:05 GMT+0800 (CST)
> a.toLocaleString()
< "1970/1/1 上午8:00:05"
> a.toUTCString()
< "Thu, 01 Jan 1970 00:00:05 GMT"

Date对象的方法

var d = new Date(); 
//getDate()                 获取日
//getDay ()                 获取星期
//getMonth ()               获取月(0-11)
//getFullYear ()            获取完整年份
//getYear ()                获取年
//getHours ()               获取小时
//getMinutes ()             获取分钟
//getSeconds ()             获取秒
//getMilliseconds ()        获取毫秒
//getTime ()                返回累计毫秒数(从1970/1/1午夜)

编写代码,将当前日期按’2017-12-27 11:11'格式输出

点击查看代码,参考示例
const WEEKMAP = {  
  0:"星期天",
  1:"星期一",
  2:"星期二",
  3:"星期三",
  4:"星期四",
  5:"星期五",
  6:"星期六"
};  //定义一个数字与星期的对应关系对象


function showTime() {
    var d1 = new Date();
    var year = d1.getFullYear();
    var month = d1.getMonth() + 1;  //注意月份是从0~11
    var day = d1.getDate();
    var hour = d1.getHours();
    var minute = d1.getMinutes() < 10?"0"+d1.getMinutes():d1.getMinutes();  //三元运算

    var week = WEEKMAP[d1.getDay()];  //星期是从0~6

    var strTime = `
    ${year}-${month}-${day} ${hour}:${minute} ${week}
    `;
    console.log(strTime)
};

showTime();

RegExp对象>>>正则

第一种创建方法:

var reg = new RegExp('^[a-zA-Z][a-zA-Z0-9]{5,11}');

第二种创建方法:

var reg1 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/;

匹配内容:

关键字:.test()

校验数据,返回结果为:布尔值

reg.test('garyiii');
reg1.test('garyiii');

2608117-20220209210542247-1041535239

注意:正则表达式中不能有空格。

.match()正则之全局匹配

正则表达式的最后不加g表示匹配成功则结束,加g表示全局匹配

前提必须是字符串

['junjie666']
a = 'junjie666'
'junjie666'
a.match(/j/)
['j', index: 0, input: 'junjie666', groups: undefined] //匹配成功就停止匹配
a.match(/j/g)
(2) ['j', 'j'] //全局匹配,g代表全局模式

全局模式的lastIndex属性

var reg2 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/g;

reg2.test('garysss');
true

reg2.test('garysss');
false

reg2.test('garysss');
true

reg2.test('garysss');
false

reg2.lastIndex;
0

reg2.test('garysss');
true
reg2.lastIndex;
7

2608117-20220209210551364-74844507

不传数值,默认传的是undefined

理论实践:

var reg3 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/;
reg3.test();  // 返回true

// 这里为什么返回为true呢 因为在什么都不传的情况下默认传的为undefined,恰好undefined符合正则,所以返回的为true

验证:

reg4.test(undefined);  
true  // 这里还验证不出来

var reg4 = /undefined/;   //  我们创建一个精准匹配来验证
reg4.test('gary');
false
reg4.test();
true

2608117-20220209210555470-392128353

函数

Python中关键字:def (自定义函数名):

JS 关键字:function(自定义函数名){函数体代码}

无参函数:

function myindex(){
    console.log('hello')
}
undefined
myindex()
hello
undefined

有参函数:

function index(n){
    console.log(n)
}
undefined
index('junjie')
junjie
undefined

index()  //不穿参,默认undefined
undefined

index(1,2,3,4,5) //传多个参数,接受一个
 1

关键字arguments()>>>获取函数接收到的所有参数

function index_two(){
    if(arguments.length==1){console.log('传了一个参数')}
    else if(arguments.length==2){console.log('传了两个参数')}
    else if(arguments.length==3){console.log('传了三个参数')}
    else {console.log('传参数超过三个')}
}

undefined
index_two(11)
传了一个参数
undefined
index_two(11,'junjie')
传了两个参数
undefined
index_two(11,'junjie','jun')
传了三个参数
undefined
index_two(11,'junjie','jun','ha')
传参数超过三个

函数的返回值return

// 函数的返回值关键字也是return
单个返回值:
function index(){
    return 666
};
res = index();
666

多个返回值:
function index1(){
    return 666,444,222,111
}
res1 = index1();
111    //  有多个返回值只返回最后一个

可使用数组的形式返回多个返回值:
function index2(){
    return [111,222,333,444]
}

2608117-20220209210713285-907716944

匿名函数:泛指不自定义函数名的函数

// 就是不指认名字的函数(很少用到)
function(){
    console.log('我没有名字')
};

// 没有名字怎么调用呢:可以使用一个变量名接收
var res = function(){
    console.log('我没有名字')
};

//可以小括号括起来直接使用
(function(){console.log('我没有名字')})()

箭头函数

语法结构:var func = 形参 => 返回值


	var func1 = v => v;
等价于:
	var func1 = function(v){
        return v
    }

// 多个参数
  var func2 = (arg1,arg2) => arg1+arg2;
等价于:
  var func2 = function(arg1,arg2){
      return arg1+arg2;
  }

函数的全局变量与局部变量

与python的作用域一致,先局部内部查找变量,找不到去外部函数查找,逐步找到最外层

举例:
1、var city = 'beijing';
   function f(){
       var city = 'shanghai';
       function inner(){
           var city = 'china';
           console.log(city);
       }
       inner();
   }
f();  // 输出结果为:china

2、var city = 'beijing';
   function bar(){
       console.log(city);
   }
   function f() {
       var city = 'shanghai';
       return bar;
   }
   var ret = f();
   ret();  //  输出结果为:beijing

3、var city = 'beijing';
   function f(){
       var city = 'shanghai';
       function inner(){
           console.log(city);
       }
       return inner;
   }
   var ret = f();
   ret();  //  输出结果为:shanghai

posted @ 2022-02-13 15:28  谢俊杰  阅读(41)  评论(0编辑  收藏  举报