前端面試題-基礎

1.移動端適配1px的問題

解答:css

(1)緣由:html

css中的1px並不等於移動設備的1px,這些因爲不一樣的手機有不一樣的像素密度。在window對象中有一個devicePixelRatio屬性,他能夠反應css中的像素與設備的像素比。前端

devicePixelRatio的官方的定義爲:設備物理像素和設備獨立像素的比例,也就是 devicePixelRatio = 物理像素 / 獨立像素。vue

關於devicePixelRatio的詳細介紹能夠參考張鑫旭的這篇博文,http://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/webpack

(2)解決方法:nginx

a:0.5px邊框

IOS8下已經支持帶小數的px值, media query 對應 devicePixelRatio 有個查詢值 -webkit-min-device-pixel-ratio, css能夠寫成這樣
經過-webkit-min-device-pixel-ratio設置。web

.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}

 

若是使用less/sass的話只是加了1句mixin
缺點: 安卓與低版本IOS不適用, 這個或許是將來的標準寫法, 如今不作期望vue-router

b.viewport + rem 實現

同時經過設置對應viewport的rem基準值,這種方式就能夠像之前同樣輕鬆愉快的寫1px了。
在devicePixelRatio = 2 時,輸出viewport:vue-cli

<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">

 

在devicePixelRatio = 3 時,輸出viewport:
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">

 

c.使用border-image實現

準備一張符合你要求的border-image:npm

.border-bottom-1px {
    border-width: 0 0 1px 0;
    -webkit-border-image: url(linenew.png) 0 0 2 0 stretch;
    border-image: url(linenew.png) 0 0 2 0 stretch;
}

 

上文是把border設置在邊框的底部,因此使用的圖片是2px高,上部的1px顏色爲透明,下部的1px使用視覺規定的border的顏色。若是邊框底部和頂部同時須要border,可使用下面的border-image:

.border-image-1px {
    border-width: 1px 0;
    -webkit-border-image: url(linenew.png) 2 0 stretch;
    border-image: url(linenew.png) 2 0 stretch;
}

 

d.使用background-image實現

background-image 跟border-image的方法同樣,你要先準備一張符合你要求的圖片。而後將邊框模擬在背景上。
樣式設置:

.background-image-1px {
    background: url(../img/line.png) repeat-x left bottom;
    -webkit-background-size: 100% 1px;
    background-size: 100% 1px;
}

 

e.使用box-shadow模擬邊框

利用css 對陰影處理的方式實現0.5px的效果

.box-shadow-1px {
    box-shadow: inset 0px -1px 1px -1px #c8c7cc;
}

 

f.僞類 + transform 實現

對於老項目,有沒有什麼辦法能兼容1px的尷尬問題了,我的認爲僞類+transform是比較完美的方法了。
原理是把原先元素的 border 去掉,而後利用 :before 或者 :after 重作 border ,並 transform 的 scale 縮小一半,原先的元素相對定位,新作的 border 絕對定位。
單條border樣式設置:

.scale-1px{
    position: relative;
    border:none;
}
.scale-1px:after{
    content: '';
    position: absolute;
    bottom: 0;
    background: #000;
    width: 100%;
    height: 1px;
    -webkit-transform: scaleY(0.5);
    transform: scaleY(0.5);
    -webkit-transform-origin: 0 0;
    transform-origin: 0 0;
}

 

四條boder樣式設置:

.scale-1px{
    position: relative;
    margin-bottom: 20px;
    border:none;
}
.scale-1px:after{
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    border: 1px solid #000;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    width: 200%;
    height: 200%;
    -webkit-transform: scale(0.5);
    transform: scale(0.5);
    -webkit-transform-origin: left top;
    transform-origin: left top;
}

 

最好在使用前也判斷一下,結合 JS 代碼,判斷是否 Retina 屏:

if(window.devicePixelRatio && devicePixelRatio >= 2){
    document.querySelector('ul').className = 'scale-1px';
}

 

2.如何解決跨域的問題

1. JSONP 

