1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > web前端面试宝典——带你直击面试重难点(40个经典题目 涵盖近90%的考点 码字2w 干

web前端面试宝典——带你直击面试重难点(40个经典题目 涵盖近90%的考点 码字2w 干

时间:2024-05-31 14:03:57

相关推荐

web前端面试宝典——带你直击面试重难点(40个经典题目 涵盖近90%的考点 码字2w 干

系列文章目录

JavaScript 知识梳理,收录了web前端面试95%以上的高频考点,满满的干货。给你做一个高效的知识梳理,为你的面试保驾护航!

文章目录

系列文章目录var 和 let const 的区别typeof 能判断哪些类型列举强制类型转换和隐式类型转换手写深度比较 lodash.isEqualsplit() 和 join() 的区别pop、shift、push、unshift数组的API,有哪些是纯函数数组 slice 和 splice 的区别[10, 20, 30].map(parseInt) 输出结果ajax 请求 get 和 post 的区别函数 call 、apply 和 bind 的区别事件代理(委托)闭包阻止事件冒泡和默认行为如何减少 DOM 操作jsonp 的本质是 ajax 吗document load 和 ready 的区别== 和 ===函数声明和函数表达式的区别关于 this 的场景题new Object() 和 Object.create() 区别作用域和自由变量作用域和自由变量 2常见正则表达式手写字符串 trim获取多个数字中的最大值class 实现继承如何捕获 JS 中的异常什么是 JSON获取当前页面 url 参数将 url 参数解析为 JS 对象数组去重手写深拷贝Object.assign()了解 RAF requestAnimationFrame优化性能的考虑方面Map 和 Object 的区别Set 和 数组的区别WeakMap 和 WeakSetreduce 的使用

声明:全篇近两万字,耗时五天完成。涵盖了JavaScript 面试高频考点,码字不易,如果对你有帮助,还请多多支持!(先点赞,后观看,期待你的一键三连!)

var 和 let const 的区别

var 是 ES5 语法,let const 是 ES6 语法;var 有变量提升var 和 let 是变量,可修改;const 是常量,不可修改let const 有块级作用域,var 没有

示例:变量提升

console.log(a) // undefinedvar a = 100// 相当于var aconsole.log(a) // undefineda = 100

示例:块级作用域

for(let i = 0; i < 5; i++) {let j = i + 1}console.log(i)

typeof 能判断哪些类型

undefined string number boolean symbolobject(注意:typeof null === ‘object’)function

列举强制类型转换和隐式类型转换

强制:parseInt parseFloat toString隐式:if、逻辑运算、==、+ 拼接字符串

手写深度比较 lodash.isEqual

isObject 来判断传入的参数是否为对象如果不是对象直接返回它们的全等如果传入的是同一个对象或数组,直接返回 true如果传入的是两个不相等的对象或数组则先判断长度,不一样直接返回 false一样的话,以 obj1 为基准,和 obj2 依次递归比较不相等返回 false,全满足了返回 true

// 判断是否是对象或数组function isObject(obj) {return typeof obj === 'object' && obj !== null}function isEqual(obj1, obj2) {if(!isObject(obj1) || !isObject(obj2)) {// 值类型(参与 equal 的一般不会是函数)return obj1 === obj2}if(obj1 === obj2) {return true}// 两个都是对象或数组,而且不相等// 1.先取出 obj1 和 obj2 的 keys,比较个数const obj1Keys = Object.keys(obj1)const obj2Keys = Object.keys(obj2)if(obj1Keys.length !== obj2Keys.length) {return false}// 2.以 obj1 为基准,和 obj2 依次递归比较for(let key in obj1) {// 比较当前 key 的 val —— 递归const res = isEqual(obj1[key], obj2[key])if(!res) {return false}}// 3.全相等return true}// 测试const obj1 = {a: 10,b: {x: 100,y: 200}}const obj2 = {a: 10,b: {x: 100,y: 200}}console.log(isEqual(obj1, obj2)) // true

split() 和 join() 的区别

Array.join()方法是String.split()方法的反向操作split('-')把字符串去掉-转为数组join('-')把数组加上-转为字符串

'1-2-3'.split('-')[1, 2, 3].join('-')

pop、shift、push、unshift

以 arr 为例

const arr = [10, 20, 30]

pop:

功能:删除数组的最后一个元素返回值:被删除的元素是否对原数组造成影响:是

// popconst popRes = arr.pop()console.log(popRes, arr)

shift:

功能:删除数组的第一个元素返回值:被删除的元素是否对原数组造成影响:是

const shiftRes = arr.shift()console.log(shiftRes, arr)

push:

功能:从尾部添加元素返回值:数组的长度是否对原数组造成影响:是

const pushRes = arr.push(40)console.log(pushRes, arr)

unshift:

功能:从头部添加元素返回值:数组的长度是否对原数组造成影响:是

const unshiftRes = arr.unshift(5)console.log(unshiftRes, arr)

数组的API,有哪些是纯函数

纯函数:

不改变源数组(没有副作用)返回一个数组

const arr = [10, 20, 30]

concat:

// concatconst arr1 = arr.concat([40, 50]) console.log(arr, arr1)

map:

// mapconst arr2 = arr.map(num => num * 2)console.log(arr, arr2)

filter:

// filterconst arr3 = arr.filter(num => num > 15)console.log(arr, arr3)

slice:

// sliceconst arr4 = arr.slice(2)console.log(arr, arr4)

数组 slice 和 splice 的区别

功能区别:slice 切片,splice 剪接参数和返回值是否纯函数

slice:

slice 切片参数可传可不传,返回值是切片后的数组是纯函数(不改变原始数组)

const arr = [10, 20, 30, 40, 50]// slice 纯函数const arr1 = arr.slice()const arr2 = arr.slice(1, 4)const arr3 = arr.slice(2)const arr4 = arr.slice(-2)console.log(arr) // [10, 20, 30, 40, 50]console.log(arr1) // [10, 20, 30, 40, 50]console.log(arr2)// [20, 30, 40]console.log(arr3) // [30, 40, 50]console.log(arr4) // [40, 50]

splice:

splice 剪接参数可传可不传,返回值是剪接掉的元素的数组非纯函数(改变原始数组)解读:开始剪接的元素索引(1),剪接的元素个数(2),要添加的元素('a', 'b', 'c'

const arr = [10, 20, 30, 40, 50]// splice 非纯函数const spliceRes = arr.splice(1, 2, 'a', 'b', 'c')console.log(spliceRes) // [20, 30]console.log(arr) // [10, 'a', 'b', 'c', 40, 50]

const arr = [10, 20, 30, 40, 50]// splice 非纯函数const spliceRes1 = arr.splice(1, 0, 'a', 'b', 'c') console.log(spliceRes1) // []console.log(arr) // [10, 'a', 'b', 'c', 20, 30, 40, 50]

const arr = [10, 20, 30, 40, 50]// splice 非纯函数const spliceRes2 = arr.splice(1, 2) console.log(spliceRes2) // [20, 30]console.log(arr) // [10, 40, 50]

[10, 20, 30].map(parseInt) 输出结果

map:

第一个参数是当前元素第二个参数是当前索引值

parseInt:

第一个参数是要被解析的字符串第二个参数是要解析的数字的基数(进制数 2~36)

const res = [10, 20, 30].map(parseInt)console.log(res)// 拆解const res2 = [10, 20, 30].map((num, index) => {return parseInt(num, index)})console.log(res2)

ajax 请求 get 和 post 的区别

get 一般用于查询操作,post 一般用于提交操作get 参数拼接在 url 上,post 放在请求体内(数据体积可更大)安全性:post 易于防止 CSRF 攻击

函数 call 、apply 和 bind 的区别

call 传入的参数直接放进去apply 传入的参数是一个数组或类数组bind 返回的是一个新的函数,必须调用它才会被执行

fn.call(this, p1, p2, p3)fn.apply(this, arguments)fn.bind(this, p1, p2, p3)()

示例:

let zhangsan = {name: '张三',age: 20,myFun(from, to) {console.log(this.name + this.age, '来自' + from + '去往' + to)}}let lisi = {name: '李四',age: 22}zhangsan.myFun.call(lisi, '济南', '青岛')zhangsan.myFun.apply(lisi, ['济南', '青岛'])zhangsan.myFun.bind(lisi, '济南', '青岛')()

事件代理(委托)

把 每个 li 的点击事件委托给 ul可以减少内存的消耗可以减少重复性操作

<ul id="list"><li>item 1</li><li>item 2</li><li>item 3</li>...<li>item n</li></ul>//获取目标元素const lis = document.getElementsByTagName('li')//循环遍历绑定元素for (let i = 0; i < lis.length; i++) {lis[i].onclick = function (event) {console.log(event.target.innerHTML)}}

闭包

闭包的影响:变量会常驻内存,得不到释放。闭包不要乱用

阻止事件冒泡和默认行为

阻止冒泡:event.stopPropagation()点击激活,不会弹出取消阻止默认行为:event.preventDefault()点击超链接,不会跳转

<p id="p1">激活</p><a id="a1" href="">取消</a>const p1 = document.getElementById('p1')const a1 = document.getElementById('a1')const body = document.bodyp1.addEventListener('click', function (event) {event.stopPropagation()alert('激活')})body.addEventListener('click', function (event) {alert('取消')})a1.addEventListener('click', function(event) {event.preventDefault()})

阻止冒泡 —> 点击激活,不会弹出取消:

如何减少 DOM 操作

缓存 DOM 查询结果多次 DOM 操作,合并到一次插入

<ul id="list"></ul>const list = document.getElementById('list')// 创建一个文档片段,此时还没有插入到 DOM 结构中const frag = document.createDocumentFragment()for(let i = 0; i < 10; i++) {const li = document.createElement('li')li.innerHTML = `List item ${i}`// 先插入文档片段中frag.appendChild(li)}// 都完成之后,再统一插入到 DOM 结构中list.appendChild(frag)

jsonp 的本质是 ajax 吗

不是,因为没有用到 XMLHttpRequest 对象,所以 jsonp 不属于 ajax。它是通过能够绕过跨域的<script>标签实现的

document load 和 ready 的区别

window.addEventListener('load', function() {// 页面的全部资源加载完才会执行,包括图片、视频等})window.addEventListener('DOMContentLoaded', function() {// DOM 渲染完即可执行,此时图片、视频还可能没有加载完})

== 和 ===

==会尝试类型转换===严格相等除了==null 之外,其他都用===

函数声明和函数表达式的区别

函数声明:function fn() {…}函数表达式:const fn = function () {…}

示例:函数声明

const res = sum(10, 20)console.log(res) // 30function sum(x, y) {return x + y}

示例:函数表达式

const res = sum(10, 20)console.log(res) // 报错const sum = function (x, y) {return x + y}

用 var 声明:

关于 this 的场景题

第一个输出:this 指向当前对象 obj第二个输出:this 指向 window

const obj = {count: 1,getCount: function() {return this.count}}console.log(obj.getCount()) // 1const func = obj.getCountconsole.log(func()) // undefined

new Object() 和 Object.create() 区别

{} 等同于 new Object(),原型 Object.prototypeObject.create(null) 没有原型Object.create({…}) 可以指定原型

示例:

const obj1 = {a: 10,b: 20,sum() {return this.a + this.b}}const obj2 = new Object ({a: 10,b: 20,sum() {return this.a + this.b}})

示例:

const obj1 = {a: 10,b: 20,sum() {return this.a + this.b}}const obj2 = new Object(obj1)

示例:

const obj1 = Object.create(null)const obj2 = new Object(null)

示例:

const obj1 = {a: 10,b: 20,sum() {return this.a + this.b}}const obj2 = Object.create(obj1)

作用域和自由变量

let 的 i 和 for 循环里面的 i 不在同一块级作用域,相当于在 for 循环里面用 var 声明

let i for(i = 0; i <= 3; i++) {setTimeout(function(){console.log(i)},0)}

for 循环里面用 let 声明,形成块级作用域,i 每增加一次,输出一次

for(let i = 0; i <= 3; i++) {setTimeout(function(){console.log(i)},0)}

作用域和自由变量 2

先把函数当成一条语句,不要看具体内容,等调用它的时候再看里面的内容

let a = 100function test() {console.log(a) // 100a = 10console.log(a) // 10}test()console.log(a) // 10

常见正则表达式

正则表达式30分钟入门教程

// 邮政编码/\d{6}/// 小写英文字母/^[a-z]+$/// 英文字母/^[a-zA-Z]+$/// 日期格式/^\d{4}-\d{1,2}-\d{1,2}$/// 用户名/^[a-zA-Z]\w{5, 17}$/

手写字符串 trim

在 String 原型上添加 trim 方法this 指向输入的字符串使用正则表达式做替换

String.prototype.trim = function () {return this.replace(/^\s+/, '').replace(/\s+$/, '')}

获取多个数字中的最大值

把要比较的数转为数组也可以用[...arguments]代替Array.from(arguments)

示例:

function max() {const nums = Array.from(arguments)let max = nums[0]nums.forEach(n => {if(n > max) {max = n}})return max}const res = max(-1, -2, 2, 4, 8)console.log(res) // 8

class 实现继承

extendssuper扩展或重写方法

// 父类class People {constructor(name) {this.name = name}eat() {console.log(`${this.name} eat food`);}}// 子类class Student extends People {constructor(name, number) {super(name)this.number = number}sayHi() {console.log(`姓名 ${this.name} 学号 ${this.number}`);}}// 子类class Teacher extends People {constructor(name, major) {super(name)this.major = major}teach() {console.log(`${this.name} 教授 ${this.major}`)}}// 实例const xialuo = new Student('夏洛', '')console.log(xialuo.name) // 夏洛console.log(xialuo.number) // xialuo.sayHi() // 姓名 夏洛, 学号 xialuo.eat() // 夏洛 eat food// 实例const wanglaoshi = new Teacher('王老师', '语文')console.log(wanglaoshi.name) // 王老师console.log(wanglaoshi.major) // 语文wanglaoshi.teach() // 王老师 教授 语文wanglaoshi.eat() // 王老师 eat food

如何捕获 JS 中的异常

try…catch 手动捕获

try {// todo} catch (ex) {console.error(ex)} finally {// todo}

自动捕获

// 自动捕获window.onerror = function (message, source, lineNum, colNum, error) {// 第一,对于跨域的 js,如 CDN 的,不会有详细的错误报告// 第二,对于压缩的 js,还要配合 sourceMap 反查未压缩代码的行、列}

什么是 JSON

JSON 是一种数据格式,本质是一段字符串JSON 格式和 JS 对象结构一致,对 JS 语言更友好window.JSON 是一个全局对象:JSON.stringify(JS 对象转为 JSON 格式) JSON.parse(JSON 格式转为 JS 对象)

获取当前页面 url 参数

传统方式,查找 location.search

substr(1)截取掉字符串的第一个字符'i'代表不区分大小写

// 传统方法function query(name) {const search = location.search.substr(1)// search: 'a=10&b=20&c=30'const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')const res = search.match(reg)if(res === null) {return null}console.log(res)return res[2]}let res = query('b')console.log(res)

新 API,URLSearchParams

// URLSearchParamsfunction query(name) {const search = location.searchconst p = new URLSearchParams(search)return p.get(name)}let res = query('b')console.log(res) // 20

将 url 参数解析为 JS 对象

传统方式,分析 search:

定义 res 为一个空对象,用来存放对象substr(1)去掉前面的?两次split,分别去掉&=key充当键,val充当值最后返回结果 res

// 传统方式,分析 searchfunction queryToObj() {const res = {}const search = location.search.substr(1)search.split('&').forEach(paramStr => {const arr = paramStr.split('=')const key = arr[0]const val = arr[1]res[key] = val})return res} let res = queryToObj()console.log(res)

使用 URLSearchParams:

// 使用 URLSearchParamsfunction queryToObj() {const res = {}const pList = new URLSearchParams(location.search)pList.forEach((val, key) => {res[key] = val})return res} let res = queryToObj()console.log(res)

数组去重

传统方式,遍历元素挨个比较、去重indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。如果没有找到匹配的字符串则返回 -1。

function unique(arr) {const res = []arr.forEach(item => {if(res.indexOf(item) < 0) {res.push(item)}})return res}let res = unique([20, 10, 20, 30, 10])console.log(res)

使用 Set(计算效率更高)

function unique(arr) {const newArr = new Set(arr)return [...newArr]}let res = unique([20, 10, 20, 30, 10])console.log(res)

手写深拷贝

深拷贝:一个对象B复制另一个对象A,当改变B对象的值是A对象的值不会改变。

function deepClone(obj = {}) {if (typeof obj !== 'object' || obj == null) {// obj 是 null,或者不是对象和数组,直接返回return obj}// 初始化返回结果let resultif (obj instanceof Array) {result = []} else {result = {}}for (let key in obj) {// 保证 key 不是原型的属性if (obj.hasOwnProperty(key)) {// 递归result[key] = deepClone(obj[key])}}// 返回结果 return result}const obj1 = {name: '张三',age: {realAge: 20},arr: ['a', 'b', 'c']}const obj2 = deepClone(obj1)obj2.name = '李四'obj2.age.realAge = 22obj2.arr[0] = 'x'console.log(obj1.name) // 张三console.log(obj1.age.realAge) // 20console.log(obj1.arr[0]) // a

Object.assign()

assign 的属性拷贝是浅拷贝用于将源对象的所有可枚举属性复制到目标对象中如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回

let sourceObj = {a: {b: 1}}let targetObj = {c: 3}let res = Object.assign(targetObj, sourceObj)console.log(res) // {c: 3, a: {b: 2}}targetObj.a.b = 2console.log(sourceObj.a.b) // 2

了解 RAF requestAnimationFrame

要想动画流畅,更新频率要60帧/s,即 16.67ms 更新一次视图setTimeout 要手动控制频率,而 RAF 浏览器会自动控制后台标签或隐藏 iframe 中,RAF 会暂停,而 setTimeout 不会停止

#div1 {width: 100px;height: 50px;background-color: red;}<div id="div1"></div>const div1 = document.getElementById('div1')let curWidth = 100const maxWidth = 640function animate() {curWidth = curWidth + 1div1.style.width = `${curWidth}px`if(curWidth <= maxWidth) {window.requestAnimationFrame(animate)}}animate()

RAF

优化性能的考虑方面

原则:多使用内存、缓存、减少计算、减少网络请求方向:加载页面更快,页面渲染更快,页面操作流畅度更好

Map 和 Object 的区别

须知:有序和无序

有序:操作慢无序:操作快,但无序map、数组是有序的,对象是无序的结合两者优点:二叉树

Map 和 Object 的区别

API 不同,Map 可以以任意类型为 keyMap 是有序结构Map 操作同样很快

基本 API:

增改:set删:delete长度:size

示例:基本 API(set、delete、size)

const m = new Map([['key1', 'hello'],['key2', 100],['key3', {x: 100}]])console.log('原始 m', m)m.set('name', '张三')console.log('添加张三', m)m.set('key1', 'hello world')console.log('修改 key1', m)m.delete('key2')console.log('删除 key2', m)console.log('有无 key3', m.has('key3'))m.forEach((value, key) => console.log('遍历结果', key, value))console.log('m 的长度', m.size)

Map 可以以任意类型为 key对象只能以字符串作为 key

const m = new Map([['key1', 'hello'],['key2', 100],['key3', {x: 100}]])// Map 以任意类型为 keyconst o = {name: 'xxx'}m.set(o, 'object key')function fn() {}m.set(fn, 'fn key')console.log(m)

object 有多快?

示例:查找第500万个元素的用时(object)

const obj = {}for(let i = 0; i < 1000 * 10000; i++) {obj[i + ''] = i}console.time('obj find')obj['5000000']console.timeEnd('obj find')console.time('obj delete')delete obj['5000000']console.timeEnd('obj delete')

map 有多快?

示例:查找第500万个元素的用时(map)

const m = new Map()for(let i = 0; i < 1000 * 10000; i++) {m.set(i + '', i)}console.time('map find')m.has('500000')console.timeEnd('map find')console.time('map delete')m.delete('500000')console.timeEnd('map delete')

可见,都在一个数量级上,相差无几

Set 和 数组的区别

API 不同Set 元素不能重复Set 是无序结构,操作很快

示例:set 的 API

add 添加delete 删除has 是否有size 长度

const set = new Set([10, 20, 30])console.log('初始值', set)set.add(40)console.log('添加40', set)set.delete(10)console.log('删除10', set);set.has(30)let length = set.sizeconsole.log('长度', length);set.forEach(val => console.log('遍历set', val))

示例:数组去重

set 元素是不能重复的(数组去重)

const arr = [10, 20, 20, 30, 10]console.log(new Set(arr))

示例:数组有多慢?set 有多快?

新增一条数据

// arr 有多慢?const arr = []for(let i = 0; i < 100 * 10000; i++) {arr.push(i)}console.time('arr unshift')arr.unshift('a')console.timeEnd('arr unshift')// set 有多快?const set = new Set()for(let i = 0; i < 100 * 10000; i++) {set.add(i)}console.time('set add')set.add('a')console.timeEnd('set add')

查找 500000,这个元素

// arr 有多慢?const arr = []for(let i = 0; i < 100 * 10000; i++) {arr.push(i)}console.time('arr find')arr.includes(500000)console.timeEnd('arr find')// set 有多快?const set = new Set()for(let i = 0; i < 100 * 10000; i++) {set.add(i)}console.time('set find')set.has(50000)console.timeEnd('set find')

WeakMap 和 WeakSet

弱引用,防止内存泄露WeakMap 只能用对象作为 key,WeakSet 只能用对象作为 value没有 forEach 和 size,只能用 add delete has

WeakMap:

WeakMap 弱引用,可防止内存泄露没有 forEach,size(因为和 key有关,对象不知道什么时候就没了),只能 has delete add

示例:

const wMap = new WeakMap()function fn() {const obj = {name: '张三'}wMap.set(obj, 'name info') // 只能用对象作为 key}fn()

WeakMap 使用场景(避免内存泄露):

// WeakMap 场景(避免内存泄露)const wMap = new WeakMap()const userInfo = {name: '张三'}const cityInfo = {city: '北京'}// 建立一种关联关系,而且两者保持独立,不影响彼此的销毁逻辑wMap.set(userInfo, cityInfo) let res = wMap.get(userInfo) console.log(res)console.log(userInfo)

WeakSet

WeakMap 弱引用,可防止内存泄露,只能用对象作为 value没有 forEach,size(因为和 key有关,对象不知道什么时候就没了),只能 has delete add

reduce 的使用

传统方法求和

封装一个 sum 函数初始化 sum,通过 forEach 遍历数组返回 sum

function sum(arr) {let sum = 0arr.forEach(item => sum += item)return sum}const arr = [1, 2, 3]let res = sum(arr)console.log(res) // 6

reduce 传递的参数

sum 当前和curVal 当前值index 索引arr 数组

const arr = [1, 2, 3]const res = arr.reduce((sum, curVal, index, arr) => {console.log('reduce function ....')console.log('sum', sum)console.log('curVal', curVal)console.log('index', index)console.log('arr', arr)// 返回值,作为下一次执行时的第一个参数 sum 的值return sum + curVal }, 0)console.log(res)

reduce 求和

const arr = [1, 2, 3]const res = arr.reduce((sum, curVal) => sum + curVal, 0)console.log(res) // 6

reduce 计数

// 计数const arr = [10, 20, 30, 20, 30, 10]const n = 10const count = arr.reduce((count, val) => {return val === n ? count + 1 : count}, 0)console.log('count', count) // count 2

输出字符串

示例:map 写法

// 输出字符串const arr = [{name: '张三', age: '20'},{name: '李四', age: '21'},{name: '王五', age: '22'},]const str = arr.map(item => {return `${item.name} - ${item.age}`}).join('\n')console.log(str)

示例:reduce 写法

// 输出字符串const arr = [{name: '张三', age: '20'},{name: '李四', age: '21'},{name: '王五', age: '22'},]const str = arr.reduce((s, item) => {return `${s}${item.name} - ${item.age}\n`}, '')console.log(str)

再次声明:全篇近两万字,耗时五天完成。涵盖了JavaScript 面试高频考点,码字不易,如果对你有帮助,还请多多支持!(期待你的一键三连【点赞、收藏+关注】)

不积跬步无以至千里 不积小流无以成江海

点个关注不迷路,持续更新中…

web前端面试宝典——带你直击面试重难点(40个经典题目 涵盖近90%的考点 码字2w 干货满满!)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。