当前位置: 代码迷 >> JavaScript >> JS基础小结
  详细解决方案

JS基础小结

热度:438   发布时间:2012-10-06 17:34:01.0
JS基础总结

弱类型语言,在变量声明时不需显式地指定其数据类型 ,变量的数据类型将根据变量的具体内容推导出来,且根据变量内容的改变而自动更改,而强类型语在变量声明时必须显式地指定其数据类型。

在强类型语言中,变量类型有多种,例如int char float boolean 等
而javascript只有一种类型var ,为变量赋值时会自动判断类型并进行转换,所以javascript是弱语言,就体现在变量定义类型VAR上了。

?

javascript中的全局函数是什么?

全局函数与内置对象的属性或方法不是一个概念。全局函数它不属于任何一个内置对象。JavaScript 中包含以下 7 个全局函数,用于完成一些常用的功能:escape( )、eval( )、isFinite( )、isNaN( )、parseFloat( )、parseInt( )、unescape( )。

注意setTimeout不是!

?

1.判断浏览器

if(document.all)判断浏览器是否支持document.all对象。只有IE支持。因此相当于判断浏览器是否是IE

             function doBack(){
			if(document.all){
				window.location.href = "backIndex.do";
			}else{
				window.location.href = "../backIndex.do";
			}
		}
if(document.all){
	cityname_div = document.getElementById("city_name_div").innerText;
}else{
	cityname_div = document.getElementById("city_name_div").textContent;
}
?

2.join:

join() 方法用于把数组中的所有元素放入一个字符串。

元素是通过指定的分隔符进行分隔的。

1.

<script type="text/javascript">

var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"

document.write(arr.join("-"))

</script>

?George-John-Thomas

?

2.

?

<script type="text/javascript">

var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"

document.write(arr.join())

</script>

?

George,John,Thomas

当join()不含任何内容时,输出带,

join("")带空格时,以空格间隔

?

split:

split() 方法用于把一个字符串分割成字符串数组。

注释:如果把空字符串 ("") 用作 separator,那么 stringObject 中的每个字符之间都会被分割。

注释:String.split() 执行的操作与 Array.join 执行的操作是相反的。

<script type="text/javascript">


var str="How are you doing today?"

document.write(str.split(" ") + "<br />")
document.write(str.split("") + "<br />")
document.write(str.split(" ",3))
</script>

输出:

How,are,you,doing,today?
H,o,w, ,a,r,e, ,y,o,u, ,d,o,i,n,g, ,t,o,d,a,y,?
How,are,you

?

3.window.open

window.close()

http://www.7880.com/Info/Article-2f4d60.html

?

4. 将字符串转换成URL编码?? ? ??

?var myString = "hello all";
?var code = encodeURI(myString);???? //结果: "hello%20all"??
?var str = decodeURI(code);????????? //结果: "hello all"??
//相应的还有:? encodeURIComponent()? decodeURIComponent()?

?

5. 截取字符串??

//截取第 6 位开始的字符  
     var myString = "Every good boy does fine.";  
     var section = myString.substring(6);    //结果: "good boy does fine."  
      
     //截取第 0 位开始至第 10 位为止的字符  
     var myString = "Every good boy does fine.";  
     var section = myString.substring(0,10); //结果: "Every good"  
      
     //截取从第 11 位到倒数第 6 位为止的字符  
     var myString = "Every good boy does fine.";  
     var section = myString.slice(11,-6);    //结果: "boy does"  
      
     //从第 6 位开始截取长度为 4 的字符  
     var myString = "Every good boy does fine.";  
     var section = myString.substr(6,4);     //结果: "good" 

? string .substr(start,length)

????? start: ? 必须

????? length: 可选

<script type="text/javascript">
var str="Hello world!";
document.write(str.substr(3)+"<br />");
document.write(str.substr(3,4));
</script> 

lo world!
lo w

?

slice()方法的两个参数如果有负数,就表示是从后往前计算,
?????? slice()的参数可以为负数,所以要比substring()更加灵活,但没那么宽容了,如果 start比end要大,它将返回一个空字符串

substring()方法的两个参数可以颠倒,
?????? 如果有负数的话计算机会被认为是 0,
??????
substr()方法在FireFox下面,如果第一个参数小于0,FireFox会从后往前的计数,IE则会把第一个参数当成0,第二个参数如果小于0,计算机会认为是0。