利用 <script> 標籤沒有跨域限制的漏洞,網頁能夠獲得從其餘來源動態產生的 JSON 數據。JSONP 請求必定須要對方的服務器作支持才能夠。

JSONP 優勢是簡單兼容性好,可用於解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持 get 方法具備侷限性,不安全可能會遭受 XSS 攻擊。

2.cors

CORS 須要瀏覽器和後端同時支持。IE 8 和 9 須要經過 XDomainRequest 來實現。

瀏覽器會自動進行 CORS 通訊,實現 CORS 通訊的關鍵是後端。只要後端實現了 CORS,就實現了跨域。

服務端設置 Access-Control-Allow-Origin 就能夠開啓 CORS。 該屬性表示哪些域名能夠訪問資源,若是設置通配符則表示全部網站均可以訪問資源。

雖然設置 CORS 和前端沒什麼關係,可是經過這種方式解決跨域問題的話,會在發送請求時出現兩種狀況,分別爲簡單請求和複雜請求。

3.postMessage 

postMessage 是 HTML5 XMLHttpRequest Level 2 中的 API,且是爲數很少能夠跨域操做的 window 屬性之一,它可用於解決如下方面的問題:

  • 頁面和其打開的新窗口的數據傳遞
  • 多窗口之間消息傳遞
  • 頁面與嵌套的 iframe 消息傳遞
  • 上面三個場景的跨域數據傳遞

postMessage()方法容許來自不一樣源的腳本採用異步方式進行有限的通訊,能夠實現跨文本檔、多窗口、跨域消息傳遞。

otherWindow.postMessage(message, targetOrigin, [transfer]);

    • message: 將要發送到其餘 window 的數據。
    • targetOrigin:經過窗口的 origin 屬性來指定哪些窗口能接收到消息事件,其值能夠是字符串"*"(表示無限制)或者一個 URI。在發送消息的時候,若是目標窗口的協議、主機地址或端口這三者的任意一項不匹配 targetOrigin 提供的值,那麼消息就不會被髮送;只有三者徹底匹配,消息纔會被髮送。
    • transfer(可選):是一串和 message 同時傳遞的 Transferable 對象. 這些對象的全部權將被轉移給消息的接收方,而發送一方將再也不保有全部權。

4.Websocket 

Websocket 是 HTML5 的一個持久化的協議,它實現了瀏覽器與服務器的全雙工通訊,同時也是跨域的一種解決方案。WebSocket 和 HTTP 都是應用層協議,都基於 TCP 協議。可是 WebSocket 是一種雙向通訊協議,在創建鏈接以後,WebSocket 的 server 與 client 都能主動向對方發送或接收數據。同時,WebSocket 在創建鏈接時須要藉助 HTTP 協議,鏈接創建好了以後 client 與 server 之間的雙向通訊就與 HTTP 無關了。 

原生 WebSocket API 使用起來不太方便,咱們使用Socket.io,它很好地封裝了 WebSocket 接口,提供了更簡單、靈活的接口,也對不支持 WebSocket 的瀏覽器提供了向下兼容。

5. Node 中間件代理(兩次跨域) 

實現原理:同源策略是瀏覽器須要遵循的標準,而若是是服務器向服務器請求就無需遵循同源策略。

代理服務器,須要作如下幾個步驟:

  • 接受客戶端請求 。
  • 將請求轉發給服務器。
  • 拿到服務器響應數據。
  • 將響應轉發給客戶端。 

6.nginx 反向代理

實現原理相似於 Node 中間件代理,須要你搭建一箇中轉 nginx 服務器,用於轉發請求。

使用 nginx 反向代理實現跨域,是最簡單的跨域方式。只須要修改 nginx 的配置便可解決跨域問題,支持全部瀏覽器,支持 session,不須要修改任何代碼,而且不會影響服務器性能。

實現思路:經過 nginx 配置一個代理服務器(域名與 domain1 相同,端口不一樣)作跳板機,反向代理訪問 domain2 接口,而且能夠順便修改 cookie 中 domain 信息,方便當前域 cookie 寫入,實現跨域登陸。

7.window.name + iframe

