開發過程中遇到的js知識點總結,面試題等,持續更新

 1.Object.freeze()

  方法用於凍結一個對象,即將對象設置為不可擴展、將對象的所有自有的屬性和方法(包括Symbol值的屬性和方法)配置為不可配置,不可寫。

  Object.freeze(obj)

  返回值是已凍結的對象。

  這個操作不會影響從原型對象繼承來的屬性和方法,即隻影響自有的屬性和方法,一旦對象被凍結,其自身的所有屬性都不可能以任何方式被修改。

 

2.深度選擇器 >>> 與 /deep/

.fuck >>> .weui-cells {
    // ...
}

有些想sass、less、scss之類的預處理器無法正確解析可以使用 /deep/ 取而代之,/deep/是 >>> 的別名

vue組件中,在style設置為scoped的時候,裡面在寫樣式對子組件是不生效的,如果想讓某些樣式對子組件都生效,可以使用/deep/深度選擇器

.wrap{
  /deep/ .class{
    font-size: 12px;// 對所有子組件生效  
    /deep/ .class2{};// 沒有必要寫多層deep,父類有deep,子類自動也會深度選擇,在firfox里會失效  
  }  
}

 

3.在循環中使用break、continue和return語句之間的區別

  break:此語句將退出循環執行循環後面的語句。如果break語句包含在嵌套循環里,它只跳出最裡面的循環。

  continue:程式運行到此語句時,不再執行循環體中continue後面的語句,而是跳到下一個循環入口執行下一個循環

  return:表示被調函數返回到主調函數繼續執行,返回時可附帶一個返回值,由return後面的參數指定。return之後,函數就結束了,後面的語句不再執行

 

4.JS中數組函數的總結

  push():像數組的末尾添加一個或多個元素,並返回新的長度

  pop():刪除數組的最後一個元素,數組的長度減1,並且返回刪除元素的值(當數組為空的時候,不改變數組,並返回undefined)

  unshift():向數組的頭部添加一個或多個元素,返回新的長度

  shift():刪除並返回數組的第一個元素

  reverse():倒敘

  sort():對數組元素進行排序(默認按照ASCLL),可以傳遞一個函數,自定義排序規則

  concat():連接兩個或多個數組

  split(): 把字元串轉換成數組

  join():把所有的元素放進一個字元串,通過指定的分隔符進行分割

  splice(index, howmany, item1, …, itemX):可以做 刪除/插入/替換 操作(替換就是先刪除再添加,響應式的)

    index 必需,規定添加刪除項目的位置,使用負數可從數組結尾處規定位置

    howmany 必需,要刪除的項目數量,如果設置為 0,則不會刪除項目

    item, …, itemX 可選,像數組添加的新的元素

  substr(start, length)

  substring(start, end)

  slice(start, end):截取。從某個已有的數組返回選定的元素,不包含 end 元素(沒有 end 參數,就到數組的結尾)

  indexOf(x, start):返回某個指定字元串再字元串中首次出現的位置

    x 必需,規定檢索的字元串

    start 可選,規定在字元串中開始檢索的位置,合法值是 0 至 arr.length – 1,如果省略該值,則從字元串的首字元開始檢索

  lastIndexOf(x, fromindex):指定字元串值,最後出現的位置,在一個字元串中的指定位置從後向前搜索

    x 必需,規定檢索的字元串

    fromindex 可選,規定字元串開始檢索的位置,合法值 0 至 arr.length -1 ,若省略,則將從字元串最後一個字元處開始檢索

  toString():轉換為字元串

  valueOf():返回數組的原始值,返回數組本身

  includes(x, start):判斷字元串或數組中是否包含指定的字元串,返回Boolean

    x 必需,要查找的數據

    start 可選,設置從那個位置開始查找,默認0

  find(function(currentValue, index, arr), thisValue):返回通過判斷的數組的第一個元素的值,不會對空數組執行

    currentValue 必需,當前元素

    index 可選,當前元素的索引值

    arr 可選,當前數組對象

   thisValue 可選,傳遞給函數的值一般用 this 值

  instanceof:檢測是否是數組

    arr instanceof Array;// 返回Boolean

  Array.isArray(obj):判斷obj是否是數組,返回Boolean

 

 JS中數組高階函數的理解 some()、every()、fiflter()、map()、forEach()、reduce()

  some((item, index, arr)=>{}):返回一個Boolean,判斷是否有元素符合func中的條件

      item 必選,index 可選,索引值。arr 當前數組,可選

      不會對空數組進行檢測,不會改變原始數組

    const arr = [1,2,3,4,5];
    arr.some(item=>item>2);// 返回true

  every((item,index,arr)=>{}):返回一個Boolean,判斷每個元素是否符合func中的條件

       item必選,index 可選,索引值。arr當前數組,可選

      不會對空數組進行檢測,不會改變原始數組

    const arr = [2,3,4,5,6];
    arr.every(item=>item>2);// 返回false

  filter((item,index,arr)=>{}):返回一個符合func條件的元素數組(es6)

       item必選,index 可選,索引值。arr當前數組,可選

      filter() 不會對空數組進行檢測。不會改變原始數組

    let arr = [2,3,4,5];
    arr.filter(item=>item>2);// 返回 [3,4,5]

  map((item, index, arr)=>{}):返回一個新的array,數組元素由每一次調用函數產生結果組成

      item必選,index 可選,索引值。arr當前數組,可選

      map() 不會對空數組進行檢測,不會改變原始數組

    const arr = [1,2,3,4,5];
    arr.map(item=>item*2);// 返回 [2,4,6,8,10]

  forEach((item, index, arr)=>{}):調用數組的每個元素,並將元素傳遞給回調函數(沒有返回值,將數組遍歷)

      item 參數是必須的,當前元素。index 可選,當前索引值。arr 可選,當前元素所屬的數組對象

      forEach() 對於空數組是不會執行回調函數的

    const a = [1, 22, 333]
    a.forEach((item, index, arr)=>{
      console.log(item);// 1 22 333
      console.log(index);// 0 1 2
      console.log(arr);// arr 為遍歷的數組
    })    

  reduce():接收一個函數作為累加器,數組中的每個值(從左到右)開始縮減,最終計算為一個值

    可以作為一個高階函數,用於函數的 compose ,對於空數組是不會執行回調函數的

    let arr = [{count: 1, price: 2},{count: 3, price: 3}, {count: 2, price: 2}];
    arr.reduce(function(total, currentValue, index, arr){}, initialValue)// initialValue 可選,傳遞給函數的初始值
    // 參數 total 必需,初始值,或者計算結束後的返回值。currentValue必需,當前元素。index 可選,索引。arr 可選,數組對象
    let sum = arr.reduce(function(total, item){
      return total + item.count * item.price;
    },0)// 最終返回15 

 

