百度前端一面??际謱?xiě)面試題及答案

實(shí)現(xiàn)千位分隔符

// 保留三位小數(shù)
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'


function parseToMoney(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.');
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
  return integer + '.' + (decimal ? decimal : '');
}


正則表達(dá)式(運(yùn)用了正則的前向聲明和反前向聲明):

function parseToMoney(str){
    // 僅僅對(duì)位置進(jìn)行匹配
    let re = /(?=(?!\b)(\d{3})+$)/g; 
   return str.replace(re,','); 
}

將js對(duì)象轉(zhuǎn)化為樹(shù)形結(jié)構(gòu)

// 轉(zhuǎn)換前:
source = [{
            id: 1,
            pid: 0,
            name: 'body'
          }, {
            id: 2,
            pid: 1,
            name: 'title'
          }, {
            id: 3,
            pid: 2,
            name: 'div'
          }]
// 轉(zhuǎn)換為: 
tree = [{
          id: 1,
          pid: 0,
          name: 'body',
          children: [{
            id: 2,
            pid: 1,
            name: 'title',
            children: [{
              id: 3,
              pid: 1,
              name: 'div'
            }]
          }
        }]

代碼實(shí)現(xiàn):

function jsonToTree(data) {
  // 初始化結(jié)果數(shù)組,并判斷輸入數(shù)據(jù)的格式
  let result = []
  if(!Array.isArray(data)) {
    return result
  }
  // 使用map,將當(dāng)前對(duì)象的id與當(dāng)前對(duì)象對(duì)應(yīng)存儲(chǔ)起來(lái)
  let map = {};
  data.forEach(item => {
    map[item.id] = item;
  });
  // 
  data.forEach(item => {
    let parent = map[item.pid];
    if(parent) {
      (parent.children || (parent.children = [])).push(item);
    } else {
      result.push(item);
    }
  });
  return result;
}

前端手寫(xiě)面試題詳細(xì)解答

循環(huán)打印紅黃綠

下面來(lái)看一道比較典型的問(wèn)題,通過(guò)這個(gè)問(wèn)題來(lái)對(duì)比幾種異步編程方法:紅燈 3s 亮一次,綠燈 1s 亮一次,黃燈 2s 亮一次;如何讓三個(gè)燈不斷交替重復(fù)亮燈?

三個(gè)亮燈函數(shù):

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

這道題復(fù)雜的地方在于需要“交替重復(fù)”亮燈,而不是“亮完一次”就結(jié)束了。

(1)用 callback 實(shí)現(xiàn)

const task = (timer, light, callback) => {
    setTimeout(() => {
        if (light === 'red') {
            red()
        }
        else if (light === 'green') {
            green()
        }
        else if (light === 'yellow') {
            yellow()
        }
        callback()
    }, timer)
}
task(3000, 'red', () => {
    task(2000, 'green', () => {
        task(1000, 'yellow', Function.prototype)
    })
})

這里存在一個(gè) bug:代碼只是完成了一次流程,執(zhí)行后紅黃綠燈分別只亮一次。該如何讓它交替重復(fù)進(jìn)行呢?

上面提到過(guò)遞歸,可以遞歸亮燈的一個(gè)周期:

const step = () => {
    task(3000, 'red', () => {
        task(2000, 'green', () => {
            task(1000, 'yellow', step)
        })
    })
}
step()

注意看黃燈亮的回調(diào)里又再次調(diào)用了 step 方法 以完成循環(huán)亮燈。

(2)用 promise 實(shí)現(xiàn)

const task = (timer, light) => 
    new Promise((resolve, reject) => {
        setTimeout(() => {
            if (light === 'red') {
                red()
            }
            else if (light === 'green') {
                green()
            }
            else if (light === 'yellow') {
                yellow()
            }
            resolve()
        }, timer)
    })
const step = () => {
    task(3000, 'red')
        .then(() => task(2000, 'green'))
        .then(() => task(2100, 'yellow'))
        .then(step)
}
step()

這里將回調(diào)移除,在一次亮燈結(jié)束后,resolve 當(dāng)前 promise,并依然使用遞歸進(jìn)行。

(3)用 async/await 實(shí)現(xiàn)

const taskRunner =  async () => {
    await task(3000, 'red')
    await task(2000, 'green')
    await task(2100, 'yellow')
    taskRunner()
}
taskRunner()

實(shí)現(xiàn)一個(gè)call

call做了什么:

  • 將函數(shù)設(shè)為對(duì)象的屬性
  • 執(zhí)行&刪除這個(gè)函數(shù)
  • 指定this到函數(shù)并傳入給定參數(shù)執(zhí)行函數(shù)
  • 如果不傳入?yún)?shù),默認(rèn)指向?yàn)?window
