最後億遍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'
    

捕獲與冒泡

  • 事件捕獲與冒泡過程

image-20210701104838356

捕獲

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

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

    • 使用 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)

image-20210701174620575

防抖與節流

閉包

深淺拷貝

  • 基本類型 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'

image-20210702095035320

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'

image-20210702134922212

  • 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
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章