摘要:構造函數返回值是或,這種情況下指向的是返回的對象。并執行了構造函數中的方法如果函數沒有返回其他對象,那么指向這個新對象,否則指向構造函數中返回的對象。對于文檔來說應當是唯一的。的值意味著其不可能是基本數據類型。
關于【Step-By-Step】
不積跬步無以至千里。
Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,項目愿景:一步一個腳印,量變引起質變。
Step-By-Step 僅會在工作日發布面試題,主要考慮到部分小伙伴平時工作較為繁忙,或周末有出游計劃。每個周末我會仔細閱讀大家的答案,整理最一份較優答案出來,因本人水平有限,有誤的地方,大家及時指正。參與答題的小伙伴,可以對比自己的回答。
答題不是目的,不希望大家僅僅是簡單的搜索答案,復制粘貼到issue下。更多的是希望大家及時查漏補缺 / 鞏固相關知識。
更多優質文章可戳: https://github.com/YvetteLau/...
1.如何正確判斷this的指向?(2019-05-20)如果用一句話說明 this 的指向,那么即是: 誰調用它,this 就指向誰。
但是僅通過這句話,我們很多時候并不能準確判斷 this 的指向。因此我們需要借助一些規則去幫助自己:
this 的指向可以按照以下順序判斷:
1. 全局環境中的 this瀏覽器環境:無論是否在嚴格模式下,在全局執行環境中(在任何函數體外部)this 都指向全局對象 window;
node 環境:無論是否在嚴格模式下,在全局執行環境中(在任何函數體外部),this 都是空對象 {};
2. 是否是 new 綁定如果是 new 綁定,并且構造函數中沒有返回 function 或者是 object,那么 this 指向這個新對象。如下:
構造函數返回值不是 function 或 object。
function Super(age) { this.age = age; } let instance = new Super("26"); console.log(instance.age); //26
構造函數返回值是 function 或 object,這種情況下 this 指向的是返回的對象。
function Super(age) { this.age = age; let obj = {a: "2"}; return obj; } let instance = new Super("hello"); console.log(instance.age); //undefined
你可以想知道為什么會這樣?我們來看一下 new 的實現原理:
創建一個新對象。
這個新對象會被執行 [[原型]] 連接。
屬性和方法被加入到 this 引用的對象中。并執行了構造函數中的方法.
如果函數沒有返回其他對象,那么 this 指向這個新對象,否則 this 指向構造函數中返回的對象。
function new(func) { let target = {}; target.__proto__ = func.prototype; let res = func.call(target); //排除 null 的情況 if (res && typeof(res) == "object" || typeof(res) == "function") { return res; } return target; }3. 函數是否通過 call,apply 調用,或者使用了 bind 綁定,如果是,那么this綁定的就是指定的對象【歸結為顯式綁定】。
function info(){ console.log(this.age); } var person = { age: 20, info } var age = 28; var info = person.info; info.call(person); //20 info.apply(person); //20 info.bind(person)(); //20
這里同樣需要注意一種特殊情況,如果 call,apply 或者 bind 傳入的第一個參數值是 undefined 或者 null,嚴格模式下 this 的值為傳入的值 null /undefined。非嚴格模式下,實際應用的默認綁定規則,this 指向全局對象(node環境為global,瀏覽器環境為window)
function info(){ //node環境中:非嚴格模式 global,嚴格模式為null //瀏覽器環境中:非嚴格模式 window,嚴格模式為null console.log(this); console.log(this.age); } var person = { age: 20, info } var age = 28; var info = person.info; //嚴格模式拋出錯誤; //非嚴格模式,node下輸出undefined(因為全局的age不會掛在 global 上) //非嚴格模式。瀏覽器環境下輸出 28(因為全局的age會掛在 window 上) info.call(null);4. 隱式綁定,函數的調用是在某個對象上觸發的,即調用位置上存在上下文對象。典型的隱式調用為: xxx.fn()
function info(){ console.log(this.age); } var person = { age: 20, info } var age = 28; person.info(); //20;執行的是隱式綁定5. 默認綁定,在不能應用其它綁定規則時使用的默認規則,通常是獨立函數調用。
非嚴格模式: node環境,執行全局對象 global,瀏覽器環境,執行全局對象 window。
嚴格模式:執行 undefined
function info(){ console.log(this.age); } var age = 28; //嚴格模式;拋錯 //非嚴格模式,node下輸出 undefined(因為全局的age不會掛在 global 上) //非嚴格模式。瀏覽器環境下輸出 28(因為全局的age不會掛在 window 上) //嚴格模式拋出,因為 this 此時是 undefined info();6. 箭頭函數的情況:
箭頭函數沒有自己的this,繼承外層上下文綁定的this。
let obj = { age: 20, info: function() { return () => { console.log(this.age); //this繼承的是外層上下文綁定的this } } } let person = {age: 28}; let info = obj.info(); info(); //20 let info2 = obj.info.call(person); info2(); //28
點擊查看更多2.JS中原始類型有哪幾種?null 是對象嗎?原始數據類型和復雜數據類型有什么區別?(2019-05-21) 目前,JS原始類型有六種,分別為:
Boolean
String
Number
Undefined
Null
Symbol(ES6新增)
ES10新增了一種基本數據類型:BigInt
復雜數據類型只有一種: Object
null 不是一個對象,盡管 typeof null 輸出的是 object,這是一個歷史遺留問題,JS 的最初版本中使用的是 32 位系統,為了性能考慮使用低位存儲變量的類型信息,000 開頭代表是對象,null 表示為全零,所以將它錯誤的判斷為 object 。
基本數據類型和復雜數據類型的區別為:內存的分配不同
基本數據類型存儲在棧中。
復雜數據類型存儲在堆中,棧中存儲的變量,是指向堆中的引用地址。
訪問機制不同
基本數據類型是按值訪問
復雜數據類型按引用訪問,JS不允許直接訪問保存在堆內存中的對象,在訪問一個對象時,首先得到的是這個對象在堆內存中的地址,然后再按照這個地址去獲得這個對象中的值。
復制變量時不同(a=b)
基本數據類型:a=b;是將b中保存的原始值的副本賦值給新變量a,a和b完全獨立,互不影響
復雜數據類型:a=b;將b保存的對象內存的引用地址賦值給了新變量a;a和b指向了同一個堆內存地址,其中一個值發生了改變,另一個也會改變。
let b = { age: 10 } let a = b; a.age = 20; console.log(a); //{ age: 20 }
參數傳遞的不同(實參/形參)
函數傳參都是按值傳遞(棧中的存儲的內容):基本數據類型,拷貝的是值;復雜數據類型,拷貝的是引用地址
//基本數據類型 let b = 10 function change(info) { info=20; } //info=b;基本數據類型,拷貝的是值得副本,二者互不干擾 change(b); console.log(b);//10
//復雜數據類型 let b = { age: 10 } function change(info) { info.age = 20; } //info=b;根據第三條差異,可以看出,拷貝的是地址的引用,修改互相影響。 change(b); console.log(b);//{ age: 20 }
點擊查看更多3.說一說你對HTML5語義化的理解(2019-05-22)
語義化意味著顧名思義,HTML5的語義化指的是合理正確的使用語義化的標簽來創建頁面結構,如 header,footer,nav,從標簽上即可以直觀的知道這個標簽的作用,而不是濫用div。
語義化的優點有:代碼結構清晰,易于閱讀,利于開發和維護
方便其他設備解析(如屏幕閱讀器)根據語義渲染網頁。
有利于搜索引擎優化(SEO),搜索引擎爬蟲會根據不同的標簽來賦予不同的權重
語義化標簽主要有:title:主要用于頁面的頭部的信息介紹
header:定義文檔的頁眉
nav:主要用于頁面導航
main:規定文檔的主要內容。對于文檔來說應當是唯一的。它不應包含在文檔中重復出現的內容,比如側欄、導航欄、版權信息、站點標志或搜索表單。
article:獨立的自包含內容
h1~h6:定義標題
ul: 用來定義無序列表
ol: 用來定義有序列表
address:定義文檔或文章的作者/擁有者的聯系信息。
canvas:用于繪制圖像
dialog:定義一個對話框、確認框或窗口
aside:定義其所處內容之外的內容。 的內容可用作文章的側欄。
section:定義文檔中的節(section、區段)。比如章節、頁眉、頁腳或文檔中的其他部分。
figure:規定獨立的流內容(圖像、圖表、照片、代碼等等)。figure 元素的內容應該與主內容相關,但如果被刪除,則不應對文檔流產生影響。
details:描述文檔或者文檔某一部分細節
mark:義帶有記號的文本。
語義化應用例如使用這些可視化標簽,構建下面的頁面結構:
對于早期不支持 HTML5 的瀏覽器,如IE8及更早之前的版本,我們可以引入 html5shiv 來支持。
點擊查看更多4.如何讓 (a == 1 && a == 2 && a == 3) 的值為true? 4.1 利用隱式轉換規則
== 操作符在左右數據類型不一致時,會先進行隱式轉換。
a == 1 && a == 2 && a == 3 的值意味著其不可能是基本數據類型。因為如果 a 是 null 或者是 undefined bool類型,都不可能返回true。
因此可以推測 a 是復雜數據類型,JS 中復雜數據類型只有 object,回憶一下,Object 轉換為原始類型會調用什么方法?
如果部署了 [Symbol.toPrimitive] 接口,那么調用此接口,若返回的不是基本數據類型,拋出錯誤。
如果沒有部署 [Symbol.toPrimitive] 接口,那么根據要轉換的類型,先調用 valueOf / toString
非Date類型對象,hint 是 default 時,調用順序為:valueOf >>> toString,即valueOf 返回的不是基本數據類型,才會繼續調用 valueOf,如果toString 返回的還不是基本數據類型,那么拋出錯誤。
如果 hint 是 string(Date對象默人的hint是string) ,調用順序為:toString >>> valueOf,即toString 返回的不是基本數據類型,才會繼續調用 valueOf,如果valueOf 返回的還不是基本數據類型,那么拋出錯誤。
如果 hint 是 number,調用順序為: valueOf >>> toString
var obj = { [Symbol.toPrimitive](hint) { console.log(hint); return 10; }, valueOf() { console.log("valueOf"); return 20; }, toString() { console.log("toString"); return "hello"; } } console.log(obj + "yvette"); //default //如果沒有部署 [Symbol.toPrimitive]接口,調用順序為`valueOf` >>> `toString` console.log(obj == "yvette"); //default //如果沒有部署 [Symbol.toPrimitive]接口,調用順序為`valueOf` >>> `toString` console.log(obj * 10);//number //如果沒有部署 [Symbol.toPrimitive]接口,調用順序為`valueOf` >>> `toString` console.log(Number(obj));//number //如果沒有部署 [Symbol.toPrimitive]接口,調用順序為`valueOf` >>> `toString` console.log(String(obj));//string //如果沒有部署 [Symbol.toPrimitive]接口,調用順序為`toString` >>> `valueOf`
那么對于這道題,只要 [Symbol.toPrimitive] 接口,第一次返回的值是 1,然后遞增,即成功成立。
let a = { [Symbol.toPrimitive]: (function(hint) { let i = 1; //閉包的特性之一:i 不會被回收 return function() { return i++; } })() } console.log(a == 1 && a == 2 && a == 3); //true
調用 valueOf 接口的情況:
let a = { valueOf: (function() { let i = 1; //閉包的特性之一:i 不會被回收 return function() { return i++; } })() } console.log(a == 1 && a == 2 && a == 3); //true
另外,除了i自增的方法外,還可以利用 正則,如下
let a = { reg: /d/g, valueOf () { return this.reg.exec(123)[0] } } console.log(a == 1 && a == 2 && a == 3); //true
調用 toString 接口的情況,不再做說明。
4.2 利用數據劫持使用 Object.defineProperty 定義的屬性,在獲取屬性時,會調用 get 方法。利用這個特性,我們在 window 對象上定義 a 屬性,如下:
let i = 1; Object.defineProperty(window, "a", { get: function() { return i++; } }); console.log(a == 1 && a == 2 && a == 3); //true
ES6 新增了 Proxy ,此處我們同樣可以利用 Proxy 去實現,如下:
let a = new Proxy({}, { i: 1, get: function () { return () => this.i++; } }); console.log(a == 1 && a == 2 && a == 3); // true4.3 數組的 toString 接口默認調用數組的 join 方法,重寫數組的 join 方法。
let a = [1, 2, 3]; a.join = a.shift; console.log(a == 1 && a == 2 && a == 3); //true4.4 利用 with 關鍵字
我本人對 with 向來是敬而遠之的。不過 with 的確也是此題方法之一:
let i = 0; with ({ get a() { return ++i; } }) { console.log(a == 1 && a == 2 && a == 3); //true }
點擊查看更多5.防抖(debounce)函數的作用是什么?有哪些應用場景,請實現一個防抖函數。 防抖函數的作用
防抖函數的作用就是控制函數在一定時間內的執行次數。防抖意味著N秒內函數只會被執行一次,如果N秒內再次被觸發,則重新計算延遲時間。
舉例說明:小思最近在減肥,但是她非常貪吃。為此,與其男朋友約定好,如果10天不吃零食,就可以購買一個包(不要問為什么是包,因為包治百病)。但是如果中間吃了一次零食,那么就要重新計算時間,直到小思堅持10天沒有吃零食,才能購買一個包。所以,管不住嘴的小思,沒有機會買包(悲傷的故事)...這就是防抖。
不管吃沒吃零食,每10天買一個包,中間想買包,忍著,等到第十天的時候再買,這種情況是節流。如何控制女朋友的消費,各位攻城獅們,get到了嗎?防抖可比節流有效多了!
防抖應用場景搜索框輸入查詢,如果用戶一直在輸入中,沒有必要不停地調用去請求服務端接口,等用戶停止輸入的時候,再調用,設置一個合適的時間間隔,有效減輕服務端壓力。
表單驗證
按鈕提交事件。
瀏覽器窗口縮放,resize事件等。
防抖函數實現事件第一次觸發時,timer 是 null,調用 later(),若 immediate 為true,那么立即調用 func.apply(this, params);如果 immediate 為 false,那么過 wait 之后,調用 func.apply(this, params)
事件第二次觸發時,如果 timer 已經重置為 null(即 setTimeout 的倒計時結束),那么流程與第一次觸發時一樣,若 timer 不為 null(即 setTimeout 的倒計時未結束),那么清空定時器,重新開始計時。
function debounce(func, wait, immediate = true) { let timer; // 延遲執行函數 const later = (context, args) => setTimeout(() => { timer = null;// 倒計時結束 if (!immediate) { func.apply(context, args); //執行回調 context = args = null; } }, wait); let debounced = function (...params) { let context = this; let args = params; if (!timer) { timer = later(context, args); if (immediate) { //立即執行 func.apply(context, args); } } else { clearTimeout(timer); //函數在每個等待時延的結束被調用 timer = later(context, args); } } debounced.cancel = function () { clearTimeout(timer); timer = null; }; return debounced; };
immediate 為 true 時,表示函數在每個等待時延的開始被調用。
immediate 為 false 時,表示函數在每個等待時延的結束被調用。
只要高頻事件觸發,那么回調函數至少被調用一次。
點擊查看更多
參考文章:
[1] https://www.ecma-internationa...
[2] 嗨,你真的懂this嗎?
[3] 【面試篇】寒冬求職季之你必須要懂的原生JS(上)
[4] 【面試篇】寒冬求職季之你必須要懂的原生JS(中)
[5] https://digcss.com/throttle-t...
謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發,請不要吝嗇你的贊和Star,您的肯定是我前進的最大動力。https://github.com/YvetteLau/...
關注公眾號,加入技術交流群
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109867.html
摘要:禁止內聯腳本執行規則較嚴格,目前發現使用。合理使用上報可以及時發現,利于盡快修復問題。因為事件會從目標元素一層層冒泡至對象。允許給一個事件注冊多個監聽。表示在捕獲階段觸發,表示在冒泡階段觸發。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的答案,整理最一份...
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。即使這個時間周期內,小明取得多次滿分。創建作用域鏈在執行期上下文的創建階段,作用域鏈是在變量對象之后創建的。這種一層一層的關系,就是作用域鏈。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的答...
摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。即使這個時間周期內,小明取得多次滿分。創建作用域鏈在執行期上下文的創建階段,作用域鏈是在變量對象之后創建的。這種一層一層的關系,就是作用域鏈。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的答...
閱讀 3952·2021-11-18 13:21
閱讀 4759·2021-09-27 14:01
閱讀 3110·2019-08-30 15:53
閱讀 2388·2019-08-30 15:43
閱讀 1730·2019-08-30 13:10
閱讀 1508·2019-08-29 18:39
閱讀 887·2019-08-29 15:05
閱讀 3340·2019-08-29 14:14