javascript高级

it2025-03-16  21

JS高级:

数据类型:

基本数据类型:

string

number

boolean

null

undefined

引用数据类型:

object:

array:引用下标的对象

function:可以执行

 

判断数据类型:

instanceof:判断对象的具体类型(判断对象是否是某个构造函数的实例);但是这个不一定能够 判断数组和对象的区别;可以通过isArray()判断数组;

typeof:可以判断出string/number/boolean/object;不能判断Object 和 null,Object和Array

===:可以判断null和undefined;

isArray():判断一个对象是否是数组;

 

构造函数和普通的区别:

首先构造函数首字母一般定义为大写;

其次可以通过new关键字创建一个对象;

 

undefined和null的区别:

undefined:表示未定义(初始赋值);

null:表示设置初始赋值为null(表示赋值是一个对象);

在对象调用结束,设置为null即释放对象的内存;

 

引用数据类型和基本数据类型的区别:

基本数据类型:

存储在栈内存;保存的是值;

 

引用数据类型:

存储在堆内存;引用类型的变量保存的是地址值;

 

什么是数据?什么是信息?

存储在内存上表达信息的特殊介质。本质上是01010101;

数据的特点,可传递,可运算;

内存中的所有操作目标都是数据;

算数运算

逻辑运算

赋值运算

运行函数

 

什么是内存?

内存条通电之后产生可以临时存储数据的空间;

内存的产生和死亡:内存条(电路板)-》通电-》产生内存空间-》存储数据-》处理数据-》断电-》内存空间和数据都消失;

一块内存有两个数据:

地址和内部存储的数据;

内存大鹅分类;

栈:全局变量/局部变量;

堆:对象;

 

什么是变量?

可以变化的量,由变量名和变量值组成;

每个变量都有对应一块内存,变量名用来查找对应的内存,变量值就是内存中保存的数据;

 

内存、数据、变量之间的区别?

内存是用来存储数据的临时空间;

变量内存空间的标识;

 

赋值和内存的关系?

变量赋值:将值拷贝一份给另一个变量;修改一个变量不影响其他变量保存的值

引用赋值:将保存的地址值保存赋值给另一个变量;修改地址指向内存的数据,其他指向同一块内存的变量保存的内容会发生变化;

 

函数的基本传递:引用传递;

传递的基本数据类型:实参赋值给形参,基本传递;

传递的引用数据类型:实参对象传递引用给形参对象,引用传递;

 

JS引擎如果管理内存?

1.内存的生命周期;

分配小内存空间,得到它的使用权;

存储数据,可以进行反复操作;

释放空间;

2.释放内存:

局部变量:函数执行完自动释放;

对象:成为垃圾对象,被垃圾回收器在页面的某个时刻回收;

 

什么是对象?

多个数据的封装体;

用来保存多个数据的容器;

一个对象对应现实世界中的一个事物;

 

为什么要用对象?

统一管理多个数据;

 

对象的组成?

属性:属性名(字符串,默认是字符串,所以允许不写双引号)和属性值;

方法:一种特殊的属性,属性值为函数;

 

如何访问对象的内部数据?

*.属性名;编码简单,有时不能用;

*[属性名]:编写麻烦能通用;变量名包含特殊字符_和空格;还有变量名不确定的情况;

 

什么是函数?

实现特定功能的n 条语句的封装体;

只有函数是能够执行的;

 

为什么要用函数?

提高代码复用;

便于阅读交流;

 

 

如何定义一个函数?

函数声明

表达式;

 

如何调用一个函数?

test();直接调用

obj.test();对象方法

new test();new调用

test.call(obj)临时让test成为obj的方法调用;

 

回调函数:

什么函数是回调函数?

1.定义的

2.没有调用

3.但最终执行了

 

需要时间和触发的条件:

常见的回调函数?

dom事件回调函数;

定时器回调函数;

 

Ajax请求回调函数;

生命周期回调函数。

 

IIFE:立即执行函数表达式;immediately-invoked function expression(匿名函数的自调用;),只调用一次;

作用:

隐藏实现;

不会影响外部命名空间;

用它来编辑js模块;

(function(){

var a=0;

function test(){

console.log(a++);

}

//向外输出一个全局函数;

window.$=function(){

return={test=test;}

}

})()

