前端面試之JS

三石流浪記發表於2019-03-06

es5原型繼承的例子

// 組合繼承
function Father(value){
 this.val = value
}
Father.prototype.getVal = function(){
    return this.val
}

function Son(value){
    Father.apply(this,arguments)
}

Son.prototype = new Father() 
var son = new Son(1234)

console.log(son)

// 寄生組合繼承
function Father(value){
    this.val = value
}
Father.prototype.getVal = function(){
    console.log(this.val)
}

function Son(){
    Father.apply(this,arguments)
}

Son.prototype = Object.create(Father.prototype)
Son.prototype.constructor = Son

// Son.prototype = Object.create(Parent.prototype,{
//     constructor:{
//         value:Son,
//         enumerable:false,
//         writeable:true,
//         configurable:true
//     }
// })

//es6的寫法
    class Person {
        constructor(value) {
            this.val = value
        }

        get() {
            console.log(this.val)
        }
    }


    class son extends Person {
        constructor(value,name) {
            super(value)
            this.name = name
        }
    }
複製程式碼

手寫promise

    const PENDING = 'pedeing'
    const RESOLVED = 'resolved'
    const REJECTED = 'rejected'

    function myPromise(fn) {
        const that = this
        that.state = PENDING
        that.value = null
        that.reslovedCallbacks = []
        that.rejectedCallbacks = []
        function resolve(value) {
            if (that.state === PENDING) {
                that.state = RESOLVED
                that.value = value
                //  完整then裡面的第一個回撥
                that.reslovedCallbacks.map(thenResolve => thenResolve(that.value))
            }
        }
        function reject(value) {
            if (that.state === PENDING) {
                that.state = REJECTED
                that.value = value
                // 完整then裡面的第一個回撥
                that.rejectedCallbacks.map(thenReject => thenReject(that.value))
            }
        }
        fn(resolve, reject)
    }

    myPromise.prototype.then = function(onFullfilled, onRejected){
        const that = this
        onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : v => v
        onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r }
        if(that.state === PENDING){
            that.reslovedCallbacks.push(onFullfilled)
            that.rejectedCallbacks.push(onRejected)
        }
        if(that.state === RESOLVED){
            onFullfilled(that.value)
        }
        if(that.state === REJECTED){
            onRejected(that.value)
        }
    }
複製程式碼

原生XHR

   var xhr = new XMLHttpRequest()
    xhr.open('GET','api',false)
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
          if(xhr.status === 200){

          }
      }
    }
    xhr.send(null)
複製程式碼

原生實現事件代理

function bindEvent(elem, type, selecter, fn) {
    if (!fn) {
      fn = selecter
      selecter = null
    }
    elem.addEventListener(type, (e) => {
      var target
      if (selecter) {
        target = e.target
        if (target.matches(selecter)) {
          fn.call(target, e)
        }
      } else {
        fn(e)
      }
    })
  }
複製程式碼

節流和防抖

1、防抖就是控制函式在高頻觸發的狀態下,在最後一次事件觸發後延遲一定時間執行

var debounce = function(time , func){
    let lastTimer = 0
    return function(...args){
        clearTimeout(lastTimer)
        lastTimer = setTimeout(()=>{
            func.apply(this,args)
        },time)
    }
}
複製程式碼

2、節流就是在高頻請求中,將請求控制在時間範圍之外,以減少請求次數

var throttle = function(func, time){
    let  lastTime = 0
    return function(...args){
        let  now = +new Date()
        if(now-lastTime > time){
            lastTime = now
            func.apply(this,args)
        }
    }
}
複製程式碼

手動實現前端路由

function Router(){
  this.currentUrl='';
  this.routes={};
}
Router.prototype.route = function(path,callback){
  this.routes[path] = callback || function(){}
}
Router.prototype.refresh = function(){
  this.currentUrl = location.hash.slice(1) || '/';
  this.routes[this.currentUrl]();
}
Router.prototype.init = function(){
  window.addEventListener('load',this.refresh.bind(this),false);
  window.addEventListener('hashchange',this.refresh.bind(this),false);
  // console.log(location.hash)
  // if(location.hash.slice(1)!=='/')
  //     location.hash = '#/';
}


 // hash  hashChage