// 模擬 call bar.mycall(null);
//實(shí)現(xiàn)一個(gè)call方法:
Function.prototype.myCall = function(context) {
  //此處沒(méi)有考慮context非object情況
  context.fn = this;
  let args = [];
  for (let i = 1, len = arguments.length; i < len; i++) {
    args.push(arguments[i]);
  }
  context.fn(...args);
  let result = context.fn(...args);
  delete context.fn;
  return result;
};

手寫(xiě)類(lèi)型判斷函數(shù)

function getType(value) {
  // 判斷數(shù)據(jù)是 null 的情況
  if (value === null) {
    return value + "";
  }
  // 判斷數(shù)據(jù)是引用類(lèi)型的情況
  if (typeof value === "object") {
    let valueClass = Object.prototype.toString.call(value),
      type = valueClass.split(" ")[1].split("");
    type.pop();
    return type.join("").toLowerCase();
  } else {
    // 判斷數(shù)據(jù)是基本數(shù)據(jù)類(lèi)型的情況和函數(shù)的情況
    return typeof value;
  }
}

實(shí)現(xiàn)JSON.parse

var json = '{"name":"cxk", "age":25}';
var obj = eval("(" + json + ")");

此方法屬于黑魔法,極易容易被xss攻擊,還有一種new Function大同小異。

判斷對(duì)象是否存在循環(huán)引用

循環(huán)引用對(duì)象本來(lái)沒(méi)有什么問(wèn)題,但是序列化的時(shí)候就會(huì)發(fā)生問(wèn)題,比如調(diào)用JSON.stringify()對(duì)該類(lèi)對(duì)象進(jìn)行序列化,就會(huì)報(bào)錯(cuò): Converting circular structure to JSON.

下面方法可以用來(lái)判斷一個(gè)對(duì)象中是否已存在循環(huán)引用:

const isCycleObject = (obj,parent) => {
    const parentArr = parent || [obj];
    for(let i in obj) {
        if(typeof obj[i] === 'object') {
            let flag = false;
            parentArr.forEach((pObj) => {
                if(pObj === obj[i]){
                    flag = true;
                }
            })
            if(flag) return true;
            flag = isCycleObject(obj[i],[...parentArr,obj[i]]);
            if(flag) return true;
        }
    }
    return false;
}


const a = 1;
const b = {a};
const c = ;
const o = {d:{a:3},c}
o.c.b.aa = a;

console.log(isCycleObject(o)

查找有序二維數(shù)組的目標(biāo)值:

var findNumberIn2DArray = function(matrix, target) {
    if (matrix == null || matrix.length == 0) {
        return false;
    }
    let row = 0;
    let column = matrix[0].length - 1;
    while (row < matrix.length && column >= 0) {
        if (matrix[row][column] == target) {
            return true;
        } else if (matrix[row][column] > target) {
            column--;
        } else {
            row++;
        }
    }
    return false;
};


二維數(shù)組斜向打?。?/p>

function printMatrix(arr){
  let m = arr.length, n = arr[0].length
    let res = []

  // 左上角,從0 到 n - 1 列進(jìn)行打印
  for (let k = 0; k < n; k++) {
    for (let i = 0, j = k; i < m && j >= 0; i++, j--) {
      res.push(arr[i][j]);
    }
  }

  // 右下角,從1 到 n - 1 行進(jìn)行打印
  for (let k = 1; k < m; k++) {
    for (let i = k, j = n - 1; i < m && j >= 0; i++, j--) {
      res.push(arr[i][j]);
    }
  }
  return res
}

模板引擎實(shí)現(xiàn)

let template = '我是{{name}},年齡{{age}},性別{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年齡18,性別undefined


function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // 模板字符串正則
  if (reg.test(template)) { // 判斷模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找當(dāng)前模板里第一個(gè)模板字符串的字段
    template = template.replace(reg, data[name]); // 將第一個(gè)模板字符串渲染
    return render(template, data); // 遞歸的渲染并返回渲染后的結(jié)構(gòu)
  }
  return template; // 如果模板沒(méi)有模板字符串直接返回
}


