米娜桑,哦哈喲~
該篇章主要基於連接中的參考內容和代碼測試得出的結論,面向具備必定基礎的前端開發者。若有錯誤,請指出與包涵。html
白話翻譯:原型鏈就至關於對象上的一個鏈條,經過隱性原型屬性__proto__ 將其餘相關的屬性綁定以達到引用的效果,其鏈條的終點就是 Object.prototype,這就是原型鏈。前端
class.prototype === obj.__proto__
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 的實例,故不存在class.prototype === obj.__proto__
;不過能夠經過Object.prototype.toString.call()
能夠得知它依然是一個函數。
並與 Function.prototype 是一致的。github
建立有 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{ }
也就是說 a.b.c.fn(),那麼 fn 裏面的 this 的指向 c 的屬性。
若是令 d = b.c.fn;則 a.d() 中的 this 是指向 a 的。異步
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的運行機制。
事件循環還分宏任務和微任務
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('代碼執行結束');
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)