【JavaScript】要點知識的我的總結(1)

米娜桑,哦哈喲~
該篇章主要基於連接中的參考內容和代碼測試得出的結論,面向具備必定基礎的前端開發者。若有錯誤,請指出與包涵。html


原型鏈的解釋

白話翻譯:原型鏈就至關於對象上的一個鏈條,經過隱性原型屬性__proto__ 將其餘相關的屬性綁定以達到引用的效果,其鏈條的終點就是 Object.prototype,這就是原型鏈。前端

class.prototype === obj.__proto__

Object.create()聲明的對象

Object.create({name: 1}) 至關於在這個對象的鏈條上再增長一條鏈條__proto__,因此 Object.create(null) 的結果是沒有任何隱藏屬性很是純淨且可高度定製的 {} (通常用於一些開源項目),固然經過node

let a = {}
a.__proto__ = null

也能夠得到純淨的對象,不過生成的性能通常,經過如下代碼實現 Object.create()。git

function create(obj) {
        let F = function () {}
        F.prototype = obj
        return new F()
    }
    
function create(obj) {
        let a = {}
        a.__proto = obj
        return a
    }

Function.prototype.bind() 的特別之處

經過 Function.prototype.bind() 聲明的函數不是 Function 的實例,故不存在class.prototype === obj.__proto__;不過能夠經過
Object.prototype.toString.call()能夠得知它依然是一個函數。
並與 Function.prototype 是一致的。github


new

  1. new 的過程以下
建立有 Object 函數實例出來的空對象;
將該對象綁定到相應的構造函數上,其此爲 this 的上下文,若是沒有返回對象的話,就返回 this
function zxNew() {
    let fn = arguments[0]
    if (typeof fn !== 'function') {
        throw new TypeError('first param must be a function')
    }
    Array.prototype.shift.call(arguments)
    let obj = Object.create(null);
    obj.__proto__ = fn.prototype;
    let res = fn.apply(obj, arguments);
    return res instanceof Object ? res : obj
}

原型鏈繼承的方式

原型繼承
function Parent () {
    this.name = 'temax'
}

Parent.prototype.fn = function () {
    console.log(233)
}

function Child () {}

Child.prototype = new Parent()

let child = new Child()

缺點:屬性共享、不能傳參segmentfault

經典繼承——使用 call 方法借用構造函數的方式
function Parent (name) {
    this.name = name
}

Parent.prototype.fn = function () {
    console.log(233)
} 

function Child (name) {
    Parent.call(this, name)
}

let child = new Child('temax')

缺點:不能繼承prototype上的屬性數組

組合繼承——即原型鏈繼承和經典繼承的結合
function Parent(name) {
    this.name = name
}

Parent.prototype.fn = function () {
    console.log(233)
}

function Child(name) {
    Parent.call(this, name) 
}

Child.prototype = new Parent()
Child.prototype.constructor = Child

let child = new Child(1)

缺點:父類函數執行兩次app

組合繼承的優化
function Parent(name) {
    this.name = name
}

function.prototype.fn = function () {
    console.log(233)
}

function Child(name) {
    Parent.call(this, name) 
}

Child.prototype = Parent.prototype
child.prototype.constructor = Child

let child = new Child()

缺點:prototype 的保存地址其實就是父級的保存地址,也就是說若是改變 child.prototype 會直接影響到父級的 prototype,因此須要加一個__proto__進行隔離dom

寄生組合的方式——比較理想的組合方式
function Parent(name) {
    this.name = name
}

function.prototype.fn = function () {
    console.log(233)
}

function Child(name) {
    Parent.call(this, name) 
}

Child.prototype = Object.create(Parent.prototype)
child.prototype.constructor = Child
class的繼承方式
class Parent{

}
class Child extends Parent{

}

this的指向

  1. this永遠指向最後調用的對象。

也就是說 a.b.c.fn(),那麼 fn 裏面的 this 的指向 c 的屬性。
若是令 d = b.c.fn;則 a.d() 中的 this 是指向 a 的。異步

  1. setTimeout 的 function 中的 this 是指向 window 對象的。由於 setTimeout 實則爲 window.setTimeout。
  2. 可經過定義this的變量來調用或使用 apply/call/bind 的方式改變 this 指向。

箭頭函數的this

  1. 自身沒有聲明 this,因此會在做用域鏈上找最近的 this。
  2. 不能夠看成構造函數。
  3. 不能用做 Generator 函數。
  4. 不能使用 arguments,arguments 能夠用 ...rest 取代。

apply、call、bind

  1. 能夠經過 apply 和 call 對對象進行調用, 使得函數內的 this 綁定到該調用的值。
  2. apply 和 call 的區別就是傳入方式的不同,前者是數組傳參的方式,後者是直接傳參的方式。
  3. bind 跟 call 一致,只不過 bind 會建立並返回一個新的函數。
  4. 實現機制以下