函數(shù)珂里化

指的是將一個(gè)接受多個(gè)參數(shù)的函數(shù) 變?yōu)?接受一個(gè)參數(shù)返回一個(gè)函數(shù)的固定形式,這樣便于再次調(diào)用,例如f(1)(2)

經(jīng)典面試題:實(shí)現(xiàn)add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;

function add() {
  const _args = [...arguments];
  function fn() {
    _args.push(...arguments);
    return fn;
  }
  fn.toString = function() {
    return _args.reduce((sum, cur) => sum + cur);
  }
  return fn;
}

實(shí)現(xiàn)深拷貝

  • 淺拷貝: 淺拷貝指的是將一個(gè)對(duì)象的屬性值復(fù)制到另一個(gè)對(duì)象,如果有的屬性的值為引用類(lèi)型的話(huà),那么會(huì)將這個(gè)引用的地址復(fù)制給對(duì)象,因此兩個(gè)對(duì)象會(huì)有同一個(gè)引用類(lèi)型的引用。淺拷貝可以使用 Object.assign 和展開(kāi)運(yùn)算符來(lái)實(shí)現(xiàn)。
  • 深拷貝: 深拷貝相對(duì)淺拷貝而言,如果遇到屬性值為引用類(lèi)型的時(shí)候,它新建一個(gè)引用類(lèi)型并將對(duì)應(yīng)的值復(fù)制給它,因此對(duì)象獲得的一個(gè)新的引用類(lèi)型而不是一個(gè)原有類(lèi)型的引用。深拷貝對(duì)于一些對(duì)象可以使用 JSON 的兩個(gè)函數(shù)來(lái)實(shí)現(xiàn),但是由于 JSON 的對(duì)象格式比 js 的對(duì)象格式更加嚴(yán)格,所以如果屬性值里邊出現(xiàn)函數(shù)或者 Symbol 類(lèi)型的值時(shí),會(huì)轉(zhuǎn)換失敗

(1)JSON.stringify()

  • JSON.parse(JSON.stringify(obj))是目前比較常用的深拷貝方法之一,它的原理就是利用JSON.stringifyjs對(duì)象序列化(JSON字符串),再使用JSON.parse來(lái)反序列化(還原)js對(duì)象。
  • 這個(gè)方法可以簡(jiǎn)單粗暴的實(shí)現(xiàn)深拷貝,但是還存在問(wèn)題,拷貝的對(duì)象中如果有函數(shù),undefined,symbol,當(dāng)使用過(guò)JSON.stringify()進(jìn)行處理之后,都會(huì)消失。
let obj1 = {  a: 0,
              b: {
                 c: 0
                 }
            };
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}

(2)函數(shù)庫(kù)lodash的_.cloneDeep方法

該函數(shù)庫(kù)也有提供_.cloneDeep用來(lái)做 Deep Copy

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

(3)手寫(xiě)實(shí)現(xiàn)深拷貝函數(shù)

// 深拷貝的實(shí)現(xiàn)
function deepCopy(object) {
  if (!object || typeof object !== "object") return;

  let newObject = Array.isArray(object) ? [] : {};

  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] =
        typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }

  return newObject;
}

實(shí)現(xiàn)數(shù)組的flat方法

function _flat(arr, depth) {
  if(!Array.isArray(arr) || depth <= 0) {
    return arr;
  }
  return arr.reduce((prev, cur) => {
    if (Array.isArray(cur)) {
      return prev.concat(_flat(cur, depth - 1))
    } else {
      return prev.concat(cur);
    }
  }, []);
}

Object.assign

Object.assign()方法用于將所有可枚舉屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。它將返回目標(biāo)對(duì)象(請(qǐng)注意這個(gè)操作是淺拷貝)