window.name屬性的獨特之處:name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在,而且能夠支持很是長的 name 值(2MB)。

經過 iframe 的 src 屬性由外域轉向本地域,跨域數據即由 iframe 的 window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操做。

8.location.hash + iframe

實現原理: a.html 欲與 c.html 跨域相互通訊,經過中間頁 b.html 來實現。 三個頁面,不一樣域之間利用 iframe 的 location.hash 傳值,相同域之間直接 js 訪問來通訊。

具體實現步驟:一開始 a.html 給 c.html 傳一個 hash 值,而後 c.html 收到 hash 值後,再把 hash 值傳遞給 b.html,最後 b.html 將結果放到 a.html 的 hash 值中。

9.document.domain + iframe

該方式只能用於二級域名相同的狀況下,好比 a.test.com 和 b.test.com 適用於該方式。

只須要給頁面添加 document.domain ='test.com' 表示二級域名都相同就能夠實現跨域。

實現原理:兩個頁面都經過 js 強制設置 document.domain 爲基礎主域,就實現了同域。

3.路由的動態加載

在Vue項目中,通常使用vue-cli構建項目後,咱們會在Router文件夾下面的index.js裏面引入相關的路由組件,如:

import Hello from '@/components/Hello'
import Boy from '@/components/Boy'
import Girl from '@/components/Girl'

這樣作的結果就是webpack在npm run build的時候會打包成一個整個的js文件,若是頁面一多,會致使這個文件很是大,加載緩慢,爲了解決這個問題,須要將他分紅多個小文件,並且還要實現異步按需加載,即用到了再加載,而不用一股腦所有加載。

1.webpack提供的require.ensure(),這樣能夠實現按需加載,而且你能夠將多個相同類的組件打包成一個文件,只要給他們指定相同的chunkName便可,如示例中的demo將會打包成一個文件。

{
     path:  '/promisedemo' ,
     name:  'PromiseDemo' ,
     component: r => require.ensure([], () => r(require( '../components/PromiseDemo' )),  'demo' )
},
{
     path:  '/hello' ,
     name:  'Hello' ,
     // component: Hello
     component: r => require.ensure([], () => r(require( '../components/Hello' )),  'demo' )
}

2.Vue的異步組件技術,這種方法能夠實現按需加載,而且一個組件會打包成一個js文件

{
            path:  '/promisedemo' ,
            name:  'PromiseDemo' ,
            component: resolve => require([ '../components/PromiseDemo' ], resolve)
        }


3.es提案的import(),也是我推薦的方法

首先,能夠將異步組件定義爲返回一個 Promise 的工廠函數 (該函數返回的 Promise 應該 resolve 組件自己):

const Foo = () => Promise.resolve({  /* 組件定義對象 */  })

第二,在 Webpack 2 中,咱們可使用動態 import語法來定義代碼分塊點 (split point):