call (apply同理,只是改變參數的形式)
Function.prototype.call = function(){
    if (typeof this !== 'function') {
        throw new TypeError('Called object must be a function')
    }
    let obj = Object(arguments[0]) || window,
        obj.fn = this,
        result;
        arr = arguments.slice();
    arr.shift();
    result = obj.fn(...arr)
    delete obj.fn
    return result
}
bind

若是要模仿 Function.prototype.bind() 獲得的函數 fn,須要設置

fn.__proto__ = Object.prototype
fn.prototype = undifned
Function.prototype.bind1 = function () {
    let fn = this;
    if(typeof fn === 'function'){
        if(fn !== Function.prototype){
            let obj = Object(arguments[0]) || window,
                args = Array.prototype.slice.call(arguments,1),
                TempFn = function () {},
                FnBind = function () {
                    //當使用new來構造函數(經過bind獲得的)時,會忽略bind(obj)的obj
                    //對於這個的理解,應該結合new和call的實現來解釋,這個時候的this是指向 Object.create(null),若是沒有設置prototype的話
                    //由於原生獲得的(new fn1) instanceof fn1 爲 true,
                    //              (new fn1) instanceof fn 也爲 true,
                    //又由於        TempFn.prototype = fn.prototype;(只要知足 ojb.__proto__ ===  class.prototype.constructor)
                    //因此用改造後獲得的 (new fn2) instanceof TempFn 爲 true

                    //簡單來講就是,當爲構造函數的時候,這裏的this是等於指向實例,而實例 instanceof fn1 爲true, 實例 instanceof fn也爲true,
                    //而只要知足 ojb.__proto__ ===  class.prototype
                    //那麼 instanceof 獲得的就是 true,因此能夠用 this instanceof TempFn 來表示
                    let bindArgs = Array.prototype.slice.call(arguments);
                    return fn.apply(this instanceof TempFn ? this : obj, args.concat(bindArgs))
                };
            TempFn.prototype = fn.prototype;
            FnBind.prototype = new TempFn;
            return FnBind
        }else {
            fn.prototype = undefined
            fn.__proto__ = Object.prototype
            return fn
        }
    }else {
        throw TypeError('Called object must be a function')
    }
}

事件循環

事件循環是和消息隊列共同實現JS同步異步運行的機制。消息隊列先進先出,一個循環裏,先從隊列中取出宏任務並執行,若是存在異步任務,便會將其回調函數註冊發放到消息隊列當中;再執行微任務,直到相應的執行棧爲空爲止;執行完後啓動新的循環;事件循環是js的運行機制。
事件循環還分宏任務和微任務

  1. 宏任務:總體代碼;setTimeout;setInterval,宏任務消息隊列
    微任務:Promise; process.nextTick,微任務消息隊列
  2. 經過如下代碼測試
setTimeout(function(){
    console.log('定時器開始啦')
});

new Promise(function(resolve){
    console.log('立刻執行for循環啦');
    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){
    console.log('執行then函數啦')
});

console.log('代碼執行結束');

15fdcea13361a1ec.jpg


節點操做

  1. 添加節點能夠經過 appendChild
  2. 經過 innerText 對節點添加文本信息
  3. 也能夠經過 innerHTML 對節點添加 html 文本
  4. 利用 document.createElement 添加對應元素。
  5. 經過 node.addEventListener(type,listener,capture) 添加事件。第三參數爲是否捕獲,默認false,即冒泡
  6. document 爲最大的節點。
  7. 固然也能夠經過 DocumentFragment 來減小 dom 操做
  8. 如下是 ul 元素添加 li 元素的例子
let ul = document.querySelector('.cul');
// let ul = document.querySelectorAll('.cul')[0];
let a = '';
function myFunction(e){console.log(e)}
for(let i=0;i<5;i++){
    a += `<li onclick='myFunction(${i})'>${i}</li>`
}
ul.innerHTML = a
let ul = document.querySelectorAll('.cul')[0];
for(let i=0;i<5;i++){
    let a = document.createElement('li');
    a.innerHTML = i
    a.addEventListener('click',function () {
        console.log(i)
    })
    ul.appendChild(a)
}
let ul = document.querySelectorAll('.cul')[0];
let fragment = document.createDocumentFragment()
for(let i=0;i<5;i++){
    let a = document.createElement('li');
    a.innerHTML = i
    a.addEventListener('click',function () {
        console.log(i)
    })
    fragment.appendChild(a)
}
ul.appendChild(fragment)
相關文章
相關標籤/搜索