因爲本人仍是一個前端菜鳥,因此是很是有可能出錯誤的,熱烈歡迎指正!javascript
運行在瀏覽器的V8引擎中的腳本語言,不要編譯就能夠由解釋器直接運行的,此外變量鬆散定義,屬於弱類型語言。html
<script>
元素中設置defer屬性,至關於告訴瀏覽器馬上下載,延遲執行。會等頁面解析完後,按指定順序執行原始類型有:boolean,string,number,null,undefined,symbol 6種。前端
null不是對象,typeof null會輸出object,是由於之前32爲系統會將000開頭的表明爲對象,但null表示爲全零因此被錯誤判斷成objectjava
1.原始類型存儲的是值,引用類型存儲的是指針。 2.原始數據類型直接存儲在棧中,引用數據類型存儲在堆中。node
undefined,string,number,boolean,object,function面試
typeof只能區分除null的原始類型,引用類型只能區分function。算法
在計算機中,數字以二進制形式存儲。在JavaScript中數字採用IEEE754的雙精度標準進行存儲,由於存儲空間有限,當出現沒法整除的小數時會取一個近似值。編程
在0.1+0.2中0.1和0.2都是近似表示的,因此相加時兩個近似值進行計算致使最後結果爲0.3000,0000,0000,0004,此時對於JS來講不夠近似於0.3,因此出現了0.1+0.2!=0.3json
解決方法:parseFloat((0.1+0.2).toFixed(10)) === 0.3 // true後端
undefine: 表示變量被聲明瞭但沒有賦值。
null:變量被定義賦值了,可是爲空,沒有任何屬性方法和值
做爲屬性名的使用
var mySymbol=Symbol();
var a={};
a[mySymbol]='hello'
複製代碼
==會進行類型轉換後再比較,===不會,儘可能都用===.
如下兩種狀況能夠用==
var obj={}
if(obj.a == null){
// 至關於obj.a===null || obj.a===undefined
}
複製代碼
function fn(a, b){
if(b == null){
// 至關於b===null || b===undefined
}
}
複製代碼
Object.is()能夠說是升級版,Object.is(NaN,NaN)會返回true,Object.is(-0,+0)返回false
1.判斷是否具備數組某些方法
if(arr.splice){}
2.instanceof(某些IE版本不正確)
arr instanceof Array
3.Array.isArray()
4.Object.prototype.toString.call(arr); // '[object Array]'
5.constructor方法
arr.constructor === Array
let arrayLike = {
'0' : 'a',
'1' : 'b',
'2' : 'c',
length : 3
};
let arr1 = Array.prototype.slice.call(arrayLike);
let arr2 = [].slice.call(arrayLike);
let arr3 = Array.from(arrayLike);
複製代碼
// 會改變原數組
pop() // 末尾刪除
push() // 末尾新增
shift() // 開頭刪除
unshift() // 開頭新增
reverse() // 數組反轉
sort() // 排序
splice() // 修改數組(刪除插入替換)
// 不會改變原數組
concat() // 合併數組
slice() // 選擇數組的一部分
indexOf() // 順序查找指定元素下標
lastIndexOf() // 倒序查找指定元素下標
複製代碼
// 迭代方法
// every()查詢數組是否每一項都知足條件
// some()查詢數組中是否有知足條件的項
// filter()過濾,返回true的項組成的數組
// map()對每一項運行給定函數,返回每次函數調用結果組成的數組
// forEach()對每一項運行給定函數,無返回值
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.every(function(item,index,array){
return item>2;
}) // false
numbers.some(function(item,index,array){
return item>2;
}) // true
numbers.filter(function(item,index,array){
return item>2;
}) // [3,4,5,4,3]
numbers.map(function(item,index,array){
return item*2;
}) // [2,4,6,8,10,8,6,4,2]
numbers.forEach(function(item,index,array){
// 執行某些操做
}) // 無返回值
複製代碼
// 歸併方法
// reduce()從第一項開始逐個遍歷到最後
// reduceRight()從最後一項開始向前遍歷到第一項
var values = [1,2,3,4,5];
values.reduce(function(prev,cur,index,array){
return prev+cur;
}) // 15
// reduceRight()結果同樣,順序相反
複製代碼
插入排序和快速排序結合的排序算法
[1,NaN,NaN]
由於parentInt須要兩個參數(val,radix),radix表示解析進制,而map傳入三個(element,index,array)致使對應的radix不合法致使解析錯誤。
function formatDate(dt){
if(!dt){
dt=new Date()
}
var year = dt.getFullYear();
var month = dt.getMonth()+1;
var day = dt.getDate();
if(month<10){
month= '0'+month;
}
if(day<0){
day = '0'+day;
}
var formatDt = year+'-'+month+'-'+day
return formatDt;
}
複製代碼
getHour() // 返回時
getMinutes() // 返回分
getSeconds() // 返回秒
getDay() // 返回星期天數
getTime() // 返回毫秒數
複製代碼
解析器會先讀取函數聲明,提高到最前。而函數表達式會等到執行到它所在的代碼才真正被解釋執行
// 函數聲明
function sum(a,b){
return a+b;
}
// 函數表達式
var sum = function(a,b){
return a+b;
}
複製代碼
this引用函數執行的環境對象,老是指向函數的直接調用者,在執行時才能肯定值
1.默認綁定,在瀏覽器中爲window,在node中是global
2.隱式綁定 例:window.a()
3.顯式綁定
4.new綁定
5.箭頭函數
這裏寫的不是很詳細,推薦看JavaScript深刻之史上最全--5種this綁定全面解析
相同點
不一樣點
callee是arguments對象的一個屬性,指向arguments對象的函數即當前函數。遞歸可使用arguments.callee()。 箭頭函數中this做用域與函數外一致,且沒有arguments對象,因此箭頭函數沒有callee
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1)
}
}
複製代碼
caller是函數對象的一個屬性,指向調用當前函數的函數,好比A調用B,則B.caller指向A()。全局做用域下調用當前函數,caller的值爲null
toFixed()按指定小數位返回數值的字符串表示
var num=10; num.toFixed(2); // '10.00'
// charAt()根據字符位置返回所在位置的字符串
// charCodeAt()根據字符位置返回所在位置字符串的字符編碼
var str = 'hello world';
str.charAt(1); // 'e'
str.charCode(1); // '101'
// fromCharCode() 接收字符編碼轉爲字符串
String.fromCharCode(104,101,108,108,111) //'hello'
// concat()將字符拼接起來獲得新字符串
var str="hello"
str.concat(' world'); // "hello world"
// indexOf()和lastIndexOf() 返回字符位置
// trim() 刪除空格
// toLowerCase() 轉小寫,toUpperCase() 轉大寫
// localeCompare() 根據字母表比較排序
複製代碼
slice和substring接收的是起始位置和結束位置,substr接收的是起始位置和所要返回的字符串長度
對於負數,slice會將負數與字符串長度相加,substr會將負的第一個參數加上字符串長度,第二個參數轉爲0,substring會將全部負數轉爲0
join()將數組中的元素放入一個字符串中,split將字符串分割成數組
var arr=[1,2,3];
var str=arr.join('|'); // '1|2|3'
str.split('|'); // [1,2,3]
複製代碼
URI編碼方法
encodeURI和encodeURICcomponent encodeURI和decodeURICcomponent
eval的做用?
功能是將對於字符串解析出JS代碼執行。要避免使用eval,不安全且很是消耗性能
var max=Math.max(3,54,32,16); // 54
複製代碼
構造函數特色:函數名首字母大寫,它就相似一個模板,能夠new出多個實例
var a={} // var a=new Object()的語法糖
var a=[] // var a=new Array()的語法糖
function Foo(){} // var Foo=new Function()的語法糖
複製代碼
instanceof判斷引用類型屬於哪一個構造函數
f instanceof Foo的判斷邏輯:f的__proto__一層層往上,可否找到Foo.prototype。
可是由於原型鏈上全部特殊對象的__proto__最終都會指向Object.prototype,因此instanceof判斷類型也不徹底準確
function _new(){
let obj = {};
let con=[].shift.call(arguments);
obj.__proto__ = con.prototype;
let res = con.apply(obj, arguments);
return res instanceof Object ? res : obj;
}
複製代碼
更推薦字面量的方式建立對象,性能和可讀性都更好。使用var o=new Object()和var o={}的區別是前者會調用構造函數
hasOwnProperty判斷該對象自己是否有指定屬性,不會到原型鏈上查找。
使用方法:object.hasOwnProperty(proName)
利用它能夠循環對象自身屬性
for(let item in f){
if(f.hasOwnProperty(item)){
console.log(item)
}
}
複製代碼
訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找,這就是原型鏈
function createPerson(name, age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(this.name)
}
return o;
}
var person1 = createPerson('chen',21)
複製代碼
沒有顯示建立對象,直接將屬性方法賦給this,沒有return語句
function Person(name, age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name)
}
}
var person1 = new Person('chen',21)
複製代碼
缺點:每一個方法都要在每一個實例上從新定義一遍,沒法獲得複用
function Person(){}
Person.prototype.name="chen"
Person.prototype.age=21
Person.prototype.sayName=function(){
console.log(this.name)
}
var person1 = new Person()
複製代碼
缺點:全部實例都取得相同屬性值
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype = {
constructor: Person,
sayName: function(){
console.log(this.name)
}
}
var person1=new Person('chen',21)
複製代碼
JavaScript經過原型鏈實現繼承
function Parent(){
this.name = 'parent'
}
Parent.prototype.sayName = function(){
return this.name
}
function Child(){
}
// 繼承了Parent
Child.prototype = new Parent();
var child1=new Child();
child1.say();
複製代碼
缺點:對象實例共享全部繼承的屬性和方法
function Parent(){
this.arr=[1,2,3]
}
function Child(){
// 繼承了Parent
Parent.call(this)
}
var child1 = new Child();
child.arr.push(4); // [1,2,3,4]
var child2 = new Child();
child.arr; // [1,2,3]
複製代碼
使用原型鏈繼承共享的屬性和方法,經過借用構造函數繼承實例屬性
function Parent(name){
this.name = name;
this.arr = [1,2,3]
}
Parent.prototype.sayName = function(){
console.log(this.name)
}
function Child(name,age){
// 繼承屬性
Parent.call(this, name)
this.age=age
}
// 繼承方法
Child.prototype = new Parent()
Child.prototype.constructor = Child;
Child.prototype.sayAge = function(){
console.log(this.age)
}
var child1=new Child('chen',21);
child1.arr.push(4); //[1,2,3,4]
child1.sayName() // 'chen'
child1.sayAge() // 21
var child2=new Child('miao', 12)
child2.arr // [1,2,3]
child2.sayName() // "miao"
child2.sayAge() // 12
複製代碼
缺點:不管在什麼狀況都會調用兩次父構造函數,一次是建立子類型原型,另外一次是在子構造函數內部
var person = {
name: 'chen',
arr: [1,2,3]
}
var person1 = Object.create(person);
person1.name = 'run'
person1.arr.push(4)
var person2 = Object.create(person);
person2.name = 'miao'
person2.arr.push(5)
person.arr; // [1,2,3,4,5]
複製代碼
function create(original){
// 經過調用函數建立一個新對象
var clone = object(original);
// 以某種方式加強對象
clone.sayHi = function(){
console.log('hi')
}
return clone;
}
var person = {
name: 'chen'
}
var person1 = create(person);
person1.sayHi();
複製代碼
function Parent(name){
this.name = name;
this.arr = [1,2,3]
}
// 將共享的屬性/方法放到原型上
Parent.prototype.sayName = function(){
console.log(this.name)
}
// 借用構造函數加強子類實例屬性(支持傳參和避免篡改)
function Child(name,age){
// 繼承屬性
Parent.call(this, name)
this.age=age
}
function inheritPrototype(Child, Parent){
var prototype=Object.create(Parent.prototype);
prototype.constructor=Child;
Child.prototype=prototype;
}
// 將父類原型指向子類
inheritPrototype(Child, Parent);
Child.prototype.sayAge=function(){
console.log(this.age)
}
var child1=new Child('chen',21);
child1.arr.push(4); //[1,2,3,4] 繼承自父類實例屬性
child1.sayName() // 'chen' 繼承自父類原型方法
child1.sayAge() // 21 繼承自子類原型方法
var child2=new Child('miao', 12)
child2.arr // [1,2,3]
child2.sayName() // "miao"
child2.sayAge() // 12
複製代碼
class只是語法糖,本質是函數
class Parent{
constructor(value){
this.val=value
}
getValue(){
console.log(this.val)
}
}
class Child extends Parent{
constructor(value){
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
複製代碼
核心:使用extends代表繼承自哪一個父類,而且在子類構造函數中必須使用super,能夠看作是Parent.call(this,value)
面向過程就是分析出解決問題所需的步驟,而後用函數把這些步驟一步步實現,使用的時候一個個依次調用
面向對象是把構成問題的事務分解成各個對象,獎勵對象的目的不是爲了完成一個步驟,而是爲了描述某個事物在整個解決問題的步驟中的行爲。
優勢:易維護,可讀性高,易擴展,繼承性高,下降重複工做量,縮短了開發走起
閉包指有權訪問另外一個函數內部變量的函數,當在函數內部定義了其餘函數,也就建立了閉包
使用閉包能夠模仿塊級做用域
優勢:能夠避免全局變量的污染,實現封裝和緩存;
缺點:閉包會常駐內存,增大內存使用量,使用不當很容易形成內存泄漏。解決方法:在退出函數以前,將不適用的局部變量設爲null。
閉包最大的兩個用處:1.能夠讀取函數內部的變量;2.使這些變量始終保存在內存中;3.封裝對象的私有屬性和私有方法
做用域鏈的做用是保證執行環境裏有權訪問的變量和函數是有序的,做用域鏈的變量只能享受訪問,訪問到window對象即被終止。
簡單來講,做用域就是變量和函數的可訪問範圍
alert(),prompt(),confirm(),open(),close(),print(),focus(),blur(),moveBy(),moveTo(),resizeBy(),resizeTo(),scrollBy(),scrollTo(),setInterval(),setTimeout(),clearInterval(),clearTimeout()
history.go(-1); // 後退一頁
history.go(1); // 前進一頁
history.back(); // 後退一頁
history.forward(); // 前進一頁
複製代碼
var leftPos=(typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX;
var topPos=(typeof window.screenTop == 'number') ? window.screenTop : window.screenY;
複製代碼
moveTo():接收新位置的x,y座標值
moveBy():接收在水平垂直方向上移動的像素數
outerWidth和outerHeight返回瀏覽器窗口自己的尺寸,innerWidth和innerHeight返回容器視圖區的大小
使用navigator.userAgent
var ua = navigator.userAgent;
var isChrome = ua.indexOf('Chrome')
複製代碼
使用location的屬性
history.go(1)
複製代碼
由於DOM屬於渲染引擎的東西,JS又是JS引擎的東西,當咱們經過JS操做DOM的時候,涉及到兩個線程間的通訊,並且操做DOM可能會帶來重繪迴流的狀況,因此就致使了性能問題。
解決方法:
重繪是當節點改變樣式而不影響佈局,迴流是當佈局或幾何屬性須要改變
迴流一定會發生重繪,迴流的成本比重繪高
性能問題:1.改變window大小 2.改變字體 3.添加或刪除樣式 4.文字改變 5.定位或浮動 6.盒模型
注意事項:
parentNode.insertBefore(newNode, refNode)
parentNode表示父節點,newNode表示要添加的節點,refNode表示參照節點parent.replaceChild(newChild,oldChild)
element.setAttribute(name, value)
ele.style.color = 'red'
ele.style.setProperty('font-size', '16px')
ele.style.removeProperty('color')
複製代碼
var style = document.createElement('style');
style.innerHTML='body{color:red;} #top{color:white;}';
document.head.appendChild(style);
複製代碼
attribute是dom元素在文檔中做爲HTML標籤擁有的屬性
prototype是dom元素在JS中做爲對象擁有的屬性
dom.style.width/height
dom.currentStyle.width/height
window.getCompontedStyle(dom).width/height
dom.getBoundingClientRect().width/height
dom.offsetWidth/offsetHeight
element.onclick=function(){}
element.addEventListener('click',function(){},false)
element.addEventListener('keyup',function(){},false)
DOM0級事件就是將一個函數賦值給一個事件處理屬性,缺點在於一個處理程序沒法同時綁定多個處理函數。
DOM2級事件運行給一個程序添加多個處理函數,定義了addEventListener和removeEventListener兩個方法,分別用於綁定和解綁事件,方法包含三個參數分別是綁定的事件處理的屬性名稱,處理函數,是否在捕獲時執行事件
IE8如下使用attachEvent和detachEvent實現,不須要傳入第三個參數,由於IE8如下只支持冒泡型事件
btn.attachEvent('onclick', showFn);
btn.detachEvent('onclick', showFn);
複製代碼
DOM3級事件是在DOM2級事件的基礎上添加不少事件類型如load,scroll,blur,focus,dbclick,mouseup,mousewheel,textInput,keydown,keypress,同時也容許使用者自定義一些事件。
<div onclick="clicked()"></div>
複製代碼
優缺點:簡單,但與HTML代碼緊密耦合,更改不方便
document.onclick = function(){}; // 指定事件
docuemtn.onclick = null; // 刪除事件
複製代碼
優缺點:簡單且跨瀏覽器
addEventListener('click', function(){},布爾值) // 指定事件
removeListener('click', function(){}, 布爾值) // 移除事件
複製代碼
優缺點:能夠添加多個監聽函數,若是指定處理函數是匿名函數,則沒法刪除
attachEvent('onclick', function(){}) // 指定事件
detachEvent('click', function(){}) // 移除事件
複製代碼
優缺點:能夠添加多個監聽函數,若是指定處理函數是匿名函數,則沒法刪除
attachEvent()第一個參數比addEventListener()的事件名多一個「on」;且沒有第三個參數,由於IE事件模型只支持冒泡事件流
IE中事件處理程序處於全局做用域,其內的this會指向window,而DOM事件模型是做用於元素,其內的this執行所屬元素
阻止冒泡
IE:cancelBubble=true
DOM: stopPropagation()
阻止元素默認事件
IE:returnValue=false
DOM:preventDefault()
事件目標
IE: srcElement DOM: target
var ev = ev || window.event;
document.docuemntElement.clientWidth || document.body.clientWidth
var target = ev.srcElement || ev.target
複製代碼
addEventListener第三個參數爲false,事件在冒泡時候執行,事件目標target一級一級往上冒泡
如何阻止事件冒泡
child.addEventListener('click', function(e){
console.log('目標事件')
e.stopPropagation();
}, false)
複製代碼
事件捕獲是自上而下執行的,將addEventListener第三個參數爲true
DOM2級事件規定的事件流包括三個階段:事件捕獲階段,處於目標階段和事件冒泡階段。首先發送的是事件捕獲,爲截獲事件提供了機會,而後是實際的目標接收到事件,最後一個階段是冒泡階段,能夠在這個階段對事件作出響應
window->docuemnt->html->body->...
一開始接收事件的window,window接收完之後給到document,第三個纔是html標籤,再就是body,而後在一級一級往下傳。與之至關的就是冒泡,從當前元素到window的過程
var event = new Event('custome');
ev.addEventListener('custome', function(){
console.log('custome');
});
ev.dispatchEvent(event);
複製代碼
mouseover:當鼠標移入元素或其子元素都會觸發事件,因此有一個重複觸發,冒泡的過程。對應的移除事件是mouseout
mouseenter: 當鼠標移入元素自己(不包含子元素)會觸發事件,不會冒泡,對應的移除事件是mouseleave
事件代理(事件委託)把本來須要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。原理是DOM元素的事件冒泡。
好處:能夠提升性能,大量節省內存佔用,減小事件註冊,能夠實現新增子對象時無需再次對其綁定
document.onload是在樣式結構加載完才執行,window.onload()不只要樣式結構加載完還要執行完全部樣式圖片資源文件所有加載完後纔會觸發
document.ready原生中無此方法,jQuery中有$().ready(),ready事件不要求頁面全加載完,只須要加載完DOM結構便可觸發。
JSON是JS的一個內置對象,也是一種輕量級的數據交換格式。 經常使用兩個方法:
// 建立XMLHTTPRequest對象
var xhr = new XMLHttpRequest();
// 建立一個新的http請求
xhr.open("get", url, true)
// 設置響應HTTP請求狀態變化的函數
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
// 獲取異步調用返回的數據
alert(xhr.responseText)
}
}
}
// 發送HTTP請求
xhr.send(null);
複製代碼
狀態碼readyState說明:0:未初始化,未調用send();1:已調用send(),正在發生請求;2:send()方法執行完畢,已經接收到所有響應內容;3:正在解析響應內容;4:解析完成,能夠在客戶端調用了
優勢:1.經過異步模式,提高用戶體驗;2.優化了瀏覽器和服務器之間的傳輸,減小沒必要要的數據往返,減小了帶寬佔用;3.Ajax在客戶端運行,承擔了一部分原本由服務器承擔的工做,減小了大用戶量下的服務器負載;4.Ajax能夠實現局部刷新
缺點:1.Ajax暴露了與服務器交互的的細節;2.對搜索引擎的支持較弱;3.不容易調試
誤區:咱們常認爲GET請求參數的大小存在限制,而POST無限制
實際上HTTP協議沒有限制GET/POST的請求長度限制,GET的最大長度限制是由於瀏覽器和WEB服務器限制了URL的長度。
GET和POST方法沒有實質區別,只是報文格式不一樣。
能夠跨域的三個標籤:<img><link><script>
跨域指經過JS在不一樣的域之間進行數據傳入或通訊。 協議,域名,端口有一個不一樣就是不一樣域
同源策略是爲了防止CSRF攻擊,它是利用用戶的登陸態發起惡意請求。若是沒有同源策略,網站能夠被任意來源的Ajax訪問到內容。
<script>
標籤沒有跨域限制的漏洞function jsonp(url, callback, success){
let script = docuemnt.createElement('scipt');
script.src = url;
script.async = true;
script.type = "text/javascript";
window[callback] = function(data){
success && success(data)
}
document.body.appendChild(script)
}
jsonp('http://xxx.com', 'callback', function(value){
console.log(value);
})
複製代碼
JSONP使用簡單,兼容性不錯但僅限GET請求
a.test.com
和b.test.com
。在頁面添加document.domain = 'test.com'
表示二級域名相同便可跨域// 發送消息
window.parent.postMessage('message', 'http://www.test.com')
// 接收消息
let mc=new MessageChannel()
mc.addEventListener('message', event => {
let origin = event.origin || event.originalEvent.origin;
if(origin === 'http://www.test.com'){
console.log('驗證經過')
}
})
複製代碼
window.name
答:
cookie,sessionStorage,localStorage,indexDB。
indexDB不限存儲大小,不與服務器端通訊,除非主動刪除不然一直存在。
Service Worker是運行在瀏覽器背後的獨立線程,通常能夠用來實現緩存功能,傳輸協議必須爲https
箭頭函數與普通函數的區別:
let nodeList = document.querySelectorAll('div')
let arr=[...nodeList]
複製代碼
let arr1=[1,2,3]
let arr2=[4,5,6]
let arr3=[...arr1, ...arr2]
複製代碼
利用了遍歷對象內部的iterator接口,將for...of循環分解爲最原始的for循環
Generator能夠控制函數的執行
function *foo(x){
let y=2*(yield(x+1))
let z=yield(y/3)
return (x+y+z)
}
let it=foo(5)
it.next() // {value:6,done:false}
it.next(12)
it.next(13)
複製代碼
Generator函數調用會返回一個迭代器,第一次next,函數停在yield(x+1)因此value=5+1=6; 第二次next,傳入參數至關於上一個yield的值,即let y=2*12=24; let z=24/3=8;第三次next,傳入參數至關於上一個yield的值,即let z=13,y=24,x=5,相加返回42
async就是將函數返回值使用Promise.resolve()包裹一下,和then中處理返回值同樣,而且async只能搭配await使用 await其實就是generator加上Promise的語法糖,且內部實現了自動執行generator
setTimeout延後執行,setInterval每隔一段時間執行一次回調函數,以上兩種都不能保證在預期時間執行任務 requestAnimationFrame自帶函數節流功能,且延時效果是精確的
const PENDGIN='pending'
const RESOLVED='resolved'
const REJECTED='rejected'
function myPromise(fn){
const that=this
that.state=PENDING
that.value=null
that.resolvedCallbacks=[]
that.rejectedCallbacks=[]
function resolve(value){
if(value instanceof myPromise){
return value.then(resolve, reject)
}
setTimeout(()=>{
if(that.state===PENDING){
that.state=RESOLVED;
that.value=value;
that.resolvedCallbacks.map(cb => cb(that.value))
}
},0)
}
function reject(error){
setTimeout(()=>{
if(that.state===PENDING){
that.state=REJECTED
that.value=value
that.rejectedCallbacks.map(cb => cb(that.value))
}
},0)
}
try{
fn(resolve, reject)
}catch(e){
reject(e)
}
}
myPromise.prototype.then=function(onFulfilled, onRejected){
const that=this
onFulfilled=typeof onFulfilled==='function'?onFulfilled: v=>v
onRejected=typeof onRejected==='function'?onRejected: r=>{throw r}
if(that.state===PENDING){
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if(that.state===RESOLVED){
onFulfilled(that.value)
}
if(that.state===REJECTED){
onRejected(that.value)
}
}
new myPromise((resolve, reject) => {
setTimeout(()=>{
resolve(1)
},0)
}).then(value=>{
console.log(value)
})
複製代碼
進程描述了CPU在運行指令及加載和保存上下文所需的事件,線程是更小的單位,秒速了執行一段指令所需的時間
由於js能夠修改DOM,若是JS執行的過程UI線程還在工做會致使不能不能安全的渲染UI。 JS單線程運行,能夠達到節省內存,節約上下文切換時間,沒有鎖的問題
執行棧存放函數調用的棧結構,遵循先進後出的原則。 從底部開始放進去執行棧,而後從棧頂取出執行
不一樣的任務源會被分配到不一樣task隊列中,任務源能夠分爲微任務(microtask)和宏任務(macrotask) 微任務包括process.nextTick, promise, MutationObserver 宏任務包括script,setTimeout,setInterval,setImmediate,I/O,UI rendering
1.首先執行同步代碼,這屬於宏任務 2.當執行完全部同步代碼後,執行棧爲空,查詢是否有異步代碼須要執行 3.執行全部微任務 4.當執行完全部微任務後,若有必要會渲染頁面 5.而後開始下一輪EventLoop,執行宏任務中的異步代碼,也就是setTimeout中的回調函數
Vue3.0其中一個核心功能就是使用Proxy替代Object.defineProperty Proxy能夠在目標對象前架設一個攔截器,通常Reflect搭配,前者攔截對象,後者返回攔截的結果
1.沒法探測到對象根屬性的添加和刪除 2.沒法探測到對數組基於下標的修改 3.沒法探測到.length修改的監測
JavaScript高級程序設計(第3版)