1. 程式人生 > 其它 >最後億遍js總結

最後億遍js總結

原型原型鏈

繼承

call,apply,bind用法及實現

  • 作用:簡單來說就是改變this的指向,在一個物件中呼叫另一個物件的方法

用法

  • applycall的用法相似,會直接執行函式
A.sayName.call(B,'tom')
A.sayName.apply(B,['tom'])
  • bind用法與call相似,不同的是,她不會立即執行,他會返回原函式的拷貝
let bSay = A.sayName.bind(B,'tom') //拷貝
bSay()//呼叫

實現

//A.myCall(B,arg1,arg2)

//掛載到Function 的原型物件上,以便繼承給函式物件呼叫
Function.prototype.myCall = function (originFun, ...args) {
	//A點出的myCall,這裡this 指向A函式
    console.log(this)
    //在B裡面定義一個私有屬性指向A函式
    originFun.__thisFn__ = this
    //B呼叫A函式,this指向B
    let result = originFun.__thisFn__(...args) 
    //刪除無用屬性
    delete originFun.__thisFn__
    return result
}


判斷型別的方法

  • typeofinstanceof

  • Object.prototype.toString.call();每個物件都存在toString()方法,預設情況下,被每個Object物件繼承,如果未被覆蓋,會返回[object type],type是物件的型別。比如:

    let obj= new Object()
    obj.toString() //輸出 "[object Object]"
    

    但是對於其他情況

    var arr =new Array(1,2)
    arr.toString() // 輸出 "1,2"
    

    這是因為所謂的ArrayString等型別在繼承於基類Object時,重寫了toString

    方法,在呼叫的時候,通過原型鏈向上查詢方法時,找到Array上面的toString就停止查詢,直接呼叫了,所以輸出與預想的有偏差,那如何讓arr去呼叫Object上面的該方法呢?顯而易見借用call()

    Object.prototype.toString.call(arr) // 輸出 "[object Array]"
    

    然後在利用 slice方法截取出來型別

    // 是否字串
    export const isString = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'String'
    
    // 是否數字
    export const isNumber = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Number'
    
    // 是否boolean
    export const isBoolean = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Boolean'
    
    // 是否函式
    export const isFunction = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Function'
    
    // 是否為null
    export const isNull = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Null'
    
    // 是否undefined
    export const isUndefined = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Undefined'
    
    // 是否物件
    export const isObj = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Object'
    
    // 是否陣列
    export const isArray = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Array'
    
    // 是否時間
    export const isDate = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Date'
    
    // 是否正則
    export const isRegExp = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'RegExp'
    
    // 是否錯誤物件
    export const isError = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Error'
    
    // 是否Symbol函式
    export const isSymbol = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Symbol'
    
    // 是否Promise物件
    export const isPromise = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Promise'
    

捕獲與冒泡

  • 事件捕獲與冒泡過程

捕獲

  • 事件捕獲階段,會由外到內,一層一層的檢查是否註冊了事件

  • 如何在捕獲階段註冊事件?

    • 使用 addEventListener註冊事件,第三個引數代表是否在捕獲階段處理事件,設定為true
    <div class="box">
        <button class="btn">按鈕</button>
    </div>
    
    <script>
        //先顯示div 後顯示btn
        const box = document.querySelector('.box')
        box.addEventListener(
            'click',
            () => {
                alert('div被點選了')
            },
            true
        )
        const btn = document.querySelector('.btn')
        btn.addEventListener(
            'click',
            () => {
                alert('button被點選了')
            },
            true
        )
    </script>
    
    

冒泡

  • 與捕獲相反,在事件冒泡階段,會從裡到外來檢查是否註冊事件

  • 預設情況下,所有的事件都是在冒泡階段進行註冊,如何阻止事件冒泡?

    • w3標準

      event.stopPropagation()
      
    • IE瀏覽器

      event.cancelBubble=true
      

事件委託

  • 多個元素註冊了相同的事件,可以把這些事件委託到它的父元素上,比較常見的是ul裡面的li標籤點選事件
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    const ul = document.querySelector('ul')
    ul.addEventListener('click', (e) => {
        if (e.target.nodeName == 'LI') {
            alert(e.target.innerText)
        }
    })
</script>

閉包問題

  • 在一個內層函式中訪問到外層函式的作用域

隔離作用域

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

// Counter1與Counter2互不影響,在閉包內修改變數,不會影響到另一個閉包中的變數
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

經典for迴圈閉包問題

for (var i = 1, arr = []; i < 5; i++) {
    setTimeout(() => {
        console.log(i)
    }, 500)
}// 5,5,5,5

//使用閉包

for (var i = 1, arr = []; i < 5; i++) {
    ;(function (i) {
        setTimeout(() => {
            console.log(i)
        }, 1000 * i)
    })(i)
}//1,2,3,4

new物件時發生了什麼,實現一個new

const myNew = (constructorFn, ...args) => {
  // 建立一個新的物件
  let obj = {}
  // 建立一個私有屬性,指向建構函式的原型物件
  obj.__proto__ = constructorFn.prototype
  // 執行建構函式
  constructorFn.call(obj, ...args)
  //返回這個物件
  return obj
}
function Person(name, age) {
  this.name = name
  this.age = age
}

const Per1 = new Person('Tom', 12)
console.log('Per1', Per1)
const Per2 = myNew(Person, 'Tom', 12)
console.log('Per2',Per2)

防抖與節流

閉包

深淺拷貝

  • 基本型別 StringNumber等都是按值訪問的,它的值儲存在棧中
  • 引用型別都是 按引用訪問的,它將引用地址儲存在棧上,然後再去堆上開闢空間存放他們的值

淺拷貝

  • 如果是基本型別,就直接拷貝基本型別的值,如果是引用型別,就拷貝引用型別的引用地址
  • 淺拷貝對於基本型別來說,他們之間改變值不會相互影響,但是對於引用型別來說,由於拷貝的是地址,這些地址最終都指向同一個堆裡面,所以會互相影響

實現方式

  • ES6展開運算子
let obj1 ={name:'tom',age:12,addr:{lng:26.0,lag:45.0},friends:['john','jerry']}
let obj2={...obj1}
obj2.name='jerry'
obj2.frinds[0]='tom'

ES6展開運算子對於不同結構的資料,存在不同的表現.對於一維的物件或者陣列,進行的深拷貝,對於多維的物件或者陣列進行的是淺拷貝

  • Object.assign()
let obj1 ={name:'tom',age:12,addr:{lng:26.0,lag:45.0},friends:['john','jerry']}
let obj2= Object.assign({}, obj1);

與解構賦值的作用一樣

  • lodash.clone()
var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);

深拷貝

  • 主要針對於引用型別,他會在對上面重新開闢一空間存放物件,這樣兩個深拷貝的物件就不會互相影響了

實現方式

  • JSON.parse(JSON.stringfy() )
let obj1 ={name:'tom',friends:['jerry','john']}
let obj2 =JSON.parse(JSON.stringify(obj1))
obj2.name='jerry'
obj2.friends[0]='tom'
  • lodash.cloneDeep()
var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
  • 遞迴遍歷實現
function clone(targetObj) {
    //判斷是否未物件,是物件去遍歷拷貝
    if (typeof targetObj === 'object') {
        //判斷源資料是物件還是陣列
        let cloneTarget = Array.isArray(targetObj) ? [] : {}
        for (const key in targetObj) {
            //遞迴拷貝
            cloneTarget[key] = clone(targetObj[key])
        }
        return cloneTarget
    } else {
        //不是物件直接返回
        return targetObj
    }
}