let a = { valueOf() { return 0; }, toString() { return '1'; }, [Symbol.toPrimitive]() { return 2; } } 1 + a // => 3 '1' + a // => '12'
優先級: Symbol.toPrimitive>valueOf>toString
javascript
'a'++'b' 由於+'b' 會被強制轉換成NaN
function Foo() { return this; } Foo.getName = function () { console.log('1'); }; Foo.prototype.getName = function () { console.log('2'); }; new Foo.getName(); // -> 1 new Foo().getName(); // -> 2 優先級的順序 new (Foo.getName()); (new Foo()).getName(); //new Foo() 是返回return的內容而不是去看構造函數的屬性
先精後廣,一專多長css
js中隱式強制類型轉換 轉化爲數字類型 false等於0, true等於1 若是是對象,先調用valueOf,若是沒有用調用toString,這個過程叫ToPrimitive() NaN==NaN false 首先分析[]==![] Boolean() 判斷 0、-0、null、false、NaN、undefined、或者空字符串(""),則生成的 Boolean 對象的值爲 false, ![] 變成!Boolean([]) 也就是!true,也就是false
當有一個操做數是字符串時html
- 若是兩個操做數都是字符串,將兩個字符串拼接起來
- 若是隻有一個操做符石字符串,則另外一個操做數轉換爲字符串(toString)
當有一個操做數是複雜數據類型(對象,數組)時,將兩個操做數都轉換爲字符串(toString)相加vue
當 有一個操做數是簡單數據類型(true/false, null,undefined) 時,同時不存在複雜數據類型和字符串,則將兩個操做數都轉換成數值(ToNumber)相加。java
[]+{}node
知足a有對象/數組,全部都轉換爲字符串拼接 ""+"[object object]"="[object Object]"
1+{}react
知足第三條 a是對象/數組 "1[object Object]"
{}+[]git
"[object Object]"+toNumber([]) "[object Object]"+0
{}+{}github
"[object Object][object Object]"
經過構造函數方式建立的函數name都是 'anonymous' 使用bind方法獲得一個新的函數 function ff(){}; var f=ff.bind(); f.name 是bound ff
做用域是定義時的做用域,而不是執行時的做用域面試
閉包的使用場景
函數做爲返回值
function F1(){ var a = 100 //返回一個函數(函數做爲返回值) return function(){ console.log(a) } }函數做爲參數傳遞
function F1(){ var a = 100 return function(){ console.log(a) //自由變量,父做用域尋找 } }
一個函數執行若是造成一個不銷燬做用域,保護裏面的私有變量或者存儲私有變量,可是閉包容易引發內存泄露
形成內存泄露的狀況:
function fn(){ var a=1; return function(){ a++ } }
做用域
全局:關閉瀏覽器的時候銷燬 私有: 看是否返回地址而且被佔用了,決定是否銷燬 var 是沒有塊級做用域的
做用域鏈
是一個查找過程: 在一個私有做用域下遇到變量了,先看是否是本身私有的,若是不是往上一級找,沒有繼續找一隻找到window下爲之,沒有就報錯
上一級做用域
看當前整個做用域對應個人地址是在哪個做用域下定義的,那個做用域就是當前這個做用域的上一級
塊級做用域
{} if(){} for(){} while(){}
let 和const 定義的變量屬於一個私有做用域,變量是私有變量
實例便可以經過構造函數中this.屬性的方式獲得私有屬性還能夠經過__proto__
拿到所屬類的原型的公有屬性
詞法做用域
常見的變量
javaScript引擎是谷歌的v8引擎,這個引擎由兩個部分組成
建立執行上下文有兩個階段
在建立階段會發生三件事
this的綁定
在全局執行上下文中,this的值指向全局對象,(在瀏覽器中,this引用window對象)
在函數執行上下文,this的值取決於該函數式如何被調用的,若是它被一個引用對象調用,那麼this會被設置成那個對象,不然this的值被設置全局對象或者undefined(在嚴格模式下)
詞法環境內部有兩個組件
環境記錄器是存儲變量和函數聲明的實際位置
外部環境的引用意味着他能夠訪問其父級詞法環境(做用域)
詞法環境有兩種類型
全局環境(在全局執行上下文中)是沒有外部環境引用的詞法環境,
函數環境中,函數內部用戶定義的變量存儲在環境記錄器中,而且引用的外部環境多是全局環境,或者任何包含此內部函數的外部函數。
簡而言之
// 值類型:Number、string、bollean、undefined var a = 100 var b = a a = 200 console.log(b) // 100 保存與複製的是值自己 typeof abc //"undefined" typeof null //"object" 爲何要說呢?由於我錯了好屢次 typeof區分不了引用類型(除了函數) 用instanceof 來區分引用類型 alert(person instanceof Object); // 變量 person 是 Object 嗎? alert(colors instanceof Array); // 變量 colors 是 Array 嗎? alert(pattern instanceof RegExp); // 變量 pattern 是 RegExp 嗎?
function addNum(num) { num+=10; return num; } var num=10; var result=addNum(num); console.log(num);//10 console.log(result);//20 當爲函數傳遞參數的時候,是將此值複製一份傳遞給函數,因此在函數執行以後,num自己的值並無改變,函數中的被改變的值僅僅是副本而已 function mutate(obj) { obj.a = true; } const obj = {a: false}; mutate(obj) console.log(obj.a); // 輸出 true 在值傳遞的場景中,函數的形參只是實參的一個副本(至關於a拷貝了一份),當函數調用完成後,並不改變實參 在引用傳遞的場景中,函數的形參和實參指向同一個對象,當參數內部改變形參的時候,函數外面的實參也被改變 function setName(obj){ obj.name = '嘉明'; obj = new Object(); obj.name = '龐嘉明'; } var person = new Object(); setName(person); console.log(person.name); // '嘉明',爲啥不是'龐嘉明'呢? 新建的對象 obj = new Object() 它本身空間保存的地址將會被新的對象的存儲地址所覆蓋,由於是傳值不是引用,因此它不會影響到student空間所保存的地址,故最後雖然對obj的name屬性從新賦值,但也絲絕不影響以前賦值結果,按值傳遞 你傳進的一個對象 對象在內存地址中,即便你再外部賦值,可是內部 改變了,你外部就等於沒有賦值
height
、top
的百分比取值,老是相對於父元素的高度。
padding-top
、margin-top
、padding-bottom
、margin-bottom
取值爲百分比時,是相對於父元素的寬度。
一提到position:fixed,天然而然就會想到:相對於瀏覽器窗口進行定位。 但其實這是不許確的。若是說父元素設置了transform,那麼設置了position:fixed的元素將相對於父元素定位,不然,相對於瀏覽器窗口進行定位。
隱式轉換爲Boolean
if語句
字符串的隱式轉換
加運算符(+) ,可是隻要其中一個操做數是字符串,那麼它執行鏈接字符串的操做
爲何 ++[[]][+[]]+[+[]] = 10?
拆分 ++[[]][+[]] + [+[]] 繼續拆分 ++[[]][0] + [0] 繼續拆分 +([] + 1) + [0] +([] + 1) === +("」 + 1),而且 +("」 + 1) === +("1"),而且 +("1") === 1 最後簡化爲 1+[0] []=='' [0]=='0', [1]=='1' 因此 1+'0' ==='10'
由於==是比較值類型是否相等,true轉換爲數字是1 ,因此2==1爲false
'2'==true '2' 隱式轉化爲2 2==true 爲false
null == 0 // false null在設計上,在此處不嘗試轉型. 因此 結果爲false. null > 0 // false null 嘗試轉型爲number , 則爲0 . 因此結果爲 false, null >= 0 // true null<=0 //true 那麼你看 -null == 0 // true +null == 0 // true Number(null) == 0 // true
數組
使用delete運算符能夠將單元從數組中刪除,可是請注意,單元刪除後,數組的length屬性並不會發生變化。
數組經過數字進行索引,但有趣的是它們也是對象,因此也能夠包含字符串鍵值和屬性(但這些並不計算在數組長度內)
var a = [ ]; a[0] = 1; a["foobar"] = 2; a.length; // 1 a["foobar"]; // 2 a.foobar; // 2 若是字符串鍵值可以被強制類型轉換爲十進制數字的話,它就會被當作數字索引來處理 var a = [ ]; a["13"] = 42; a.length; // 14 類數組 var a = { '0': 1, '1': 2, '2': 3, length: 3 }; function foo() { var arr = Array.prototype.slice.call(arguments); arr.push("bam"); console.log(arr); } foo("bar", "baz"); // ["bar","baz","bam"] 同時用Array.from() 也能實現一樣的功能 對僞數組或可迭代對象(包括arguments Array,Map,Set,String…)轉換成數組對象
字符串
JavaScript中字符串是不可變的,而數組是可變的。
特殊的數字
var a=2/'foo'; a==NaN //false 由於NaN===NaN //false 可使用全局isNaN() 來判斷一個值是不是NaN 可是 isNaN('abc') //true ES6開始咱們使用Number.isNaN() var a = 2 / "foo"; var b = "foo"; Number.isNaN( a ); // true Number.isNaN( b ); // false——好! 無窮數 var a = 1 / 0; // Infinity var b = -1 / 0; // -Infinity 零值 常規的0(也叫+0)和-0 var a = 0 / -3; // -0 var b = 0 * -3; // -0 從字符串轉換爲數字 +"-0"; // -0 Number( "-0" ); // -0 JSON.parse( "-0" ); // -0 咱們爲何須要負零呢 數字的符號位用來表明其餘信息(好比移動的方向) 此時若是一個值爲0的變量失去了它的符號位,它的方向信息就會丟失。
值和引用
var a = [1,2,3]; var b = a; a; // [1,2,3] b; // [1,2,3] // 而後 b = [4,5,6]; a; // [1,2,3] b; // [4,5,6] 因爲引用指向的是值自己而非變量,因此一個引用沒法更改另外一個引用的指向 function foo(x) { x.push(4); x; // [1,2,3,4] // 而後 x = [4, 5, 6]; x.push(7); x; // [4,5,6,7] } var a = [1, 2, 3]; foo(a); a; // 是[1,2,3,4],不是[4,5,6,7] 咱們沒法自行決定使用值複製仍是引用複製,一切由值得類型來決定。 若是經過值複製的方式來傳遞複合值(如數組),就須要爲其建立一個複本,這樣傳遞的就再也不是原始值 foo(a.slice()) 若是要將標量基本類型值傳遞到函數內並進行更改,就須要將該值封裝到一個複合值(對象、數組等)中,而後經過引用複製的方式傳遞。 function foo(wrapper) { wrapper.a = 42; } var obj = { a: 2 }; // var obj=new Object(); obj.a=2; foo(obj); obj.a; // 42 與預期不一樣的是,雖然傳遞的是指向數字對象的引用複本,但咱們並不能經過它來更改其中的基本類型值 function foo(x) { x = x + 1; x; // 3 } var a = 2; var b = new Number(a); // Object(a)也同樣 foo(b); console.log(b); // 是2,不是3 只是多數狀況下咱們應該優先考慮使用標量基本類型
封裝對象包裝
var a=Boolean(false); var b=new Boolean(false); if (!b) { console.log( "Oops" ); // 執行不到這裏 } 咱們爲false建立了一個封裝對象,然而該對象是真值,因此這裏使用封裝對象獲得的結果和使用false截然相反 若是想要自行封裝基本類型值,可使用Object(...)函數(不帶new關鍵字) var a = "abc"; var b = new String( a ); var c = Object( a ); typeof a; // "string" typeof b; // "object" typeof c; // "object" b instanceof String; // true c instanceof String; // true
拆封
可使用ValueOf()函數 var a = new String( "abc" ); var b = new Number( 42 ); var c = new Boolean( true ); a.valueOf(); // "abc" b.valueOf(); // 42 c.valueOf(); // true 使用隱式拆封 var a = new String( "abc" ); var b = a + ""; // b的值爲"abc" typeof a; // "object" typeof b; // "string"
原生函數做爲構造函數
關於數組(array)、對象(object)、函數(function)和正則表達式,咱們一般喜歡以常量的形式來建立它們。實際上,使用常量和使用構造函數的效果是同樣的(建立的值都是經過封裝對象來包裝) var a = new Array( 1, 2, 3 ); a; // [1, 2, 3] var b = [1, 2, 3]; b; // [1, 2, 3] 構造函數Array(..)不要求必須帶new關鍵字。不帶時,它會被自動不上。所以Array(1,2,3)和new Array(1,2,3)的效果是同樣的。 Array構造函數只帶一個數字參數的時候,該參數會被做爲數組的預設長度(length),而非只充當數組中的一個元素
表達式的反作用
var a = 42; var b = a++; a; // 43 b; // 42 這是a++的反作用 ======================== function foo() { a = a + 1; } var a = 1; foo(); // 結果值:undefined。反作用:a的值被改變
toJSON
var obj = { key: 'foo', toJSON: function () { return 'bar'; } }; var ret = JSON.stringify(obj); console.log(ret); 區別很是明顯,toJSON 的返回值直接代替了當前對象
Number
undefined ---> NaN null ---> 0 boolean 的true爲1 false便是0 string -->數字或NaN object 若定義valueOf優先用,其次toString Number(undefined) // NaN Number(null) // 0 Number(true) // 1 Number(false) // 0 Number('12345') // 12345 Number('Hello World') // NaN Number({ name: 'Jack' }}) // NaN const a = { name: 'Apple', valueOf: function() { return '999' } } Number(a) // 999 const a = new String(''); const b = new Number(0); const c = new Boolean(false); !!a // true !!b // true !!c // true
parseInt
參數:第一個參數是string 第二個參數是介於2和36之間的整數,一般默認爲10,也就是咱們一般使用的十進制轉換,若是是5就是5進制,超出這個範圍,則返回NaN。若是第二個參數是0、undefined和null,則直接忽略 * 將字符串轉爲整數 * 若是字符串頭部有空格,空格會被自動去除 * 若是參數不是字符串,先轉爲字符串再轉換 parseInt('12px') 若是遇到不能轉爲數字的字符,就再也不進行下去,返回轉好的部分 若是字符串的第一個字符不能轉化爲數字(後面跟着數字的正負號除外),返回NaN。 若是開頭是0x按照16進制轉換,若是是0按照10進制轉換
又犯錯了一次
const a = true; const b = 123; a === b // false a == b // false true強制轉換爲1 const a = '1,2,3'; const b = [1,2,3]; a === b // false a == b // true 在a == b當中,陣列a因爲沒有valueOf(),只好使用toString()取得其基型值而獲得字串'1,2,3',此時就可比較'1,2,3' == '1,2,3',所以是相等的(true)。 Object(null) 和Object(undefined) 等同於Object()也就是{} var a = null; var b = Object(a); // 等同於 Object() a == b; // false var c = undefined; var d = Object(c); // 等同於 Object() c == d; // false var e = NaN; var f = Object(e); // 等同於 new Number(e) e == f;//false 避免修改原型的valueOf Number.prototype.valueOf = function() { return 3; }; new Number(2) == 3; // true
抽象的關係運算符
a <= b
實際上是!(b > a)
,所以!false
獲得true。a >= b
實際上是b <= a
也就是!(a > b)
等同於!false
獲得trueconst a = { b: 12 }; const b = { b: 13 }; a < b // false,'[object Object]' < '[object Object]' a > b // false,其實是比較 b < a,即 '[object Object]' < '[object Object]' a == b // false,其實是比較兩物件的 reference a >= b // true a <= b // true
`[]==[]` false 由於兩個的地址不是同樣的 `'ab' < 'cd' // true ` 以字典的字母順序形式進行比較 'Hello World' > 1 // false,字串 'Hello World' 沒法轉化爲數字,變成了NaN NaN 不大於、不小於、不等於任何值,固然也不等於本身
//①構造器Function的構造器是它自身 Function.constructor=== Function;//true //②構造器Object的構造器是Function(由此可知全部構造器的constructor都指向Function) Object.constructor === Function;//true //③構造器Function的__proto__是一個特殊的匿名函數function() {} console.log(Function.__proto__);//function() {} //④這個特殊的匿名函數的__proto__指向Object的prototype原型。 Function.__proto__.__proto__ === Object.prototype//true //⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函數 Object.__proto__ === Function.prototype;//true Function.prototype === Function.__proto__;//true
Function instanceof Object
和Object instanceof Function
運算的結果固然都是true啦全部的構造器的constructor 都指向Function
Function 和prototype指向一個特殊匿名函數,而這個特殊匿名函數的
__proto__
指向Object.prototype
### Array.of
Array.of方法用於將一組值,轉換爲數組。 Array.from() Array.from方法用於將兩類對象轉爲真正的數組:相似數組的對象和可遍歷(iterator)的對象(包括Map和Set)
Array(3).fill().map(()=>({})) Array.apply(null,{length:3}).map(()=>({}))
函數表達式,則必須等到解析器執行到它所在的代碼行,纔會真正被解析。 console.log(sum(10 , 10)); //TypeError: sum is not a function var sum = function(num1 , num2){ return num1 + num2; }
遞歸就是函數不斷調用自身 階乘 let factorial=n=>n?factorial(n-1)*n:1; const factorial = n => n === 1 ? 1 : n * factorial(n - 1) 優化 尾遞歸: 調用自身函數,計算僅用常量棧空間 const factorial = (n, total) => n === 1 ? total : factorial(n - 1, n * total) 優化 柯里化,將尾遞歸變爲只接受單個參數的變量 const fact = (n, total = 1) => n === 1 ? total : fact(n - 1, n * total) Lambda函數(匿名函數) // ES5 var f = function (x) { return x; }; // ES6 const f = x => x lambda表達式寫出遞歸(匿名函數遞歸) 將lambda表達式做爲參數之一傳入其身 const factorial= (f,n) => n===1 ? 1 : n*f(f,n-1); factorial(factorial,6) //這個也太難看了,解決方案柯里化 // 這塊不怎麼好懂我就忽略了 Lambda演算 Lambda演算中全部函數式匿名的,它們沒有名稱,只接受一個輸出變量,即獨參函數 構建一個高階函數,它接受一個函數做爲參數,並讓這個函數將自身做爲參數調用其自身: const invokeWithSelf = f => f(f) 寫個遞歸 const fact = (n, total = 1) => n === 1 ? total : fact(n - 1, n * total) 拿到前面的進行優化 const fact = f => (total = 1) => n => n === 1 ? total : f(f)(n * total)(n - 1) const factorial = fact(fact)() factorial(6) // => 720 構建Y const fact = f => (total = 1) => n => n === 1 ? total : f(n * total)(n - 1) const Y = f => (x => f(v => x(x)(v))) (x => f(v => x(x)(v))) // 瞧,這不就是黑魔法Y組合子嘛 const factorial = Y(fact)() factorial(6) // => 720
尾調用時指在函數return的時候調用一個新的函數,因爲尾調用的實現須要存儲到內存中,在一個循環體中,若是存在函數的尾調用,你的內存可能爆滿或溢出。 尾調用實際用途——遞歸函數優化 在ES5時代,咱們不推薦使用遞歸,由於遞歸會影響性能。 可是有了尾調用優化以後,遞歸函數的性能有了提高。 const factorial = (n, total) => n === 1 ? total : factorial(n - 1, n * total)
let和const都可以聲明塊級做用域,let的特色是不會變量提高,而是被鎖在當前塊中。 function test() { if(true) { console.log(a)//TDZ,俗稱臨時死區,用來描述變量不提高的現象 let a = 1 } } test() // a is not defined function test() { if(true) { let a = 1 } console.log(a) } test() // a is not defined 臨時死區的意思是在當前做用域的塊內,在聲明變量前的區域叫作臨時死區。
用來解決JavaScript中特殊類型 == 或者 === 異常的狀況。 Object.is()來處理2個值的比較。 console.log(Object.is(NaN, NaN)) // true console.log(Object.is(+0, -0)) // false console.log(Object.is(5, "5")) //false
function test(value) { console.log(value); } test({a=1,b=2}={a:2,b:3});
yield只能夠在生成器函數內部使用,若是在非生成器函數內部使用,則會報錯。 function *createIterator(items) { //你應該在這裏使用yield items.map((value, key) => { yield value //語法錯誤,在map的回調函數裏面使用了yield }) } const a = createIterator([1, 2, 3]); console.log(a.next()); //無輸出 在對象中添加生成器函數 const obj = { a: 1, *createIterator() { yield this.a } } const a = obj.createIterator(); console.log(a.next()); //{value: 1, done: false}
caller : 當前這個函數在哪一個函數調用的 function fn(){ console.log(fn.caller); } function ff() { fn(); } ff();//[Function: ff] arguments.callee 就是當前函數自己 function fn(){ console.log(argument.callee) } fn.prototype.constructor===fn;//true ,也表明的是函數自己
xxx.onclick=function(){} //DOM0事件綁定,給元素的事件行爲綁定方法,這些方法在事件傳播的冒泡階段(或者目標階段)執行的 xxx.addEventListener('xxx',function(){},false) //第三個參數false也是控制綁定的方法在事件傳播的冒泡階段執行,可是在捕獲階段執行沒有實際意義,默認是false,能夠不寫
DOM0事件綁定的原理:就是給元素的某一個事件私有屬性賦值(瀏覽器會創建監聽機制,當咱們出發元素的某個行爲,瀏覽器會本身把屬性中賦的值去執行)
DOM0事件綁定:只容許給當前元素的某個事件行爲綁定一個方法,屢次綁定後面的內容會替換前面綁定的,以最後一次綁定的方法爲主
DOM0事件綁定和DOM2事件綁定的區別
機制不同
- DOM0採用的是私有屬性賦值,全部只能綁定一個方法
- DOM2採用的是事件池機制,因此能綁定屢次方法
移出的操做
let list = document.querySelector('#list'); list.addEventListener('click',function (ev) { console.log(ev.target.innerHTML); }) list.addEventListener('click',function () { console.log(2); }) box.onclick=function(){} box.onclick=null// DOM0的移出(不須要考慮綁定的是誰) //DOM2移出的時候 function f3() { console.log(2); } list.addEventListener('click',f3); list.removeEventListener('click',f3); //DOM2移出的時候,必要清除移出的是哪一個方法技巧(不要綁定匿名函數,都綁定實名函數)
DOM0和DOM2是能夠同時使用,由於是瀏覽器的兩個運行機制,執行順序和編寫順序有關
1. over屬於滑過事件,從父元素進入子元素,屬性離開父親,會觸發父元素的out,觸發子元素的over enter屬於進入,從父元素進入子元素,並不算離開父元素,不會觸發父元素的leave,觸發子元素的enter 2. enter和leave阻止了事件的冒泡傳播,而over和out還存在冒泡傳播的 全部對於父元素嵌套子元素的這種狀況,咱們用enter的使用會比over多一些
給容器的click綁定一個方法,經過事件的冒泡傳播機制,把容器的click行爲觸發,根據事件對象中的事件源(ev.target)來作不一樣業務處理
<ul id="list"> <li>item 1</li> <li>item 2</li> <li>item 3</li> <li>item n</li> </ul> <script> let list = document.querySelector('#list'); list.onclick=function (ev) { let target=ev.target||window.event.target; console.log(target.innerHTML); } </script>
on/off : 基於DOM2事件綁定實現事件的綁定和移除
one:只綁定一次,第一次執行完成後,會把綁定的方法移出(基於on/off完成)
click/ mouseenter/... jq提供的快捷綁定方法,可是這些方法都是基於on/off完成的
delegate 事件委託方法(在1.7之前用的是live方法)
$(document).on('click',fn) $(document).off('click',fn) $(document).one('click',fn) $(document).click(fn)
任何消除函數聲明和函數表達式間歧義的方法,均可以被解析器正確識別
針對這些一元運算符,到底用哪一個好呢,測試發現()的性能最優越
(function(){ /* code */ }()); !function(){alert('iifksp')}() // true +function(){alert('iifksp')}() // NaN -function(){alert('iifksp')}() // NaN ~function(){alert('iifksp')}() // -1
思想:準備一個容器,把到達指定時候要處理的事情,事先一一增長到容器中(發佈計劃,而且向計劃表中訂閱方法),當到達指定時間點,通知容器中的方法依次執行
相同點
- forEach和map方法裏每次執行匿名函數都支持3個參數,參數分別是item(當前每一項)、index(索引值)、arr(原數組)
map
返回一個新數組,不會對空數組進行檢測,不會該變原有數組
forEach
讓數組每一項作一件事
空數組就不會執行回調函數
class Promise { constructor(excutorCallBack) { this.status = 'pending'; this.value = undefined; this.fulfilledAry = []; this.rejectedAry = []; let resolveFn = result => { let timer=setTimeout(()=>{ clearTimeout(timer); if (this.status !== 'pending') return; this.status = 'fulfilled'; this.value = result; this.fulfilledAry.forEach(item=>item(this.value)) }) }; let rejectFn = reason => { let timer=setTimeout(()=>{ if (this.status !== 'pending') return; this.status = 'rejected'; this.value = reason; this.rejectedAry.forEach(item => item(this.value)); }) }; try{ excutorCallBack(resolveFn, rejectFn()); }catch(err){ // 有異常信息按照rejected狀態處理 rejectFn(err); } excutorCallBack(resolveFn, rejectFn); } then(fulfilledCallBack, rejectedCallBack) { //處理不傳遞的情況 typeof fulfilledCallBack!=='function'?fulfilledCallBack=result=>result:null; typeof rejectedCallBack!=='function'?rejectedCallBack=reason=>{ throw new Error(reson.message); }:null; //返回一個新的promise實例 return new Promise((resolve,reject)=>{ this.fulfilledAry.push(()=>{ try{ let x=fulfilledCallBack(this.value); x instanceof Promise?x.then(resolve,reject):resolve(x); // if(x instanceof Promise){ // x.then(resolve, reject); // return; // } // resolve(x); }catch(err){ reject(err) } }); this.rejectedAry.push(()=>{ try{ let x=rejectedCallBack(this.value); x instanceof Promise?x.then(resolve,reject):resolve(x); // resolve(x); }catch(err){ reject(err) } }); }); // this.fulfilledAry.push(fulfilledCallBack); // this.rejectedAry.push(rejectedCallBack); } catch(rejectedCallBack) { return this.then(null,rejectedCallBack) } static all(promiseAry=[]){ return new Promise((resolve, reject)=>{ //index:記錄成功的數量 result記錄成功的結果 let index=0, result=[]; for (let i = 0; i <promiseAry.length; i++) { //promiseAry[i] 每個須要處理的promise實例 promiseAry[i].then(val=>{ index++; result[i]=val; //索引須要和promiseAry對應,保證結果的順序和數組的順序一致 if (index === promiseAry.length) { resolve(result); } }, reject); } }); } } module.exports = Promise;
cat.call(dog, a, b) = cat.apply(dog, [a, b]) = (cat.bind(dog, a, b))() = dog.cat(a, b)
用到本地存儲的地方:
- 頁面之間的信息通訊
- 性能優化
session和cookie
session是服務器存儲
- 不兼容IE8及如下
- 也有存儲的大小限制,一個源下最多隻能存儲5MB內容
- 本地永久存儲,只要你不手動刪除,永久存儲在本地(可是咱們能夠基於API removeItem/clear手動清除)
- 殺毒軟件或者瀏覽器的垃圾清除暫時不會清除localStorage(新版本谷歌會清除localStorage)
- 在隱私或者無痕瀏覽下,是記錄localStorage
- localStorage和服務器沒有半毛錢關係
cookie是客戶端存儲
- 兼容全部的瀏覽器
- 有存儲的大小限制,通常一個源只能存儲4kb內容
- cookie有過時時間(當前咱們本身能夠手動設置這個時間)
- 殺毒軟件或者瀏覽器的垃圾清理均可能會把cookie信息強制掉
- 在隱私或者無痕瀏覽器模式下,是不記錄cookie的
- cookie不是嚴格的本地存儲,由於要和服務器之間來回傳輸
localStorage.gsetItem([key],[value])//[value]必須是字符串格式(即便寫的不是字符串,也會默認轉換爲字符串) localStorage.getItem([key]) //經過屬性名獲取存儲的信息 localStorage.removeItem([key])//刪除指定的存儲信息 localStorage.clear()//清除當前域下存儲的全部信息 localStorage.key(0)//基於索引獲取指定的key名
須要一個容器 display:flex flex-direction (元素排列方向) row, row-reverse, column, column-reverse flex-wrap (換行) nowrap, wrap, wrap-reverse flex-flow (以上二者的簡寫) flex-direction || flex-wrap justify-content (水平對齊方式) flex-start, flex-end, center, space-between, space-around align-items (垂直對齊方式) stretch, flex-start, flex-end, center, baseline align-content (多行垂直對齊方式) stretch, flex-start, flex-end, center, space-between, space-around
在算法中咱們會遇到不少遞歸實現的案例,全部的遞歸均可以轉換成非遞歸實現,
其轉換的本質是:遞歸是解析器(引擎)來幫咱們作了棧的存取,非遞歸是手動建立棧來模擬棧的存取過程
遞歸組件能夠轉換成扁平數組來實現:
更改DOM結構成平級結構,點擊節點以及節點的視覺樣式經過操做總的list數據區實現
而後使用虛擬長列表來控制vue組組建實例建立的數量
減小DNS查找,避免重定向
DNS:負責將域名URL轉化爲服務器主機IP DNS查找流程:首先查看瀏覽器緩存是否存在,不存在則訪問本機DNS緩存,再不存在則訪問本地DNS服務器。因此DNS也是開銷,一般瀏覽器查找一個給定URL的IP地址要花費20-120ms,在DNS查找完成前,瀏覽器不能從host那裏下載任何東西。 當客戶端的DNS緩存爲空時,DNS查找的數量與WEB頁面中惟一主機名的數量相等,因此減小惟一主機名的數量就能夠減小DNS查找的數量
資源async defer
defer
若是script設置了該屬性,則瀏覽器會異步的下載該文件,而且不會影響後續DOM的渲染
若是有多個設置了
defer
的script
標籤存在,則會按照順序執行全部的script
;defer
腳本會在文檔渲染完畢後,DOMContentLoaded
事件調用前執行。<script defer src='1.js'></script> <script> window.addEventListener('DOMContentLoader',function(){ console.log('DOMContentLoader') }) </script>async
async
的設置,會使得script
腳本異步的加載並在容許的狀況下執行async
的執行,並不會按着
script
在頁面中的順序來執行,而是誰先加載完誰執行。推薦使用場景
defer 若是你的腳本代碼依賴於頁面中的DOM元素(文檔是否加載解析完畢),或者被其餘腳本文件依賴
- 評論框 代碼語法高亮
頁面靜態直出
- 就是瀏覽器直接輸出渲染好數據的html頁面(簡稱直出)
- 直出就是須要node.js的支持,服務器上的瀏覽器渲染好的東西,直接輸出給客戶端的瀏覽器
- 簡單來講,就是直接把配件選好,讓店家幫忙組裝器,一次性發過來,就是直出這個道理
apply()方法接受兩個參數:一個是運行函數的做用域,另外一個是參數數組,這個參數數組能夠是Array實例,也能夠是arguments對象(類數組對象)
function sum(num1 , num2){ return num1 + num2; } function callSum1(num1,num2){ return sum.apply(this,arguments); // 傳入arguments類數組對象 } function callSum2(num1,num2){ return sum.apply(this,[num1 , num2]); // 傳入數組 } console.log(callSum1(10 , 10)); // 20 console.log(callSum2(10 , 10)); // 20
Object.create()
Object.create(null) 建立的對象是一個空對象,在該對象上沒有繼承 Object.prototype 原型鏈上的屬性或者方法
Object.create()方法接受兩個參數:Object.create(obj,propertiesObject) ;
obj:一個對象,應該是新建立的對象的原型。
propertiesObject:可選。該參數對象是一組屬性與值,該對象的屬性名稱將是新建立的對象的屬性名稱,值是屬性描述符(這些屬性描述符的結構與
Object.defineProperties()
的第二個參數同樣)。注意:該參數對象不能是undefined
,另外只有該對象中自身擁有的可枚舉的屬性纔有效,也就是說該對象的原型鏈上屬性是無效的。var o = Object.create(Object.prototype, { // foo會成爲所建立對象的數據屬性 foo: { writable:true, configurable:true, value: "hello" },
事件循環是指:執行一個宏任務,而後執行清空微任務列表,循環再執行宏任務,再清微任務列表
DNS解析
TCP三次握手
發送請求,分析url,設置請求報文(頭,主體)
服務器返回請求的文件(html)
瀏覽器渲染
html parse==>DOM Tree
標記化算法,進行元素狀態的標記
dom樹構建
css parser==>Styel tree
解析css代碼,生成樣式樹
attachment==>Render Tree
結合dom樹與style樹,生成渲染樹
layout:佈局
GPU painting:像素繪製頁面
從源碼來看:
attr
是經過setAttribute
和getAttribute
來設置的,使用的是DOM屬性節點prop
是經過document.getElementById(el)[name]=vlaue
來設置的,是轉化爲js對象的屬性checked,selected,readonly,disabled
等的時候使用prop效果更好,減小了訪問dom屬性節點的頻率。new Date().toISOString().slice(0,10)
方法返回一個字符串表示數組中的元素,數組中的元素使用各自toLocaleString方法轉成字符串,這些字符串將使用一個特定語言環境的字符串
var number = 1337; var date = new Date(); var myArr = [number, date, "foo"]; var str = myArr.toLocaleString(); console.log(str); // 輸出 "1,337,2019/2/15 下午8:32:24,foo" let a=3500 a.toLocaleString() //3,500
key的做用是爲了在diff算法執行時更快的找到對應的節點,提升diff速度
和null.toString()同樣也是報錯的,原始數據類型存儲的是值,是沒有函數能夠調用的
沒有返回值則按照其餘語言同樣返回實例化對象
如有返回值則檢查其返回值是否爲引用類型。若是是非引用類型,如基本類型(string,number,boolean,null,undefined)則與無返回值相同,實際返回其實例化對象。
若返回值是引用類型,則實際返回值爲這個引用類型。