// history  pushState replaceState
複製程式碼

vue實現資料響應式的原理

vue在2.0時代使用object.definedProperty()實現資料的響應,通過此函式監聽get() 和 set() 事件,3.0時代將採用proxy實現,一下是程式碼

function observe(obj){
    // 邊界
    if(!obj || typeof obj !== 'object')
    return
    
    Object.keys(obj).forEach(key => {
        definedReacted(obj,key,obj[key])
    })
}

function definedReacted(obj,key,value){
    observe(value)
    Object.definedProperty(obj,key,{
        // 可列舉
        enumerabeke:true,
        //可配置
        configurable:true,
        get:()=>{
            console.log(value)
            return value
        },
        set:(newValue)=>{
            console.log(newValue)
            val = newValue
        }
    })
}


function observer(obj){

    // 遞迴邊界
    if(!obj || typeof obj !== 'object'){
        return
    }

    Object.keys(obj).forEach((item,index,arr)=>{
        defineReactive(obj,item,obj[item])
        observer(obj[item])
    })

}


function defineReactive(data,key,val){
    Object.defineProperty(data,key ,{
        enumerable:true,
        configurale:true,
        get:function(){


            // 需要在這裡新增訂閱者
            return val
        },
        set:function(newValue){
            val = newValue
            console.log('屬性'+key+'被監聽了'+'現在的值為'+newValue)

            // 在這裡通知訂閱者去更新檢視
        }
    })
}


var library = {
    book1: {
        name: ''
    },
    book2: ''
};

observer(library)

library.book1.name = 'vue權威指南'; // 屬性name已經被監聽了,現在值為:“vue權威指南”
library.book2 = '沒有此書籍';

複製程式碼

兩個數不使用四則運算得出和

function sum(a, b) {
    if (a == 0) return b
    if (b == 0) return a
    let newA = a ^ b   // 獲得個數值
    let newB = (a & b) << 1 // 獲得進位的數值
    return sum(newA, newB)  // 最後執行自己 
}
複製程式碼

氣泡排序

前端面試之JS

    function isArray(array) {
        if (Object.prototype.toString.call(array) !== '[object Array]') {
            return array
        }
    }

    function swap(array, left,right) {
        var temp = array[left]
        array[left] = array[right]
        array[right] = temp
    }

    function bubble(arr) {
        isArray(arr)
        for (let i = arr.length - 1; i > 0; i--) {
            for (let j = 0; j < i; j++) {
                if (arr[j] > arr[j + 1]) swap(arr,j,j+1)
            }
        }
        return arr
    }

複製程式碼

插入排序

function insert(array){
    isArray(array)
    for(let i = 0 ;i<array.length-1;i++){
        for(let j=i;j>=0&&array[j]>array[j+1];j--){
            swap(array,j,j+1)
        }
    }
    return array
}

複製程式碼

選擇排序

const choose = (arr) => {
    isArray(arr)
    for (let i = 0; i < arr.length - 1; i++) {
       let minIndex = i
        for (let j = i + 1; j < arr.length; j++) {
            minIndex = arr[j] < arr[minIndex] ? j : minIndex
        }
        if (i !== minIndex) {
            swap(arr, i, minIndex)
        }
    }
    return arr
}
複製程式碼

快速排序

// 阮一峰版本
function quicksort(arr){
    isArray(arr)
    if(arr.length<=1) return arr
    // const pivotIndex = Math.floor(Math.random()*arr.length)
    const pivotIndex = Math.floor(arr.length/2)
    const pivot = arr.splice(pivotIndex,1)[0]
    console.log(pivot,arr)
    const left = []
    const right = []
    for(let i = 0; i<arr.length;i++){
        if(arr[i]>pivot){
            right.push(arr[i])
        }else{
            left.push(arr[i])
        }
    }
    return quicksort(left).concat([pivot],quicksort(right))
}

