北京MedPeer涼經
一、請說出三種不同含義的this使用場景
1.作為對象方法調用
在 javascript 中,函數也是對象,因此函數可以作為一個對象的屬性,此時該函數被稱為該對象的方法,在使用這種調用方式時,this 被自然綁定到該對象
var test = {
a:0,
b:0
get:function(){
return this.a;
}
}
2.作為全局變數使用
函數也可以直接被調用,此時 this 綁定到全局對象。在瀏覽器中,window 就是該全局對象。比如下面的例子:函數被調用時,this 被綁定到全局對象,接下來執行賦值語句,相當於隱式的聲明了一個全局變數。
var x = 10;
function foo() {
console.log(this); //Window
console.log(this.x); //10
}
foo();
3.作為構造函數調用
javascript 支援面向對象式編程,與主流的面向對象式程式語言不同,javascript 並沒有類(class)的概念,而是使用基於原型(prototype)的繼承方式。相應的,javascript 中的構造函數也很特殊,如果不使用 new 調用,則和普通函數一樣。作為又一項約定俗成的準則,構造函數以大寫字母開頭,提醒調用者使用正確的方式調用。如果調用正確,this 綁定到新創建的對象上。
function Foo() {
this.x = 10;
console.log(this); //Foo {x:10}
}
var foo = new Foo();
console.log(foo.x); //10
4.在call、apply、bind中調用
當一個函數被 call、apply 或者 bind 調用時,this 的值就取傳入的對象的值。
var obj = {
x: 10,
};
function foo() {
console.log(this); //{x: 10}
console.log(this.x); //10
}
foo.call(obj);
foo.apply(obj);
foo.bind(obj)();
5.作為DOM節點
在一個 html dom 事件處理程式里,this 始終指向這個處理程式所綁定的 html dom 節點
function Listener() {
document.getElementById('foo').addEventListener('click', this.handleClick); //這裡的 this 指向 Listener 這個對象。不是強調的是這裡的 this
}
Listener.prototype.handleClick = function(event) {
console.log(this); //<div id="foo"></div>
};
var listener = new Listener();
document.getElementById('foo').click();
6.箭頭函數中的this
箭頭函數內部的 this 是詞法作用域,由上下文確定。
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10}
console.log(this.x); //10
};
};
};
fn()()();
},
};
obj.foo();
以上是六種this的使用環境,原文鏈接
二、編程題
實現一個獲取當前頁面鏈接search值的函數。(如下例子中,輸入key1輸出value1)例:』//test.com/index?key1=value1&key2=value2』
思路:需要根據key找到value,首先將?後面拼接的參數提取出來,然後將其轉為對象形式,再按鍵取值
實現:使用DOM中location對象的屬性+substr+split方法獲取鍵值對數組,再遍曆數組將裡面的key1=value1通過等號分開,再存入對象中
var arr = window.location.search.substr(1).split("&")
var obj={}
for(var i=0;i<arr.length;i++){
var key = arr[i].split('=')[0]
var value = arr[i].split('=')[1]
obj[key]=value
}
var str = prompt('請輸入鍵')
console.log(obj[str])
三、說一下你對原型鏈的理解、應用場景、以及下面程式碼報錯原因
function Dog(name) {
this.name = name
}
Dog.bark = function() {
console.log(this.name + ' says woof')
}
let fido = new Dog('fido');
fido.bark()
理解:在javascript裡面,每個對象都有一個prototype屬性,指向它的原型對象.這個原型對象裡面同時還有自己的原型,原型一環扣一環,直到某個對象的原型為null,這一級一級的鏈結構就是原型鏈。
應用場景:js通過原型鏈實現函數或對象的繼承
報錯原因:fido這個對象並沒有bark方法。程式碼中雖然給dog對象綁定了bark方法,但是綁定的是靜態方法,調用形式只能通過dog.bark去調用。Fido並沒有繼承這個方法,所以會報錯。
Ps:如果需要fido.bark()不報錯。可以在dog函數的內部寫this.bark=function(){}或者在綁定bark方法時,使用Dog.prototype.bark=function(){}
四、什麼是事件委託、簡述它的作用和原理
概念:事件委託就是事件目標(子元素)不處理事件,把事件委託給父元素去處理。
作用:1、減少頁面綁定事件的數量,提高頁面的性能
2、可以靈活的處理子節點動態變化的場景,無論子節點增加還是減少,事件都無需重新綁定
原理:利用事件冒泡,將事件加在父元素或者祖先元素上,觸發該事件。
五、下面的程式碼執行結果是什麼,請說明理由
function greet(person) {
if (person == { name: 'amy' }) {
return 'hey amy'
} else {
return 'hey arnold'
}
}
greet({ name: 'amy' })
執行結果在控制台列印出’hey arnold’。greet方法傳入一個對象時,與內部的對象{ name: ‘amy’ }進行==判斷,儘管這兩個對象的結構、鍵值相同,但是他們的地址不同,所以返回flase,執行了else語句裡面的程式碼,返回了’hey arnold’
六、寫出下面程式碼執行後正確順序的輸出結果以及原因解析
var a=1;
function fun1(){
console.log(a);
}
function fun2(){
console.log(a);
var a=2;
console.log(a);
}
fun1();
fun2();
console.log(a);
原因:首先執行fun1()函數,因為已經有全局變數a=1,所以列印出1,然後執行fun2()函數,函數內部存在變數提升,函數內部實際執行順序:
var a;
console.log(a)//undefined;
a=2;
conosle.log(a)//2
所以列印出undefined和2,最後在執行console.log(a),這裡仍然列印出的是全局變數a的值,所以是1
七、寫出下面程式碼執行後正確順序的輸出結果以及原因解析
var a=1;
console.log(1);
setTimeout(function () {
console.log(2);
},0);
function fun() {
console.log(3);
setTimeout(function () {
console.log(4);
},0);
for (var i=0;i<10000;i++){
a++;
}
console.log(5);
return a;
};
console.log(6);
console.log(fun());
正確輸出順序1 6 3 5 10001 2 4
原因:
按照順序執行程式碼,先列印出1,然後遇到第一個setTimeout,會將其存放到非同步隊列,等待主執行緒執行完成之後才會執行。然後列印6,再執行fun函數。Fun函數內部首先列印出3,然後遇到第二個setTimeout,也將其放入非同步隊列。,再執行for循環,for循環內部a的最終值是10001。然後列印5,再返回a,列印a的值為10001。此時主執行緒執行完畢,執行先前存放的setTimeout。又因為隊列是先進先出,所以先列印2再列印4。
八、JS的基本數據類型有哪些
7種:Boolean Null Undefined Number String Symbol(ES6) BigInt (ES10)
九、vue的雙向數據綁定原理
首先要對數據進行劫持監聽,vue設置了一個監聽器observer,用來監聽所有屬性。如果屬性發上變化了,就需要告訴訂閱者watcher看是否需要更新。因為訂閱者是有很多個,vue設置了一個消息訂閱器dep來專門收集這些訂閱者,然後在監聽器observer和訂閱者watcher之間進行統一管理的。接著,我們還需要有一個指令解析器compile,對每個節點元素進行掃描和解析,將相關指令對應初始化成一個訂閱者watcher,並替換模板數據或者綁定相應的函數,此時當訂閱者watcher接收到相應屬性的變化,就會執行對應的更新函數,從而更新視圖。(後面複習vue的時候我會單獨寫一篇文章解釋,這個就先放到這裡,不全面!)
十、原生js如何實現雙向數據綁定
這篇文章非常詳細了,傳送門
十一、如何監聽多個promise的執行狀態以及promise的應用場景
使用promise.all()方法,promise.all(iterable) 方法返回一個 promise 實例,此實例在 iterable 參數內所有的 promise 都「完成(resolved)」或參數中不包含 promise 時回調完成(resolve);如果參數中 promise 有一個失敗(rejected),此實例回調失敗(reject),失敗原因的是第一個失敗 promise 的結果
應用場景:js非同步編程的新方案,解決了以往的回調嵌套回調出現回調地獄的問題
十二、DOM節點的增刪改查,getElementBy和querySelector獲取DOM節點的區別
DOM的增刪改查就不再贅述了,傳送門。詳細的說一下getElementBy和querySelector的區別
1.W3C標準
queryselectorall 屬於 w3c 中的 selectors api 規範 。而 getelementsby 系列則屬於 w3c 的 dom 規範 。(了解)
2.瀏覽器兼容
queryselectorall 已被 ie 8+、ff 3.5+、safari 3.1+、chrome 和 opera 10+ 良好支援 。getelementsby 系列,以最遲添加到規範中的 getelementsbyclassname 為例,ie 9+、ff 3 +、safari 3.1+、chrome 和 opera 9+ 都已經支援該方法了。
3.接收參數
querySelectorAll 方法接收的參數是一個 CSS 選擇符。而 getElementsBy 系列接收的參數只能是單一的className、tagName 和 name。程式碼如下:
var c1 = document.querySelectorAll('.b1 .c');
var c2 = document.getElementsByClassName('c');
var c3 = document.getElementsByClassName('b2')[0].getElementsByClassName('c');
需要注意的是,queryselectorall 所接收的參數是必須嚴格符合 css 選擇符規範的。所以下面這種寫法,將會拋出異常。(css 選擇器中的元素名,類和 id 均不能以數字為開頭。)程式碼如下:
try {
var e1 = document.getElementsByClassName('1a2b3c');
var e2 = document.querySelectorAll('.1a2b3c');
} catch (e) {
console.error(e.message);
}
console.log(e1 && e1[0].className);
console.log(e2 && e2[0].className);
4.返回值(獲取的結點)
大部分人都知道,queryselectorall 返回的是一個static node list,而 getelementsby 系列的返回的是一個Live Node List。
// Demo 1
var ul = document.querySelectorAll('ul')[0],
lis = ul.querySelectorAll("li");
for(var i = 0; i < lis.length ; i++){
ul.appendChild(document.createElement("li"));
}
// Demo 2
var ul = document.getElementsByTagName('ul')[0],
lis = ul.getElementsByTagName("li");
for(var i = 0; i < lis.length ; i++){
ul.appendChild(document.createElement("li"));
}
因為 demo 2 中的 lis 是一個動態的 node list, 每一次調用 lis 都會重新對文檔進行查詢,導致無限循環的問題。 而 demo 1 中的 lis 是一個靜態的 node list,是一個 li 集合的快照,對文檔的任何操作都不會對其產生影響。
至於為什麼queryselectorall拿到的是一個快照,源自W3C的規定【the nodelist object returned by the queryselectorall() method must be static ([dom], section 8)】
ps:queryselectorall 的返回值是一個靜態的 nodelist 對象,而 getelementsby 系列的返回值實際上是一個 htmlcollection 對象 。nodelist 對象會包含文檔中的所有節點,如 element、text 和 comment 等。 htmlcollection 對象只會包含文檔中的 element 節點。
5.性能
因為queryselectorall返回一個靜態的nodelist(深克隆) getelementby系列返回一個動態的實時變化的nodelist(htmlcollection)(淺克隆,每次都返回一個指針) 所以queryselectorall會降低性能
十三、總結
由於一直沒來得及複習,js還沒開始,不過老本已經不夠吃了,需要加油啊,很多基礎的東西都忘記了。還有一部分問題,我打算放到其他複習文章裡面,就不在這裡說了