//$是一个函数,它返回的是一个对象

$.().test();

 

函数中的this:

this是什么

任何函数本质上都是通过某个对象来调用的,,

所有函数内部都有一个rhis

它的值是当前调用这个函数的对象,没有就是window;

如何确定this

 

关于分号的问题:

尤雨溪:认为不需要加分号

什么情况不可以加分号:

在下面两种情况下不加分号会有问题;

小括号开头的前一句需要加;立即响应函数,匿名函数自调用//以为调用函数

中方括号开头的前一条语句;[1,2,3,4,5]forEach()//以为不该是数组;

合并压缩(节省空间,将变量名缩短):

一般在行首会加上;

vue.js在写框架中的不需要写,在写应用的时候需要写

 

设置代码块,将自己常用的代码块,通过快捷字符,说明;尤其是那么含有()和需要调整光标的位置;

 

函数高级:

原型与原型链:

函数的prototype属性:

每个函数都有一个唯一的prototype属性,它默认指向一个object空对象 (即称为:原型对象);

 

每个原型对象都有一个constructor,它指向函数对象;构造函数和构造实例有着相互引用的关系;原型对象constructor指向对象,对象prototype指向原型对象;

给原型对象添加属性(一般为方法)

作用:函数的所有实例对象都会自动拥有原型对象的属性和方法;

 

对象的prototype显示原型===对象的隐式原型__proto__,他们都保存的是地址值。

 

当创建一个构造的时候,内部会自动创建一条this.prototype={};prototype保存了指向原型对象的地址值;在创建一个函数的同时执行this.__proto__=Fn.prototype;

 

创建一个实例对象,它的__proto__,也会指向这个原型对象的地址,所以可以通过实例对象能够给构造函数的原型添加属性和方法;

内部隐式创建this.__proto__=Fn.prototype

 

程序员可以直接操作显示原型,但是不要直接操作隐式原型;

 

原型链:

Object的函数对象的prototype保存Object原型对象的地址值,原型对象保存大量的可供调用的函数,它的__proto__=null;

 

在创建一个函数对象时它的原型对象指向Object原型对象,它最终指向null;

 

创建一个函数实例,那么它的原型对象的指向Object原型对象,最终指向null

 

function的显示原型和隐式原型都是相等的。

所以所有的函数的显示原型和隐式原型都是相等的。

并不是所有的原型对象的显示原型都指向空的Object原型;

Object是Function()的实例,所以任何函数对象都指向Function;

原型链的尽头是Object原型对象;

 

访问一个对象的属性时:

先在自身属性中查找,然后沿着__proto__这条链向上查找,找到返回;

如果最终没有找到则返回undefined

查找属性本质上隐式原型链;

 

查找对象找作用域链:

 

instanceof:说明这个函数是不是一个对象的实例;

A instanceof  B;

如果B函数的显示原型对象在A对象的原型对象上,就返回true,否则返回false;

 

练习:

function F(){};

 

Object.prototype.a=function(){console.log("a()");}

F.prototype.b=function(){console.log("b()");}

var f=new Foo();

f.a();//true

f.b();//false,f指向F的原型对象,最终指向Object对象,但是不经过函数对象,不经过Function对象原型

F.a();//true

F.b();//true

 

变量提升:

var a=3;

function fn(){

console.log(a);//undefined,原因是在函数体内会自动进行变量提升,所以先访问函数体内的变量(也就是说一般先从函数体找变量,但是没有赋值就是undefined);

var a=4;

}

函数变量提升是怎么产生的;

 

执行上下文:

1.代码分类:

全局代码

局部代码

 

2.全局执行上下文

执行全局代码前将window确定为全局执行上下文;

对全局数据进行预处理;

var定义的全局变量》undefined添加为window的属性;

function声明全局函数》赋值,添加window的方法;funciton内的方法已经执行过了;

 

this--》赋值给window

 

3.函数执行上下文;

在调用函数,准备执行函数之前,创建对应的全文执行上下文对象(虚拟对象);

形参变量arguments 赋值实参列表 添加执行上下文属性

var定义局部变量 undefined添加执行上下文属性