console.log(quicksort([2,4,1,88,2,5,90,564,32]))
複製程式碼

手寫call apply bind

let a = {
    name: 'leolei',
    fn: function (a, b) {
        console.log(this.name + a + b)
    }
}

const b = {
    name: 'cuisiyao'
}

Function.prototype.test = function (){
    // 檢視在prototype上新增方法 在呼叫時this是什麼
    console.log(this === a.fun.__proto__)
}

Function.prototype.Mycall = function () {
    if (typeof this === 'Function') {
        throw new TypeError('Error')
    }

    // 處理沒有引數的預設情況
    const context = arguments[0] || window
    console.log(this)
    context.fn = this
    const args = [...arguments].slice(1)
    console.log(args)
    const results = context.fn(...args)
    delete context.fn
    return results
}

Function.prototype.Myapply = function () {
    if (typeof this === 'Function') {
        throw new TypeError('Error')
    }
    const context = arguments[0] || window
    console.log(this)
    context.fn = this
    const args = arguments[1]
    console.log(args)
    const results = context.fn(...args)
    delete context.fn
    return results
}

Function.prototype.Mybind = function () {
    const context = arguments[0] || window
    const args1 = [...arguments].slice(1)
    const _this = this
    return function F(...args) {
        // 處理new function 的情況
        if (this instanceof F) {
            return new _this(...args, ...args1)
        }
        return _this.Myapply(context, args.concat(args1))
    }
}

a.fn.Mycall(b, 123, 456)
a.fn.Myapply(b, [123, 456])

// 當方法執行
a.fn.Mybind(b, 123, 456)()
a.fn.Mybind(b)(123, 456)

a = {
    name: 'leolei',
    fn: function (a, b) {
        console.log(a + b)
    }
}
// 使用new執行
new a.fn(123, 456)
const test = new (a.fn.Mybind(b))(123, 456)
複製程式碼

講述一個new的過程

  • 先建立一個空的obj
  • 然後將需要構造的函式的繫結到空物件的obj原型原型上
  • 在新建空物件的環境下執行建構函式,繫結this為這個空物件
  • 返回this,並確保this為Object
function creat(){
    const obj = {}
    const con = [].shift.call(arguments)
    obj.__proto__ = con
    const results = con.aplly(obj,arguments)
    return results instanceof Object ? results : obj
}

複製程式碼

常見的瀏覽器安全漏洞

  • XSS攻擊,分為兩種。
    • 一種是持久行的,持久型的XSS攻擊主要是通過可執行的網頁程式碼,將資料寫到後臺資料庫中,比如評論時寫入<script>aler(1)</script>,將此評論注入到資料庫中,如果不做處理,頁面在訪問時會受到攻擊。
    • 第二種非持久型的,主要通過導航欄工具http://www.domain.com?name=<script>alert(1)</script>. 解決方案:1. 一般採用轉義字元來解決,比如轉義<&lt
  • CSRP 跨站偽造請求。
    • 當使用者登入A頁面時,已登入網站,未關閉,此時存在了cookie。使用者在此時開啟了另外一個攻擊網站B,B盜取A的cookie後對A頁面的後臺進行請求,並攻擊。
    • 解決方案:在http頭中設定SameSite,讓cookie不隨跨域請求傳送。驗證refrer欄位 判斷 請求來源,禁止第三方請求傳送。使用token通過伺服器驗證使用者登入是否有效。
  • 點選劫持。解決方案,在響應頭設定X—FRSME—OPTIONS。
  • 中間人攻擊 容易出現在公用產所的 wifi,解決方案就是使用https協議。

vue路由守衛

全域性守衛

  • beforeEnter(to,from,next)
  • afterEnter(....)
  • beforeResolve

路由獨享守衛

  • beforeEnter

元件內的守衛

  • beforeRouteEnter
  • beforeRouteUpdata
  • beforeRouteLeave

