筆試題

實現一個new的偽代碼

function _new(){
  let obj = new Object();
  let Con = [].shift.call(arguments);
  obj.__proto__ = Con.prototype;
  let result = Con.apply(obj,arguments);
  return typeof result === 'object' ? result : obj
}

深拷貝和淺拷貝

    // ----------------------------------------------淺拷貝
    // 只是把對象的屬性和屬性值拷貝到另一個對象中
    var obj1 = {
      a: {
        a1: { a2: 1 },
        a10: { a11: 123, a111: { a1111: 123123 } }
      },
      b: 123,
      c: "123"
    }
    // 方式1
    function shallowClone1(o) {
      let obj = {}

      for (let i in o) {
        obj[i] = o[i]
      }
      return obj
    }

    // 方式2
    var shallowObj2 = { ...obj1 }

    // 方式3
    var shallowObj3 = Object.assign({}, obj1)

    let shallowObj = shallowClone1(obj1);

    shallowObj.a.a1 = 999
    shallowObj.b = true

    console.log(obj1);  //第一層的沒有被改變,一層以下就被改變了



    // ----------------------------------------------深拷貝

    // 簡易版  
    function deepClone(o) {
      let obj = {}
      for (var i in o) {
        // if(o.hasOwnProperty(i)){
        if (typeof o[i] === "object") {
          obj[i] = deepClone(o[i])
        } else {
          obj[i] = o[i]
        }
        // }
      }
      return obj
    }


    var myObj = {
      a: {
        a1: { a2: 1 },
        a10: { a11: 123, a111: { a1111: 123123 } }
      },
      b: 123,
      c: "123"
    }

    var deepObj1 = deepClone(myObj)
    deepObj1.a.a1 = 999
    deepObj1.b = false
    console.log(myObj);



    // 簡易版存在的問題:參數沒有做檢驗,傳入的可能是 Array、null、regExp、Date
    function deepClone2(o) {
      if (Object.prototype.toString.call(o) === "[object Object]") {  //檢測是否為對象
        let obj = {}
        for (var i in o) {
          if (o.hasOwnProperty(i)) {
            if (typeof o[i] === "object") {
              obj[i] = deepClone(o[i])
            } else {
              obj[i] = o[i]
            }
          }
        }
        return obj
      } else {
        return o
      }
    }

    function isObject(o) {
      return Object.prototype.toString.call(o) === "[object Object]" || Object.prototype.toString.call(o) === "[object Array]"
    }

    // 繼續升級,沒有考慮到數組,以及ES6中的map、set、weakset、weakmap
    function deepClone3(o) {
      if (isObject(o)) {//檢測是否為對象或者數組
        let obj = Array.isArray(o) ? [] : {}
        for (let i in o) {
          if (isObject(o[i])) {
            obj[i] = deepClone(o[i])
          } else {
            obj[i] = o[i]
          }
        }
        return obj
      } else {
        return o
      }
    }


    // 有可能碰到循環引用問題  var a = {}; a.a = a; clone(a);//會造成一個死循環
    // 循環檢測
    // 繼續升級
    function deepClone4(o, hash = new map()) {
      if (!isObject(o)) return o//檢測是否為對象或者數組
      if (hash.has(o)) return hash.get(o)
      let obj = Array.isArray(o) ? [] : {}

      hash.set(o, obj)
      for (let i in o) {
        if (isObject(o[i])) {
          obj[i] = deepClone4(o[i], hash)
        } else {
          obj[i] = o[i]
        }
      }
      return obj
    }

    // 遞歸易出現爆棧問題
    //  將遞歸改為循環,就不會出現爆棧問題了
    var a1 = { a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' };
    var b1 = { b: { c: { d: 1 } } }
    function cloneLoop(x) {
      const root = {};
      // 棧 
      const loopList = [  //->[]->[{parent:{a:1,b:2},key:c,data:{ c1: 3, c2: { c21: 4, c22: 5 } }}]
        {
          parent: root,
          key: undefined,
          data: x,
        }
      ];
      while (loopList.length) {
        // 深度優先
        const node = loopList.pop();
        const parent = node.parent; //{} //{a:1,b:2}
        const key = node.key; //undefined //c
        const data = node.data; //{ a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' }  //{ c1: 3, c2: { c21: 4, c22: 5 } }}
        // 初始化賦值目標,key 為 undefined 則拷貝到父元素,否則拷貝到子元素
        let res = parent; //{}->{a:1,b:2,d:'asd'} //{a:1,b:2}->{}
        if (typeof key !== 'undefined') {
          res = parent[key] = {};
        }
        for (let k in data) {
          if (data.hasOwnProperty(k)) {
            if (typeof data[k] === 'object') {
              // 下一次循環 
              loopList.push({
                parent: res,
                key: k,
                data: data[k],
              })
            } else {
              res[k] = data[k];
            }
          }
        }
      }
      return root
    }


    function deepClone5(o) {
      let result = {}
      let loopList = [
        {
          parent: result,
          key: undefined,
          data: o
        }
      ]

      while (loopList.length) {
        let node = loopList.pop()
        let { parent, key, data } = node
        let anoPar = parent
        if (typeof key !== 'undefined') {
          anoPar = parent[key] = {}
        }

        for (let i in data) {
          if (typeof data[i] === 'object') {
            loopList.push({
              parent: anoPar,
              key: i,
              data: data[i]
            })
          } else {
            anoPar[i] = data[i]
          }
        }
      }
      return result
    }


    let cloneA1 = deepClone5(a1)
    cloneA1.c.c2.c22 = 5555555
    console.log(a1);
    console.log(cloneA1);


    // ------------------------------------------JSON.stringify()實現深拷貝

    function cloneJson(o) {
      return JSON.parse(JSON.stringify(o))
    }

    // let obj = { a: { c: 1 }, b: {} };
    // obj.b = obj;
    // console.log(JSON.parse(JSON.stringify(obj))) // 報錯 // Converting circular structure to JSON

    

防抖節流

    // ---------------------------------------------------------防抖函數
    function debounce(func, delay) {
      let timeout
      return function () {
        let arg = arguments
        if (timeout) clearTimeout(timeout)
        timeout = setTimeout(() => {
          func(arg)
        }, delay);
      }
    }

    // ---------------------------------------------------------立即執行防抖函數
    function debounce2(fn, delay) {
      let timer

      return function () {
        let args = arguments
        if (timer) clearTimeout(timer)


        let callNow = !timer
        timer = setTimeout(() => {
          timer = null
        }, delay);
        if (callNow) { fn(args) }
      }
    }
    // ---------------------------------------------------------立即執行防抖函數+普通防抖
    function debounce3(fn, delay, immediate) {
      let timer

      return function () {
        let args = arguments
        let _this = this
        if (timer) clearTimeout(timer)

        if (immediate) {
          let callNow = !timer
          timer = setTimeout(() => {
            timer = null
          }, delay);

          if (callNow) { fn.apply(_this, args) }
        } else {
          timeout = setTimeout(() => {
            func.apply(_this, arguments)
          }, delay);
        }
      }
    }

    // ---------------------------------------------------------節流 ,時間戳版

    function throttle(fn, wait) {

      let previous = 0
      return function () {
        let now = Date.now()
        let _this = this
        let args = arguments
        if (now - previous > wait) {
          fn.apply(_this, arguments)
          previous = now
        }
      }
    }

    // ---------------------------------------------------------節流 ,定時器版
    function throttle2(fn, wait) {
      let timer
      return function () {
        let _this = this
        let args = arguments
        if (!timer) {
          timer = setTimeout(() => {
            timer = null
            fn.apply(_this, arguments)
          }, wait);
        }
      }
    }

call

    Function.prototype.myCall = function (context) {
      // 先判斷調用myCall是不是一個函數
      // 這里的this就是調用myCall的
      if (typeof this !== 'function') {
        throw new TypeError("Not a Function")
      }

      // 不傳參數默認為window
      context = context || window

      // 保存this
      context.fn = this

      // 保存參數
      let args = Array.from(arguments).slice(1)   //Array.from 把偽數組對象轉為數組

      // 調用函數
      let result = context.fn(...args)

      delete context.fn

      return result

    }

apply

Function.prototype.myApply = function (context) {
      // 判斷this是不是函數
      if (typeof this !== "function") {
        throw new TypeError("Not a Function")
      }

      let result

      // 默認是window
      context = context || window

      // 保存this
      context.fn = this

      // 是否傳參
      if (arguments[1]) {
        result = context.fn(...arguments[1])
      } else {
        result = context.fn()
      }
      delete context.fn

      return result
    }

bind

    Function.prototype.myBind = function(context){
      // 判斷是否是一個函數
      if(typeof this !== "function") {
        throw new TypeError("Not a Function")
      }
      // 保存調用bind的函數
      const _this = this 
      // 保存參數
      const args = Array.prototype.slice.call(arguments,1)
      // 返回一個函數
      return function F () {
        // 判斷是不是new出來的
        if(this instanceof F) {
          // 如果是new出來的
          // 返回一個空對象,且使創建出來的實例的__proto__指向_this的prototype,且完成函數柯里化
          return new _this(...args,...arguments)
        }else{
          // 如果不是new出來的改變this指向,且完成函數柯里化
          return _this.apply(context,args.concat(...arguments))
        }
      } 
    }

原型繼承

var person = {
      friends: ["a", "b", "c", "d"]
    }

    var p1 = Object.create(person)

    p1.friends.push("aaa")//缺點:子類的實例共享了父類構造函數的引用屬性

    console.log(p1);
    console.log(person);//缺點:子類的實例共享了父類構造函數的引用屬性

組合繼承

function Father(name) {
      this.name = name
      this.hobby = ["籃球", "足球", "乒乓球"]
    }

    Father.prototype.getName = function () {
      console.log(this.name);
    }

    function Son(name, age) {
      Father.call(this, name)
      this.age = age
    }

    Son.prototype = new Father()
    Son.prototype.constructor = Son


    var s = new Son("ming", 20)

寄生組合繼承

    function Father(name) {
      this.name = name
      this.hobby = ["籃球", "足球", "乒乓球"]
    }

    Father.prototype.getName = function () {
      console.log(this.name);
    }

    function Son(name, age) {
      Father.call(this, name)
      this.age = age
    }

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

    var s2 = new Son("ming", 18)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容