1。 JavaScript的『預解釋』與『變量提高』javascript
先看如下代碼輸出啥?css
var a= 1; function f() { console.log(a); var a = 2; } f();
首先答案是:undefined;html
因此該題目中:在函數fn的做用域中,首先提取變量聲明:var a;由於該做用域中有對a的賦值,因此不會繼續查找上級做用域。且在賦值前打印,因此是undefined;vue
相似的看下題:java
var a= 1; function f() { console.log(a); } f(); // 1
var a= 1; function f() { console.log(a); var a; } f(); //undefined
咱們看到,在編譯器處理階段,除了被var
聲明的變量會有變量提高這一特性以外,函數也會產生這一特性,可是函數聲明與函數表達式兩種範式建立的函數卻表現出不一樣的結果.node
f(); g(); //函數聲明 function f() { console.log('f'); } //函數表達式 var g = function() { console.log('g'); };
//fes6
//報錯:VM693:2 Uncaught TypeError: g is not a functionsegmentfault
f() 好理解屬於函數聲明提高;可是對於函數表達式g,被賦予undefined,undefeated沒法被執行而報錯。瀏覽器
變量之間衝突app
var a = 3; var a = 4; console.log(a); //4
函數衝突
f(); function f() { console.log('f'); } function f () { console.log('g'); }; // g
3.函數與變量之間衝突
console.log(f); function f() { console.log('f'); } var f ='g';
ƒ f() {
console.log('f');
}
說明函數覆蓋了變量;
相似的let,存在暫時性死區:
function f() { console.log(a); let a = 2; } f();
報錯: //ReferenceError: a is not defined
這段代碼直接報錯顯示未定義,let
與const
擁有相似的特性,阻止了變量提高,當代碼執行到console.log(a)
時,執行換將中a
還從未被定義,所以產生了錯誤
======================
JS的執行機制:
https://www.cxymsg.com/guide/mechanism.html#javascript%E7%9A%84%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83
這個問題關鍵在於兩個點,一個是原型對象是什麼,另外一個是原型鏈是如何造成的
絕大部分的函數(少數內建函數除外)都有一個prototype
屬性,這個屬性是原型對象用來建立新對象實例,而全部被建立的對象都會共享原型對象,所以這些對象即可以訪問原型對象的屬性。
例如hasOwnProperty()
方法存在於Obejct原型對象中,它即可以被任何對象當作本身的方法使用.
用法:
object.hasOwnProperty( propertyName )
hasOwnProperty()
函數的返回值爲Boolean
類型。若是對象object
具備名稱爲propertyName
的屬性,則返回true
,不然返回false
。
var person = { name: "Messi", age: 29, profession: "football player" }; console.log(person.hasOwnProperty("name")); //true console.log(person.hasOwnProperty("hasOwnProperty")); //false console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); //true
由以上代碼可知,hasOwnProperty()
並不存在於person
對象中,可是person
依然能夠擁有此方法.
因此person
對象是如何找到Object
對象中的方法的呢?靠的是原型鏈。]
因爲js中存在複雜類型和基本類型,對於基本類型而言,是按值傳遞的.
var a = 1; function test(x) { x = 10; console.log(x); } test(a); console.log(a);
結果是: 10---1
雖然在函數test
中a
被修改,並無有影響到 外部a
的值,基本類型是按值傳遞的.
咱們將外部a
做爲一個對象傳入test
函數.
var a = { a: 1, b: 2 }; function test(x) { x.a = 10; console.log(x); } test(a); console.log(a);
// { a: 10, b: 2 }
// { a: 10, b: 2 }
能夠看到,在函數體內被修改的a
對象也同時影響到了外部的a
對象,可見覆雜類型是按引用傳遞的.
但是若是再作一個實驗:
var a = { a: 1, b: 2 }; function test(x) { x = 10; console.log(x); } test(a); console.log(a);
結果是:
//10
// { a: 1, b: 2 }
外部的a
並無被修改,若是是按引用傳遞的話,因爲共享同一個堆內存,a
在外部也會表現爲10
纔對. 此時的複雜類型同時表現出了按值傳遞
和按引用傳遞
的特性.
複雜類型之因此會產生這種特性,緣由就是在傳遞過程當中,對象a
先產生了一個副本a
,這個副本a
並非深克隆獲得的副本a
,副本a
地址一樣指向對象a
指向的堆內存.
所以在函數體中修改x=10
只是修改了副本a
,a
對象沒有變化. 可是若是修改了x.a=10
是修改了二者指向的同一堆內存,此時對象a
也會受到影響.
有人講這種特性叫作傳遞引用,也有一種說法叫作按共享傳遞
當聲明瞭一個變量並將一個引用類型值賦給該變量時,則這個值的引用次數就是1。若是同一個值又被賦給另外一個變量,則該值的引用次數加1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數減1
聲明一個對象A,每多一個引用,A引用次數+1,每少一個引用,A的引用次數-1
缺點:相互引用的沒法消除
當變量進入環境(例如,在函數中聲明一個變量)時,就將這個變量標記爲「進入環境」。內存不能釋放。
當變量離開環境時,則將其標記爲「離開環境」。釋放變量,回收內存。
相似於,函數執行順序中,各個變量和函數執行在函數調用棧中,此時有標記,當執行完畢以後,退出調用棧,則消除標記,因此垃圾回收機制在必定時間內回收沒有標記的變量
==========================
瀏覽器會『從右往左』解析CSS選擇器。
咱們知道DOM Tree與Style Rules合成爲 Render Tree,其實是須要將Style Rules附着到DOM Tree上,所以須要根據選擇器提供的信息對DOM Tree進行遍歷,才能將樣式附着到對應的DOM元素上。
如下這段css爲例
.mod-nav h3 span {font-size: 16px;}
咱們對應的DOM Tree 以下
若從左向右的匹配,過程是:
若是從右至左的匹配:
後者匹配性能更好,是由於從右向左的匹配在第一步就篩選掉了大量的不符合條件的最右節點(葉子節點);而從左向右的匹配規則的性能都浪費在了失敗的查找上面
『重繪』不必定會出現『重排』,『重排』必然會出現『重繪』
即大小,位置等變化會帶來重排;顏色等變化會致使重繪;
任何改變用來構建渲染樹的信息都會致使一次重排或重繪:
咱們每每經過改變class的方式來集中改變樣式
// 判斷是不是黑色系樣式 const theme = isDark ? 'dark' : 'light' // 根據判斷來設置不一樣的class ele.setAttribute('className', theme)
咱們能夠經過createDocumentFragment建立一個遊離於DOM樹以外的節點,而後在此節點上批量操做,最後插入DOM樹中,所以只觸發一次重排
var fragment = document.createDocumentFragment(); for (let i = 0;i<10;i++){ let node = document.createElement("p"); node.innerHTML = i; fragment.appendChild(node); } document.body.appendChild(fragment);
事件冒泡(event bubbling),即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,而後逐級向上傳播到較爲不具體的節點。
<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <body> <div></div> </body> </html>
若是單擊了頁面中的<div>
元素,那麼這個click事件沿DOM樹向上傳播,在每一級節點上都會發生,按照以下順序傳播:
事件捕獲的思想是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。事件捕獲的用意在於在事件到達預約目標以前就捕獲它。
仍是以上一節的html結構爲例:
在事件捕獲過程當中,document對象首先接收到click事件,而後事件沿DOM樹依次向下,一直傳播到事件的實際目標,即<div>
元素
事件流又稱爲事件傳播,DOM2級事件規定的事件流包括三個階段:事件捕獲階段(capture phase)、處於目標階段(target phase)和事件冒泡階段(bubbling phase)。
觸發順序一般爲
事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件.
在綁定大量事件的時候每每選擇事件委託。
<ul id="parent"> <li class="child">one</li> <li class="child">two</li> <li class="child">three</li> ... </ul> <script type="text/javascript"> //父元素 var dom= document.getElementById('parent'); //父元素綁定事件,代理子元素的點擊事件 dom.onclick= function(event) { var event= event || window.event; var curTarget= event.target || event.srcElement; if (curTarget.tagName.toLowerCase() == 'li') { //事件處理 } } </script>
優勢:
侷限性:
// 模擬 instanceof function instance_of(L, R) { //L 表示左表達式,R 表示右表達式 var O = R.prototype; // 取 R 的顯示原型 L = L.__proto__; // 取 L 的隱式原型 while (true) { if (L === null) return false; if (O === L) // 這裏重點:當 O 嚴格等於 L 時,返回 true return true; L = L.__proto__; //L在上面已經等於了其隱式原型,即父級的顯示原型,因此這裏至關於L往上走了一級 } }
function Person(name, age) { this.name = name; this.age = age; } var person = new Person("Alice", 23);
new一個對象的四個過程:
一、建立一個空對象 var obj = new Object();
二、讓Person中的this指向obj,並執行Person的函數體 var result = Person.call(obj);
三、設置原型鏈,將obj的__proto__成員指向了Person函數對象的prototype成員對象 obj.__proto__ = Person.prototype;
四、判斷Person的返回值類型,若是是值類型,返回obj。若是是引用類型,就返回這個引用類型的對象。
if (typeof(result) == "object") person = result; else person = obj;
es5和es6實現類的繼承 : http://www.javashuo.com/article/p-mcbrfimx-cx.html
React/Vue不一樣組件之間是怎麼通訊的?
首先看一下es6中規定的Map的用法:
1,js建立map對象
var map = new Map();
2.將鍵值對放入map對象
map.set("key",value)
map.set("key1",value1)
map.set("key2",value2)
3.根據key獲取map值
map.get(key)
4.刪除map指定對象
delete map[key]
5.循環遍歷map
map.
forEach(function(key){
console.log("key",key) //輸出的是map中的value值
})
------------------
Vue
$dispatch
(已經廢除)和$broadcast
(已經廢除)React
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <div class="bottom">bottom</div> </body> <script> class EventEmeitter { constructor() { this._events = this._events || new Map(); // 儲存事件/回調鍵值對 this._maxListeners = this._maxListeners || 10; // 設立監聽上限 } } // 觸發名爲type的事件 EventEmeitter.prototype.emit = function (type, ...args) { //type--arson //...args---low-end let handler; // 從儲存事件鍵值對的this._events中獲取對應事件回調函數 handler = this._events.get(type); //console.log(handler); if (args.length > 0) { handler.apply(this, args); } else { handler.call(this); } return true; }; // 監聽名爲type的事件 EventEmeitter.prototype.addListener = function (type, fn) { // 將type事件以及對應的fn函數放入this._events中儲存 if (!this._events.get(type)) { this._events.set(type, fn); } }; const emitter = new EventEmeitter(); // 監聽一個名爲arson的事件對應一個回調函數 emitter.addListener('arson', man => { console.log(`expel ${man}`); }); var bottom = document.querySelector('.bottom'); bottom.addEventListener('click', function () { // 咱們觸發arson事件,發現回調成功執行 emitter.emit('arson', 'low-end'); }) </script> </html>