1舰涌、介紹原型/原型鏈/構(gòu)造函數(shù)/實(shí)例/繼承
構(gòu)造函數(shù):用new 操作后面的函數(shù),即便是空函數(shù)你稚,結(jié)果生成一個(gè)實(shí)例原型:聲明一個(gè)函數(shù)時(shí)瓷耙,就自動(dòng)給該函數(shù)增加一個(gè)propotype 屬性,指向原型對(duì)象(初始化一個(gè)空對(duì)象)刁赖,為了實(shí)現(xiàn)函數(shù)復(fù)用原型鏈:實(shí)例上的__proto_?屬性指向構(gòu)造函數(shù)的原型對(duì)象搁痛,最頂端是 Object.prototype,然后再往上就是 null宇弛,Object.prototype.__proto__ = null函數(shù)也有__proto__ 屬性鸡典,指向 Function.prototype,函數(shù)是構(gòu)造函數(shù) Function 的實(shí)例構(gòu)造器:原型對(duì)象的constructor 屬性枪芒,默認(rèn)指向聲明的函數(shù)
2彻况、如何實(shí)現(xiàn) new 運(yùn)算符
let?new2 =?function(func) {
??//創(chuàng)建一個(gè)空對(duì)象 o,并且繼承構(gòu)造函數(shù)的原型對(duì)象
??let?o =?Object.create(func.prototype);
??//執(zhí)行構(gòu)造函數(shù)舅踪,并且上下文 this 指向 o 對(duì)象
??let?k =?func.call(o);
??//如果構(gòu)造函數(shù)返回的是對(duì)象就返回該對(duì)象纽甘,否則返回 o 對(duì)象
??if(typeof?k ===?'object') {
????return?k
??}else?{
????return?o
??}
}
3、有幾種方式可以實(shí)現(xiàn)繼承
//借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承:缺點(diǎn)是父構(gòu)造函數(shù)的原型鏈繼承不了硫朦,若要全部繼承除非將所有屬性和方法定義在構(gòu)造函數(shù)中
function?Parent1 () {
??this.name =?'parent1';
}
function?Child1 () {
??//這么做的好處是定義在父構(gòu)造函數(shù)中的引用類型的屬性贷腕,對(duì)于子構(gòu)造函數(shù)的每個(gè)實(shí)例來說是獨(dú)立的
??//并且在子構(gòu)造函數(shù)實(shí)例化時(shí),可以給父構(gòu)造函數(shù)傳參
??Parent.call(this);
??this.type =?'child1';
}
//借助原型鏈實(shí)現(xiàn)繼承:缺點(diǎn)是繼承的引用類型屬性是共享的咬展,子構(gòu)造函數(shù)的實(shí)例更改會(huì)影響其他實(shí)例上的這個(gè)屬性泽裳,比如 play 屬性
function?Parent2 () {
??this.name =?'parent2';
??this.play =?[1, 2, 3];
}
function?Child2 () {
??this.type =?'Child2';
}
Child2.prototype =?new?Parent2();
//組合方式:缺點(diǎn)是會(huì)執(zhí)行兩次父構(gòu)造函數(shù)
function?Child3 () {
??//執(zhí)行第一次
??Parent2.call(this);
??this.type =?'Child3';
}
Child3.prototype =?new?Parent2(); //執(zhí)行第二次
//組合優(yōu)化1,不需要再將定義在父構(gòu)造函數(shù)中的屬性和方法再繼承一次破婆,只需要繼承原型鏈上的
Child3.prototype =?Parent2.prototype;
//缺點(diǎn)是無(wú)法區(qū)分一個(gè)實(shí)例是子函構(gòu)造函數(shù)實(shí)例化的還是父構(gòu)造函數(shù)實(shí)例化的
let?s1 =?new?Child3();
//s1.constructor指向了 Parent2涮总,而不是 Child3,因?yàn)?Child3 原型對(duì)象的屬性 constructor 繼承了 Parent2 原型對(duì)象上的
//如果你強(qiáng)行執(zhí)行 Child3.prototype.constructor = Child3 的話祷舀,也會(huì)將 Parent2.prototype.constructor 改成 Child3
//組合優(yōu)化2瀑梗,通過 Object.create() 創(chuàng)建一個(gè)中間對(duì)象烹笔,將兩個(gè)原型對(duì)象區(qū)別開來,并且繼承父構(gòu)造函數(shù)的原型鏈
Child3.prototype =?Object.create(Parent2.prototype);
Child3.prototype.constructor =?Child3;
//即 Child3.prototype.__proto__ === Parent2.prototype 為 true
//如此 Child3.prototype 和 Parent2.prototype 被隔離開抛丽,是兩個(gè)對(duì)象谤职,不會(huì)相互影響
//這種方式為理想繼承方式
4、arguments
可變參/不定參亿鲜,為一個(gè)類數(shù)組允蜈,有 length 屬性,可以通過循環(huán)找到每個(gè)參數(shù)蒿柳,可以用 arguments[index] 來找到具體的參數(shù)饶套,但是不是真正的數(shù)組,如果要轉(zhuǎn)換成真正的數(shù)組(意味著可以使用數(shù)組方法)垒探,可以這么做:
//以 forEach 為例:
Array.prototype.forEach.call(arguments, (item) => {console.log(item);});
[].forEach.call(arguments, () => {});
Array.from(arguments).forEach(() => {}); //直接將 arguments 轉(zhuǎn)換成了數(shù)組
當(dāng)我們給參數(shù)命名妓蛮,是為了增加可讀性。
5圾叼、數(shù)據(jù)類型判斷
判斷基本類型(除null)蛤克,使用 typeof 運(yùn)算符:
let num = 1;
typeof num ==='number'; //為 true
//還可以判斷是否為 function
typeof func === 'function';
//還可以判斷是否為 object
typeof obj === 'object';
判斷null 或者 undefined,直接跟 null 或者 undefined 作比較褐奥,num === null
判斷數(shù)組:
1)arr instanceof Array //返回 true 表示 arr 是數(shù)組類型咖耘,前提是必須保證由同一個(gè) Array 構(gòu)造函數(shù)生成的實(shí)例,如果是另一個(gè) iframe 的話撬码,則不為 true,則即便是數(shù)組類型版保,也得不到正確的結(jié)果2)arr.constructor === Array //問題跟 instanceof 相同呜笑,且這個(gè)屬性是可以被改寫的3)Array.isArray(arr) //一般使用這個(gè)方式,不存在上面的問題4)Object.prototype.toString.call(arr) //必須使用 Object 原型對(duì)象上的 toString 方法彻犁,而不能使用 Array 重寫的 toString 方法叫胁,這個(gè)方式可以區(qū)分函數(shù)/數(shù)組/對(duì)象/null,也沒有上面的問題汞幢,且兼容性比第三種更好(第三就是這種方式的語(yǔ)法糖驼鹅,只不過只能判斷是否是數(shù)組類型,直接使用這種方式可以判斷更多類型)森篷,這么使用:
Object.prototype.toString.call(arr) === '[object Function]' || '[object Array]' || '[object null]' || '[object Object]'
PS:instranceof 的作用是判斷一個(gè)對(duì)象是否是另一個(gè)對(duì)象的實(shí)例输钩,其問題除了上面的以外,還有就是只要是原型鏈上的對(duì)象仲智,返回都為 true买乃,因此我們使用 constructor 來判斷更精準(zhǔn),但是 constructor 屬性是可以被改寫的钓辆,因此使用第二道面試題中理想繼承方式剪验,就可以使用 constructor 來精準(zhǔn)判斷一個(gè)對(duì)象是否是另一個(gè)對(duì)象的實(shí)例肴焊。
6、作用域鏈功戚、閉包娶眷、作用域
作用域即執(zhí)行環(huán)境,每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象啸臀,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中茂浮。在web 瀏覽器中,全局執(zhí)行環(huán)境被認(rèn)為是 window 對(duì)象壳咕。且每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境席揽,在進(jìn)入函數(shù)時(shí)入棧,在函數(shù)執(zhí)行之后谓厘,棧將其環(huán)境彈出幌羞,把控制權(quán)返回給之前的執(zhí)行環(huán)境。
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí)竟稳,會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈属桦,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端他爸,始終是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象聂宾,最后端,始終是全局執(zhí)行環(huán)境的變量對(duì)象诊笤。
閉包指的是有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)系谐。創(chuàng)建閉包的常見方式,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)讨跟。也就是一個(gè)閉包的作用域鏈上至少包含三個(gè)作用域纪他,并且只有閉包才有自由變量(即不是本地定義的,也不是全局變量)
//使用閉包要作內(nèi)存管理
let?func =?(function() { return?function?closure() {} })();
//會(huì)將 closure 函數(shù)和它的作用域鏈賦值給 func 全局變量晾匠,外部函數(shù)的作用域會(huì)一直保存在內(nèi)存中茶袒,直到 closure 函數(shù)不存在
//為了優(yōu)化性能,在調(diào)用完 func 后應(yīng)該給 func 變量作解除引用的操作凉馆,即
func =?null; //讓值脫離環(huán)境薪寓,讓垃圾收集器下次運(yùn)行時(shí)將值回收。
7澜共、Ajax的原生寫法
??????function?ajax1() {
????????//創(chuàng)建一個(gè) XHR 對(duì)象
????????let?oAjax =?window.XMLHttpRequest ??(new?XMLHttpRequest()) :?(new?window.ActiveXobject('Microsoft.XMLHTTP'));
????????//返回一個(gè)函數(shù)向叉,這是函數(shù)柯里化操作,不用每次調(diào)用 ajax 都判斷瀏覽器環(huán)境
????????//但是會(huì)占用更多的內(nèi)存咳胃,因?yàn)榭偸菚?huì)保存外部函數(shù)的作用域
????????return?function(url, fnSucc, fnFaild) {
??????????//只要 XHR 對(duì)象的 readyState 屬性的值發(fā)生改變植康,就觸發(fā)這個(gè)事件
??????????oAjax.onreadystatechange =?function() {
????????????// readyState屬性是 0-4 的值,當(dāng)為 4 時(shí)展懈,表示已經(jīng)接收到全部響應(yīng)數(shù)據(jù)销睁,并可以在客戶端使用
????????????if(oAjax.readyState ===?4) {
??????????????//響應(yīng)的 HTTP 狀態(tài)
??????????????let?s =?oAjax.status;
??????????????if(s ===?200?||?s ===?206?||?s ===?304) {
????????????????//將響應(yīng)主體被返回的文本作為參數(shù)傳給這個(gè)函數(shù)供璧,并執(zhí)行這個(gè)函數(shù)
????????????????if(fnSucc) fnSucc(oAjax.responseText);
??????????????}else?{
????????????????if(fnFaild) fnFaild(oAjax.status);
??????????????}
????????????}
??????????};
??????????//啟動(dòng)一個(gè)請(qǐng)求,準(zhǔn)備發(fā)送
??????????oAjax.open('GET', url, true);
??????????//發(fā)送請(qǐng)求
??????????oAjax.send(null);
????????}
??????}
8冻记、對(duì)象深拷貝睡毒、淺拷貝
對(duì)象淺拷貝是共用一個(gè)引用,因此更改新拷貝的對(duì)象時(shí)冗栗,也會(huì)更改原來的對(duì)象
對(duì)象深拷貝是兩個(gè)引用演顾,有以下幾種方式實(shí)現(xiàn)深拷貝:
//使用 Object.assign,只能實(shí)現(xiàn)第一層屬性的深拷貝
let clone = Object.assign({},obj)
//使用 slice隅居,如果數(shù)組中有引用類型的元素的話钠至,只能實(shí)現(xiàn)第一層的深拷貝
let clone = arr.slice(0);
//使用 concat,同 slice
let clone = [].concat(arr);
//使用 JSON 對(duì)象胎源,無(wú)法實(shí)現(xiàn)屬性值為 function 和 undefined 的拷貝棉钧,并且拷貝從原型鏈繼承的值也會(huì)有問題,比如 constructor 的值變成了 Object
function deepClone(obj) {
??let _obj = JSON.stringify(obj);
??let clone = JSON.parse(_obj);
??return clone;
}
//使用遞歸涕蚤,在不使用庫(kù)的情況下宪卿,這種方式可以實(shí)現(xiàn)真正的深層度的拷貝
function deepClone(obj) {
??let clone = Array.isArray(obj) ? [] : {};
??if(obj && typeof obj === 'object') {
????for(let key in obj) {
??????if(obj.hasOwnProperty(key) {
????????if(obj[key] && typeof obj[key] === 'object') {
??????????clone[key] = deepClone(obj[key]);
????????}else {
??????????clone[key] = obj[key];
????????}
??????}
????}
??}
??return clone;
}
//通過 JQuery 的 extend 方法
//使用 lodash 函數(shù)庫(kù)
for-in 和 for-of 的區(qū)別:for-in 和 for-of 的區(qū)別1、for-in 遍歷的總是對(duì)象的下標(biāo)万栅,因此如果給數(shù)組增加屬性辜窑,那么這個(gè)屬性(key)也會(huì)遍歷出來减途,更適合遍歷對(duì)象,遍歷順序可能不是按照內(nèi)部順序宁仔,通常配合 hadOwnProperty() 方法一起使用恭金;2软驰、for-of 就是迭代器载城,遍歷的是數(shù)組元素的值瞧捌,不會(huì)遍歷增加的屬性,是按照內(nèi)部順序遍歷的义黎,并且還可以遍歷 Map 和 Set 數(shù)據(jù)結(jié)構(gòu)。如果沒有在內(nèi)部使用let 那么默認(rèn)為 var
在使用console.log 這類方法輸出對(duì)象時(shí)會(huì)顯示內(nèi)存的最新狀態(tài)豁跑,在同一個(gè) tick 中廉涕,即便更改對(duì)象是在 console.log 之后,那么輸出的對(duì)象也是被更改過的對(duì)象艇拍,因此狐蜕,如果想輸出某個(gè)時(shí)刻的對(duì)象值時(shí),應(yīng)該進(jìn)行深拷貝進(jìn)行輸出卸夕。
9层释、圖片懶加載、預(yù)加載
圖片懶加載是為了降低一次性的HTTP 請(qǐng)求數(shù)量快集,當(dāng)圖片很多時(shí)贡羔,或者同時(shí)在線人數(shù)較多時(shí)廉白,圖片懶加載可以起到很好的性能優(yōu)化的作用。
實(shí)現(xiàn)步驟:
1)設(shè)置自定義屬性 data-src 來存儲(chǔ)圖片資源乖寒;
2)頁(yè)面初始化或者在滾動(dòng)時(shí)判斷圖片是否出現(xiàn)在可視區(qū)域猴蹂;
3)在可視區(qū)域的話,將自定義屬性 data-src 的值賦值給 src 屬性楣嘁。
????class LazyLoad {
??????constructor(tag) {
????????this.boundTop = 0;
????????this.clientHeight = 0;
????????this.timer = null;
????????this.aImg = Array.from(document.querySelectorAll(tag));
????????this.init();
??????}
??????isShow(el) {
????????this.boundTop = el.getBoundingClientRect().top;
????????this.clientHeight = document.documentElement.clientHeight;
????????return this.boundTop <= this.clientHeight - 100;
??????}
??????canLoad() {
????????for(let i=0;i<this.aImg.length;i++) {
??????????let img = this.aImg[i]
??????????if(this.isShow(img)) {
????????????img.src = img.dataset.src
//使用 for 循環(huán)是為了在改動(dòng)數(shù)組后來操作 i磅轻,使得正確遍歷每個(gè)元素
????????????this.aImg.splice(i, 1)
????????????i-- ?????????
??????????}
????????}
??????}
??????addEvent() {
????????window.addEventListener('scroll', () => {
??????????if(!this.aImg.length && this.timer) return;
??????????this.timer = setTimeout(() => {
????????????this.canLoad();
????????????this.timer = null;
??????????}, 200);
????????});
??????}
??????init() {
????????this.canLoad();
????????this.addEvent();
??????}
????}
????let lazy = new LazyLoad('img');
圖片預(yù)加載:在需要顯示圖片之前,就加載完畢逐虚,當(dāng)需要顯示圖片時(shí)聋溜,就從緩存中取圖片,在圖片不是特別多的時(shí)候叭爱,可以使用預(yù)加載撮躁。
有多種實(shí)現(xiàn)方式,以下為較喜歡的一種:
????<script type="text/javascript">
//使用 Image 對(duì)象來實(shí)現(xiàn)
??????let imgs = [];
???????function preload() {
?????????for(let i=0; i<arguments.length;i++) {
//因?yàn)橛羞@個(gè)對(duì)象涤伐,所以不用像生成其他對(duì)象那樣來創(chuàng)建新對(duì)象
???????????imgs[i] = new Image();
//這時(shí)就會(huì)下載遠(yuǎn)程圖片到本地
???????????imgs[i].src = arguments[i];
?????????}
???????}
???????preload(
?????????"http://img4.imgtn.bdimg.com/it/u=951914923,777131061&fm=26&gp=0.jpg",
?????????"http://domain.tld/gallery/image-002.jpg",
?????????"http://domain.tld/gallery/image-003.jpg"
???????);
//將圖片都緩存到了 imgs 數(shù)組中馒胆,當(dāng)需要顯示它時(shí),就從里面取
????</script>
10凝果、實(shí)現(xiàn)頁(yè)面加載進(jìn)度條
<body>
??<div style="position:fixed;left:0;top:0;z-index:99;width:0%;height:3px;background:#24e1b6;" id="bar"></div>
??<img src="./img/1.png" />
??<img src="./img/2.png" />
??<img src="./img/3.png" />
??<img src="./img/4.png" />
??<img src="./img/5.png" />
</body>
<script>
??(function(){
????let count = 0;
????let script = ['./js/lottie.js', './js/1.js', './js/2.js', './js/3.js'];
????let link = ['./css/1.css', './css/2.css', './css/3.css', './css/4.css', './css/5.css'];
????let img = document.querySelectorAll('img');
????let num = img.length + script.length + link.length;
????for(let i1 = 0, len1 = img.length; i1 < len1; i1++) {
??????(function(j1){
????????img[j1].onload = function () {
??????????count++;
??????????console.log('img=' + count);
??????????document.querySelector('#bar').style.width = (count / num) * 100 + '%';
????????};
??????})(i1);
????}
????for(let i2 = 0, len2 = script.length; i2 < len2; i2++) {
//這里使用立即執(zhí)行函數(shù)祝迂,是讓這些變量成為臨時(shí)變量,而不是全局變量器净,全局對(duì)象以參數(shù)的形式傳入型雳,即修改參數(shù)不會(huì)污染全局變量
??????(function(j2){
????????let scriptE = document.createElement('script');
????????scriptE.src = script[j2];
????????scriptE.onload = function () {
??????????count++;
??????????console.log('script=' + count);
??????????document.querySelector('#bar').style.width = (count / num) * 100 + '%';
????????};
????????document.head.appendChild(scriptE);
??????})(i2);
????}
????for(let i3 = 0, len3 = link.length; i3 < len3; i3++) {
??????(function(j3){
????????let linkE = document.createElement('link');
????????linkE.href = link[j3];
????????linkE.rel = 'stylesheet';
????????linkE.onload = function () {
??????????count++;
??????????console.log('script=' + count);
??????????document.querySelector('#bar').style.width = (count / num) * 100 + '%';
????????};
????????document.head.appendChild(linkE);
??????})(i3);
????}
??})();
</script>
如果是Ajax 請(qǐng)求,那么就使用 XHR 的 progress 事件山害,事件處理程序接受一個(gè) event 參數(shù)纠俭,這個(gè)參數(shù)有三個(gè)屬性:lengthComputabel:表示進(jìn)度信息是否可用,為一個(gè)布爾值浪慌;potion:表示已經(jīng)接收的字節(jié)數(shù)冤荆;totalSize:便是根據(jù) Content-Length 響應(yīng)頭部確定的預(yù)期字節(jié)數(shù),即需要加載的總字節(jié)數(shù)
11权纤、this 關(guān)鍵字
在全局環(huán)境中钓简,this 指向 window 對(duì)象,ES5 函數(shù)中汹想,this 對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境(變量對(duì)象外邓,如全局是 window),匿名函數(shù)的執(zhí)行環(huán)境具有全局性古掏,因此其 this 對(duì)象通常指向 window(在非嚴(yán)格模式下)损话,在嚴(yán)格模式下 this 指向的是 undefined,為了在嚴(yán)格模式下槽唾,this 重新指向 window丧枪,可以使用非直接調(diào)用 eval 的方式光涂,如 (0, eval)('this'),使用了逗號(hào)運(yùn)算符豪诲,括號(hào)前面會(huì)返回 eval顶捷,但是和直接調(diào)用的區(qū)別就是在嚴(yán)格模式下 this 指向不一樣。ES6 的箭頭函數(shù)中屎篱,this 對(duì)象是在函數(shù)定義的執(zhí)行環(huán)境服赎。
12、函數(shù)式編程
函數(shù)式編程中的函數(shù)指的數(shù)學(xué)概念中的函數(shù)交播,即自變量的映射重虑,得到的結(jié)果由輸入的值決定,不依賴于其他狀態(tài)秦士,是聲明式(依賴于表達(dá)式)缺厉,而非命令式,組合純函數(shù)來構(gòu)建軟件的編程方式隧土。
13提针、手動(dòng)實(shí)現(xiàn) parseInt
幾乎與原生parseInt 的結(jié)果一樣,如果有不同的結(jié)果曹傀,請(qǐng)一定留言告訴我
????function compare(str, radix) {
??????let code = str.toUpperCase().charCodeAt(0),
????????num;
??????if(radix >= 11 && radix <= 36) {
????????if(code >= 65 && code <= 90) {
??????????num = code - 55;
????????}else {
??????????num = code - 48;
????????}
??????}else {
????????num = code - 48;
??????}
??????return num;
????}
????function isHex(first, str) {
??????return first === '0' && str[1].toUpperCase() === 'X'
????}
????function _parseInt(str, radix) {
??????str = String(str);
??????if(typeof str !== 'string') return NaN;
??????str = str.trim();
??????let first = str[0],
????????sign;
//處理第一個(gè)字符為 '-' || '+' 的情況
??????if(first === '-' || first === '+') {
????????sign = str[0];
????????str = str.slice(1);
????????first = str[0];
??????}
//當(dāng) radix 不存在或者小于 11 時(shí)辐脖,第一個(gè)字符只能為數(shù)字
??????if(radix === undefined || radix < 11) {
????????if(isNaN(first)) return NaN;
??????}
??????let reg = /^(0+)/;
//截取 str 前面符合要求的一段,直到遇到非數(shù)字和非字母的字符
??????let reg2 = /^[0-9a-z]+/i;
??????str = str.match(reg2)[0];
??????let len = str.length;
//在沒有第二個(gè)參數(shù)時(shí)或者不是數(shù)字時(shí)皆愉,給第二個(gè)參數(shù)賦值
//isNaN('0x12')會(huì)執(zhí)行 Number('0x12') 可以轉(zhuǎn)換成十進(jìn)制
??????if(radix === undefined || isNaN(radix)) {
????????if(len === 1) return str;
//如果 str 是十六進(jìn)制形式嗜价,就轉(zhuǎn)換成十進(jìn)制
????????if(isHex(first, str)) {
??????????return Number(str);
????????}else {
??????????radix = 10;
????????}
??????}else {
//如果有第二個(gè)參數(shù),并且是數(shù)字幕庐,要處理第二個(gè)參數(shù)
????????radix = String(radix);
//如果有小數(shù)點(diǎn)久锥,取小數(shù)點(diǎn)前面一段,處理不為整數(shù)的情況
????????radix = radix.split('.')[0];
//如果 radix 等于零的話异剥,就按照 str 來判斷 radix 的基數(shù)
????????if(radix === '0') {
??????????if(isHex(first, str)) {
????????????return Number(str);
??????????}else {
????????????radix = 10;
??????????}
????????}
//如果 radix 前面有零將零去除瑟由,十六進(jìn)制除外
????????if(radix.length > 1) {
??????????let twoR = radix[1].toUpperCase();
??????????if(radix[0] === '0' && twoR !== 'X') radix = radix.replace(reg, '');
????????}
//如果 radix 是十六進(jìn)制的字符串類型,也會(huì)轉(zhuǎn)變成十進(jìn)制的數(shù)字類型
????????radix = Number(radix);
//radix是否在正確的區(qū)間
????????if(radix >= 2 && radix <= 36) {
//如果 radix 為 16冤寿,且 str 是十六進(jìn)制形式的話错妖,直接將十六進(jìn)制轉(zhuǎn)換成十進(jìn)制
??????????if(radix === 16 && isHex(first, str)) return Number(str);
????????}else {
//只要 radix 是一個(gè)有效的數(shù)字,但不在正確的區(qū)間里疚沐,就返回 NaN
??????????return NaN;
????????}
??????}
//去除 str 前面的零
??????str = str.replace(reg, '');
??????if(str.length === 0) return 0;
??????let strArr = str.split(''),
????????numArr = [],
????????result = 0,
????????num;
??????for(let i=0; i<strArr.length; i++) {
????????num = compare(strArr[i], radix);
????????if(num < radix) {
??????????numArr.push(num);
????????}else {
??????????break;
????????}
??????}
??????let lenN = numArr.length;
??????if(lenN > 0) {
????????numArr.forEach(function(item, index) {
??????????result += item * Math.pow(radix, lenN - index -1);
????????});
??????}else {
//str開頭有零的話要返回零
????????return first === '0' ? 0 : NaN;
??????}
??????if(sign === '-') result = -result;
??????return result;
????}
14、為什么會(huì)有同源策略
同源策略限制從一個(gè)源加載的文檔或腳本如何與另一個(gè)源的資源進(jìn)行交互潮模。這是用于隔離潛在惡意文件的關(guān)鍵安全機(jī)制亮蛔。
同源策略:協(xié)議相同、域名相同擎厢、端口相同究流,三者都必須相同
什么叫限制:不同源的文檔不能操作另一個(gè)源的文檔辣吃,在以下幾個(gè)方面操作不了:
1)Cookie、localStorage芬探、indexDB 無(wú)法讀取2)DOM 無(wú)法獲得3)AJAX 請(qǐng)求無(wú)法發(fā)送
15神得、怎么判斷兩個(gè)對(duì)象是否相等
1)判斷引用是否為同一個(gè)引用;
2)如果是不同引用偷仿,判斷長(zhǎng)度是否相同哩簿;
3)通過 Object.getOwnpropertyNames(a) 拿到所有屬性,判斷是否有相同的屬性 key酝静,如果相同节榜,再判斷值是否相同。
參考資料
16别智、事件模型
一個(gè)完整的事件流分為三個(gè)階段:第一階段是捕獲宗苍,第二階段是目標(biāo)階段,第三階段是目標(biāo)元素通過冒泡上傳到window 對(duì)象薄榛。這個(gè)過程就是用戶和瀏覽器交互的過程讳窟,瀏覽器通過事件捕獲知道了用戶的操作,再通過事件冒泡將操作信息傳遞給瀏覽器敞恋。
//事件捕獲的具體流程:
window -> document -> html -> body -> … ->目標(biāo)元素
//事件冒泡的流程是反過來的
1)事件委托丽啡、代理
事件委托是將事件監(jiān)聽器綁定到父元素上,點(diǎn)擊任意一個(gè)子元素耳舅,通過事件冒泡將事件傳給父元素
event.currentTarget ?//傳回事件綁定的這個(gè)元素
event.target //傳回被點(diǎn)擊的這個(gè)元素對(duì)象
2)如何讓事件先冒泡后捕獲
window.addEventListener('click', ?func, false);
//false為冒泡時(shí)觸發(fā)事件碌上,默認(rèn)
//true為捕獲時(shí)觸發(fā)事件
//還可以這樣寫:
el.addEventListener(type, listener, {
????capture: false, // useCapture
once: false, ???//是否設(shè)置單次監(jiān)聽
passive: false ?//是否讓阻止默認(rèn)行為preventDefault()失效
})
17、window 的 onload 事件和 DOMContentLoaded 事件
第一個(gè)是當(dāng)頁(yè)面所有的資源(圖片浦徊、音頻馏予、視頻等)全部加載完畢才會(huì)觸發(fā)第二個(gè)是當(dāng)DOM 結(jié)構(gòu)加載完畢就會(huì)觸發(fā)簡(jiǎn)單來說,頁(yè)面的load 事件會(huì)在 DOMContentLoaded 被觸發(fā)之后才觸發(fā)盔性。
18霞丧、for...in 迭代和 for...of 有什么區(qū)別
在循環(huán)對(duì)象屬性時(shí)使用for...in,因?yàn)闀?huì)將所有可枚舉屬性都遍歷出來冕香,但是有以下幾個(gè)問題:
1)將自定義屬性也會(huì)遍歷出來蛹尝;
2)遍歷出來的屬性順序是隨機(jī)的
在循環(huán)數(shù)組時(shí)使用for...of,我們可以配合迭代器悉尾,可以對(duì)復(fù)雜的突那、自定義的數(shù)據(jù)結(jié)構(gòu)輸出我們想要的結(jié)果,且不會(huì)輸出自定義的屬性:
//以 Symbol.iterator 為 key构眯,以函數(shù)為值愕难,為對(duì)象構(gòu)建一個(gè)迭代器
????Array.prototype[Symbol.iterator] = function () {
????????let arr = [].concat(this);
????????let getFirst = function(array) {
??????????return array.shift();
????????};
return { ???????//規(guī)定返回一個(gè)對(duì)象
next () { ????//規(guī)定返回的對(duì)象中必須有 next 方法
????????????let item = getFirst(arr);
????????????if(item) {
return { ????//規(guī)定 next 方法必須返回一個(gè)對(duì)象,有以下兩個(gè)屬性
value: item, ??//這里做了隱式類型轉(zhuǎn)換,進(jìn)行了 toString()
????????????????done: false
??????????????};
????????????}else {
??????????????return {
????????????????done: true
??????????????};
????????????}
??????????}
????????}
??????};
??????let arr = ['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4];
??????function flat() {
????????let r = [];
for(let i of arr) { ?????//調(diào)用 for...of 就是內(nèi)部不斷調(diào)用了 next()
??????????r.push(i);
????????}
????????return r.join(',');
??????}
??????console.log(flat(arr));
//結(jié)果為:a,b,c,2,d,e,f,g,3,4
//當(dāng)然還可以使用遞歸和類型轉(zhuǎn)換來達(dá)到目的
如果想輸出帶有順序的對(duì)象屬性值的話猫缭,要配合Object.keys() 來使用:
let obj = {};
for(let key of Object.keys(obj)) { ?//這里 for...of 迭代的是數(shù)組
??console.log(obj[key]);
}
在使用它們時(shí)葱弟,建議加上let 來使用,且在使用 for...of 時(shí)猜丹,要注意迭代對(duì)象是否是可迭代的對(duì)象芝加,即是否有迭代器,對(duì)象默認(rèn)是沒有迭代器的射窒,只有數(shù)組有迭代器藏杖,無(wú)法直接使用 for...of。
//比如無(wú)法直接迭代對(duì)象
let obj = {};
for(let key of obj) {} //會(huì)報(bào)錯(cuò)
19轮洋、函數(shù)柯里化
函數(shù)柯里化是將多參數(shù)或者差異化參數(shù)轉(zhuǎn)變成單參數(shù)或者無(wú)差異化參數(shù)的一種函數(shù)式編程的運(yùn)算方式制市。這么做的好處是可以將核心邏輯跟其他邏輯(包括環(huán)境判斷等只需要一次性操作的邏輯)分離開來,做法是使用一個(gè)函數(shù)返回一個(gè)函數(shù)弊予,在第一個(gè)函數(shù)中執(zhí)行其他邏輯祥楣,并且對(duì)差異化參數(shù)進(jìn)行處理,然后返回一個(gè)無(wú)差異參數(shù)且只有核心邏輯代碼的函數(shù)汉柒,以后每次執(zhí)行都只需要執(zhí)行這個(gè)函數(shù)误褪。在vue.js 源碼中大量使用了函數(shù)柯里化方式。舉一個(gè) ajax 的例子:
??????let ajax = (function() {
????????let oAjax = window.XMLHttpRequest ? (new XMLHttpRequest()) : (new window.ActiveXobject('Microsoft.XMLHTTP'));
????????return function(url, fnSucc, fnFaild) {
??????????oAjax.onreadystatechange = function() {
????????????if(oAjax.readyState === 4) {
??????????????let s = oAjax.status;
??????????????if(s === 200 || s === 206 || s === 304) {
????????????????if(fnSucc) fnSucc(oAjax.responseText);
??????????????}else {
????????????????if(fnFaild) fnFaild(oAjax.status);
??????????????}
????????????}
??????????};
??????????oAjax.open('GET', url, true);
??????????oAjax.send(null);
????????}
??????})();
//如此環(huán)境判斷只需要執(zhí)行一次碾褂,當(dāng)然這樣又因?yàn)槭褂昧碎]包兽间,要保存外部函數(shù)的作用域,占用更多的內(nèi)存正塌,所以也這都是有一定的取舍的
20嘀略、call,apply乓诽,bind 三者用法和區(qū)別帜羊,原生實(shí)現(xiàn) bind
function fn(num1, num2) {
????console.log(num1 + num2);
????console.log(this);
}
fn.call(obj , 100 , 200); ?//call是一個(gè)一個(gè)傳入?yún)?shù)
fn.apply(obj , [100, 200]); ?//apply是傳入一個(gè)數(shù)組
//它們都是將第一個(gè)參數(shù)替換 fn 函數(shù)中的 this 關(guān)鍵字,然后執(zhí)行 fn 函數(shù)
//只是替換 this 關(guān)鍵字鸠天,不執(zhí)行 tempFn 函數(shù)
var tempFn = fn.bind(obj, 1, 2);
//在需要執(zhí)行時(shí)讼育,執(zhí)行這個(gè)函數(shù)
tempFn();
//原生實(shí)現(xiàn) bind
Function.prototype.bind2 = function(newThis) {
var aArgs = Array.prototype.slice.call(arguments, 1) //拿到除了newThis之外的預(yù)置參數(shù)序列
??var that = this
??return function() {
????return that.apply(newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
//綁定this同時(shí)將調(diào)用時(shí)傳遞的序列和預(yù)置序列進(jìn)行合并,比如 tempFn(3) 其中 3 就是調(diào)用是傳遞的序列
??}
}
//原生實(shí)現(xiàn) call
??????Function.prototype.call2 = function(context, ...arg) {
????????context = context || window;
context.fn = this; ?//調(diào)用 call2 的函數(shù)就是 this稠集,比如這里的 show 函數(shù)
let result = context.fn(...arg); //調(diào)用 show 函數(shù)奶段,這個(gè)函數(shù)里面的 this 是執(zhí)行環(huán)境中的對(duì)象
delete context.fn; ???//不要污染 context 對(duì)象
????????return result;
??????}
??????function show (){
????????console.log(this);
??????}
??????let obj = {haha: '123'}
show.call2(obj); ?//跟原生的 call 還是有差異的,會(huì)將對(duì)象的 __proto__ 屬性也顯示出來
??????show.call(obj);
21剥纷、async/await
//await一般會(huì)返回一個(gè) promise 的表達(dá)式
//await只能放在async函數(shù)里痹籍,await 后面的代碼是異步執(zhí)行的,就像使用了 then 來注冊(cè)回調(diào)函數(shù)一樣
function doubleAfter2seconds(num) {
????return new Promise((resolve, reject) => {
????????setTimeout(() => {
????????????resolve(2 * num)
????????}, 2000);
????} )
}
//aync函數(shù)會(huì)返回一個(gè) promise 對(duì)象
async function testResult() {
????let result = await doubleAfter2seconds(30);
????console.log(result);
}
testResult();
22晦鞋、立即執(zhí)行函數(shù)和使用場(chǎng)景
通過定一個(gè)匿名函數(shù)词裤,創(chuàng)建了一個(gè)新的函數(shù)作用域刺洒,該命名空間的變量和方法,不會(huì)污染全局的命名空間(反過來也一樣)吼砂。如果在這個(gè)函數(shù)作用域中要訪問全局對(duì)象,將全局對(duì)象以參數(shù)形式傳入進(jìn)去鼎文,雖然函數(shù)體內(nèi)可以直接訪問全局對(duì)象渔肩,但為了不污染全局的命名空間,所以以參數(shù)形式傳入拇惋,那么對(duì)這個(gè)參數(shù)的修改不會(huì)污染全局變量周偎。
有多種方式使用立即執(zhí)行函數(shù):
//一般使用第一種
(function(num) {
??console.log(num);
})(123);
(function(num) {
??console.log(num);
}(123));
!function(num) {
??console.log(num);
}(123);
+function(num) {
??console.log(num);
}(123);
-function(num) {
??console.log(num);
}(123);
//使用運(yùn)算符 =
let fn = function(num) {
??console.log(num);
}(123);
23、設(shè)計(jì)模式(要求說出如何實(shí)現(xiàn),應(yīng)用,優(yōu)缺點(diǎn))/單例模式實(shí)現(xiàn)
單列模式工廠模式觀察者模式適配器模式代理模式僑接模式外觀模式訪問者模式模仿方法模式中介者模式迭代器模式組合模式備忘錄模式職責(zé)鏈模式享元模式狀態(tài)模式
24撑帖、iframe的缺點(diǎn)有哪些
1)頁(yè)面太多不好管理蓉坎;2)框架個(gè)數(shù)多的話,會(huì)出現(xiàn)上下左右滾動(dòng)條胡嘿,會(huì)分散訪問者的注意力蛉艾,用戶體驗(yàn)度差;3)代碼復(fù)雜衷敌,不利于搜索引擎勿侯;4)設(shè)備兼容性差,一些移動(dòng)設(shè)備無(wú)法完全顯示框架缴罗;5)增加服務(wù)器 HTTP 請(qǐng)求助琐。
25、數(shù)組問題
1)數(shù)組去重
2)數(shù)組常用方法
3)扁平化數(shù)組
4)按數(shù)組中各項(xiàng)和特定值差值排序
26面氓、BOM 屬性對(duì)象方法
BOM 提供了很多對(duì)象兵钮,用于訪問瀏覽器的功能
window 對(duì)象扮演全局對(duì)象的角色,所有在全局作用域中聲明的變量和函數(shù)都會(huì)變成 window 對(duì)象的屬性和方法舌界,以下列出操作瀏覽器的方法:
窗口關(guān)系即框架:
window.frames[0] 每個(gè)框架都有自己的全局對(duì)象掘譬,并且保存在 frames 集合中;window.parent 指向當(dāng)前框架的上層框架禀横,如果是當(dāng)前框架是上層框架屁药,則 parent 就等于自身的 window 對(duì)象;
窗口位置:用來確定和修改window 對(duì)象位置的屬性和方法
window.screenLeft/screenTop 分別用于表示窗口相對(duì)于屏幕(電腦顯示屏)左邊和上邊的位置柏锄,在火狐中是 window.screenX/screenY
確定窗口大心鸺:
四個(gè)屬性:innerWidth innerHeight outerWidth outerHeight,不同瀏覽器返回的具體值不同趾娃,由于有瀏覽器兼容性問題缭嫡,選擇用:document.documentElement.clientWidth/clientHeight 用來取得頁(yè)面視口的大小
導(dǎo)航和打開窗口:
使用window.open(url, frameName) 方法可以導(dǎo)航到一個(gè)特定的 url,也可以打開一個(gè)新的瀏覽器窗口抬闷,此方法返回指向新窗口的引用妇蛀,第二個(gè)參數(shù)還可以是一個(gè)特殊的窗口名稱:_self _parent _top _blank
let frame = window.open(url, frameName);
frame.close();
alert(frame.closed); //true
系統(tǒng)對(duì)話框:
alert() 只顯示 ok 按鈕confirm() 顯示 ok 按鈕和 cancel 按鈕prompt(文本提示耕突,輸入域默認(rèn)值) 這是一個(gè)提示框,顯示文本輸入域评架,供用戶在其中輸入內(nèi)容
location 對(duì)象眷茁,提供了與當(dāng)前窗口中加載的文檔有關(guān)的信息,還提供了一些導(dǎo)航功能纵诞。其用處不只表現(xiàn)在它保存著當(dāng)前文檔的信息上祈,還表現(xiàn)在它將 url 解析為獨(dú)立的片段,使開發(fā)人員可以通過不同的屬性訪問這些片段浙芙。
hash:url 中包含 '#' 即后面的內(nèi)容登刺,如果沒有則返回空字符串host:返回服務(wù)器名稱和端口號(hào)(如果有)hostname:返回不帶端口號(hào)的服務(wù)器名稱href:返回當(dāng)前加載頁(yè)面的完整 urlpathname:返回 url 中的目錄或文件名port:返回端口號(hào)protocol:返回頁(yè)面使用的協(xié)議search:返回 url 中包含 '?' 即后面的內(nèi)容,即查詢字符串
每次修改location 的屬性(hash 除外)嗡呼,頁(yè)面都會(huì)以新 url 重新加載
位置操作:
location.assign(url) 立即打開新 url 并在瀏覽器的歷史記錄中生成一條記錄纸俭。以下方式都是內(nèi)部調(diào)用了這個(gè)方法:
window.location = url;
location.href = url;
location.replace(url) 方法導(dǎo)致頁(yè)面改變,但不會(huì)在歷史記錄中生成新記錄南窗,調(diào)用這個(gè)方法后揍很,用戶不能回到前一個(gè)頁(yè)面。
location.reload() 方法沒有參數(shù)時(shí)矾瘾,會(huì)使頁(yè)面重新加載女轿,如果有緩存就從緩存中獲取,如果要強(qiáng)制從服務(wù)器重新加載壕翩,傳入?yún)?shù) location.reload(true)
navigator 對(duì)象蛉迹,通常用于檢測(cè)顯示網(wǎng)頁(yè)的瀏覽器類型,根據(jù)瀏覽器的不同有不同的屬性可以用放妈,主要講下三個(gè)通用的又實(shí)用的屬性:
1)appCodeName 瀏覽器的名稱北救,除了 IE 瀏覽器之外,基本都返回:"Netscape"2)online 指明系統(tǒng)是否處于脫機(jī)狀態(tài)芜抒,返回布爾值3)userAgent 返回客戶端的完整信息
screen 對(duì)象珍策,在編程中用處不大,基本上只用來表明客戶端的能力宅倒,其中包括瀏覽器窗口外部的顯示器的信息攘宙,如像素寬度和高度等。這些信息經(jīng)常集中出現(xiàn)在測(cè)定客戶端能力的站點(diǎn)跟蹤工具中拐迁。列舉一個(gè)屬性(只有 IE 瀏覽器支持):
deviceXDPI:返回顯示屏幕的每英寸水平點(diǎn)(像素)數(shù)蹭劈。返回的其實(shí)就是分辨率,而那個(gè)水平點(diǎn)數(shù)中的一點(diǎn)就是一像素线召,DPI 是顯示分辨率的一個(gè)比例值铺韧,DPI 越高說明分辨率越高,如果安卓 DPI 比率分辨值為 1 的話缓淹,蘋果必然是 2哈打,蘋果的像素高
history 對(duì)象保存著用戶上網(wǎng)的歷史記錄塔逃,其有一個(gè) length 屬性,保存這歷史記錄的數(shù)量
history.go() 方法可以在用戶的歷史記錄中任意跳轉(zhuǎn)料仗,可以向后也可以向前湾盗,接收一個(gè)整數(shù)值作為參數(shù);還可以傳遞一個(gè)字符串參數(shù)罢维,此時(shí)瀏覽器會(huì)跳轉(zhuǎn)到歷史記錄中包含該字符串的第一個(gè)位置淹仑,可能前進(jìn)可能后退,具體要看哪個(gè)位置更近肺孵,如果不包含該字符串,那么這個(gè)方法什么都不做颜阐∑骄剑可以用back() 來后退,forward() 來前進(jìn)
history.pushState(state/null, title/null, url) 和 history.replaceState() 凳怨,第二個(gè) API 不會(huì)加入到歷史記錄中瑰艘,用法和第一個(gè)相同,第一個(gè) API 的第三個(gè)參數(shù)必須是與當(dāng)前頁(yè)面處于同一個(gè)域肤舞,它們的作用是使 url 跳轉(zhuǎn)而無(wú)需重新加載頁(yè)面紫新,不會(huì)觸發(fā) popstate 事件,只有在執(zhí)行了 history.back history.forward 以及 history.go李剖,或者點(diǎn)擊了瀏覽器的前進(jìn)后退或者跳轉(zhuǎn)按鈕(雙擊前進(jìn)或者后退按鈕)芒率,就會(huì)觸發(fā) popstate 事件。
vue 的編程式導(dǎo)航利用了 location.hash 屬性以及 window.history 上面兩個(gè) API 實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)但不重新加載頁(yè)面的效果篙顺。
27偶芍、服務(wù)端渲染
服務(wù)端渲染指的是后端(比如JAVA PHP)生成 html 文件,客戶端發(fā)送 http 請(qǐng)求直接獲得 html 文件然后直接顯示出來德玫,優(yōu)點(diǎn)是首屏渲染快匪蟀,有利于 SEO 搜索引擎,缺點(diǎn)是頁(yè)面切換慢宰僧,因?yàn)槎家l(fā)起 http 請(qǐng)求材彪;客戶端渲染指的是客戶端通過ajax 請(qǐng)求數(shù)據(jù),首次渲染通過 http 請(qǐng)求 JS 和 CSS 文件琴儿,然后在客戶端拼接成 html 文件段化,再顯示出來,優(yōu)點(diǎn)是頁(yè)面切換快(通過 JS 渲染凤类,動(dòng)態(tài)更新頁(yè)面)穗泵,但首屏渲染慢,且不利于 SEO 搜索引擎谜疤,React 和 Vue 等 MVVM 框架都是客戶端渲染佃延。如果對(duì)于百度的SEO 有需求(因?yàn)楣雀枰呀?jīng)可以支持 JS 動(dòng)態(tài)網(wǎng)頁(yè))现诀,那么就考慮使用前端同構(gòu),指的是原本在客戶端生成 html 文件履肃,在中間層 node 環(huán)境來生成 html 文件仔沿,首屏渲染是在 node 環(huán)境生成,之后頁(yè)面切換還是在客戶端尺棋,中間層和客戶端使用一套 JS 代碼封锉,最終都是將虛擬 DOM 轉(zhuǎn)換成 html 文件,因?yàn)樘摂M DOM 本質(zhì)上是一個(gè) JS 對(duì)象膘螟,所以可以跑在 node 環(huán)境成福。React 的前端同構(gòu)的支持框架是 next.js,Vue 的前端同構(gòu)的支持框架是 nuxt.js荆残,在使用這些框架時(shí)奴艾,要特別注意 node 環(huán)境和客戶端環(huán)境是不同的,比如說沒有 window 對(duì)象的内斯,因此使用前端框架會(huì)報(bào)錯(cuò)蕴潦,當(dāng)使用這些框架時(shí),要聲明只在客戶端渲染俘闯,再如引入 npm 包潭苞,帶有 DOM 操作的,不能用 import 來引入真朗,要使用 require() 方式此疹,總之一套代碼在兩個(gè)環(huán)境下運(yùn)行,會(huì)遇到很多坑蜜猾。因此學(xué)習(xí)成本也是較高的秀菱。
28、垃圾回收機(jī)制
JS 具有自動(dòng)垃圾收集機(jī)制蹭睡,執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存衍菱,所需內(nèi)存的分配以及無(wú)用內(nèi)存的回收完全實(shí)現(xiàn)了自動(dòng)管理。垃圾收集機(jī)制的原理:找到那些不再繼續(xù)使用的變量肩豁,然后釋放其占用的內(nèi)存脊串。垃圾收集機(jī)制會(huì)按照固定的時(shí)間間隔周期性地執(zhí)行這一操作。垃圾收集機(jī)制必須跟蹤哪個(gè)變量有用哪個(gè)變量無(wú)用清钥,對(duì)于不再有用的變量打上標(biāo)記琼锋,以備將來收回其占用的內(nèi)存。JS 中最常用的跟蹤方法是標(biāo)記清除祟昭,當(dāng)函數(shù)執(zhí)行完后缕坎,就會(huì)給局部變量打上“離開環(huán)境”的標(biāo)記(除了閉包),在下一次垃圾回收時(shí)間到來時(shí)就會(huì)清除這一塊內(nèi)存篡悟,手動(dòng)將一個(gè)有值的變量賦值為 null谜叹,也是讓這個(gè)值離開環(huán)境匾寝,也可以釋放內(nèi)存。還有一種跟蹤方法是引用計(jì)數(shù)荷腊,這會(huì)引起循環(huán)引用的問題艳悔,但現(xiàn)在所有的瀏覽器都使用了標(biāo)記清除式的垃圾回收機(jī)制,所以不用考慮這個(gè)問題了女仰。
29猜年、eventloop
1)進(jìn)程和線程
進(jìn)程是一個(gè)程序完成的過程= CPU 加載上下文 + CPU 執(zhí)行 + CPU 保存上下文
線程是構(gòu)成一個(gè)程序的任務(wù)組合,在CPU 執(zhí)行階段就是執(zhí)行這一個(gè)一個(gè)任務(wù)
不同進(jìn)程間數(shù)據(jù)很難共享疾忍,不同線程間數(shù)據(jù)容易共享進(jìn)程可以拓展到多機(jī)(多進(jìn)程)乔外,線程最多適合多核(多線程),多核中可以開啟多進(jìn)程一罩,比如瀏覽器打開一個(gè)新的頁(yè)面(除了多個(gè)空白頁(yè)面外)袁稽,就會(huì)創(chuàng)建一個(gè)進(jìn)程某個(gè)線程使用某些共享內(nèi)存時(shí),可以上一把“互斥鎖”擒抛,其他線程只能排隊(duì)等待,防止多線程同時(shí)讀取某一塊內(nèi)存區(qū)域某些內(nèi)存區(qū)域只能供給固定的數(shù)目線程使用补疑,其他線程排隊(duì)等待歧沪,這叫“信號(hào)量“,保證多線程不會(huì)互相沖突
2)任務(wù)隊(duì)列
JS 是單線程的莲组,即同一時(shí)間只能完成一個(gè)任務(wù)
任務(wù)隊(duì)列也可以叫異步隊(duì)列诊胞,當(dāng)一個(gè)事件被觸發(fā)時(shí),就會(huì)將回調(diào)函數(shù)體扔進(jìn)這個(gè)任務(wù)隊(duì)列中锹杈,當(dāng)運(yùn)行棧的同步任務(wù)執(zhí)行完畢時(shí)(一個(gè)tick 完成)撵孤,就會(huì)去任務(wù)隊(duì)列中取異步任務(wù),然后執(zhí)行(此時(shí)為下一個(gè) tick)竭望。所以 JS 遇到一個(gè)異步任務(wù)時(shí)會(huì)掛起邪码,而不是立即執(zhí)行。
30咬清、如何快速讓字符串變成以千為精度的數(shù)字
我也不知道理解題目是否到位闭专,應(yīng)該是為了考Number() 的精準(zhǔn)度的問題,代碼如下:
(Number('12.69598')*1000).toFixed(0)
//Number('12.69598')*1000得到的結(jié)果為 12695.970000000001
//通過 toFixed() 完全去除小數(shù)點(diǎn)
ES6--http://es6.ruanyifeng.com/