JS面试题重点

面试题更新中~

1.几种基本数据类型?复杂数据类型?值类型和引用数据类型?堆栈数据结构?

基本数据类型:Undefined、Null、Boolean、Number、String
值类型:数值、布尔值、null、undefined。
引用类型:对象、数组、函数。
堆栈数据结构:是一种支持后进先出(LIFO)的集合,即后被插入的数据,先被取出!
js数组中提供了以下几个方法可以让我们很方便实现堆栈:
shift:从数组中把第一个元素删除,并返回这个元素的值。
unshift: 在数组的开头添加一个或更多元素,并返回新的长度
push:在数组的中末尾添加元素,并返回新的长度
pop:从数组中把最后一个元素删除,并返回这个元素的值。

2.判断数据类型?

typeof返回的类型都是字符串形式,可以判断function的类型;在判断除Object类型的对象时比较方便。
判断已知对象类型的方法: instanceof,后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。

3.异步编程?

方法1:回调函数,优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

方法2:时间监听,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以“去耦合”(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

方法3:发布/订阅,性质与“事件监听”类似,但是明显优于后者。

方法4:Promises对象,是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。
简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

4.事件流?事件捕获?事件冒泡?

事件流:从页面中接收事件的顺序。也就是说当一个事件产生时,这个事件的传播过程,就是事件流。

IE中的事件流叫事件冒泡;事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。对于html来说,就是当一个元素产生了一个事件,它会把这个事件传递给它的父元素,父元素接收到了之后,还要继续传递给它的上一级元素,就这样一直传播到document对象(亲测现在的浏览器到window对象,只有IE8及下不这样

事件捕获是不太具体的元素应该更早接受到事件,而最具体的节点应该最后接收到事件。他们的用意是在事件到达目标之前就捕获它;也就是跟冒泡的过程正好相反,以html的click事件为例,document对象(DOM级规范要求从document开始传播,但是现在的浏览器是从window对象开始的)最先接收到click事件的然后事件沿着DOM树依次向下传播,一直传播到事件的实际目标;

5.如何清除一个定时器?

window.clearInterval();
window.clearTimeout();

6.如何添加一个dom对象到body中?innerHTML和innerText区别?

body.appendChild(dom元素);
innerHTML:从对象的起始位置到终止位置的全部内容,包括Html标签。
innerText:从起始位置到终止位置的内容, 但它去除Html标签
分别简述五个window对象、属性

成员对象
window.event window.document window.history
window.screen window.navigator window.external
Window对象的属性如下:
window //窗户自身
window.self //引用本窗户window=window.self
window.name //为窗户命名
window.defaultStatus //设定窗户状态栏信息
window.location //URL地址,配备布置这个属性可以打开新的页面

7.回调函数?

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

8.什么是闭包?* 堆栈溢出有什么区别? 内存泄漏? 那些操作会造成内存泄漏?怎么样防止内存泄漏?

闭包:就是能够读取其他函数内部变量的函数。
堆栈溢出:就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界,结果覆盖了别的数据。经常会在递归中发生。
内存泄露是指:用动态存储分配函数内存空间,在使用完毕后未释放,导致一直占据该内存单元。直到程序结束。指任何对象在您不再拥有或需要它之后仍然存在。

造成内存泄漏:
setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
防止内存泄露:
1、不要动态绑定事件;
2、不要在动态添加,或者会被动态移除的dom上绑事件,用事件冒泡在父容器监听事件;
3、如果要违反上面的原则,必须提供destroy方法,保证移除dom后事件也被移除,这点可以参考Backbone的源代码,做的比较好;
4、单例化,少创建dom,少绑事件。

9 简述ajax执行流程

基本步骤:

var xhr =null;//创建对象

if(window.XMLHttpRequest){

       xhr = new XMLHttpRequest();

}else{

       xhr = new ActiveXObject("Microsoft.XMLHTTP");

}

xhr.open(“方式”,”地址”,”标志位”);//初始化请求

   xhr.setRequestHeader(“”,””);//设置http头信息

xhr.onreadystatechange =function(){}//指定回调函数

xhr.send();//发送请求

10.自执行函数?用于什么场景?好处?

自执行函数:1、声明一个匿名函数2、马上调用这个匿名函数。
作用:创建一个独立的作用域。

好处:防止变量弥散到全局,以免各种js库冲突。隔离作用域避免污染,或者截断作用域链,避免闭包造成引用变量无法释放。利用立即执行特性,返回需要的业务函数或对象,避免每次通过条件判断来处理

场景:一般用于框架、插件等场景

11. 什么是构造函数?与普通函数有什么区别?

构造函数:是一种特殊的方法、主要用来创建对象时初始化对象,总与new运算符一起使用,创建对象的语句中构造函数的函数名必须与类名完全相同。
与普通函数相比只能由new关键字调用,构造函数是类的标示

12. 通过new创建一个对象的时候,函数内部有哪些改变

function Person(){}

Person.prototype.friend = [];

Person.prototype.name = '';

// var a = new Person();

// a.friend[0] = '王琦';

// a.name = '程娇';

// var b = new Person();

// b.friend?

// b.name?

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

13.事件委托?有什么好处?

1)利用冒泡的原理,把事件加到父级上,触发执行效果
2)好处:新添加的元素还会有之前的事件;提高性能。