function声明函数 赋值 添加为上下文的方法;

this赋值调用的对象;

 

 

执行上下文栈:

在全局代码执行前,js解释器就会创建一个栈来存储管理所有上下文对象;

 

在执行上下文后,将其添加到栈中;

 

在函数执行上下文后,将其添加到栈中;

 

在当前函数执行完后,将栈顶对象移除;

 

当所有的代码执行完后,栈中只剩下window;

 

后进先出;

 

复习:

在定义函数的时候自动创建一个prototype;

 

在函数对象的内部定义原型对象的属性this.prototype.

 

作用域和作用域链:

作用域是一个区域,它是静态的对于上下文来说;在编写代码时就确定了

 

分类:

全局作用域

函数作用域

没有块作用域(es6有了)

 

隔离变量:

不同作用域的同名变量不会发生冲突;

 

函数作用域和全局作用域的区别:

全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义的鹅时候就已经出现了,而不是在调用的时;

全局执行上下文环境是在全局作用域确定之后,js代码自动创建的;

函数执行上下文是在调用函数时,函数代码执行之前创建的;

 

作用域是静态的,只要函数定义好了就一直存在不会发生变化;

执行上下文是动态的,调用函数时创建的,函数调用结束后会自动释放;

 

联系

上下文环境从属于所在作用域;

全局上下文环境,全局作用域;

函数上下文环境 对应函数的作用域;

 

作用域链

一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。

但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

var x=10;

function fn(){

console.log(x);

}

function show(f){

var x=20;

f();

}

show(fn);

 

求x=?

答案:10:使用作用域去理解?

 

var fn=function(){

console.log(fn);

}

fn();

 

var obj{

fn2:function(){

console.log(fn2);//搜索不到fn2,需要使用this.fn2;沿着作用域找没有找到;

}

}

 

循环遍历加监听:

类数组,它的长度每次都需要重新统计,需要重新赋值给变量,再去遍历,否则需要消耗大量内存;

var btns=get.ElementsByTagName("btn")

for(var i=0,length=btns.length;i<length;i++){

var btn = btn[i];

var index=i;

btn.οnclick=function(){

console.log("第"+this.index+"个");//该事件是在循环结束之后再调用的;

}

}

闭包(实现循环遍历加监听)处理:

for(var i=0,length=btns.length;i<length;i++){

(function(i){

var btn = btn[i];

var index=i;

btn.οnclick=function(){//这里产生了一个闭包,内部函数通过外部的一个元素来调用btn可以调用这个空间,所以 

console.log("第"+this.index+"个");//该事件是在循环结束之后再调用的;

})(i)

}

闭包:(在嵌套的函数中内部函数引用了外部的变量,不会将外部函数的变量释放掉,实现类似于全局变量的效果,但是其他函数不能访问)

如何产生闭包?

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就会产生了闭包;

闭包到底是什么?

使用chrome调试查看

理解一:闭包是嵌套的内部函数(绝大部分人)

理解二:包含被引用变量(函数)的对象(极少数人)

注意:闭包存在于嵌套的内部函数中;

 

产生闭包的条件?

函数嵌套;

内部函数引用了外部函数的数据(变量/函数)

执行外部函数;

 

常见的闭包:

 

将函数作为另一个函数的返回值;

function f1(){

function f2(){

}

return f2;

}

 

将函数作为实物传递给另一个函数调用;

 

闭包的作用:

1.使用函数内部的变量在函数执行完,仍然存活在内存中(延长了局部变量的生命周期);

2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)

*也可以不让外部自由的操作,比如当调用一次这个函数的时候,就会在函数内部自增;

 

导致闭包的根本原因:

function ffn1(){

var a=2;

function fn2(){

a++;

console.log(a);

}

function fn3(){

a--;

console.log(a);}

return fn3;

}

var f=fn1();//根本原因是f保存了指向fn1的空间,空间中还有a没有释放,fn3的地址被释放了,但是a保存了,所以fn3内部的空间没有被清除;

f();

f();

 

问题:函数执行完后,函数内部声明的局部变量是否还存在?一般不存在,存在于闭包中的变量才有可能存在;

问题:在函数外部能直接访问函数内部的局部变量吗?不能,但我们可以通过闭包让外部操作它;

 

闭包的生命周期:

产生;在嵌套内部函数定义执行完时就产生了(不是在调用);函数声明提升

死亡;在嵌套的内部函数成为垃圾对象时?函数没有连接

 

闭包的应用,自定义js模块;

具有自定义功能的js文件;

将所有的数据和功能封装到一个函数内部(私有时)

只向外暴露一个包括n个方法的对象或函数;

模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

function myModule(){

var msg="my jioank";

function dosomething(){

 

}

function dothething(){

 

}

return dosomething;//暴露一个函数;

 

return {

dosomething:dosomething.

dothething:dothething//暴露两个以上的函数使用对象,左边的属性名,右边是函数体;

}

}

上面的方法需要先执行函数才能得到,

匿名函数自调用怎么向外暴露方法呢?使用将暴露对象添加为window的属性即可:

 

 

(function(){

var msg="my jioank";

function dosomething(){

 

}

function dothething(){

 

}

window.myModule2={

dosomething:dosomething.

dothething:dothething

}

})()

使用myModule2.dosomething();调用闭包中的方法;

 

一般匿名函数的闭包写法:

 

(function(window){

var msg="my jioank";

function dosomething(){

 

}

function dothething(){

 

}

window.myModule2={

dosomething:dosomething.

dothething:dothething

}

})(window)//添加了window,这是函数提升的一种方式,可以在内部直接找到window对象

为什么在后面的括号中添加window,这样代码压缩的话chule最后的括号中的window之外其它的可以压缩为w。

 

内存溢出与内存泄露:

闭包的缺点:

函数执行完后,函数内部的局部变量没有释放,占用内存的时间会变长;

容易造成内存泄露

 

解决:

能不用闭包就不用闭包;

及时释放;将调用的对象设置为null;

 

内存溢出、一个程序运行时出现错误;

当程序运行需要的内存超过了剩余的内存,就会抛出内存溢出的错误

 

内存泄露:(就是垃圾回收机器被卡bug了,最终导致没有空间申请给其他对象,或者自己漏掉一些不会被释放的内存)

占用的空间没有及时释放

内存泄露积累多了就容易导致内存溢出;

常见的内存泄露:

意外的全局变量

没有及时清理的计时器和回调函数;

闭包

 

面试题:

 

var name = "the window";

var object = {

name: "My object",

getNameFunc:funtion(){

return function(){

return this.name;

};

}

};

//window

 

var name = "the window";

var object = {

name2: "My object",

getNameFunc:funtion(){

var that=this;//经常用一个变量存储另一个函数的this,通过这个变量调用这个函数

return function(){

return that.name2;

};

}

};

Myobject

 

面试题:

function fun(n,o){

console.log(o);

return {

fun:function(m){

return fun(m,n);

}

}

}

 

var a=fun(0); a.fun(1); a=fun(2); a.fun(3);//undefined,0,0,0

 

 

var b=fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2

var c=fun(0).fun(1); c.fun(2);c.fun(3);//undefined,0,1,1

 

 

 

对象的创建模式:

方法一:Object构造方法创建模式;

 

套路:县创建一个空的object对象,在动态添加属性/方法;

 

适用场景:起始时不确定对象内部数据

 

问题:语句太多;

 

var p = new Object();

p.name="hello";

 

方法二:对象的字面量形式;

套路适用{}创建对象,同时指定属性和方法;

适用场景:起始时对象内部数据是确定的;

问题:创建多个对象,有重复代码;

 

方式三:工厂模式

套路:通过工厂函数动态的创建对象并返回;

适用场景:需要创建多个对象

问题:对象没有一个具体的模型,都是Object类型;

 

方法四:自定义构造模式

套路:自定义构造函数,通过new创建对象

适用场景:需要创建多个类型确定的对象;

问题:每个对象都有相同的数据,浪费内存

 

方法五:构造函数+原型的组合模式

套路:自定义构造函数,属性在函数中初始化,方法添加到原型上;

适用场景:需要创建多个类型确定的对象;

 

 

原型链的继承:

套路:

1.定义父类型构造函数

2.给父类型的原型添加方法

3.定义子类型的构造方法

4.创建父类型的对象赋值给子类型对象的原型;

5.将子类型的原型的构造属性设置为子类型;

6.给子类型原型添加方法

7.创建子类型对象,可以调用父类型方法;

 

关键,子类型的原型,是父类型的一个实例对象;

Sub.prototype=new Supper();

 

创建对象:

function Supper(){

 

}

Supper.prototype.showSupperProp=function(){

 

}

function Sub(){

 

}

 

Sub.prototype=new Supper();

Sub.ptototype.constructor=Sub;

 

 

 

//如果没有设置constructor,指向Supper,所以需要将它的构造器指向Sub,因为创建Supper的实例对象时,将Sub的指向指错了;

套路;

1.定义父类型构造函数;

2.定义子类型的构造函数;

3.在子构造函数中调用父类型构造;

 

关键在子类型的构造函数中通用super()调用父类型构造函数

借用构造函数的继承:(假的)

function Persion(name,age){

this.name=name

this.age=age;

}

 

function Student(name,age,price){

Person.call(this,name,age);

//相当于Student借用了Person的类型;其中没有继承关系;

this,age=age;

}

 

组合继承,原型链+借用构造函数的组合继承;

1.利用原型链实现对父类型的方法进行继承;

2.利用super()借用父类型构造函数化相间属性;

借方法:

Student.prototype=new Person();

Student.prototype.constructot=Student;//修正Student.constructor;

搜索window.a 和a的区别,一个是返回undefined一个是直接提交错误 

 

进程与线程:

进程:程序的一次执行,它占用一片独有的内存空间,可以通过window任务管理器查看

 

线程:进程中一种独立运行的单元,是程序执行的一个完整流程,,是CPU调度最小的单元;

 

可以开启多进程的程序:称为多进程程序;

比如chrome浏览器;

 

如果有多线程,那么为多线程;

 

应用程序的代码,需要运行在某个进程的线程之上

一个进程至少运行一个线程,主线程,进程启动后自动创建;

一个进程中也可以同时运行多个线程,我们说程序是多线程运行的;

一个进程内的数据可以提供其中的多个线程直接共享;

多个线程中的数据不能直接共享;

线程池(thread pool)保存多个线程对象的容器,实现线程对象的反复利用;

 

何为多进程,何为多线程?

 

比较单线程和多线程?

多线程:可以在同一个时间段同时执行不同的程序;

优点:

能有效的提高CPU的利用率;

创建多个线程开销

缺点:

线程间切换开销//每一个线程之间执行的时间被限定,切换线程的开销会有

死锁与状态同步问题

 

单线程:同一个时间段只能执行一条线程

(单线程,编码简单,顺序执行)

优点:顺序编程,简单易懂

缺点:效率低

 

JS是单线程还是多线程的?单线程;

但使用h5中的web workers可以多线程运行;

(主线程,分线程)

 

浏览器是多进程的还是单进程的?

单进程?老版IE、firefox

多进程?chrome、新版ie

如何查看多进程?window任务管理器;

 

浏览器内核:

什么叫内核?

支撑浏览器最核心的程序;

不同的浏览器可能不一样

chrome/safari:webkit

firefox:gecko

ie:trident

360,suogou等大多是trident+webkit;(双核)什么时候用trident或者webkit?涉及钱的切换到trident安全性高一点(一般银行使用ie),不涉及官方服务的一般自动切换webkit;

 

需要使用的网站:

google

github

前端收藏夹

掘金

segmentfault

jquery

vue

react

aunglar 

node

require.js

sea.js

grunt

gulp

webpack

browserify

es6

babel

npm

cnpm

yarn

bower

eslint

mock.js

lodash

less教程

sass教程

stylus中文文档

mdn

awesome

cdn服务

百度地图api

最全前端资源简书

前端面试题

hsdoo汇聚

小程序

 

 

内核有很多模块组成:

主线程模块:

js引擎模块:负责JS程序的编译与运行(动态进行编译的)

html,css文档解析模块:负责页面文本的解析,生成一个个对象根据存储的对象结构,生成不同的元素

dom/css模块:负责dom/css在内存中的相关处理

布局渲染模块:负责页面的布局和效果的绘制(内存中的对象)通过布局标签,将这些对象分别显示在确定的位置,并且将对象中的信息绘制出来;

……

分线程:

定时器模块:负责定时器的管理;//回调函数运行在主线程

事件响应模块:负责事件的管理

网络请求模块:负责ajax请求;

 

定时器引发的思考:

定时器真的是反向执行的吗?

定时器并不能保证真正的定时执行;

一般会延时一下(并不会相差太大),也有可能延时很长时间;//当计算量太大时等都会使延时

 

定时器的回调函数再分线程上进行的吗?

在主线程中执行的,JS是单线程的

 

定时器是如何实现的?

//因为定时器是在分线程执行的

 事件循环模型;

 

如何证明JS是单线程执行的!

setTimeout()的回调函数是在主线程中执行的;

定时器的回调函数只有在运行栈的代码全部执行完才有可能执行;

 

为什么JS设计成单线程?

JS设计成单线程与它的功能有关;

最初JS主要用来与用户交互,以及操作DOM;

这决定了它只能是单线程的,否则会带来复杂的同步问题;(类似于数据库中的脏读等操作)所以只有一个线程能够更新界面,即便有H5

 

setTimeout(function(){console.log("timeout 22222")},2000);

setTimeout(function(){console.log("timeout 11111")},2000);

 

function fn(){

console.log("fn()");

}

fn();

 

console.log("alert之前");

alert("-----");//暂停当前多线程的执行,同时暂停计时,确定后恢复;

console.log("alert之后");

 

                         

代码分类:

初始化代码:(直接执行的代码)setTimeout是初始化代码,但是内部包裹的内容是回调代码;

回调代码:(过段时间才执行的代码)

 

JS引擎执行代码的基本流程;

先执行初始化代码,包含特别的代码;回调函数(异步执行)

设置定时器,本来是初始化代码,不过后面绑定了回调代码

绑定监听

发送ajax请求

后面在某个时刻才会执行回调函数

 

事件循环模型:

JS                                                 \\ webapis

heap             stack                        \\dom将回调函数放入回调队列中

                                                     \\ajax

 存储                  存储                     \\setTimeout定时器管理模块,将回调函数交给这个模块,这已经不是JS引擎来执行管理的。定时器的时间不会出现问题;

对象                   函数

 

event loop

 

callbackqueue          onclick    onload    onDonc//放入待处理的函数对象

 

//在回调函数队列中,只会一个一个的执行回调函数,虽然已经定时器已经将回调函数放入里面,但还是需要等待前面的先执行;

 

任务队列

消息队列

事件队列

都是指的是同一个;

事件轮询:event loop//从任务队列中循环取出回调函数放入执行栈中处理(一直到执行完)

 

事件驱动模型:上面是事件驱动模型;

请求响应的模型:浏览器发请求给服务器处理请求,处理请求返回一个响应数据,浏览器接受响应数据,并在平台上展示出来;

 

H5 web workers多线程:

H5规范了JS多线程的实现;

 

2.相关的API

Worker:构造函数,用于加载分进程的JS

Worker.prototype.onmessage:用于接受另一个线程的回调函数

Worker.prototype.postMessage:向另一个线程发送消息

3.不足:

稍微慢一点;

worker内的代码不能操作DOM

不能跨域加载JS

不是每个浏览器都支持这个新特性;

 

有了web worker我们可将大计算量的代码交给web workers使用,运行时不会冻结用户界面;

但是子线程不得操控DOM且完全受主线程控制,所以这一特性没有改变JS单进程的特性;

主线程代码:

创建worker对象:在主线程创建;

var worker=new Worker("worker.js");//需要创建一个worker.js实现分线程

//接受worker传过来的数据函数;

worker onmessage=function(event){ //onmessage事件监听,获取传递的信息;

console.log(event.data);};

//向worker发送数据

worker.postMessage("helloworld");

 

分线程代码:

//this不是window,

//分线程中看不到window,它不能更新界面;,它不能调用window的方法,console是浏览器实现的,不是window;

var onmessage=function(event){

var number=event.data;

console.log("接受主线程的数据"+number);

var result = fibonacci(number);

postMessage(result)

console.log("分线程向主线程返回数据"+result);

};

来自2017年的视频笔记

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

最新回复(0)