5.jquery獲取radio, checkbox, checked 的值的時候不能使用attr

  要使用prop進行獲取和設置值

  獲取選中的屬性

    $("#id").prop('checked');

    $("#id").get(0).checked

    document.getElementById("#id").checked

    $("#id").is(":checked")

  設置選中

    $("#id").prop('checked', true);

    $("#id").get(0).checked = true;

    document.getElementById("#id").checked = true;

 

6.多個非同步請求成功後在執行後續方法

  1–jquery 中 $.when() 方法

  $.when($.ajax(), $.ajax()).done(function(a1, a2){
    // a1是第一個非同步返回的數據,a2是第二個非同步返回的數據
  }).file(function(){
    alert('file')
  })
// 以上程式碼是先執行兩個操作$.ajax,如果都成功了,就運行done(),如果有一個或都失敗了就執行fail()

  $.when(deferreds)

    Deferred 類型 一個或多個延遲對象,或者普通的js對象

   2–使用promise中的all()方法實現

  Promise.all([
      new Promise(resolve => {
        setTimeout(() => {
          resolve('100')
        }, 1000)
      }),
      new Promise(resolve => {
        setTimeout(() => {
          resolve('200')
        }, 1000)
      })
    ]).then(data => {
      console.log('5555')
    })

 

7.word-wrap: break-word;

  允許長單詞換行到下一行

 

8.數組引用的時候,直接改變數據會影響原數據的值

   let a = [1,2,3,4,5];

  let b = a;

  b[2] = 22;// 此時a數組也是[1,2,22,4,5]

  要想不改變原始數據的值,

  let c = JSON.parse(JSON.stringify(a))

  c[2] = 2222;// 此時改變的只是c中的值    

 

9.js淺拷貝與深拷貝的區別和實現

  區別:假設 B 複製了 A ,當修改 A 時,看 B 是否會發生改變,如果 B 變了,說明是淺拷貝,若 B 沒有改變,那就是深拷貝

  1.如果是基本數據類型,名字和值都會儲存在棧記憶體中

    let a = 1;
    let b = a;// 棧記憶體會開闢一個新的記憶體空間,此時 a 和 b 都是相互獨立的
    b=2;//此時a還是1

  以上算不上深拷貝,因為深拷貝本身只針對比較複雜的 object 類型數據

  2.如果是引用類型,名字存在棧記憶體中,值存在堆的記憶體中,但是棧記憶體會提供一個引用的地址指向堆記憶體中的值

    let a  = [1,2,3]
    let b = a;// 此時進行拷貝,其實複製的是 a 的引用地址,而並非堆裡面的值
    a[1] = 222;// 在修改數組時,由於 a 與 b 指向的時同一個地址,所以 b 也跟著相應的改變,這就是淺拷貝

 

  3.實現淺拷貝的方法

    1.for…in 只循環第一層

    // 只複製第一層的淺拷貝
    function simpleCopy(obj1) {
      var obj2 = Array.isArray(obj1) ? [] : {};
      for (let i in obj1) {
        obj2[i] = obj1[i];
      }
      return obj2;
    }
    var obj1 = {
      a: 1,
      b: 2,
      c: {
        d: 3
      }
    }
    var obj2 = simpleCopy(obj1);
    obj2.a = 3;
    obj2.c.d = 4;
    alert(obj1.a); // 1
    alert(obj2.a); // 3
    alert(obj1.c.d); // 4
    alert(obj2.c.d); // 4

    2.Object.assign方法

    var obj = {
        a: 1,
        b: 2
    }
    var obj1 = Object.assign(obj);
    obj1.a = 3;
    console.log(obj.a) // 3

    3.使用等於號 =

    let a = [0, 1, 2, 3, 4];
    let b