14.节点类型?判断当前节点类型?

1. 元素节点
2. 属性节点
3. 文本节点
8. 注释节点
9. 文档节点
通过nodeObject.nodeType判断节点类型:其中,nodeObject 为DOM节点(节点对象)。该属性返回以数字表示的节点类型,例如,元素节点返回 1,属性节点返回 2 。

15.如何合并两个数组?数组删除一个元素?

//三种方法。

 1)var arr1=[1,2,3];

               var arr2=[4,5,6];

               arr1 = arr1.concat(arr2);

               console.log(arr1);

 2)var arr1=[1,2,3];

               var arr2=[4,5,6];

               Array.prototype.push.apply(arr1,arr2);

               console.log(arr1);

 3)var arr1=[1,2,3];

               var arr2=[4,5,6];

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

                   arr1.push( arr2[i] );

               }

               console.log(arr1);   

16.强制转换 显式转换 隐式转换?

//强制类型转换:

Boolean(0)                // => false - 零

           Boolean(new object())   // => true - 对象

               Number(undefined)       // =>   NaN

               Number(null)              // => 0

               String(null)              // => "null"

parseInt( )

parseFloat( )

JSON.parse( )

JSON.stringify ( )

隐式类型转换:
在使用算术运算符时,运算符两边的数据类型可以是任意的,比如,一个字符串可以和数字相加。之所以不同的数据类型之间可以做运算,是因为JavaScript引擎在运算之前会悄悄的把他们进行了隐式类型转换的
(例如:x+"" //等价于String(x)
+x //等价于Number(x)
x-0 //同上
!!x //等价于Boolean(x),是双叹号)

显式转换:
如果程序要求一定要将某一类型的数据转换为另一种类型,则可以利用强制类型转换运算符进行转换,这种强制转换过程称为显示转换。
显示转换是你定义让这个值类型转换成你要用的值类型,是底到高的转换。例 int 到float就可以直接转,int i=5,想把他转换成char类型,就用显式转换(char)i

17.Jq中get和eq有什么区别?

get() :取得其中一个匹配的元素。num表示取得第几个匹配的元素,get多针对集合元素,返回的是DOM对象组成的数组 eq():获取第N个元素,下标都是从0开始,返回的是一个JQuery对象

18.如何通过原生js 判断一个元素当前是显示还是隐藏状态?

if( document.getElementById("div").css("display")==='none')

if( document.getElementById("div").css("display")==='block')

$("#div").is(":hidden"); // 判断是否隐藏

$("#div").is(":visible")

19.Jq如何判断元素显示隐藏?

//第一种:使用CSS属性

var display =$('#id').css('display');

if(display == 'none'){    alert("我是隐藏的!"); }

//第二种:使用jquery内置选择器

<div id="test"> <p>仅仅是测试所用</p> </div>

if($("#test").is(":hidden")){        $("#test").show();    //如果元素为隐藏,则将它显现 }else{       $("#test").hide();     //如果元素为显现,则将其隐藏 }

//第三种:jQuery判断元素是否显示 是否隐藏

var node=$('#id');

if(node.is(':hidden')){  //如果node是隐藏的则显示node元素,否则隐藏

  node.show(); 

}else{

  node.hide();

}

20.Jq绑定事件的几种方式?on bind ?

jQuery中提供了四种事件监听方式,分别是bind、live、delegate、on,对应的解除监听的函数分别是unbind、die、undelegate、off

Bind( )是使用频率较高的一种,作用就是在选择到的元素上绑定特定事件类型的监听函数;

Live( )可以对后生成的元素也可以绑定相应的事件,处理机制就是把事件绑定在DOM树的根节点上,而不是直接绑定在某个元素上;

Delegate( )采用了事件委托的概念,不是直接为子元素绑定事件,而是为其父元素(或祖先元素也可)绑定事件,当在div内任意元素上点击时,事件会一层层从event target向上冒泡,直至到达你为其绑定事件的元素;

on( )方法可以绑定动态添加到页面元素的事件,on()方法绑定事件可以提升效率;

21.Jq中如何将一个jq对象转化为dom对象?

方法一:
jQuery对象是一个数据对象,可以通过[index]的方法,来得到相应的DOM对象。
如:var $v =$("#v") ; //jQuery对象
var v=$v[0]; //DOM对象
alert(v.checked) //检测这个checkbox是否被选中

方法二:
jQuery本身提供,通过.get(index)方法,得到相应的DOM对象
如:var $v=$("#v"); //jQuery对象
var v=$v.get(0); //DOM对象
alert(v.checked) //检测这个checkbox是否被选中

22.Jq中有几种选择器?分别是什么?

层叠选择器、基本过滤选择器、内容过滤选择器、可视化过滤选择器、属性过滤选择器、子元素过滤选择器、表单元素选择器、表单元素过滤选择器

23. $(function(){})和window.onload 和 $(document).ready(function(){})

window.onload:用于当页面的所有元素,包括外部引用文件,图片等都加载完毕时运行函数内的函数。load方法只能执行一次,如果在js文件里写了多个,只能执行最后一个。

$(document).ready(function(){})和$(function(){})都是用于当页面的标准DOM元素被解析成DOM树后就执行内部函数。这个函数是可以在js文件里多次编写的,对于多人共同编写的js就有很大的优势,因为所有行为函数都会执行到。而且$(document).ready()函数在HMTL结构加载完后就可以执行,不需要等大型文件加载或者不存在的连接等耗时工作完成才执行,效率高。

24. 简述下 this 和定义属性和方法的时候有什么区别?Prototype?

this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。

prototype本质上还是一个JavaScript对象。 并且每个函数都有一个默认的prototype属性。

prototype上定义的属性方法为所有实例共享,所有实例皆引用到同一个对象,单一实例对原型上的属性进行修改,也会影响到所有其他实例。

25.ajax 和 jsonp ?

ajax和jsonp的区别:
相同点:都是请求一个url
不同点:ajax的核心是通过xmlHttpRequest获取内容
jsonp的核心则是动态添加<script>标签来调用服务器 提供的js脚本。

26.ajax执行流程?

1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象
2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
3. 设置响应HTTP请求状态变化的函数
4. 发送HTTP请求
5. 获取异步调用返回的数据
6. 使用JavaScript和DOM实现局部刷新

27.xhr对象 status ? readystate?

status是XMLHttpRequest对象的一个属性,表示响应的HTTP状态码。
readyState是XMLHttpRequest对象的一个属性,用来标识当前XMLHttpRequest对象处于什么状态。

28.readystate 0~4

0:未初始化状态:此时,已经创建了一个XMLHttpRequest对象
1: 准备发送状态:此时,已经调用了XMLHttpRequest对象的open方法,并且XMLHttpRequest对象已经准备好将一个请求发送到服务器端
2:已经发送状态:此时,已经通过send方法把一个请求发送到服务器端,但是还没有收到一个响应
3:正在接收状态:此时,已经接收到HTTP响应头部信息,但是消息体部分还没有完全接收到
4:完成响应状态:此时,已经完成了HTTP响应的接收

29.说出几个http协议状态码?

200, 201, 302, 304, 400, 404, 500

200:请求成功
201:请求成功并且服务器创建了新的资源
302:服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。
304:自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
400:服务器不理解请求的语法。
404:请求的资源(网页等)不存在
500: 内部服务器错误

30.sessionStorage和localstroage与cookie之间有什么关联, cookie最大存放多少字节

三者共同点:都是保存在浏览器端,且同源的。

区别:
1、cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存

2、存储大小限制也不同,cookie数据不能超过4k,sessionStorage和localStorage 但比cookie大得多,可以达到5M

3、数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭

4、作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面(即数据不共享);localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的( 即数据共享 )。

31.ajax中 get 和 post 有什么区别?

get和post都是数据提交的方式。
get的数据是通过网址问号后边拼接的字符串进行传递的。post是通过一个HTTP包体进行传递数据的。
get的传输量是有限制的,post是没有限制的。
get的安全性可能没有post高,所以我们一般用get来获取数据,post一般用来修改数据。

32.Gc机制是什么?为什么闭包不会被回收变量和函数?

1、Gc垃圾回收机制;
2、外部变量没释放,所以指向的大函数内的小函数也释放不了

33.this是什么 在不同场景中分别代表什么

1)function a(){ this ?} //This:指向windows

2)function b(){ return function(){ this ?}}b()(); //This:指向windows