Object.defineProperty(Object, 'assign', {
  value: function(target, ...args) {
    if (target == null) {
      return new TypeError('Cannot convert undefined or null to object');
    }

    // 目標(biāo)對(duì)象需要統(tǒng)一是引用數(shù)據(jù)類(lèi)型,若不是會(huì)自動(dòng)轉(zhuǎn)換
    const to = Object(target);

    for (let i = 0; i < args.length; i++) {
      // 每一個(gè)源對(duì)象
      const nextSource = args[i];
      if (nextSource !== null) {
        // 使用for...in和hasOwnProperty雙重判斷,確保只拿到本身的屬性、方法(不包含繼承的)
        for (const nextKey in nextSource) {
          if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
    }
    return to;
  },
  // 不可枚舉
  enumerable: false,
  writable: true,
  configurable: true,
})

驗(yàn)證是否是郵箱

function isEmail(email) {
    var regx = /^([a-zA-Z0-9_\-])+@([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/;
    return regx.test(email);
}


手寫(xiě) new 操作符

在調(diào)用 new 的過(guò)程中會(huì)發(fā)生以上四件事情:

(1)首先創(chuàng)建了一個(gè)新的空對(duì)象

(2)設(shè)置原型,將對(duì)象的原型設(shè)置為函數(shù)的 prototype 對(duì)象。

(3)讓函數(shù)的 this 指向這個(gè)對(duì)象,執(zhí)行構(gòu)造函數(shù)的代碼(為這個(gè)新對(duì)象添加屬性)

(4)判斷函數(shù)的返回值類(lèi)型,如果是值類(lèi)型,返回創(chuàng)建的對(duì)象。如果是引用類(lèi)型,就返回這個(gè)引用類(lèi)型的對(duì)象。

function objectFactory() {
  let newObject = null;
  let constructor = Array.prototype.shift.call(arguments);
  let result = null;
  // 判斷參數(shù)是否是一個(gè)函數(shù)
  if (typeof constructor !== "function") {
    console.error("type error");
    return;
  }
  // 新建一個(gè)空對(duì)象,對(duì)象的原型為構(gòu)造函數(shù)的 prototype 對(duì)象
  newObject = Object.create(constructor.prototype);
  // 將 this 指向新建對(duì)象,并執(zhí)行函數(shù)
  result = constructor.apply(newObject, arguments);
  // 判斷返回對(duì)象
  let flag = result && (typeof result === "object" || typeof result === "function");
  // 判斷返回結(jié)果
  return flag ? result : newObject;
}
// 使用方法
objectFactory(構(gòu)造函數(shù), 初始化參數(shù));

實(shí)現(xiàn)AJAX請(qǐng)求

AJAX是 Asynchronous JavaScript and XML 的縮寫(xiě),指的是通過(guò) JavaScript 的 異步通信,從服務(wù)器獲取 XML 文檔從中提取數(shù)據(jù),再更新當(dāng)前網(wǎng)頁(yè)的對(duì)應(yīng)部分,而不用刷新整個(gè)網(wǎng)頁(yè)。

創(chuàng)建AJAX請(qǐng)求的步驟:

  • 創(chuàng)建一個(gè) XMLHttpRequest 對(duì)象。
  • 在這個(gè)對(duì)象上使用 open 方法創(chuàng)建一個(gè) HTTP 請(qǐng)求,open 方法所需要的參數(shù)是請(qǐng)求的方法、請(qǐng)求的地址、是否異步和用戶(hù)的認(rèn)證信息。
  • 在發(fā)起請(qǐng)求前,可以為這個(gè)對(duì)象添加一些信息和監(jiān)聽(tīng)函數(shù)。比如說(shuō)可以通過(guò) setRequestHeader 方法來(lái)為請(qǐng)求添加頭信息。還可以為這個(gè)對(duì)象添加一個(gè)狀態(tài)監(jiān)聽(tīng)函數(shù)。一個(gè) XMLHttpRequest 對(duì)象一共有 5 個(gè)狀態(tài),當(dāng)它的狀態(tài)變化時(shí)會(huì)觸發(fā)onreadystatechange 事件,可以通過(guò)設(shè)置監(jiān)聽(tīng)函數(shù),來(lái)處理請(qǐng)求成功后的結(jié)果。當(dāng)對(duì)象的 readyState 變?yōu)?4 的時(shí)候,代表服務(wù)器返回的數(shù)據(jù)接收完成,這個(gè)時(shí)候可以通過(guò)判斷請(qǐng)求的狀態(tài),如果狀態(tài)是 2xx 或者 304 的話(huà)則代表返回正常。這個(gè)時(shí)候就可以通過(guò) response 中的數(shù)據(jù)來(lái)對(duì)頁(yè)面進(jìn)行更新了。
  • 當(dāng)對(duì)象的屬性和監(jiān)聽(tīng)函數(shù)設(shè)置完成后,最后調(diào)用 sent 方法來(lái)向服務(wù)器發(fā)起請(qǐng)求,可以傳入?yún)?shù)作為發(fā)送的數(shù)據(jù)體。
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 創(chuàng)建 Http 請(qǐng)求
xhr.open("GET", SERVER_URL, true);
// 設(shè)置狀態(tài)監(jiān)聽(tīng)函數(shù)
xhr.onreadystatechange = function() {
  if (this.readyState !== 4) return;
  // 當(dāng)請(qǐng)求成功時(shí)
  if (this.status === 200) {
    handle(this.response);
  } else {
    console.error(this.statusText);
  }
};
// 設(shè)置請(qǐng)求失敗時(shí)的監(jiān)聽(tīng)函數(shù)
xhr.onerror = function() {
  console.error(this.statusText);
};
// 設(shè)置請(qǐng)求頭信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 發(fā)送 Http 請(qǐng)求
xhr.send(null);

使用Promise封裝AJAX請(qǐng)求

// promise 封裝實(shí)現(xiàn):
function getJSON(url) {
  // 創(chuàng)建一個(gè) promise 對(duì)象
  let promise = new Promise(function(resolve, reject) {
    let xhr = new XMLHttpRequest();
    // 新建一個(gè) http 請(qǐng)求
    xhr.open("GET", url, true);
    // 設(shè)置狀態(tài)的監(jiān)聽(tīng)函數(shù)
    xhr.onreadystatechange = function() {
      if (this.readyState !== 4) return;
      // 當(dāng)請(qǐng)求成功或失敗時(shí),改變 promise 的狀態(tài)
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    // 設(shè)置錯(cuò)誤監(jiān)聽(tīng)函數(shù)
    xhr.onerror = function() {
      reject(new Error(this.statusText));
    };
    // 設(shè)置響應(yīng)的數(shù)據(jù)類(lèi)型
    xhr.responseType = "json";
    // 設(shè)置請(qǐng)求頭信息
    xhr.setRequestHeader("Accept", "application/json");
    // 發(fā)送 http 請(qǐng)求
    xhr.send(null);
  });
  return promise;
}

判斷括號(hào)字符串是否有效(小米)

題目描述

給定一個(gè)只包括 '(',')','{','}','[',']' 的字符串 s ,判斷字符串是否有效。

有效字符串需滿(mǎn)足:
- 左括號(hào)必須用相同類(lèi)型的右括號(hào)閉合。
- 左括號(hào)必須以正確的順序閉合。

示例 1:

輸入:s = "()"
輸出:true

示例 2:

輸入:s = "()[]{}"
輸出:true

示例 3:

輸入:s = "(]"
輸出:false

答案

const isValid = function (s) {
  if (s.length % 2 === 1) {
    return false;
  }
  const regObj = {
    "{": "}",
    "(": ")",
    "[": "]",
  };
  let stack = [];
  for (let i = 0; i < s.length; i++) {
    if (s[i] === "{" || s[i] === "(" || s[i] === "[") {
      stack.push(s[i]);
    } else {
      const cur = stack.pop();
      if (s[i] !== regObj[cur]) {
        return false;
      }
    }
  }

  if (stack.length) {
    return false;
  }

  return true;
};

數(shù)組去重方法匯總

首先:我知道多少種去重方式

1. 雙層 for 循環(huán)

function distinct(arr) {
    for (let i=0, len=arr.length; i<len; i++) {
        for (let j=i+1; j<len; j++) {
            if (arr[i] == arr[j]) {
                arr.splice(j, 1);
                // splice 會(huì)改變數(shù)組長(zhǎng)度,所以要將數(shù)組長(zhǎng)度 len 和下標(biāo) j 減一
                len--;
                j--;
            }
        }
    }
    return arr;
}

思想: 雙重 for 循環(huán)是比較笨拙的方法,它實(shí)現(xiàn)的原理很簡(jiǎn)單:先定義一個(gè)包含原始數(shù)組第一個(gè)元素的數(shù)組,然后遍歷原始數(shù)組,將原始數(shù)組中的每個(gè)元素與新數(shù)組中的每個(gè)元素進(jìn)行比對(duì),如果不重復(fù)則添加到新數(shù)組中,最后返回新數(shù)組;因?yàn)樗臅r(shí)間復(fù)雜度是O(n^2),如果數(shù)組長(zhǎng)度很大,效率會(huì)很低

2. Array.filter() 加 indexOf/includes

function distinct(a, b) {
    let arr = a.concat(b);
    return arr.filter((item, index)=> {
        //return arr.indexOf(item) === index
        return arr.includes(item)
    })
}

思想: 利用indexOf檢測(cè)元素在數(shù)組中第一次出現(xiàn)的位置是否和元素現(xiàn)在的位置相等,如果不等則說(shuō)明該元素是重復(fù)元素

3. ES6 中的 Set 去重

function distinct(array) {
   return Array.from(new Set(array));
}

思想: ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set,Set 結(jié)構(gòu)的一個(gè)特性就是成員值都是唯一的,沒(méi)有重復(fù)的值。

4. reduce 實(shí)現(xiàn)對(duì)象數(shù)組去重復(fù)

var resources = [
    { name: "張三", age: "18" },
    { name: "張三", age: "19" },
    { name: "張三", age: "20" },
    { name: "李四", age: "19" },
    { name: "王五", age: "20" },
    { name: "趙六", age: "21" }
]
var temp = {};
resources = resources.reduce((prev, curv) => {
 // 如果臨時(shí)對(duì)象中有這個(gè)名字,什么都不做
 if (temp[curv.name]) {

 }else {
    // 如果臨時(shí)對(duì)象沒(méi)有就把這個(gè)名字加進(jìn)去,同時(shí)把當(dāng)前的這個(gè)對(duì)象加入到prev中
    temp[curv.name] = true;
    prev.push(curv);
 }
 return prev
}, []);
console.log("結(jié)果", resources);

這種方法是利用高階函數(shù) reduce 進(jìn)行去重, 這里只需要注意initialValue得放一個(gè)空數(shù)組[],不然沒(méi)法push

實(shí)現(xiàn)類(lèi)數(shù)組轉(zhuǎn)化為數(shù)組

類(lèi)數(shù)組轉(zhuǎn)換為數(shù)組的方法有這樣幾種:

  • 通過(guò) call 調(diào)用數(shù)組的 slice 方法來(lái)實(shí)現(xiàn)轉(zhuǎn)換
Array.prototype.slice.call(arrayLike);

  • 通過(guò) call 調(diào)用數(shù)組的 splice 方法來(lái)實(shí)現(xiàn)轉(zhuǎn)換
Array.prototype.splice.call(arrayLike, 0);

  • 通過(guò) apply 調(diào)用數(shù)組的 concat 方法來(lái)實(shí)現(xiàn)轉(zhuǎn)換
Array.prototype.concat.apply([], arrayLike);

  • 通過(guò) Array.from 方法來(lái)實(shí)現(xiàn)轉(zhuǎn)換
Array.from(arrayLike);

實(shí)現(xiàn)Vue reactive響應(yīng)式

// Dep module
class Dep {
  static stack = []
  static target = null
  deps = null

  constructor() {
    this.deps = new Set()
  }

  depend() {
    if (Dep.target) {
      this.deps.add(Dep.target)
    }
  }

  notify() {
    this.deps.forEach(w => w.update())
  }

  static pushTarget(t) {
    if (this.target) {
      this.stack.push(this.target)
    }
    this.target = t
  }

  static popTarget() {
    this.target = this.stack.pop()
  }
}

// reactive
function reactive(o) {
  if (o && typeof o === 'object') {
    Object.keys(o).forEach(k => {
      defineReactive(o, k, o[k])
    })
  }
  return o
}

function defineReactive(obj, k, val) {
  let dep = new Dep()
  Object.defineProperty(obj, k, {
    get() {
      dep.depend()
      return val
    },
    set(newVal) {
      val = newVal
      dep.notify()
    }
  })
  if (val && typeof val === 'object') {
    reactive(val)
  }
}

// watcher
class Watcher {
  constructor(effect) {
    this.effect = effect
    this.update()
  }

  update() {
    Dep.pushTarget(this)
    this.value = this.effect()
    Dep.popTarget()
    return this.value
  }
}

// 測(cè)試代碼
const data = reactive({
  msg: 'aaa'
})

new Watcher(() => {
  console.log('===> effect', data.msg);
})

setTimeout(() => {
  data.msg = 'hello'
}, 1000)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容