= a;     console.log(a === b);     a[0] = 1;     console.log(a, b);

  4.實現深拷貝的方法

    1.使用遞歸去拷貝所有層級的屬性

    let a = [1, 2, 3, 4, 5];
    let b = deepClone(a);
    function deepClone(obj){
      let objClone = Array.isArray(obj) ? [] : {};
      if (obj && typeof obj === "object") {
        for(key in obj) {
          // 判斷對象是否包含特定的自身屬性,返回Boolean
          if(obj.hasOwnProperty(key)) {
            // 判斷 obj 子元素是否為對象,如果是,遞歸複製
            if(obj[key] && typeof obj[key] === 'object') {
              objClone[key] = deepClone(obe[key])
            } else {
              objClone[key] = obj[key]
            }
          }
        }
      }
      return objClone;    
    }

    2.通過 JSON 對象來實現深拷貝

      // 缺點:無法實現對對象中方法的拷貝,會顯示為 undefined
      let a = [1,2,3,4,5];
      let b = JSON.parse(JSON.stringify(a));

    3.通過 jQuery 的 extend 方法實現深拷貝

      let arr = [1,2,3,4];
      let bbb = $.extend(true, [], array);// true 為深拷貝,false為淺拷貝

    4.可以通過 lodash 函數庫實現深拷貝

      let res = _.clone(arr);

    5.Reflect方法

      // 代理法
      function deepClone(obj) {
        if(!isObject(obj)){// 判斷是否為對象
          throw new Error('obj 不是一個對象')
        }
        let isArray = Array.isArray(obj);
        let cloneObj = isArray ? [...obj] : {...obj};
        Reflect.ownKeys(cloneObj).forEach(key => {
          cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
        })
        return cloneObj;
      }

    6.手動實現深拷貝

      let obj = {a: 1, b: 2};
      let obj2 = {a: obj.a, b: obj.b};

    7. 如果對象的 value 時基本類型的話,也可以用 Object.assign 來實現深拷貝,但是要把它賦值給一個空對象

      var obj = {a: 1, b: 2}
      var obj1 = Object.assign({}, obj);// obj賦值給一個空{}

    8.使用 slice 實現對數組的深拷貝

      // 但數組裡面的值是基本數據類型,比如 String,Number,Boolean 時,屬於深拷貝
      // 當數組裡面的值時引用數據類型比如 Object, Array時,屬於淺拷貝
      let a = [1,2,3];
      let b = a.slice(0);

    9.用 concat 實現對數組的深拷貝

    // 當數組裡面的值是基本數據類型,比如String, Number, Boolean時,屬於深拷貝
    let a = [1,2,3];
    let b = a.concat();
    // 當數組裡面的值是引用數據類型,比如Object,Array時,屬於淺拷貝
    let a = [{a: 1},{b: 2}];
    let b = a.concat;

    10.使用 let newObj = Object.create(oldObj) 可以達到深拷貝的效果

      function deepClone(initalObj, finalObj){
        var obj = finalObj || {};
        for(var i in initalObj) {
          var prop = initalObj[i];// 難免相互引用對象導致死循環,比如initalObj.a = initalObj
          if(prop === obj) {
            continue;
          }
          if (typeof prop === 'object') {
            obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
          } else {
            obj[i] = prop;
          }
        }
        return obj;
      }

    11.使用擴展運算符實現深拷貝

      // 當value是基本數據類型,比如String,Number,Boolean時,是可以使用拓展運算符進行深拷貝的
      // 當value是引用類型的值,比如Object,Array,引用類型進行深拷貝也只是拷貝了引用地址,所以屬於淺拷貝
      var car = {sf: "james", pf: "davis", cf: "cf"}
      var car1 = { ...car, pf: "kobe" }
      console.log(car1); // { sf: "james", pf: "davis", cf: "cf" }
      console.log(car); // { sf: "james", pf: "kobe", cf: "cf" }

 