3)function c(){ return {s:function(){this}}}c().s(); //This:指向object

由于其运行期绑定的特性,JavaScript 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。

34.在jq中 mouseover mouseenter mouseout mouseleave 和 hover有什么关联?

mouseenter与mouseover:
不论鼠标指针穿过被选中元素或其子元素,都会触发mouseover事件。
只有在鼠标指针穿过被选元素时,才会触发mouseentr事件。

mouseout与mouseleave:
不论鼠标离开被选元素还是任何子元素,都会触发mouseout事件。
只有在鼠标指针离开被选元素时,才会触发mouseleave事件。

hover:
hover是一个符合方法,相当于mouseenter+mouseleave。

35.split() join()?

前者是切割成数组的形式,
后者是将数组转换成字符串

36.slice() splice()?

slice() 方法可从已有的数组中返回选定的元素。
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。

37.typeof?typeof [ ]返回数据类型是?

//判断基本数据类型;var a=[];typeof a输出object;

//本来判断一个对象类型用typeof是最好的,不过对于Array类型是不适用的,

//可以使用 instanceof操作符:

       var arrayStr=new Array("1","2","3","4","5");    

       alert(arrayStr instanceof Array);

//当然以上在一个简单的页面布局里面是没有问题的,如果是复杂页面情况,