<script>
var p="我爱我家的作者是梁左"
document.write(p.slice(-5,-3))
document.write(p.substring(-5,2))
document.write(p.substr(-7,3))
</script>
//显示:作者 我爱 家的作
?
var fullString = "Every dog has his day.";
    var section = fullString.substring(0, 4); // section is "Ever".
    section = fullString.substring(4, 0);     // section is also "Ever".  //substring()参数可以颠倒
    section = fullString.substring(1, 1);     // section is an empty string.
    section = fullString.substring(-2, 4);    // section is "Ever", same as fullString.substring(0, 4);  
?

?

?

6.日期

 获取时间的某部份   
     var myDate = new Date();   
     myDate.getYear();       //获取当前年份(2位)   
     myDate.getFullYear();   //获取完整的年份(4位,1970-????)   
     myDate.getMonth();      //获取当前月份(0-11,0代表1月)   
     myDate.getDate();       //获取当前日(1-31)   
     myDate.getDay();        //获取当前星期X(0-6,0代表星期天)     
        
计算之前或未来的时间   
     var myDate = new Date();   
     myDate.setDate(myDate.getDate() + 10);  //当前时间加10天   
     //类似的方法都基本相同,以set开头,具体参考第2点  

?7.数字处理

 小数转整数   
     var f = 1.5;   
     var i = Math.round(f);  //结果:2 (四舍五入)   
     var i = Math.ceil(f);   //结果:2 (返回大于f的最小整数)   
     var i = Math.floor(f);  //结果:1 (返回小于f的最大整数)   
   
   格式化显示数字   
     var i = 3.14159;   
       
     //格式化为两位小数的浮点数   
     var str = i.toFixed(2);     //结果: "3.14"   
       
     //格式化为五位数字的浮点数(从左到右五位数字,不够补零)   
     var str = i.toPrecision(5); //结果: "3.1415"   
   
   X进制数字的转换        
     var i = parseInt("0x1f",16);   
     var i = parseInt(i,10);   
     var i = parseInt("11010011",2);   
   
   随机数   
     //返回0-1之间的任意小数   
     var rnd = Math.random();   
     //返回0-n之间的任意整数(不包括n)      
     var rnd = Math.floor(Math.random() * n)    

?8.JS中Map

  function HashMap()   
 {   
     /** Map 大小 **/  
     var size = 0;   
     /** 对象 **/  
     var entry = new Object();   
        
     /** 存 **/  
     this.put = function (key , value)   
     {   
         if(!this.containsKey(key))   
         {   
             size ++ ;   
         }   
         entry[key] = value;   
     }   
        
     /** 取 **/  
     this.get = function (key)   
     {   
         return this.containsKey(key) ? entry[key] : null;   
     }   
        
     /** 删除 **/  
     this.remove = function ( key )   
     {   
         if( this.containsKey(key) && ( delete entry[key] ) )   
         {   
             size --;   
         }   
     }   
        
     /** 是否包含 Key **/  
     this.containsKey = function ( key )   
     {   
         return (key in entry);   
     }   
        
     /** 是否包含 Value **/  
     this.containsValue = function ( value )   
     {   
         for(var prop in entry)   
         {   
             if(entry[prop] == value)   
             {   
                 return true;   
             }   
         }   
         return false;   
     }   
        
     /** 所有 Value **/  
     this.values = function ()   
     {   
         var values = new Array();   
         for(var prop in entry)   
         {   
             values.push(entry[prop]);   
         }   
         return values;   
     }   
        
     /** 所有 Key **/  
     this.keys = function ()   
     {   
         var keys = new Array();   
         for(var prop in entry)   
         {   
             keys.push(prop);   
         }   
         return keys;   
     }   
        
     /** Map Size **/  
     this.size = function ()   
     {   
         return size;   
     }   
        
     /* 清空 */  
     this.clear = function ()   
     {   
         size = 0;   
         entry = new Object();   
     }   
 }   
    
 var map = new HashMap();   
    
 /*  
 map.put("A","1");  
 map.put("B","2");  
 map.put("A","5");  
 map.put("C","3");  
 map.put("A","4");  
 */  
 /*  
 alert(map.containsKey("XX"));  
 alert(map.size());  
 alert(map.get("A"));  
 alert(map.get("XX"));  
 map.remove("A");  
 alert(map.size());  
 alert(map.get("A"));  
 */  
    
 /** 同时也可以把对象作为 Key **/  
 /*  
 var arrayKey = new Array("1","2","3","4");  
 var arrayValue = new Array("A","B","C","D");  
 map.put(arrayKey,arrayValue);  
 var value = map.get(arrayKey);  
 for(var i = 0 ; i < value.length ; i++)  
 {  
     //alert(value[i]);  
 }  
 */  
 /** 把对象做为Key时 ,自动调用了该对象的 toString() 方法 其实最终还是以String对象为Key**/  
    
 /** 如果是自定义对象 那自己得重写 toString() 方法 否则 . 就是下面的结果 **/  
    
 function MyObject(name)   
 {   
     this.name = name;   
 }   
    
 /**  
 function MyObject(name)  
 {  
     this.name = name;  
       
     this.toString = function ()  
     {  
         return this.name;  
     }  
 }  
 **/  
 var object1 = new MyObject("小张");   
 var object2 = new MyObject("小名");   
    
 map.put(object1,"小张");   
 map.put(object2,"小名");   
 alert(map.get(object1));   
 alert(map.get(object2));   
 map.remove("xxxxx");   
 alert(map.size());   
    
 /** 运行结果 小名 小名 size = 1 **/  
    
 /** 如果改成复写toString()方法的对象 , 效果就完全不一样了 **/  
    
 </script>  

?JS中in

var arr = ["a","b","2","3","str"];
var result=false;
var result1=false;
for(var i in arr){
 if("b"==arr[i])
   result=true;
 if(4==arr[i])
   result1=true;
}
document.write(result+"<br>");
document.write(result1+"<br>");

这样可以判断的
?

?

?

9.常用DOM的API

 document方法:   
 getElementById(id) Node 返回指定结点的引用   
 getElementsByTagName(name) NodeList 返回文档中所有匹配的元素的集合   
 createElement(name) Node Node   
 createTextNode(text) Node 创建一个纯文本结点   
 ownerDocument Document 指向这个节点所属的文档   
 documentElement Node 返回html节点   
 document.body Node 返回body节点   
   
 element方法:   
 getAttribute(attributeName) String 返回指定属性的值   
 setAttribute(attributeName,value) String 给属性赋值   
 removeAttribute(attributeName) String 移除指定属性和它的值   
 getElementsByTagName(name) NodeList 返回结点内所有匹配的元素的集合   
   
 node方法:   
 appendChild(child) Node 给指定结点添加一个新的子结点   
 removeChild(child) Node 移除指定结点的子结点   
 replaceChild(newChild,oldChild) Node 替换指定结点的子结点   
 insertBefore(newChild,refChild) Node 在同一层级的结点前面插入新结点   
 hasChildNodes() Boolean 如果结点有子结点则返回true   
   
 node属性:   
 nodeName String 以字符串的格式存放结点的名称   
 nodeType String 以整型数据格式存放结点的类型   
 nodeValue String 以可用的格式存放结点的值   
 parentNode Node 指向结点的父结点的引用   
 childNodes NodeList 指向子结点的引用的集合   
 firstChild Node 指向子结点结合中的第一个子结点的引用   
 lastChild Node 指向子结点结合中的最后一个子结点的引用   
 previousSibling Node 指向前一个兄弟节点;如果这个节点就是兄弟节点,那么该值为null   
 nextSibling Node 指向后一个兄弟节点;如果这个节点就是兄弟节点,那么该值为null 

来源:http://www.iteye.com/topic/867588

?

10、Number/parseInt

prompt()函数实际上把用户输入的值返回为字符串类型 ,如同写成了var myCal = 1+"2" ;

如果使用减法运算符 var myCal = 1- num ;会执行减法操作。


Number(): 把括号里的变量值转换为一个数

parseFloat(): 把括号里的值转换成一个浮点数。从左到右逐个字符地解析字符串,直到遇到一个字符不能用在数字里 。然后它会在那个点停止并把这个字符串转换成数字。如果第一个字符就不能再数字里使用,放回NaN(Not a Number);

parseInt(): 把括号里的值转换成一个整数,它不用四舍五入,而是把小数部分直接去掉

?

var num = prompt("Enter a number" , "");
typeof(num)
parseFloat(num)
parseInt(num)

num = Number(num)
num
typeof(num)

?输入23.50

string
23.5
23
23.5
number

?输入23.50abc

string
23.5
23
NaN
number

?这次Number()函数返回了NaN,parseFloat()和parseInt()仍然返回一个数字,因为它们是从左到右尽可能多地把字符串转换成数字,当遇到一个非数字时才停止 Number()函数会拒绝任何包含非数字字符的字符串 (阿拉伯数字、小数、+和-符号都是允许的,其他的字符则不行)

输入abc

string
NaN
NaN
NaN
number

?

?11.动态添加下拉框:

var countrySelect  = document.getElementById('findMarkuplayer1Bean.countryList');
for(var i=0; i<countries.content.length; i++) {
	var opt   = document.createElement("OPTION");   //动态添加下拉框
	opt.value = countries.content[i].code;
	opt.text  = countries.content[i].countrynameEn;
	countrySelect.options.add(opt);   //注意
}

?12、For...in

a = {"a" : "Athens" , "b" : "Belgrade", "c" : "Cairo"}

   for (key in a) {
      s += a[key];
   }
?
for(var d in dataList){ 
     alert("name:"+dataList[d].name);        
      }

?..

?

javascript本身只支持一维数组
有时非要用到二维数组的时候,
可以将一维数组的分项又定义为一个数组,
二维数组就是用数组构成的数组

Var aa=new Array(); //定义一维数组??
for(i=1;i<=10;i++)??
{??
??? aa[i]=new Array(); //将每一个子元素又定义为数组??
??? for(n=0;n<=10;n++)??
??? {??
??????? aa[i][n]=i+n; //此时aa[i][n]可以看作是一个二级数组??
??? }??
}


定义数组还可以这样:var arr=[];
定义对象:var objectname={};

?

12.如何实现鼠标点击页面中的任意标签,alert该标签的名称.(注意兼容性)

document.onclick=function(e){
  e=e||window.event;
  var o=e.srcElement||e.target;
  alert(o.tagName);
}

?

13.对对象进行||操作

a.操作符"|| ". 如果对几个对象同时进行"||"操作的时候,JavaScript首先将这些对象 转换成 true和false,然后在进行"||"。如果一个对象 是存在的(不是undefined,不是null),那么这个对象就会转成true ,否则就为false。在同时进行||操作的几个对象进行计算的时候,顺序从左到右,碰到第一个为true的对象后,就把这个对象返回(把剩下还没有判断的对象忽略),返回的时候,不是返回true或者false,而是返回这个对象自身 ,如下:

(function(passedObj) {
            var existedObj = {};
            var validObject = null ||false ||passedObj || "validString" || existedObj;
            alert(validObject);
  })();
?

(其中passedObj是一个 "需要"传入的参数,实际调用的时候,并没有传入,因此其必定为undefined)

那么其将会弹出 第一为true的对象,即 validString.

b.操作符 "&& " 道理亦然.碰到第一为 false的对象返回,同时忽略剩下还没有判断的对象.

c.操作符"! " 将要判断的对象 转成 true或false后,然后取反.其返回的是true或false

?

14.JS中return

今天一个刚学js的朋友给了我一段代码问为什么方法不执行,代码如下:

function  makefunc(x)  {
			        return function (){
		           		 return  x;
		        	}
	    		}
				alert(makefunc(0));

?其实不是不执行,只是朋友的意思这里alert出来的应该是“0”,而不是function (){return x;}。
不是脚本写错了,只是没搞懂return,从当前函数退出,并从那个函数返回一个值。如果返回的是一个函数,那么返回的也是函数本身。

如果要返回函数执行的结果那么首先要让这个函数执行,例如:

	function  makefunc(x)  {
	        return (function (){
	            return  x;
	        })();
	    }
	alert(makefunc(0));

?这里有一个匿名函数,

	(function (){
	           return  x;
       })();

? 在第一个括号内是匿名函数,第二个括号用于调用该匿名函数, 您可以在第二个括号中传入所需的参数。例如:

(function( x , y){
	    alert( x + y);
	})(2 ,3 );

