JavaScript比较新的前端面试题

xiaoxiao2021-02-28  95

0. 序

本文没有太多基础性的js内容,推荐一本书《JavaScript高级程序设计》,基础不好的同学建议先学习该本书。重点内容:事件,闭包,作用域练,原型,继承。

1.情简述一下javascript的执行环境(执行上下文)以及所涉及到到一些概念?(该文的整体笔记丢失,详细内容建议查看引用链接)

每当控制器转到可执行环境时,就会进入一个执行环境。

而执行环境大致分为三类:

全局环境函数环境eval(个人不了解,开发也不会用到不做讲解)

执行环境是靠栈来存储,每当开始执行js代码的时候,就进入到了全局执行环境,于是乎,就把全局执行环境压到了这个栈(我们暂时称它为执行环境栈)中。

而在全局代码中,会定义很多函数,函数中又会定义很多函数,而每当控制器执行到函数时,则就进入到了这个函数的执行环境中。

http://www.jianshu.com/p/cd3fee40ef59

2.sessionStorage,localStorage,cookie区别

共同点:都是浏览器端的数据存储,同源;不同点: cookie在同源的http请求中携带,浏览器与服务器之前回传。cookie有路径的概念;cookie大小不超过4k。sessionStorage(浏览器关闭窗口前有效)和localStorage(一直有效)达到5M或者更大。cookie在设置的有效期前有效session不在不同的浏览器窗口中共享,loacl共享,cookie也共享。sessionStorage和localStorage支持事件机制

3.localStorage应该如何进行存储?

// 将数据存在loaclStorage中 export function saveToLocal(id, key, value) { let seller = window.localStorage.__seller__; if (!seller) { seller = {}; seller[id] = {}; } else { seller = JSON.parse(seller); if (!seller[id]) { seller[id] = {}; } } seller[id][key] = value; window.localStorage.__seller__ = JSON.stringify(seller); }; export function loadFromLocal(id, key, def) { let seller = window.localStorage.__seller__; if (!seller) { return def; } seller = JSON.parse(seller)[id]; if (!seller) { return def; } let ret = seller[key]; return ret || def; };

4.GET与POST的区别?

GET与POST都是HTTP协议中的请求发送方式,实际上他们都是TCP,所能做的事情都是一样的。不同在于,HTTP规定,发送GET请求时,在HTTP Header的请求行上就声明GET,反之POST就声明POST。HTTP要求,GET把数据放在url上,所以GET常用于发少量的数据用于查询;POST把数据放在body中,数据量相对较大用来存储。GET请求只发一次,POST发两次,header返回后再发送date。

5.同源策略指的是什么?

同源指的是以下三个都相同: 协议相同域名相同端口相同 所谓同源策略指的是:浏览器对不同源的脚本或者文本的访问方式进行的限制。比如源a的js不能操作引入的源b的元素属性。限制主要为: Cookie、LocalStorage 和 IndexDB 无法读取。DOM无法获取AJAX请求不能发送

6.如何才能跨域,跨域方式有哪些?

设置document.domain

Cookie是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页的一级域名相同,只是二级域名不相同,浏览器允许通过设置document.domain共享Cookie。

在 www.a.com/a.html 中

document.domain = 'a.com'; var ifr = document.createElement('iframe'); ifr.src = 'http://www.script.a.com/b.html'; ifr.display = none; document.body.appendChild(ifr); ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; // 这里就能b.html的cookie了 ifr.cooike = doc.cookie; };

在 script.a.com/b.html 中

document.cookie = "test1=hello"; document.domain = 'a.com';

这样不仅能访问b的cookie,还可以访问b的dom,但是无法访问到LocalStorage 和 IndexDB,而且主要限制是a,b必须一级域名必须相同。

window.name

浏览器窗口都有window.name这个属性,这个属性最大的特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页就可以读取它。

在b.com/data.html中有这样的数据

<script> window.name = '我是你们想要的数据。'; </script>

现在a.com/index.html想获取这个数据应该怎么办?通过iframe充当中间人