//入获取的是frame内部的Array对象,可以用这个函数判断:

       function isArray(obj) {      

          return Object.prototype.toString.call(obj) === '[object Array]';       

       }

38.同步异步?

1、进程同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事

2、异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

39.promise

Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。

40.变量作用域?

//变量作用域:一个变量的作用域是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,

//在js代码中任何地方都是有定义的。在函数内声明的变量只在函数体内有定义,它们是局部变量,

//作用域是局部性的。函数参数也是局部变量,它们只在函数体内有定义。

var a = "";

window.b=''”;

function(e) {

       var c= "";

       d="";

       e="";

}

function go() {

       console.info(this);//window

       return function() {

               console.info(this); // window

               return {

                b:function(){

                       console.info(this); //b的父对象

                   }

            }

       }

}

go()().b();

41.call & apply 两者之间的区别

call和apply都是改变this指向的方法,区别在于call可以写多个参数,而apply只能写两个参数,第二个参数是一个数组,用于存放要传的参数。

42.call和apply 有什么好处?

call和apply:实现更好的继承和扩展,更安全。

43.压缩合并目的?http请求的优化方式?

1)Web性能优化最佳实践中最重要的一条是减少HTTP请求。而减少HTTP请求的最主要的方式就是,合并并压缩JavaScript和CSS文件。

CSS Sprites(CSS精灵):把全站的图标都放在一个图像文件中,然后用CSS的background-image和background-position属性定位来显示其中的一小部分。

合并脚本和样式表; 图片地图:利用image map标签定义一个客户端图像映射,(图像映射指带有可点击区域的一幅图像)具体看: http://club.topsage.com/thread-2527479-1-1.html

图片js/css等静态资源放在静态服务器或CDN服时,尽量采用不用的域名,这样能防止cookie不会互相污染,减少每次请求的往返数据。

css替代图片, 缓存一些数据