?来源:http://www.css88.com/archives/3738

 function  makefunc(x)  {  
                     return function (){  
                          return  x;  
                     }  
                 }  
                 alert(makefunc(0)());  //0
?

...

?

15、JS中的delete操作符

Javascript中,变量 = 对象属性,这是因为 Javascript 在执行脚本之前 会创建一个Global对象,所有的全局变量都是这个Global对象的属性, 执行函数时也会创建一个Activation对象,所有的局部变量都是这个Activation对象的属性。

不会删除o.x指向的对象,而是删除o.x属性本身

// Javascript
var o = {};
o.x = new Object();
delete o.x;     // 上一行new的Object对象依然存在
alert(o.x);            // undefined,o的名为x的属性被删除了
?
var o = {};
var a = { x: 10 };
o.a = a;
delete o.a;    // o.a属性被删除
o.a;           // undefined
a.x;           // 10, 因为{ x: 10 } 对象依然被 a 引用,所以不会被回收

?通过var声明的变量和通过function声明的函数 拥有DontDelete特性,无法被删除。

var x = 36;
delete x;
x;     // 36, x没有被删除

y = 12;
delete y;
y;     // undefined

function foo() { return 42; }
delete foo;
foo();  // 42

?prototype中声明的属性就无法被delete

function C() { this.x = 42; }
C.prototype.x = 12;

var o = new C();
o.x;     // 42, 构造函数中定义的o.x
delete o.x;
o.x;     // 12,  prototype中定义的o.x,即使再次执行delete o.x也不会被删除

?

但是有一点例外,就是通过 eval 执行的代码中,通过var声明的变量虽然与正常的var声明变量同属于Global对象,但它们不具有DontDelete特性,能被删除。

eval("var x = 36;");
x;     // 42
delete x;
x;     // undefined

?但是这也有一点例外,eval的代码中的函数内通过var定义的变量具有DontDelete,不能被删除。

eval("(function() { var x = 42; delete x; return x; })();");
// 返回 42

?

delete是普通运算符,会返回true或false。

function C() { this.x = 42; }
C.prototype.y = 12;
var o = new C();

delete o.x; // true
o.x;        // undefined
"x" in o;   // false
// o.x存在并且没有DontDelete,返回true

delete o.y; // true
o.y;        // 12
// o自身没有o.y属性,所以返回true
// 从这里也可以看到prototype链的存在,对象自身属性和prototype属性是不同的

delete o;   // false
// Global.o拥有DontDelete特性所以返回false

delete undefinedProperty;  // true
// Global没有名为undefinedProperty的属性因此返回true

delete 42;  // true
// 42不是属性所以返回true。有的实现会抛出异常(违反ECMAScript标准)

var x = 24;
delete x++;  // true
x;           // 25
// 被删除的是x++的返回值(24),不是属性,所以返回true

?来源:http://tech.idv2.com/2008/01/09/javascript-variables-and-delete-operator/#content_2_0

?

16、Function

很多人都不理解javaScript 中的Function是什么,与function 又有什么区别
简单的说呢 function 是函数,而Function是类

在javaScript 我们声明一个函数,实际上是创建了一个 Function 类的对象。

例如:

function sayHello(name)  
    {  
      alert("Hello "+name);  
    } 

??????
这是一个最简单的函数,参数为name,然后在参数前加上"Hello ",输出
同样,我们也可以这样声明函数
var sayHello = new Function("name","alert('Hello '+name)");
在这种形式中,第一个参数是sayHello 函数的参数,第二个参数是sayHello 函数的函数体

通过这种形式,我们可以很清楚的看出所谓的函数只不过是Function类的一个对象
而函数名只不过是对这个对象的引用。可以简单的把函数名理解成为一个指针,指向所引用的
Function 类的实例。
因为函数名只不过是对象名,所以可以把函数名作为参数传递,如下:

function callSay(funNam,argu)  
    {  
      funNam(argu);  
    }  
     
    callSay(sayHello,"xiaofei");   

?
ok,看到了,你完全可以把函数名当一个变量名来使用,因为它就是一个变量名。

来源:http://xiaofeizm55333.iteye.com/blog/83591

?

17.? void和with的用法

1. void 操作符用法格式如下:
??? ?javascript:void (expression)

void 操作符指定要计算一个表达式但是不返回值。expression 是一个要计算的 JavaScript 标准的表达式。