import( './Foo.vue' // 返回 Promise

注意:若是您使用的是 Babel,你將須要添加 syntax-dynamic-import 插件,才能使 Babel 能夠正確地解析語法。

結合這二者,這就是如何定義一個可以被 Webpack 自動代碼分割的異步組件。

const Foo = () => import( './Foo.vue' )
這樣作的結果是每一個組件都會打包成一個js文件,有時候咱們想把某個路由下的全部組件都打包在同個異步塊 (chunk) 中。只須要使用 命名 chunk,一個特殊的註釋語法來提供 chunk name
const Foo = () => import( /* webpackChunkName: "group-foo" */  './Foo.vue' )

 

4.怎麼實現對象的深拷貝

/*
  this的使用:在預編譯的過程this指向是window
  在全局做用域裏this指向是window
  call/apply 改變this的指向
  obj.function();function()裏面的this指向的是obj
  */
 
var  obj = {
     a:  function  () {
         console.log( this .name)
 
     },
     name:  '123'
}
obj.a();   //誰調用這個方法this指向誰,沒用調用就是預編譯
 
var  foo = 123;
function  print() {
     this .foo = 234;
     console.log(foo);
 
}
 
/*print();*/  //234
new  print();  //123
 
 
/*
  arguments.callee:指向函數的引用,也就是它自己
  */
 
var  num = ( function  (n) {
 
     if  (n == 1) {
         return  1;
     else  {
         return  n * arguments.callee(n - 1);
     }
 
}(10))
 
 
/*深層拷貝*/
/*
  遍歷對象 for (var prop in obj)
  1:判斷是否是原始值  typeof()
  2:判斷是數組仍是對象
  3:判斷相應的數組和對象
  */
 
var  obj = {
     name:  'tom' ,
     age: 23,
     son: {},
     wife: [ 'sss' 'ddd' ]
}
 
 
function  deepClone(origin, target) {
     //容錯
     var  target = target || {},
         toStr = Object.prototype.toString(),
         arrStr =  "[Object Array]" ;
 
 
     for  ( var  prop  in  origin){
         if  (origin.hasOwnProperty(prop)){
             if  (origin[prop] !==  'null'  &&  typeof  (origin[prop]) ==  'object' ){
                if  ( toStr.call(origin[prop]) == arrStr){
                    target[prop]= [];
                } else  {
                    target[prop] ={};
                }
                deepClone(origin[prop],target[prop]);
             } else  {
                 target[prop] == origin[prop];
             }
         }
     }
 
}

5.什麼叫優雅降級和漸進加強

優雅降級:

Web站點在全部新式瀏覽器中都能正常工做,若是用戶使用的是老式瀏覽器,則代碼會檢查以確認它們是否能正常工做。因爲IE獨特的盒模型佈局問題,針對不一樣版本的IE的hack實踐過優雅降級了,爲那些沒法支持功能的瀏覽器增長候選方案,使之在舊式瀏覽器上以某種形式降級體驗卻不至於徹底失效。

漸進加強:

從被全部瀏覽器支持的基本功能開始,逐步地添加那些只有新式瀏覽器才支持的功能,向頁面增長無害於基礎瀏覽器的額外樣式和功能的。當瀏覽器支持時,它們會自動地呈現出來併發揮做用。

.transition {  /*漸進加強寫法*/
   -webkit-transition: all .5s;
      -moz-transition: all .5s;
        -o-transition: all .5s;
           transition: all .5s;
}
.transition {  /*優雅降級寫法*/
           transition: all .5s;
        -o-transition: all .5s;
      -moz-transition: all .5s;
   -webkit-transition: all .5s;
}

  前綴CSS3(-webkit- / -moz- / -o-*)和正常的 CSS3 在瀏覽器中的支持狀況是這樣的:

  1. 好久之前:瀏覽器前綴CSS3和正常的CSS3都不支持。
  2. 不久之前:瀏覽器只支持前綴CSS3,不支持正常CSS3。
  3. 如今:瀏覽器既支持前綴CSS3,又支持CSS3。
  4. 將來:瀏覽器不支持前綴CSS3,僅支持正常CSS3。

 注意: css中須要知道的是 - 若是屬性不可用,則不發揮任何做用,無影響;若是屬性是相同的做用,則後者會覆蓋前者。

      漸進加強的寫法,優先考慮老版本瀏覽器的可用性,最後才考慮新版本瀏覽器的可用性。

    而優雅降級的寫法,優先考慮新版本瀏覽器的可用性,最後才考慮瀏覽器的可用性。 

   就CSS3來講,咱們更加推薦漸進加強的寫法。  

6.promise、async有什麼區別(對async、await的理解,內部原理、介紹下Promise,內部實現)

理解 async/await

7.介紹AST(Abstract Syntax Tree)抽象語法樹

AST抽象語法樹

8.什麼是防抖和節流?有什麼區別?如何實現?

防抖

觸發高頻事件後n秒內函數只會執行一次,若是n秒內高頻事件再次被觸發,則從新計算時間

思路:

每次觸發事件時都取消以前的延時調用方法

function  debounce(fn) {
       let timeout =  null // 建立一個標記用來存放定時器的返回值
       return  function  () {
         clearTimeout(timeout);  // 每當用戶輸入的時候把前一個 setTimeout clear 掉
         timeout = setTimeout(() => {  // 而後又建立一個新的 setTimeout, 這樣就能保證輸入字符後的 interval 間隔內若是還有字符輸入的話,就不會執行 fn 函數
           fn.apply( this , arguments);
         }, 500);
       };
     }
     function  sayHi() {
       console.log( '防抖成功' );
     }
 
     var  inp = document.getElementById( 'inp' );
     inp.addEventListener( 'input' , debounce(sayHi));  // 防抖

 

節流

高頻事件觸發,但在n秒內只會執行一次,因此節流會稀釋函數的執行頻率

 思路: 

每次觸發事件時都判斷當前是否有等待執行的延時函數

function  throttle(fn) {
       let canRun =  true // 經過閉包保存一個標記
       return  function  () {
         if  (!canRun)  return // 在函數開頭判斷標記是否爲true,不爲true則return
         canRun =  false // 當即設置爲false
         setTimeout(() => {  // 將外部傳入的函數的執行放在setTimeout中
           fn.apply( this , arguments);
           // 最後在setTimeout執行完畢後再把標記設置爲true(關鍵)表示能夠執行下一次循環了。當定時器沒有執行的時候標記永遠是false,在開頭被return掉
           canRun =  true ;
         }, 500);
       };
     }
     function  sayHi(e) {
       console.log(e.target.innerWidth, e.target.innerHeight);
     }
     window.addEventListener( 'resize' , throttle(sayHi));

9.vue-router有哪幾種導航鉤子

  • 全局導航鉤子
  • router.beforeEach(to, from, next),
  • router.beforeResolve(to, from, next),
  • router.afterEach(to, from ,next)
  • 組件內鉤子
  • beforeRouteEnter,
  • beforeRouteUpdate,
  • beforeRouteLeave
  • 單獨路由獨享組件
  • beforeEnter

10.Vue的雙向數據綁定原理是什麼?

vue.js 是採用數據劫持結合發佈者-訂閱者模式的方式,經過 Object.defineProperty()來劫持各個屬性的 setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。

具體步驟:
第一步:須要 observe 的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter 和 getter 這樣的話,給這個對象的某個值賦值,就會觸發 setter,那麼就能監聽到了數據變化

第二步:compile 解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖

第三步:Watcher 訂閱者是 Observer 和 Compile 之間通訊的橋樑,主要作的事情是:

  • 在自身實例化時往屬性訂閱器(dep)裏面添加本身
  • 自身必須有一個 update()方法
  • 待屬性變更 dep.notice()通知時,能調用自身的 update() 方法,並觸發 Compile 中綁定的回調,則功成身退。

第四步:MVVM 做爲數據綁定的入口,整合 Observer、Compile 和 Watcher 三者,經過 Observer 來監聽本身的 model 數據變化,經過 Compile 來解析編譯模板指令,最終利用 Watcher 搭起 Observer 和 Compile 之間的通訊橋樑,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據 model 變動的雙向綁定效果。

11.請詳細說下你對vue生命週期的理解?

總共分爲 8 個階段建立前/後,載入前/後,更新前/後,銷燬前/後。

  • 建立前/後: 在 beforeCreate 階段,vue 實例的掛載元素 el 尚未。
  • 載入前/後:在 beforeMount 階段,vue 實例的$el 和 data 都初始化了,但仍是掛載以前爲虛擬的 dom 節點,data.message 還未替換。在 mounted 階段,vue 實例掛載完成,data.message 成功渲染。
  • 更新前/後:當 data 變化時,會觸發 beforeUpdate 和 updated 方法。
  • 銷燬前/後:在執行 destroy 方法後,對 data 的改變不會再觸發周期函數,說明此時 vue 實例已經解除了事件監聽以及和 dom 的綁定,可是 dom 結構依然存在

12.客戶端存儲

13.路由的history模式

14.flex佈局

15.原型鏈

16.ES6數組操做:some、every、find、filter、map、forEach有什麼區別

17.數組去重(ES5/ES6)

18.項目如何管理模塊

相關文章
相關標籤/搜索