少用location.reload():使用location.reload() 会刷新页面,刷新页面时页面所有资源 (css,js,img等) 会重新请求服务器。建议使用location.href="当前页url" 代替location.reload() ,使用location.href 浏览器会读取本地缓存资源。

44.如何copy一个dom元素?

原生Js方法:var div = document.getElementsByTagName('div')[0];
var clone = div.cloneNode();
Jquery方法:$('div').clone();
在默认情况下,.clone()方法不会复制匹配的元素或其后代元素中绑定的事件。不过,可以为这个方法传递一个布尔值参数,将这个参数设置为true, 就可以连同事件一起复制,即.clone(true)。

45.数组的排序方法(sort)?排序?汉字排序?

数组的排序方法:reverse()和sort()。reverse()方法会对反转数组项的顺序。
Eg:var values = [0, 1, 5, 10, 15]; values.sort();//0,1,10,15,5
var values = [1, 2, 3, 4, 5]; values.reverse();//5,4,3,2,1

js中的排序(详情参考:http://www.tuicool.com/articles/IjInMbU
利用sort排序, 冒泡排序, 快速排序, 插入排序, 希尔排序, 选择排序
归并排序

localeCompare() 方法用于字符串编码的排序
localeCompare 方法:返回一个值,指出在当前的区域设置中两个字符串是否相同。

46. js的几种继承方式?

1.使用对象冒充实现继承
2.采用call、Apply方法改变函数上下文实现继承
3.原型链方式继承

47. JavaScript原型,原型链 ? 有什么特点?

JavaScript中,一共有两种类型的值,原始值和对象值.每个对象都有一个内部属性[[prototype]],我们通常称之为原型.原型的值可以是一个对象,也可以是null.如果它的值是一个对象,则这个对象也一定有自己的原型.这样就形成了一条线性的链,我们称之为原型链.

访问一个对象的原型可以使用ES5中的Object.getPrototypeOf方法,或者ES6中的__proto__属性. 原型链的作用是用来实现继承,比如我们新建一个数组,数组的方法就是从数组的原型上继承而来的。

48. eval是做什么的?

它的功能是把对应的字符串解析成JS代码并运行; 应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)。

49. null,undefined 的区别?

undefined表示变量声明但未初始化的值,null表示准备用来保存对象,还没有真正保存对象的值。从逻辑角度看,null表示一个空对象指针。

50. JSON 的了解?

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小。

defer和async、动态创建DOM方式(用得最多)、按需异步载入js

51. ajax 是什么?

异步javascript和XML,是指一种创建交互式网页应用的网页开发技术。通过后台与服务器进行少量数据交换,AJAX可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

52. 同步和异步的区别?

javascript同步表示sync,指:代码依次执行 javascript异步表示async,指:代码执行不按顺序,‘跳过’执行,待其他某些代码执行完后再来执行,成为异步。

53. 如何解决跨域问题?

Jsonp、iframe、window.name、window.postMessage、服务器上设置代理页面

54. 你有哪些性能优化的方法?

(1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip, CDN托管,data缓存 ,图片服务器。

(2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数

(3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。

(4) 当需要设置的样式很多时设置className而不是直接操作style。

(5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。

(6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。

(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。

(8) 避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示比div+css布局慢。

55. 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?(流程说的越详细越好)

查找浏览器缓存
DNS解析、查找该域名对应的IP地址、重定向(301)、发出第二个GET请求
进行HTTP协议会话
客户端发送报头(请求报头)
服务器回馈报头(响应报头)
html文档开始下载
文档树建立,根据标记请求所需指定MIME类型的文件
文件显示

浏览器这边做的工作大致分为以下几步:
加载:根据请求的URL进行域名解析,向服务器发起请求,接收文件(HTML、JS、CSS、图象等)。
解析:对加载到的资源(HTML、JS、CSS等)进行语法解析,建议相应的内部数据结构(比如HTML的DOM树,JS的(对象)属性表,CSS的样式规则等等)

56. ajax的缺点

1、ajax不支持浏览器back按钮。
2、安全问题 AJAX暴露了与服务器交互的细节。
3、对搜索引擎的支持比较弱。
4、破坏了程序的异常机制。
5、不容易调试

以上仅限于个人总结,仅作交流学习。

猜你喜欢

转载自www.cnblogs.com/w-yue/p/11987160.html