下面的代码创建了一个超级链接,当用户点击以后不会发生任何事。当用户点击链接时,void(0) 计算为 0,但在 JavaScript 上没有任何效果。
<A HREF="javascript:void(0)">单击此处什么也不会发生</A>

下面的代码创建了一个超级链接,用户单击时会提交表单。
<A HREF="javascript:void(document.form.submit())">单击此处提交表单</A>

2、with 语句通常用来缩短特定情形下必须写的代码量。
with(document)
{
??? write?? ( "test ");
??? write?? ( "dsasfda ");
}
如果不用的话就要这样写了
document.write?? ( "test ")
document.write?? ( "dsasfda ");

?

18、null、 undefined 、typeof

undefined:表示一个对象没有被定义或者没有被初始化
null???????? :表示一个尚未存在的对象的占位符
a.
undefined对象是从null派生出来的,所以他们是相等的。

alert(null == undefined); //true


?

?

//var oTemp;
if(typeof oTemp == undefined)  //false

?这里if将永远是false.要时刻铭记typeof返回的是字符串,应该用字符串比较。

//var oTemp;
if(typeof oTemp == "undefined")  //true

?

alert(typeof null == "object")  //true

? 数组类型

alert(typeof array)    //object

?

var blen = new Boolean(false);   
     if(blen){  //true
     	alert("Test");  
     }
     
     var str = new String("")  //若str = "",则if(str)为false
     if(str){  //true
     	alert("Test");  
     }
     alert(typeof str);  //object, 若str = ""则为string

?

alert(NaN == NaN);  //false

?

b.相等运算符和等同运算符



?

?

c.

一个没有返回值的function(或者直接return 返回)实际上返回的是undefined .

function cal(){
		return;  //undefined
		//return 0 ; 结果0
	}
	alert(cal()); 

?

function本质:


19、JS中变量的声明

注意隐式声明的作用域是全局的,即使是在一个函数内部隐式声明,也是全局的。



20、使用prototype创建的方法是实例方法



?21、使用原型链实现继承

function Car(){
		this.color = "red";
	}
	Car.prototype.showColor = function(){
		alert(this.color);
	}
		
	function GoodCar(){
	
	}
	GoodCar.prototype = new Car();
	//让GoodCar的prototype属性成为Car类的一个实例,即可实现类型的继承
	GoodCar.prototype.run = function(){
		alert("Run fast !");
	}
	var goodCar = new GoodCar();
	goodCar.run();
	goodCar.showColor();

?

?
?。。。

?

22.DOM与HTML



?

24、设置默认值的几种方法



25.Javascript的变量范围

var herp=”one”;
{
var herp=”two”;
}
alert(herp); 

? 在这种情况下你得到的herp 不是“one”,而是“two”。Javascript的变量有效范围并不是跟其它语言一样依赖于代码块。Javascript的变量范围是以函数 为基础的。每个函数都有它自己的变量范围 ,Javascript这一点上表现的很酷,根本不理睬这毫无意义的花括弧包起来的范围。

?

26、javascript命名空间

function derp(){ alert(“one”); }
function derp(){ alert(“two”); }
derp(); 

?答案是“two”。并不是一定会这样,它也可能是“one”。所以,把你所有的代码都放在自己的命名空间里,这很容易。下面是定义自己的命名空间的一个简单做法。

var foospace={};
foospace.derp=function(){ alert(“one”); }
function derp(){ alert(“two”); }
foospace.derp(); 

?。。。

?

27.回到头部

?

function scroller(){	        
	       var sTop=document.documentElement.scrollTop+document.body.scrollTop;
	       		//获取scrollTop值,声明了DTD的标准网页取document.documentElement.scrollTop,否则取document.body.scrollTop;因为二者只有一个会生效,另一个就恒为0,所以取和值可以得到网页的真正的scrollTop值
	       
	        window.scrollBy(0,0-sTop);  //把内容滚动指定的像素数(第一个参数是向右滚动的像素数,第二个参数是向下滚动的像素数)
	             
		}

?scrollTo(10,50);

scrollBy,中文"滚动到"的意思

to是绝对的意思(从整体而言),by是相对的意思(从原先的位置而言)

?

?

?

?

?

?

?

?

  相关解决方案