10.用on給動態添加的元素綁定hover事件,不會生效

  在jQuery中,hover() 函數本身是對 mouseenter && mouseleave 的封裝,然而在原生 event 中,並沒有 hover 這一事件,所以在傳遞參數 hover 時,並不會有任何的效果。

  如果我想綁定類似 hover 的效果,可以綁定兩個事件 mouseenter && mouseleave + function ,樣式就用類名來 toggle, 也可以在map裡面,一個事件對應一個function

  $('ul.course').on('mouseenter mouseleave', 'li', function(){
    $(this).toggleclass('borderClass');// 滑鼠移入移出改變 class
  })

 

11.JSON.stringify() 和 JSON.parse()

  JSON.stringify({'a': '1', 'b': '2'});
  // '{'a': '1', 'b': '2'}'  從一個對象中解析出字元串
  JSON.parse('{'a': '1', 'b': '2'}');   // Object{a:'1', 'b': '2'}  從一個字元串中解析出JSON對象

 

12.for…in 與 for…of 的區別

   對於數組的遍歷,for…in 會返回數組中所有可枚舉的屬性(包括原型鏈上可枚舉的屬性),for…of 只返回數組的下標對應的屬性值

  for…of 循環的原理其實也是利用了遍歷對象內部的 iterator 介面,將 for…of 循環分解成最原始的 for 循環,內部實現的機制可以這麼理解

  // 1.for...of 遍歷獲取的是對象的鍵值,for...in 獲取的是對象的鍵名
  let arr = ['one', 'two', 'three'];
  for(let i in arr){
    console.log(i);// 分別輸出 0 1 2
  }
  for(let i of arr){
    console.log(i);// 分別輸出 one two three
  }

  // 2.對於普通對象,沒有部署原生的 iterator 介面,直接使用 for...of 會報錯
  let obj ={name: 'zhangning187', age: '24'}
  for(let key of obj) {
    console.log(key);// obj is not iterable
  }

  // 可以使用 for...in 循環遍歷鍵名
  for(let key in obj) {
    console.log(key);// name age
  }
  // 也可以使用 Object.keys(obj) 方法將對象的鍵名生成一個數組,然後遍歷這個數組
  for(let key of Object.keys(obj)) {
    console.log(key);// name age
  }

  // 3.for...in 循環不僅遍曆數字鍵名,還會遍歷手動添加的其他鍵,甚至包括原型鏈上的鍵。for...of則不會這樣
  let array = [1, 2, 3];
  arr.name = 'zhangning187';// 手動添加的鍵
  Array.prototype.age = '23';// 原型鏈上的鍵
  for(let i in arr) {
    console.log(i);// 1 2 3 name age
  }
  
  // 4.forEach 循環無法中途跳出,break 命令或 return 命令都不能奏效
  let arr1 = [1, 2, 3, 4]
  arr.forEach(i=>{
    if(i == 2) return;// 它會像 continue 一樣,跳過當前循環繼續執行下一次循環
    console.log(i);// 1 3 4
  })
  for...of 循環可以與 breakcontinuereturn 配合使用,跳出循環

  // 5.無論是 for...in 還是 for...of 都不能遍歷出 Symbol 類型的值,遍歷 Symbol 類型的值需要用Object.getOwnPropertySymbols()方法
  let a = Symbol('a')
  let b = Symbol('b')
  let obj = {
    [a]: 'zhang',
    [b]: 'ning',
    c: '23',
    d: 'web'
  }
  for(let key in obj) {
    console.log(key);// c d
  }
  let objSymbols = Object.getOwnPropertySymbols(obj)
  console.log(objSymbols);// [Symbol(a), Symbol(b)]
  objSymbols.forEach(i=>{
    console.log(obj[i]);// zhang ning
  })
  
// Reflect.ownKeys 方法可以返回所有類型的鍵名,包括常規鍵名和 Symbol 鍵名   let keyArr = Reflect.ownKeys(obj)   console.log(keyArr);// ["c", "d", Symbol(a), Symbol(b)]

  for…in 循環主要是為了遍歷對象,不適用於遍曆數組

  for…of 循環可以用來遍曆數組、類數組對象、字元串、Set、Map、Generator 對象

 

13.vue過濾器中的this值

   過濾器中的this值為undefined

 14.$nextTick()

   在下次DOM更新循環結束之後執行延遲回調,在修改數據之後立即使用這個方法,獲取更新後的DOM

  在 created() 鉤子函數進行的DOM操作,一定要放在this.$nextTick() 回調函數中,原因是在 create() 鉤子函數執行的時候 DOM 其實並未進行任何渲染,而此時進行DOM操作無異於徒勞,所以放在nextTice()回調函數中。與之對應的就是 mounted() 鉤子函數,該鉤子函數執行的時候,所有的 DOM 掛載已完成。

  

 

Tags: