整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript瀏覽器對象

          OM--Browser Object Model

          瀏覽器 對象 模型

          window對象,是BOM中所有對象的核心

          一、屬性

          A、位置類型--獲得瀏覽器的位置

          IE:

          window.screenLeft--可以獲得瀏覽器距屏幕左上角的左邊距

          alert(window.screenLeft);

          window.screenTop--可以獲得瀏覽器距屏幕左上角的上邊距

          alert(window.screenTop);

          FF:

          screenX--左邊距

          screenY--上邊距

          B、獲得瀏覽器的尺寸

          獲得窗口寬度--document.documentElement.clientWidth

          獲得窗口高度--document.documentElement.clientHeight

          C、關系類型

          1.parent返回父窗口

          2.top 返回頂層窗口

          3.self===window

          4.opener--開啟者

          D、stutas 設置窗口狀態欄的文本

          二、方法

          A、窗體控制

          IE

          window.moveBy(x,y) 相對于當前位置,沿著x/y移動指定的像素,負數向反方向移動

          window.moveBy(x,y) 相對于瀏覽器的左上角沿著x/y移動指定的像素,負數向反方向移動

          B、窗口尺寸的改變

          IE

          resizeBy(x,y); 相對于當前窗體大小,調整寬度和高度

          resizeTo(x,y) 把窗體調整為指定的寬度的高度

          C、對窗體滾動條的控制

          scrollBy(x,y) 相對于滾動條的位置移動的像素(前提有滾動條)

          scrollTo(x,y) 相對于當前窗口的高度或寬度,移動到指定的像素

          D、時間間隔的函數

          1.setInterval("函數或者代碼串",指定的時間(毫秒));

          在指定的周期不斷的執行函數或者代碼串

          var i=0;

          setInterval(change,1000);

          function change(){

          alert(i);

          i++;

          }

          2.clearInterval()--停止調用

          window.onload=function(){

          var bb=setInterval("alert('hah')",3000);

          var aa=document.getElementById("stop");

          aa.onclick=function(){

          clearInterval(bb);

          }

          }

          3.setTimeout("函數或代碼串",毫秒)--在指定的毫秒數后,只執行一次代碼

          window.onload=function(){

          var aa= setTimeout("alert('bbs')",3000);

          var bb=document.getElementById("stop");

          bb.onclick=function(){

          clearTimeout(aa);

          }

          }

          E、打開新的窗口

          open(url,name,feafurse,replace);--在腳本中打開新的窗口

          備JavaScript面試時應了解的事項。

          JavaScript現在是一種非常流行的編程語言,基于該語言,派生了大量庫和框架。 但是,無論高層生態系統如何發展,離不開原始的JavaScript。 在這里,我選擇了4個JavaScript面試問題來測試程序員使用普通JavaScript的技能。

          1.實現Array.prototype.map

          如何手動實現Array.prototype.map方法?

          熟練使用數組的內置方法并不難。但是,如果您只是熟悉語法而又不了解原理,那么很難真正理解JavaScript。

          對于Array.prototype.map,它將創建一個新數組,其中將填充在調用數組中每個元素上調用提供的函數的結果。

          如果引用lodash,我們可以編寫一個map函數,如下所示:

          function map(array, iteratee) { 
            let index = -1 
            const length = array == null ? 0 : array.length 
            const result = new Array(length) 
            while (++index < length) { 
              result[index] = iteratee(array[index], index, array) 
            } 
            return result
          }

          使用示例:

          2. Object.defineProperty和代理

          如何實現這種編碼效果?

          我們可以看到,當我們嘗試連續打印obj.a三次時,會得到三種不同的結果。看起來多么不可思議!

          您可以創建一個神秘的對象obj來實現此效果嗎?

          實際上,此問題有三種解決方案:

          · 訪問者屬性

          · Object.defineProperty

          · 代理

          根據ECMAScript,對象的屬性可以采用兩種形式:

          從邏輯上講,對象是屬性的集合。每個屬性都是數據屬性或訪問器屬性:

          · 數據屬性將鍵值與ECMAScript語言值和一組布爾屬性相關聯。

          · 訪問器屬性將鍵值與一個或兩個訪問器函數以及一組布爾屬性相關聯。訪問器函數用于存儲或檢索與屬性關聯的ECMAScript語言值。

          所謂的數據屬性通常是我們寫的:

          let obj = { a: 1, b: 2}

          我們對一個對象的屬性只有兩個操作:讀取屬性和設置屬性。對于訪問器屬性,我們使用get和set方法定義屬性,其編寫方式如下:

          let obj = { 
            get a(){ 
              console.log('triggle get a() method') 
              console.log('you can do anything as you want') 
              return 1 
            }, 
            set a(value){ 
              console.log('triggle set a() method') 
              console.log('you can do anything as you want') 
              console.log(`you are trying to assign ${value} to obj.a`) 
            }
          }

          訪問屬性為我們提供了強大的元編程能力,因此我們可以通過以下方式滿足我們的要求:

          let obj = { 
            _initValue: 0, 
            get a() { 
              this._initValue++; 
              return this._initValue 
            }
          }
          
          console.log(obj.a, obj.a, obj.a)

          第二種方法是使用Object.defineProperty,該方法的工作方式與我們用來訪問屬性的方法相同,除了不是直接聲明訪問屬性,而是通過Object.defineProperty配置訪問屬性。

          這使用起來更加靈活,因此我們可以這樣編寫:

          let obj = {}Object.defineProperty(obj, 'a', { get: (function(){ let initValue = 0; return function(){ initValue++; return initValue } })()})console.log(obj.a, obj.a, obj.a)

          在這里的get方法中,我們使用了一個閉包,以便我們需要使用的變量initValue隱藏在閉包中,并且不會污染其他范圍。

          第三種方法是使用代理。

          使用代理,我們可以攔截對對象屬性的訪問。 只要我們使用代理來攔截對obj.a的訪問,然后依次返回1、2和3,我們就可以在以下條件之前完成要求:

          let initValue = 0;
          let obj = new Proxy({}, { 
            get: function(item, property, itemProxy){ 
              if(property === 'a'){ 
                initValue++; 
                return initValue 
              } 
              return item[property] 
            }
          })
          
          console.log(obj.a, obj.a, obj.a)

          為什么理解這個問題很重要?因為Object.defineProperty和Proxy給了我們強大的元編程能力,所以我們可以適當地修改對象以做一些特殊的事情。

          在著名的前端框架Vue中,其核心機制之一是數據的雙向綁定。在Vue2.0中,Vue通過使用Object.defineProperty實現了該機制。在Vue3.0中,使用Proxy完成此機制。

          如果不掌握Vue之類的框架,您將無法真正理解。如果您掌握了這些原則,則只需學習Vue的一半,就可以獲得兩倍的結果。

          3.范圍和閉包

          運行此代碼的結果是什么?

          function foo(a,b) { 
            console.log(b) 
            return { 
              foo:function(c){ 
                return foo(c,a); 
              } 
            };
          }
          
          let res = foo(0); 
          res.foo(1); 
          res.foo(2); 
          res.foo(3);

          上面的代碼同時具有多個嵌套函數和三個foo嵌套函數,乍一看看起來非常繁瑣。那么,我們如何理解這一點呢?

          首先,請確保上面的代碼中有多少個功能?我們可以看到在上面的代碼中的兩個地方都使用了關鍵字函數,因此上面的代碼中有兩個函數,即第一行函數foo(a,b) 和第四行 foo:function(c)。并且這兩個函數具有相同的名稱。

          第二個問題:第5行的foo(c,a)調用哪個函數?如果不確定,讓我們來看一個簡單的示例:

          var obj={ 
            fn:function (){ 
              console.log(fn); 
            }
          };
          
          obj.fn()

          如果我們運行該代碼,是否會引發異常? 答案是肯定的。

          這是因為obj.fn()方法的上限是全局的,并且無法訪問obj內部的fn方法。

          回到前面的示例,以同樣的邏輯,當我們調用foo(c,a)時,實際上是在第一行上調用foo函數。

          當我們調用res.foo(1)時,將調用哪個foo? 顯然,第4行的foo函數被調用。

          因為這兩個foo函數的工作方式不同,所以我們可以將其中一個的名稱更改為bar,以使我們更容易理解代碼。

          function foo(a,b) { 
            console.log(b) 
            return { 
              bar:function(c){ 
                return foo(c,a); 
              } 
            };
          }
          
          let res = foo(0); 
          res.bar(1); 
          res.bar(2); 
          res.bar(3);

          此更改不會影響最終結果,但會使我們更容易理解代碼。如果將來遇到類似的問題,請嘗試此技巧。

          每次調用一個函數時,都會創建一個新的作用域,因此我們可以繪制圖表以幫助我們理解代碼工作原理的邏輯。

          當我們執行let res = foo(0);時,實際上是在執行foo(0,undefiend)。此時,將在程序中創建一個新的作用域,在當前作用域中a = 0,b = undefined。因此,我繪制的圖看起來像這樣。

          然后將執行console.log(b),因此它第一次在控制臺中打印出" undefined"。

          然后執行res.bar(1),創建一個新范圍,其中c = 1:

          然后從上面的函數中再次調用foo(c,a),它實際上是foo(1,0),作用域如下所示:

          在新作用域中,a的值為1,b的值為0,因此控制臺將打印出0。

          再次執行res.bar(2)。注意,res.bar(2)和res.bar(1)是并行關系,因此我們應該像這樣繪制范圍圖:

          因此,在此代碼中,控制臺也會打印出值0。

          執行res.bar(3)的過程也是如此,控制臺仍顯示0。

          因此,以上代碼的最終結果是:

          實際上,上述問題可以用其他方式改變。例如,可以將其更改為以下內容:

          function foo(a,b) { 
            console.log(b) 
            return { 
              foo:function(c){ 
                return foo(c,a); 
              } 
            };
          }
          
          foo(0).foo(1).foo(2).foo(3);

          在解決這個問題之前,我們要做的第一件事是區分兩個不同的foo函數,因此可以將上面的代碼更改為如下所示:

          function foo(a,b) { 
            console.log(b) 
            return { 
              bar:function(c){ 
                return foo(c,a); 
              } 
            };
          }
          
           foo(0).bar(1).bar(2).bar(3);

          執行foo(0)時,作用域與以前相同,然后控制臺將打印出" undefined"。

          然后執行.bar(1)創建一個新的作用域。此參數1實際上是c的值。

          然后.bar(1)方法再次調用foo(c,a),它實際上是foo(1,0)。這里的參數1實際上將是新作用域中a的值,而0將是新作用域中b的值。

          因此,控制臺隨后輸出了b的值,即0。

          再次調用.bar(2),在新作用域中c的值為2:

          然后.bar(2)調用foo(c,a),它實際上是foo(2,1),其中2是新作用域中a的值,而1是新作用域中b的值。

          因此,控制臺隨后輸出了b的值,即0。

          然后它將執行.bar(3),該過程與之前相同,因此我將不擴展其描述,此步驟控制臺將打印出2。

          如上所述,代碼運行的最終結果是:

          好了,經過漫長的旅程,我們終于得到了答案。 這個問題很好地檢驗了受訪者對封閉和范圍的理解。

          4.撰寫 Compose

          假設我們有一個看起來像這樣的函數:

          function compose (middleware) { // some code}

          compose函數接受函數數組中間件:

          let middleware = []
          middleware.push((next) => { 
            console.log(1) 
            next() 
            console.log(1.1)
          })
          
          middleware.push((next) => { 
            console.log(2) 
            next() 
            console.log(2.1)
          })
          
          middleware.push(() => { 
            console.log(3)
          })
          
          let fn = compose(middleware)
          
          fn()

          當我們嘗試執行fn時,它將調用中間件中的函數,并將下一個函數作為參數傳遞給每個小函數。

          如果我們在一個小函數中執行next,則將調用中間件中該函數的next函數。而且,如果您接下來不執行,程序也不會崩潰。

          執行完上面的代碼后,我們得到以下結果:

          1232.11.1

          那么,我們如何編寫一個compose函數來做到這一點呢?

          首先,compose函數必須返回一個composed函數,因此我們可以編寫如下代碼:

          function compose (middleware) { 
            return function () { }
          }

          然后,在返回的函數中,中間件的第一個函數開始執行。我們還將傳遞下一個函數作為其參數。所以讓我們這樣寫:

          function compose (middleware) { 
            return function () { 
              let f1 = middleware[0] 
              f1(function next(){ }) 
            }
          }

          下一個功能充當繼續在中間件中運行的開關,如下所示:

          function compose (middleware) { 
            return function () { 
              let f1 = middleware[0] 
              f1(function next(){ 
                let f2 = middleware[1] 
                f2(function next(){ ... }) 
              }) 
            }
          }

          然后繼續在下一個函數中調用第三個函數…等待,這看起來像遞歸! 因此,我們可以編寫一個遞歸函數來完成此嵌套調用:

          function compose (middleware) { 
            return function () { 
              dispatch(0) 
              function dispatch (i) { 
                const fn = middleware[i] 
                if (!fn) return null 
                fn(function next () { 
                  dispatch(i + 1) 
                }) 
              } 
            }
          }

          好的,這就是我們的撰寫功能,所以讓我們對其進行測試:

          好吧,此功能完全可以完成其所需的工作。 但是我們也可以優化我們的compose函數可以支持異步函數。 我們可以改進以下代碼:

          function compose (middleware) { 
            return async function () { 
              await dispatch(0) 
              function async dispatch (i) { 
                const fn = middleware[i] 
                if (!fn) 
                  return null 
                await fn(function next () { 
                  dispatch(i + 1) 
                }) 
              } 
            }
          }

          實際上,以上的撰寫功能是眾所周知的節點框架koa的核心機制。

          當我選擇候選人時,我接受他/她對某些框架不熟悉。畢竟,JavaScript生態系統中有太多的庫和框架,沒有人能完全掌握它們。但是我確實希望候選人知道這些重要的原始JavaScript技巧,因為它們是所有庫和框架的基礎。

          結論

          實際上,我的草稿中還有其他一些面試問題,但由于本文篇幅有限,因此在此不再繼續解釋。稍后再與您分享。

          本文主要涉及普通JavaScript,而不涉及瀏覽器,節點,框架,算法,設計模式等。如果您對這些主題也感興趣,請隨時發表評論。

          感謝您的閱讀!

          (本文由聞數起舞翻譯自bitfish的文章《Improve Your JavaScript Level With These 4 Questions》,轉載請注明出處,原文鏈接:https://medium.com/javascript-in-plain-english/i-use-these-4-questions-to-find-outstanding-javascript-developers-4a468ea17155)

          覽器解析HTML文件的過程是網頁呈現的關鍵步驟之一。具體介紹如下:


          HTML文檔的接收和預處理

          1. 網絡請求處理:當用戶輸入URL或點擊鏈接時,瀏覽器發起HTTP請求,服務器響應并返回HTML文件。此過程中,瀏覽器需要處理DNS查詢、建立TCP連接等底層網絡通信操作。
          2. 預解析優化:為了提高性能,現代瀏覽器在主線程解析HTML之前會啟動一個預解析線程,提前下載HTML中鏈接的外部CSS和JS文件。這一步驟確保了后續渲染過程的順暢進行。

          解析為DOM樹

          1. 詞法分析和句法分析:瀏覽器的HTML解析器通過詞法分析將HTML文本標記轉化為符號序列,然后通過句法分析器按照HTML規范構建出DOM樹。每個節點代表一個HTML元素,形成了多層次的樹狀結構。
          2. 生成對象接口:生成的DOM樹是頁面元素的結構化表示,提供了操作頁面元素的接口,如JavaScript可以通過DOM API來動態修改頁面內容和結構。

          CSS解析與CSSOM樹構建

          1. CSS文件加載與解析:瀏覽器解析HTML文件中的<link>標簽引入的外部CSS文件和<style>標簽中的內聯CSS,生成CSSOM樹。CSSOM樹反映了CSS樣式的層級和繼承關系。
          2. CSS屬性計算:包括層疊、繼承等,確保每個元素對應的樣式能夠被準確計算。這些計算過程為后續的布局提供必要的樣式信息。

          JavaScript加載與執行

          1. 阻塞式加載:當解析器遇到<script>標簽時,它會停止HTML的解析,轉而先加載并執行JavaScript代碼。這是因為JS可能會修改DOM結構或CSSOM樹,從而影響已解析的部分。
          2. 異步和延遲加載:為了不影響頁面的初步渲染,可以采用async或defer屬性來異步加載JS文件,這樣可以在后臺進行JS的加載和執行,而不阻塞HTML解析。

          渲染樹的構建

          1. 合并DOM樹和CSSOM樹:有了DOM樹和CSSOM樹后,瀏覽器將它們組合成渲染樹,這個樹只包含顯示界面所需的DOM節點及對應的樣式信息。
          2. 不可見元素的排除:渲染樹會忽略例如<head>、<meta>等不可見元素,只關注<body>內的可視化內容。

          布局計算(Layout)

          1. 元素位置和尺寸確定:瀏覽器從渲染樹根節點開始,遞歸地計算每個節點的精確位置和尺寸,這個過程也被稱為“回流”或“重排”,是后續繪制的基礎。
          2. 布局過程的優化:現代瀏覽器會盡量優化布局過程,例如通過流式布局的方式減少重復計算,確保高效地完成布局任務。

          繪制(Paint)

          1. 像素級繪制:繪制是一個將布局計算后的各元素繪制成像素點的過程。這包括文本、顏色、邊框、陰影以及替換元素的繪制。
          2. 層次化的繪制:為了高效地更新局部內容,瀏覽器會將頁面分成若干層次(Layer),對每一層分別進行繪制,這樣只需更新變化的部分。

          因此,我們開發中要注意以下幾點:

          • 避免過度使用全局腳本:盡量減少使用全局腳本或者將它們放在文檔底部,以減少對HTML解析的阻塞。
          • 合理組織CSS和使用CSS預處理器:合理組織CSS文件的結構和覆蓋規則,利用CSS預處理器進行模塊化管理。
          • 利用瀏覽器緩存機制:通過設置合理的緩存策略,減少重復加載相同資源,提升二次訪問的體驗。
          • 優化圖片和多媒體資源:適當壓縮圖片和優化多媒體資源的加載,減少網絡傳輸時間和渲染負擔。

          綜上所述,瀏覽器解析HTML文件是一個復雜而高度優化的過程,涉及從網絡獲取HTML文檔到最終將其渲染到屏幕上的多個步驟。開發者需要深入理解這些步驟,以優化網頁性能和用戶體驗。通過合理組織HTML結構、優化資源加載順序、減少不必要的DOM操作和合理安排CSS和JavaScript的加載與執行,可以顯著提升頁面加載速度和運行效率。


          主站蜘蛛池模板: 国产萌白酱在线一区二区| 射精专区一区二区朝鲜| 久久精品国产一区二区三区| 在线视频亚洲一区| 无码精品人妻一区| 国模私拍一区二区三区| 一区二区视频在线播放| 3D动漫精品一区二区三区| 国产成人欧美一区二区三区 | 中文字幕无线码一区| 久久se精品一区二区| 中文字幕av人妻少妇一区二区| 国产精品亚洲一区二区三区久久| 高清一区二区三区| 久久国产精品无码一区二区三区| 亚洲一区二区三区丝袜| 亚洲成人一区二区| 亚洲国产综合精品中文第一区| 亚洲国产精品一区二区三区在线观看| 国模精品一区二区三区视频 | 亚洲AV综合色区无码一区爱AV | 白丝爆浆18禁一区二区三区| 国产综合无码一区二区三区| 精品国产一区二区三区色欲| 日本韩国黄色一区二区三区| 精品一区二区三区在线成人| 精品无人乱码一区二区三区| 久久精品一区二区影院| 国产一区二区成人| 精品人妻少妇一区二区三区在线| 亚洲va乱码一区二区三区| 亚洲一区二区三区国产精品| 国产午夜精品一区二区三区小说| 亚洲一区中文字幕| 熟女性饥渴一区二区三区| 麻豆AV无码精品一区二区| 国产一区二区三区在线2021| 日本精品视频一区二区| 精品人妻一区二区三区浪潮在线 | 久久精品一区二区影院| 夜精品a一区二区三区|