<script> function getDate(url){ var iframe = document.createElement('iframe'); iframe.style.display='none'; var state = 0; iframe.onload = function(){ if(state == 1){ var dataPage = iframe.contentWindow; var data = dataPage.name; console.log(data); dataPage.document.write(''); dataPage.close(); document.body.remove(iframe); }else { state = 1; iframe.contentWindow.location = url; } }; // 代理源,创建好且设置了document.name iframe.src = 'a.com/b.html'; } getData('b.com/data.html'); </script>

些人可能会问为什么不直接把iframe的src设置为目的源(b.com/data.html)来获取数据,而是在设置为目的源之后,还要把src设置为同域名下的其他源(a.com/b.html)才获取数据?

如果直接设置src,那么iframe和本网页(a.com/index.html)会因为同源策略限制不能访问。而把iframe先设置为目的源,再设置为同域名下的其他源,那么同域名下的其他源就和目的源共享了一个窗口,故拥有同样window.name,并且由于是同域名下的源,并且设置了domain,故可以访问目标源的window.name。

state用来干什么 ?

每次设置src,都会刷新,state是标志位,让获取了数据就销毁掉。

window.postMessage

上面的方法都是破解,H5提供了官方的API:window.postMessage。允许跨窗口通信,不论这两个窗口是否同源。

// 举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。 var popup = window.open('http://bbb.com', 'title'); popup.postMessage('Hello World!', 'http://bbb.com');

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

// 子窗口向父窗口发送消息的写法类似。 window.opener.postMessage('Nice to see you', 'http://aaa.com'); // 父窗口和子窗口都可以通过message事件,监听对方的消息。 window.addEventListener('message', function(e) { console.log(e.data); },false); // event.source:发送消息的窗口 // event.origin: 消息发向的网址 // event.data: 消息内容

下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。

window.addEventListener('message', receiveMessage); function receiveMessage(event) { event.source.postMessage('Nice to see you!', '*'); } AJAX

利用jsonp

// 首先,网页动态插入<script>元素,由它向跨源网址发出请求。 function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag('http://example.com/ip?callback=foo'); } function foo(data) { console.log('Your public IP address is: ' + data.ip); };

阮一峰老师:浏览器同源政策及其规避方法

7. AJAX模型是什么?

AJAX全称为“AsynchronousJavaScript and XML” 异步的JavaScript和Xml,是一种创建交互式网页应用开发的新技术。 运用AJAX // 创建xmlHttpRequest对象 function createXmlHttp(){ var xmlHttp; if(window.XMLHttpRequest){ xmlHttp = new XMLHttpRequest(); }else{ xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); } return xmlHttp; } var xhr = createXmlHttp(); // 发送请求GET或者POST // 第三个参数代表是否异步 xhr.open('GET',url+'?date='+ new Date().getTime(),true); //GET发送的数据在url上 POST发送的数据在send()中 xhr.send(); // 回调函数响应 xhr.onreadystatechange=function(){ if(xhr.readyState == 4 && xhr.status == 200){ document.getElementById("myDiv").innerHTML=xhr.responseText; } } // xhr.readyState存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。 // 0: 请求未初始化 // 1: 服务器连接已建立 // 2: 请求已接收 // 3: 请求处理中 // 4: 请求已完成,且响应已就绪 // status:状态码 // 如果是同步,直接调用xhr.responseText即可

8.为什么说jsonp不是真正的ajax?

为什么说jsonp是ajax?

因为他们目的一样,都是请求一个url,然后把服务器返回的数据进行处理。故jquery把jsonp封装为ajax的一种形式。

那为什么jsonp不是真正的ajax?

ajax和jsonp其实本质上是不同的东西,ajax的核心通过XmlHttpRequest获取非本页面内容,而jsnop的核心是动态加载script来调用服务器提供的js脚本。

ajax 和jsonp 不是一码事 细读详解

9.property 和 attribute有什么区别?

property表示DOM的基础属性。 <input id='ipt' value='123' other='1111' /> console.log(document.getElementById('ipt'));

可以发现有attributes,value,id,以及很多我们并没有赋值的属性,但是并没有other属性。value,id这样的就叫做DOM的基础属性。

正如打印出来的结果还有一个叫attributes的属性,类型是NamedNodeMap。这里就可以说attributes是DOM 的property的其中一个。

点击开这个你会发现有

0: id 1: value 2: other length: 3 __proto__: NamedNodeMap

这个属性包含了显示定义在标签上的属性。

console.log(ipt.attributes.other); // other="1111";

并且

console.log(typeof ipt.attributes.other); // object

返回的是一个对象。

property能够从attribute中得到同步,attribute不会同步property上的值; in1.value = 'new value of prop'; console.log(in1.value); // 'new value of prop' console.log(in1.attributes.value); // 'value="1"' in1.attributes.value.value = 'new value of attr'; console.log(in1.value); // 'new value of attr' console.log(in1.attributes.value); // 'new value of attr'

10. 关于==的强制类型转换

image

重点:

Number与String比较,String转Number。Number与Object比较,Object转String。当比较中出现Boolean,无论另一个数据是什么类型,都先把Boolean转为Number。undefined与null,undefined与undefined, null与null都返回true。Object 与 Object只有引用相同返回true。

数据类型转化为Boolean的规则:

String的 “” 转换为Boolean为false其他都为trueNumber的0和NaN转换为Boolean为false其他都为trueObject的null转换为Boolean为false其他都为trueUndefined转换为false 不能转换为true [] == ![] //true // 1.由于!的优先级比较高,先执行![],[]是空数组,数组是对象,由2.3(需要了解的知识文字序号),则[]转换为boolean结果为true,![]结果就为false,表达式变为判断 []==false // 2.根据1.3,将false转为Number,结果为0,表达式变为判断 [] == 0 // 3.根据1.2,将[]变为String,结果为"",表达式变为判断 "" == 0 // 4.根据1.1,将""变为Number,结果为0,表达式变为判断 0 == 0 返回结果 true console.log(1 == true); // 文字1.3Number(true) = 1 -> 1 == 1 true console.log(2 == true); // 文字1.3Number(true) = 2 -> 2 == 1 false console.log(0 == false);//文字 1.3Number(false)= 0 -> 0 == 0 true console.log(NaN == NaN);// 图片1.c.i/1.c.ii false console.log({} == {});// 图片1.f {}是对象,比较引用指向的空间,因为是两个不同的空对象,地址也不一样 false console.log([] == []);// 同理 console.log(null == 0);// 文字1.2 null是对象,String(null) == "null" -> "null" == 0 ,文字1.1 Number("null") == NaN -> NaN == 0 false console.log(undefined == 0);// 这里将执行String(undefined),之后执行步骤同上 总结: undefined == null,结果是true。且它俩与所有其他值比较的结果都是falseString == Boolean,需要两个操作数同时转为NumberString/Boolean == Number,需要String/Boolean转为Number。 Object == Primitive,需要Object转为Primitive(具体通过valueOf和toString方法)。

11.支持正则表达式的方法:

RegExp 对象方法 // 1. complie() 用于编译正则表达式或改变正则表达式 let str = 'woman1manm'; let patt = /(wo)?man/g; let str2 = str.replace(patt,'2222'); // 222212222m let patt2 = /2222/g; patt.compile(patt2); // 改变了正则表达式 let str3 = str2.replace(patt,'woman'); // 返回 woman1womanmm // 2. exec() 未匹配到返回一个null,匹配到则返回一个数组arr // arr[0]表示匹配到的这个字符串,arr[1]...arr[n]表示依次匹配到的圆括号的值,如果有的话 // 并且arr有两个属性,index表示第一次匹配到的字符串下标,input表示需要匹配的字符串本身 let str = '22222womanmmmmmmmanmmmmman'; let patt = /(wo)?(man)/g; let what = patt.exec(str); console.log(what); // 返回 : // [ 'woman', // 'wo', // 'man', // index: 5, // input: '22222womanmmmmmmmanmmmmman' ] // 因为patt是全局搜索,所以exec还可以继续搜索,并且这个时候patt还有一个属性lastIndex表示 匹配文本的最后一个字符的下一个位置 console.log(patt.lastIndex) // 10 what = patt.exec(str); console.log(what); // 返回 : // [ 'man', // 'undefined', // 'man', // index: 16, // input: '22222womanmmmmmmmanmmmmman' ] //当patt再也匹配不到时,lastIndex会被重置为0 //3. test(str) 检索字符串是否有这个模式,有返回true否则false String对象方法 // 1. search(patt),搜索字符串是否含有这个匹配,如果有就返回其实位置下标,没有就返回-1// 2. match(patt), 在字符串内检索指定的模式,返回存放匹配结果的数组,该数组内容依赖于regexp是否具有g。 // 如果没有g,则返回的结果与exec相同。既,str.match(patt) 和 patt.exec(str)的返回结果相同。如果有g,是全局匹配,则会返回所有匹配结果。 let str = '22222womanmmmmmmmanmmmmman'; let patt = /(wo)?(man)/g; let result = str.match(patt); console.log(result); // [ 'woman', 'man', 'man' ] // 3. replace(patt,str) ,返回一个新的字符串,如果有g将所有匹配值换成目标字符串,没有则替换第一个。 // $1$2、...、$99代表圆括号所匹配的值 var name = "Doe, John"; console.log(name.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1")); // John Doe

12.常用的正则表达式:

匹配一个邮箱: /^([a-zA-Z0-9])+([a-zA-Z0-9_-\.])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/

开始必须是数组英文字母开头,然后是可以是任意数字字母下划线或者点,然后@,之后是邮箱名,域名。

匹配ip地址: /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ 匹配电话号码 /^[1][34578][0-9]{9}$/ 检查输入字符串是否是带小数的数字格式,可以是负数 /^-?(\d+)[\.]?(\d+)$/

13. es6中的let,const和var有什么区别?

let的作用域是外层块,不是外层函数,而var是外层函数。 if(true){ let a =0; var b =0; } console.log(a) // 报错,未定义 console.log(b) // 就是0 let没有声明提升,但是有暂时性死区。 console.log(a); // 结果是报错,不是undefined let a = 0; if(true){ var c = 0; // 报错,因为c已经定义在此区域了。 let c = 0; } 重新声明会报错const与let一样,不同在于const引用的地址不能改变,值也不能改变。声明必须赋值。

14.var f = function g(){ return 23; }; typeof g()结果是什么?为什么?

typeof f :function,因为f本来就是一个函数。typeof f():number,因为f()先执行,返回了一个数字,23.typeof g:undefined,因为typeof一个未声明的变量,都默认返回undefined。typeof g():报错,g未定义。因为function是函数表达式的形式,并且赋值给了f,所以这个函数就算f,而g只是一个匿名,只能在函数f中使用,在全局访问不到。

15.最完美的数组去重:

function normalize(arr) { // 判断传入的是否是数组 if (arr && Object.prototype.toString.call(arr) !== '[object Array]') { return; } // 当作为对象属性时,会发生强制类型转换,为了区别类型,创建不同对象来去重。 // 例如: 1,'1' 都会被转化为 obj['1'],导致去重出错, 故区别类型。 const objectSet = {}; // Object.create(null), 以防和原型上的函数名冲突。 // 例如: obj[toString], 本来是没有重复的,但是obj[toString]会查找到 // 原型上的toString方法,“temp in map”会为true,故利用Object.create(null) // 使原型为空 objectSet.number = Object.create(null); objectSet.string = Object.create(null); objectSet.array = Object.create(null); objectSet.boolean = Object.create(null); objectSet.object = Object.create(null); objectSet.undefined = Object.create(null); let len = arr.length, temp, type, map; for (let i = len - 1; i >= 0; i--) { temp = arr[i]; // 取相应的去重对象。 if(Object.prototype.toString.call(temp) === '[object Array]'){ map = objectSet.array; }else{ type = typeof temp; map = objectSet[type]; } if (temp in map) { arr.splice(i, 1); } else { map[temp] = true; } } return arr; } const arr = [1, '1', 1, 'toString', ['toString'], 1, '', 2, '', null, 'null', 2, 2, null, 3, 3]; console.log(normalize(arr));

16. 把字符串转化为大小写:

toUpperCase(): 转大写 toLowerCase(): 转小写

17. 为什么js是阻塞加载的?

当引用了js的时候,浏览器发送一个js请求,就会一直等待请求的返回,因为浏览器需要一个稳定的dom结构,而js中很有可能直接改变了dom结构,为了防止渲染好的树又被js代码修改,所以就会阻塞其他的下载和呈现。

18. 数组长度问题:

为什么结果不是3? var arr = []; arr[0] = 'a'; arr[1] = 'b'; arr.foo = 'c'; console.log(arr.length); //2

length返回的是array数组索引长度。

var arr = []; arr[9] = 1; console.log(arr.length); // 10; console.log(arr[8]); // undefines;

19. ToPrimitive()是什么?

简单来说,就是将对象(object)转化为原始类型(undefined, null, boolean ,string, number)的方法,在计算一些强制类型转换时,都需要将对象转化为原始类型再进行计算。

ToPrimitive(obj)等价于,obj.valueOf(),如果为原始值则返回结果,否则,计算obj.toString(),如果是原始值返回,否则抛出异常。

注:此处有个例外,即Date类型的对象,它会先调用toString()方法,后调用valueOf()方法。

不同对象的valueOf,toString结果不同 // Array console.log([,2,'ss'].valueOf()); // 返回数组本身[,2,'ss'] ,仍然不是原始类型 console.log([,2,'ss'].toString()); // 返回每个元素中间以逗号隔开的字符串:,2,ss // Object console.log({a:11,b:'uu'}.valueOf()); // 返回对象本身{ a: 11, b: 'uu' } console.log({a:11,b:'uu'}.toString()); // 返回字符串 [object Object] console.log(function(){return 1}.valueOf()); // 返回[Function]代表函数本身 console.log(function(){return 1}.toString()); // 返回函数定义的字符串 :function (){return 1}

20. '+new Array(017)' 的结果?

017是8进制,为15 -> + new Array(15);+为强制类型转Number -> Number(new Array(15));new Array(15) -> ToPrimitive([,,,,,,,,,,,,]);ToPrimitive([,,,,,,,,,,,,]) -> [,,,,,,,,,,].valueOf() 结果为 [,,,,,,,,,,][,,,,,,,,,,].toString() -> ',,,,,,,,,'Number(,,,,,,,,,) 为NaN

21. 不能用变量提升的思路取思考

var foo = {n: 1}; var bar = function(foo){ console.log(foo.n) var foo = {n:2}; } bar(foo);

这里有正常的传参和执行。

22. innerHTML, outerHTML, innerText, outerText的区别?

<div id="div1"><span>abcd</span></div> //写 //div.innerHTML = "<p>大米</p>"; //div保留 //div.outerHTML = "<p>大米</p>"; //div也被取代了 //div.innerText = "米老鼠"; //console.log(div); //div中包裹米老鼠 div.outerText = '<p>www</p>' //将原来单元素直接变成了纯文本,包括外围<div>

23. Array方法的返回结果?

返回本身:reverse(),sort(),返回一个副本:concat(),slice(),map(),filter返回其他: 改变数组: pop:返回arr最后一个元素push:返回新的长度shift:返回arr第一个元素unshift:返回新的长度splice:返回被删除的项目 不改变: join:返回字符串some/every:booleanfindIndex:下标

24. 常用排序时间复杂度?

image

25. AMD 和 CMD 的区别有哪些?

AMD规范:github.com/amdjs/amdjs-api/wiki/AMD,是RequireJS在推广过程中对模块化定义的规范化产出。CMD规范:github.com/seajs/seajs/issues/242CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。区别 对于模块的依赖,AMD是提前执行,CMD是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.CMD 推崇依赖就近,AMD 推崇依赖前置。AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。 // CMD define(function (require, exports, module) { var a = require('./a') a.doSomething() // 此处略去 100 行 var b = require('./b') // 依赖可以就近书写 b.doSomething() // ... }) // AMD 默认推荐的是 define(['./a', './b'], function (a, b) { // 依赖必须一开始就写好 a.doSomething() // 此处略去 100 行 b.doSomething() // ... }) 世界上,有两种比较流行的 JavaScript 模块化体系,一个是 Node 实现的 CommonJS,另外一个是 AMD。很多类库都同时支持 AMD 和 CommonJS,但是不支持 CMD。或许国内有很多 CMD 模块,但并没有在世界上流行起来。

26.keydown、keypress、keyup的区别

keypress主要用来捕获数字(shift+数字的符号)、字母(区分大小写)、小键盘等除了F1-12、SHIFT、Alt、Ctrl、Insert、Home、PgUp、Delete、End、PgDn、ScrollLock、Pause、NumLock、{菜单键}、{开始键}和方向键外的ANSI字符。keypress事件不能对系统功能键(例如:删除,后退等,还有中文输入法)进行正常的响应。keypress只能响应单个字符keydown和keyup可以响应除prscrn的所有按键,可以捕捉组合键。keydown和keyup不可以判断字符大小写,keypress可以。(keyCode)keypress事件的which值无法区分主键盘上的数字键和附键盘数字键的,而keydown、keyup的which值对主附键盘的数字键敏感。

27. 原生实现点击按钮切换弹框的隐藏,且点击弹框以外的地方使弹框隐藏:

<!DOCTYPE html> <html lang="en"> <head> <style> html { height: 100%; } body { height: 100%; } .div { height: 300px; width: 300px; background-color: rosybrown; transition: opacity 0.4s ease; } </style> </head> <body> <button class="btn1">toggle</button> <div class="div" style="opacity: 1;"> <button class="btn2">console1</button> <button class="btn3">console2</button> </div> <br /> <div class="div"></div> <script> var toggle = document.getElementsByTagName('button')[0]; var console1 = document.getElementsByTagName('button')[1]; var console2 = document.getElementsByTagName('button')[2]; var div = document.getElementsByClassName('div')[0]; var isShow = true; toggle.onclick = function () { if (isShow) { div.style.opacity = '0'; isShow = false; } else { div.style.opacity = '1'; isShow = true; } } div.addEventListener('click', function (e) { e.stopPropagation(); }, false); console1.addEventListener('click', function (e) { console.log(1); // e.stopPropagation(); }, false); console2.onclick = function (e) { console.log(2); // e.stopPropagation(); } document.body.addEventListener('click', function (e) { var target = e.target; // 点击toggle事件也会冒泡到body上,本身toggle的效果就没了,所以事件在toggle时直接退出不执行 if(target === toggle){ return; } div.style.opacity = '0'; isShow = false; }, false); </script> </body> </html>

思路:切换隐藏很简单,主要是点击周围的地方隐藏弹框。那么就在body上绑定一个事件,点击页面任何一个地方都隐藏,然后再去除弹框区域就行了。如何去除,就在弹框上绑定一个click事件,此事件发生后停止冒泡,那么点击弹框上任何内容的时候,事件都会冒泡到弹框上,然后弹框再停止冒泡,那么事件就不会走到body上,也就不会在弹框区域触发隐藏弹框事件。

28. js单例模式:

var Universe; (function () { var instance; Universe = function Universe() { if (instance) { return instance; } instance = this; // 其它内容 this.start_time = 0; this.bang = "Big"; }; } ()); //测试代码 var a = new Universe(); var b = new Universe(); alert(a === b); // true a.bang = "123"; alert(b.bang); // 123 var single = (function(){ var unique; function getInstance(){ if( unique === undefined ){ unique = new Construct(); } return unique; } function Construct(){ // ... 生成单例的构造函数的代码 } return { getInstance : getInstance } })();

js实现单例模式

作者:yozosann 链接:http://www.jianshu.com/p/3f03996d6502 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
转载请注明原文地址: https://www.6miu.com/read-74467.html

最新回复(0)