不要在同一行聲明多個變量
請是用 ===/!== 來比較 true/false 或者數值
使用對象字面量替代 new Array 這種形式
不要使用全局函數
Switch 語句必須帶有 default 分支
If 語句必須使用大括號
for-in 循環中的變量 應該使用 let 關鍵字明確限定做用域,從而避免做用域污染javascript
閉包就是可以讀取其餘函數內部變量的函數
閉包是指有權訪問另外一個函數做用域中變量的函數,建立閉包的最多見的方式就是在一個
函數內建立另外一個函數,經過另外一個函數訪問這個函數的局部變量,利用閉包能夠突破做用鏈域
閉包的特性:html
函數內再嵌套函數
內部函數能夠引用外層的參數和變量
參數和變量不會被垃圾回收機制回收java
優勢:可以實現封裝和緩存等
缺點:消耗內存、使用不當會內存溢出,
解決方法:在退出函數以前,將不使用的局部變量所有刪除node
做用域鏈的做用是保證執行環境裏有權訪問的變量和函數是有序的,做用域鏈的變量只能向上訪問,變量訪問到 window對象即被終止,做用域鏈向下訪問變量是不被容許的。
簡單的說,做用域就是變量與函數的可訪問範圍,即做用域控制着變量與函數的可見性和生命週期jquery
每一個對象都會在其內部初始化一個屬性,就是 prototype (原型),當咱們訪問一個對象的屬性時,若是這個對象內部不存在這個屬性,那麼他就會去 prototype 裏找這個屬性,這個prototype 又會有本身的 prototype ,因而就這樣一直找下去,也就是咱們平時所說的原型鏈的概念
關係: instance.constructor.prototype = instance._proto_
特色:JavaScript 對象是經過引用來傳遞的,咱們建立的每一個新對象實體中並無一份屬於本身的原型副本。當咱們修改原型時,與之相關的對象也會繼承這一改變當咱們須要一個屬性的時, Javascript 引擎會先看當前對象中是否有這個屬性, 若是沒有的,就會查找他的 Prototype 對象是否有這個屬性,如此遞推下去,一直檢索到 Object內建對象nginx
原型 prototype 機制或 apply 和 call 方法去實現較簡單,建議使用構造函數與原型混合方式es6
function Parent(){ this.name = 'wang'; } function Child(){ this.age = 28; } Child.prototype = new Parent();//繼承了Parent,經過原型 var demo = new Child(); alert(demo.age); alert(demo.name);//獲得被繼承的屬性
必要性:因爲字符串、對象和數組沒有固定大小,全部當他們的大小已知時,才能對他們進行動態的存儲分配。JavaScript程序每次建立字符串、數組或對象時,解釋器都必須分配內存來存儲那個實體。只要像這樣動態地分配了內存,最終都要釋放這些內存以便他們可以被再用,不然,JavaScript的解釋器將會消耗完系統中全部可用的內存,形成系統崩潰。面試
這段話解釋了爲何須要系統須要垃圾回收,JS不像C/C++,他有本身的一套垃圾回收機制(Garbage Collection)。JavaScript的解釋器能夠檢測到什麼時候程序再也不使用一個對象了,當他肯定了一個對象是無用的時候,他就知道再也不須要這個對象,能夠把它所佔用的內存釋放掉了。例如:ajax
var a="hello world"; var b="world"; var a=b; //這時,會釋放掉"hello world",釋放內存以便再引用
垃圾回收的方法:標記清除、計數引用。chrome
標記清除
這是最多見的垃圾回收方式,當變量進入環境時,就標記這個變量爲」進入環境「,從邏輯上講,永遠不能釋放進入環境的變量所佔的內存,永遠不能釋放進入環境變量所佔用的內存,只要執行流程進入相應的環境,就可能用到他們。當離開環境時,就標記爲離開環境。
垃圾回收器在運行的時候會給存儲在內存中的變量都加上標記(全部都加),而後去掉環境變量中的變量,以及被環境變量中的變量所引用的變量(條件性去除標記),刪除全部被標記的變量,刪除的變量沒法在環境變量中被訪問因此會被刪除,最後垃圾回收器,完成了內存的清除工做,並回收他們所佔用的內存。
引用計數法
另外一種不太常見的方法就是引用計數法,引用計數法的意思就是每一個值沒引用的次數,當聲明瞭一個變量,並用一個引用類型的值賦值給改變量,則這個值的引用次數爲1,;相反的,若是包含了對這個值引用的變量又取得了另一個值,則原先的引用值引用次數就減1,當這個值的引用次數爲0的時候,說明沒有辦法再訪問這個值了,所以就把所佔的內存給回收進來,這樣垃圾收集器再次運行的時候,就會釋放引用次數爲0的這些值。
用引用計數法會存在內存泄露,下面來看緣由:
function problem() { var objA = new Object(); var objB = new Object(); objA.someOtherObject = objB; objB.anotherObject = objA; }
在這個例子裏面,objA和objB經過各自的屬性相互引用,這樣的話,兩個對象的引用次數都爲2,在採用引用計數的策略中,因爲函數執行以後,這兩個對象都離開了做用域,函數執行完成以後,由於計數不爲0,這樣的相互引用若是大量存在就會致使內存泄露。
特別是在DOM對象中,也容易存在這種問題:
var element=document.getElementById(’‘); var myObj=new Object(); myObj.element=element; element.someObject=myObj;
這樣就不會有垃圾回收的過程。
在一個函數中,首先填充幾個參數,而後再返回一個新的函數的技術,稱爲函數的柯里化。一般可用於在不侵入函數的前提下,爲函數 預置通用參數,供屢次重複調用。
const add = function add(x) { return function (y) { return x + y } } const add1 = add(1) add1(2) === 3 add1(20) === 21
防抖(Debouncing)
防抖技術便是能夠把多個順序地調用合併成一次,也就是在必定時間內,規定事件被觸發的次數。
通俗一點來講,看看下面這個簡化的例子:
// 簡單的防抖動函數 function debounce(func, wait, immediate) { // 定時器變量 var timeout; return function() { // 每次觸發 scroll handler 時先清除定時器 clearTimeout(timeout); // 指定 xx ms 後觸發真正想進行的操做 handler timeout = setTimeout(func, wait); }; }; // 實際想綁定在 scroll 事件上的 handler function realFunc(){ console.log("Success"); } // 採用了防抖動 window.addEventListener('scroll',debounce(realFunc,500)); // 沒采用防抖動 window.addEventListener('scroll',realFunc);
上面簡單的防抖的例子能夠拿到瀏覽器下試一下,大概功能就是若是 500ms 內沒有連續觸發兩次 scroll 事件,那麼纔會觸發咱們真正想在 scroll 事件中觸發的函數。
上面的示例能夠更好的封裝一下
// 防抖動函數 function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; var myEfficientFn = debounce(function() { // 滾動中的真正的操做 }, 250); // 綁定監聽 window.addEventListener('resize', myEfficientFn);
防抖函數確實不錯,可是也存在問題,譬如圖片的懶加載,我但願在下滑過程當中圖片不斷的被加載出來,而不是隻有當我中止下滑時候,圖片才被加載出來。又或者下滑時候的數據的 ajax 請求加載也是同理。
這個時候,咱們但願即便頁面在不斷被滾動,可是滾動 handler 也能夠以必定的頻率被觸發(譬如 250ms 觸發一次),這類場景,就要用到另外一種技巧,稱爲節流函數(throttling)。
節流函數,只容許一個函數在 X 毫秒內執行一次。
與防抖相比,節流函數最主要的不一樣在於它保證在 X 毫秒內至少執行一次咱們但願觸發的事件 handler。
與防抖相比,節流函數多了一個 mustRun 屬性,表明 mustRun 毫秒內,必然會觸發一次 handler ,一樣是利用定時器,看看簡單的示例:
// 簡單的節流函數 function throttle(func, wait, mustRun) { var timeout, startTime = new Date(); return function() { var context = this, args = arguments, curTime = new Date(); clearTimeout(timeout); // 若是達到了規定的觸發時間間隔,觸發 handler if(curTime - startTime >= mustRun){ func.apply(context,args); startTime = curTime; // 沒達到觸發間隔,從新設定定時器 }else{ timeout = setTimeout(func, wait); } }; }; // 實際想綁定在 scroll 事件上的 handler function realFunc(){ console.log("Success"); } // 採用了節流函數 window.addEventListener('scroll',throttle(realFunc,500,1000));
上面簡單的節流函數的例子能夠拿到瀏覽器下試一下,大概功能就是若是在一段時間內 scroll 觸發的間隔一直短於 500ms ,那麼能保證事件咱們但願調用的 handler 至少在 1000ms 內會觸發一次。
一個模塊是能實現特定功能的文件,有了模塊就能夠方便的使用別人的代碼,想要什麼功能就能加載什麼模塊。
Commonjs:開始於服務器端的模塊化,同步定義的模塊化,每一個模塊都是一個單獨的做用域,模塊輸出,modules.exports,模塊加載require()引入模塊。
AMD:中文名異步模塊定義的意思。
requireJS實現了AMD規範,主要用於解決下述兩個問題。
1.多個文件有依賴關係,被依賴的文件須要早於依賴它的文件加載到瀏覽器
2.加載的時候瀏覽器會中止頁面渲染,加載文件越多,頁面失去響應的時間越長。
語法:requireJS定義了一個函數define,它是全局變量,用來定義模塊。
requireJS的例子:
//定義模塊 define(['dependency'], function(){ var name = 'Byron'; function printName(){ console.log(name); } return { printName: printName }; }); //加載模塊 require(['myModule'], function (my){ my.printName(); }
requirejs定義了一個函數define,它是全局變量,用來定義模塊:
define(id?dependencies?,factory)
在頁面上使用模塊加載函數:
require([dependencies],factory);
總結AMD規範:require()函數在加載依賴函數的時候是異步加載的,這樣瀏覽器不會失去響應,它指定的回調函數,只有前面的模塊加載成功,纔會去執行。
由於網頁在加載js的時候會中止渲染,所以咱們能夠經過異步的方式去加載js,而若是須要依賴某些,也是異步去依賴,依賴後再執行某些方法。
因爲篇幅有限,只能分享部分面試題,更多面試題及答案能夠【點擊我】閱讀下載哦~無償分享給你們,算是一個感恩回饋吧
事件代理( Event Delegation ),又稱之爲事件委託。是 JavaScript 中經常使用的綁定事件的經常使用技巧。顧名思義,「事件代理」便是把本來須要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好處是能夠提升性能
能夠大量節省內存佔用,減小事件註冊,好比在 table 上代理全部 td 的 click 事件就很是棒
能夠實現當新增子對象時無需再次對其綁定
W3C 中定義事件的發生經歷三個階段:捕獲階段( capturing )、目標階段
( targetin )、冒泡階段( bubbling )
冒泡型事件:當你使用事件冒泡時,子級元素先觸發,父級元素後觸發
捕獲型事件:當你使用事件捕獲時,父級元素先觸發,子級元素後觸發
DOM 事件流:同時支持兩種事件模型:捕獲型事件和冒泡型事件
阻止冒泡:在 W3c 中,使用 stopPropagation() 方法;在IE下設置 cancelBubble =true
阻止捕獲:阻止事件的默認行爲,例如 click - a 後的跳轉。在 W3c 中,使用preventDefault() 方法,在 IE 下設置 window.event.returnValue = false
建立一個空對象,而且 this 變量引用該對象,同時還繼承了該函數的原型
屬性和方法被加入到 this 引用的對象中
新建立的對象由 this 所引用,而且最後隱式的返回 this
Ajax 的原理簡單來講是在用戶和服務器之間加了—箇中間層( AJAX 引擎),經過XmlHttpRequest 對象來向服務器發異步請求,從服務器得到數據,而後用 javascript來操做 DOM 而更新頁面。使用戶操做與服務器響應異步化。這其中最關鍵的一步就是從服務器得到請求數據
Ajax 的過程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。 XMLHttpRequest 是ajax的核心機制
function deepClone(obj){ var newObj= obj instanceof Array ? []:{}; for(var item in obj){ var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item]; newObj[item] = temple; } return newObj; }
ES5的經常使用的對象克隆的一種方式。注意數組是對象,可是跟對象又有必定區別,因此咱們一開始判斷了一些類型,決定newObj是對象仍是數組~
var myNewAjax=function(url){ return new Promise(function(resolve,reject){ var xhr = new XMLHttpRequest(); xhr.open('get',url); xhr.send(data); xhr.onreadystatechange=function(){ if(xhr.status==200&&readyState==4){ var json=JSON.parse(xhr.responseText); resolve(json) }else if(xhr.readyState==4&&xhr.status!=200){ reject('error'); } } }) }
function ones(func){ var tag=true; return function(){ if(tag==true){ func.apply(null,arguments); tag=false; } return undefined } }
咱們假設這裏有一個user對象,
(1)在ES5中能夠經過Object.defineProperty來實現已有屬性的監聽
Object.defineProperty(user,'name',{ set:function(key,value){ } })
缺點:若是id不在user對象中,則不能監聽id的變化
(2)在ES6中能夠經過Proxy來實現
var user = new Proxy({},{ set:function(target,key,value,receiver){ } })
這樣即便有屬性在user中不存在,經過user.id來定義也一樣能夠這樣監聽這個屬性的變化哦~
(1)while循環的方式
function sleep(ms){ var start=Date.now(),expire=start+ms; while(Date.now()<expire); console.log('1111'); return; }
執行sleep(1000)以後,休眠了1000ms以後輸出了1111。上述循環的方式缺點很明顯,容易形成死循環。
(2)經過promise來實現
function sleep(ms){ var temple=new Promise( (resolve)=>{ console.log(111);setTimeout(resolve,ms) }); return temple } sleep(500).then(function(){ //console.log(222) }) //先輸出了111,延遲500ms後輸出222
(3)經過async封裝
function sleep(ms){ return new Promise((resolve)=>setTimeout(resolve,ms)); } async function test(){ var temple=await sleep(1000); console.log(1111) return temple } test(); //延遲1000ms輸出了1111
(4).經過generate來實現
function* sleep(ms){ yield new Promise(function(resolve,reject){ console.log(111); setTimeout(resolve,ms); }) } sleep(500).next().value.then(function(){console.log(2222)})
獲取一個對象的原型,在chrome中能夠經過_proto_的形式,或者在ES6中能夠經過Object.getPrototypeOf的形式。
那麼Function.proto是什麼麼?也就是說Function由什麼對象繼承而來,咱們來作以下判別。
Function.__proto__==Object.prototype //false
Function.__proto__==Function.prototype//true
咱們發現Function的原型也是Function。
首先了解下瀏覽器的同源策略 同源策略 /SOP(Same origin policy) 是一種約定,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,瀏覽器很容易受到 XSS 、 CSFR 等攻擊。所謂同源是指"協議+域名+端口"三者相同,即使兩個不一樣的域名指向同一個ip地址,也非同源
var script = document.createElement('script'); script.type = 'text/javascript'; // 傳參並指定回調執行函數爲onBack script.src = 'http://www.....:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回調執行函數 function onBack(res) { alert(JSON.stringify(res)); }
//父窗口:(http://www.domain.com/a.html) <iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script> //子窗口:(http://child.domain.com/b.html) document.domain = 'domain.com'; // 獲取父窗口中變量 alert('get js data from parent ---> ' + window.parent.user);
Object 是 JavaScript 中全部對象的父對象
數據封裝類對象: Object 、 Array 、 Boolean 、 Number 和 String
其餘對象: Function 、 Arguments 、 Math 、 Date 、 RegExp 、 Error
對象字面量: var obj = {};
構造函數: var obj = new Object();
Object.create(): var obj = Object.create(Object.prototype);
var arr = []; arr instanceof Array; // true
Array.isArray([]) //true Array.isArray(1) //false
var arr = []; arr.constructor == Array; //true
Object.prototype.toString.call([]) == '[object Array]' // 寫個方法 var isType = function (obj) { return Object.prototype.toString.call(obj).slice(8,-1); //return Object.prototype.toString.apply([obj]).slice(8,-1); } isType([]) //Array