完整的解析過程

  • 導航被觸發
  • 上一個導航的元件裡呼叫beaforeRouteLeave
  • 啟用全域性beforeEnter
  • 如果是重用元件,那就會呼叫beforeRouteUpdata
  • 然後進入路由配置的beforEnter
  • 解析非同步路由元件
  • 呼叫全域性的beforeResolve
  • 導航被確認
  • 呼叫全域性的afterEnter
  • 觸發更新dom
  • 用建立好的例項呼叫beforeRouteEnter,並把元件例項作為使用next的回撥

設計模式

  • 工廠模式 隱藏物件建立的過程
  • 單例模式 js可以藉助閉包的方式完成
  • 介面卡模式 在不對原有介面做任何改動的情況下,包裝一層新的介面,返回最終資料
  • 代理模式 proxy 在vue3.0中正在使用這個特性實現資料的雙向繫結,現在是使用Object.definedProperty實現的,
  • 觀察者模式
  • 釋出-訂閱者模式,這兩種模式看似相同其實不同 觀察者模式是觀察者直接通知物件,雙方知道對方的存在,釋出-訂閱者模式,雙方不知道對方存在,使用中間物件對釋出的資訊進行刪選然後通知訂閱者。
  • 裝飾模式 在不改變以後介面,對方法進行包裝。
  • 外觀模式,比如說寫一個原生函式建立個瀏覽器環境下的XHR物件

vue生命週期

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeupdate
  • updated
  • beforeDestroy
  • disrtoryed

對於使用keep-alive的元件 有獨有的deactived和actived 生命週期,在切換元件時此元件不會被銷燬,而是快取到記憶體並執行deactived ,命中快取之後會呼叫actived

js中的原始型別

  • boolean
  • null
  • undfined
  • Number
  • String
  • Symbol

typeof有幾種結果

  • undefined
  • boolean
  • Object
  • Number
  • String
  • Function

JS中有哪些內建函式

上面的這幾種加上 Array Regexp Date Error

移動端首屏優化

  • 採用伺服器渲染ssr
  • 按需載入配合webpack分塊打包
  • 很有必要將script標籤➕非同步
  • 有輪播圖 最好給個預設 另外要處理圖片懶載入
  • 打包線上也要注意去掉map 檔案
  • 元件懶載入
  • 路由懶載入
  • webpack的一切配置 肯定是必須的 這個百度去 做到js css 以及依賴庫分離
  • 強烈建議不要在應用依賴裡面去下載jQuery庫和一些Ui庫
  • 壓縮圖片 https://tinypng.com/
  • 建議還是用webpack的圖片壓縮外掛
  • 使用pagespeed看看有哪些可優化的選項

最優二叉樹

class Node {
    constructor(value) {
        this.value = value
        this.left = null
        this.right = null
    }
}

class BST {
    constructor() {
        this.root = null
        this.size = 0
    }

    getSize() {
        return this.size
    }

    isEmpty() {
        return this.size === 0
    }

    addNode(value) {
        this.root = this._addchild(this.root, value)
    }

    _addchild(node, value) {
        if (!node) {
            this.size++
            return new Node(value)
        }

        if (node.value > value) {
            node.left = this._addchild(node.left, value)
        } else{
            node.right = this._addchild(node.right, value)
        }

        return node
    }
}
複製程式碼

class Stack{
    constructor(){
        this.stack = []
    }
    push(val){
        this.stack.unshift(val)
    }
    pop(){
        this.stack.shift()
    }
    peek(){
        return this.stack[this.getCount()-1]
    }
    getCount(){
        return this.stack.length
    }
}
// 這裡其實可以使用陣列直接模擬棧 佇列其實也是一樣的

// 檢測下列字串是否是迴文字串
var str = 'leel'

function isValied(str){
    var strArr = str.split('')

    var stack = []
    for(let i =0;i<strArr.length;i++){
    
        if(stack.indexOf(strArr[i]) === -1){
            stack.push(strArr[i])
        }else{
            if(stack[stack.length-1] === strArr[i]){
                stack.pop()
            }
        }
    }
    
    if(stack.length !== 0 ){
        return false
    }else{
        return true
    }
}

isValied(str)
複製程式碼

相關文章