面向?qū)ο蠹笆录h(huán)

一、面向?qū)ο?/h2>

1.1 普通函數(shù)與構(gòu)造函數(shù)

函數(shù)還是之前的函數(shù),唯一的區(qū)別就是首字母大寫

function Foo(m, n) {
  let ret = m + n
  this.m = m
  this.n = n
  return ret
}

// 01 普通函數(shù)調(diào)用
let ret = Foo(10, 20)
console.log(ret)

// 02 構(gòu)造函數(shù)執(zhí)行
let res = new Foo(20, 20)
console.log(res)

1.1.1 普通函數(shù)

  • 正常調(diào)用,不需要 new 關(guān)鍵字
  • 執(zhí)行過程遵循堆棧 + 作用域及作用域鏈查找的運(yùn)行機(jī)制

1.1.2 構(gòu)造函數(shù)

  • 使用 new 關(guān)鍵字調(diào)用
  • 與普通函數(shù)類似,同樣會(huì)創(chuàng)建私有上下文,然后進(jìn)棧執(zhí)行
  • 執(zhí)行 new 操作時(shí),瀏覽器會(huì)創(chuàng)建一個(gè)空間表示空以象與 this 關(guān)聯(lián)
  • 函數(shù)體內(nèi)如果沒有 return 或者說 return 的是基本數(shù)據(jù)類型,默認(rèn)返回對象實(shí)例
  • 函數(shù)體內(nèi)如果返回引用類型,那么就以自己返回為主
  • 函數(shù)此時(shí)叫做類, 返回的結(jié)果叫做對象實(shí)例

1.1.3 new 操作符

  • 正常情況下使用 new 完成對象實(shí)例創(chuàng)建,如果當(dāng)前類不需要傳遞參數(shù),則可以不加括號運(yùn)行
  • new Foo , 沒有加小括號的時(shí)候說明 Foo 不需要傳參,稱之為無參列表
  • new Foo 與 new Foo() 的優(yōu)先級不同,前者為 19, 后者為 20
  • 每一次 new 都會(huì)將函數(shù)重新執(zhí)行,生成一個(gè)新的執(zhí)行上下文,創(chuàng)建一個(gè)新的實(shí)例對象,因此兩個(gè)實(shí)例對象不一樣

1.2 原型及原型鏈

原理圖

1.2.1 名詞說明

  • prototype 屬性
    • 每一個(gè)函數(shù)(除了箭頭函數(shù))數(shù)據(jù)類型, 都自帶一個(gè) prototype 屬性,指向原型對象(Function 除外)
    • 每個(gè)原型對象自帶一個(gè) constructor 屬性,指向當(dāng)前構(gòu)造函數(shù)本身
    • 函數(shù)數(shù)據(jù)類型
      • 普通函數(shù)、箭頭函數(shù)、生成器函數(shù)
      • 構(gòu)造函數(shù)(自定義類)
      • 內(nèi)置函數(shù)(內(nèi)置構(gòu)造函數(shù))
  • proto 屬性
    • 每一個(gè)對象數(shù)據(jù)類型,都自帶一個(gè) proto 屬性(隱式原型)
    • 該屬性的值指向所屬類的原型對象 prototype
    • 對象數(shù)據(jù)類型
      • 普通對象,數(shù)組對象,正則對象,日期對象
      • prototype 原型對象
      • 實(shí)例對象
      • 函數(shù)本身也是對象
  • Object 類
    • 所有對象都是 Object 內(nèi)置類的實(shí)例
    • Object 也是一個(gè)函數(shù), 同樣具有 prototype 屬性,指向自己的原型對象
    • 它的原型也是一個(gè)對象, 因此具有 proto 屬性
    • Object 原型對象的 proto 指向 Null (內(nèi)部設(shè)計(jì))

1.2.2 原型鏈查找機(jī)制

  1. 首先查找自己的私有屬性,私有中如果存在則使用私有
  2. 私有中如果不存在,則默認(rèn)基于 proto 找所屬類的原型對象
  3. 如果原型對象上沒有,則基于原型對象的 proto 繼續(xù)向上查找,直到找到 Object.prototype 為止

1.2.3 示例代碼

function Foo() {
  this.m = 10
  this.n = 24
  this.getM = function () {
    console.log(this.m)
  }
}
Foo.prototype.getM = function () {
  console.log(this.m)
}

Foo.prototype.getN = function () {
  console.log(this.n)
}

let foo1 = new Foo
let foo2 = new Foo
console.log(foo1.getM === foo2.getM)  
console.log(foo1.getN === foo2.getN)
console.log(foo1.__proto__.getN === Foo.prototype.getN)  
console.log(foo1.__proto__.getM === foo2.getM) 
console.log(foo1.getM === Foo.prototype.getM) 
console.log(foo1.constructor) 
console.log(Foo.prototype.__proto__.constructor) 
foo1.getM()  
foo1.__proto__.getM() 
foo2.getN()  
Foo.prototype.getN() 

1.3 重寫 new 方法

1.3.1 new 做了什么

  1. 創(chuàng)建實(shí)例對象
  2. 執(zhí)行構(gòu)造函數(shù), 將 this 指向?qū)嵗龑ο?/li>
  3. 處理返回值

1.3.2 模擬 new 實(shí)現(xiàn)

function Person(name) {
  this.name = name
}
Person.prototype.slogan = function () {
  console.log('前端開發(fā)')
}
Person.prototype.sayName = function () {
  console.log(`我們的名稱是${this.name}`)
}
//* 原生的寫法
// let p1 = new Person('syy')
// p1.slogan()
// p1.sayName()

function _new(Ctor, ...params) {
  //01 創(chuàng)建實(shí)例對象
  // let obj = {}
  // obj.__proto__ = Ctor.prototype
  let obj = Object.create(Ctor.prototype)

  // 02 調(diào)用構(gòu)造函數(shù), 改變this 指向
  let ret = Ctor.call(obj, ...params)

  // 03 處理返回結(jié)果
  if (ret !== null && /^(object|function)$/.test(typeof ret)) return ret
  return obj
}

let p2 = _new(Person, 'abc')
p2.slogan()
p2.sayName()
console.log(p2 instanceof Person)

1.4 Function 與 Object

Function 與 Object

1.4.1 函數(shù)多種角色

  1. 函數(shù)
    1. 普通函數(shù)調(diào)用(堆棧執(zhí)行及作用域)
    2. 構(gòu)造函數(shù)實(shí)例(原型及原型鏈)
  2. 對象
    1. 鍵值對
  3. 三種角色之間沒有必然的聯(lián)系,但是最核心的理念是函數(shù)就是函數(shù),函數(shù)是一等公民

1.4.2 經(jīng)典語錄

  1. Function 是一等公民,在 JS 中存在多種角色,普通函數(shù), 構(gòu)造函數(shù),對象
  2. 每一個(gè)對象都存在 --proto-- 屬性, 指向所屬類的原型對象(隱式原型,原型鏈屬性)
  3. 每一個(gè)函數(shù)都存在 prototype 屬性, 指向它的原型對象
  4. 所有函數(shù)都是 Function 內(nèi)置類的實(shí)例,且 Function 本身也是一個(gè)函數(shù)
  5. 所有對象都是 Object 的實(shí)例,且 Object 本身也是一個(gè)函數(shù)
  6. Function 與 Object 是二大并行的基類,雖然最終查找落腳點(diǎn)都是 Object 身上
  7. Function.prototype 原型對象是一個(gè)匿名函數(shù),但雖然它是一個(gè)函數(shù),它的處理機(jī)制和原型對象是一樣的,它的 --proto-- 屬性指向所屬類的原型對象,也就是 Object.prototype

1.4.3 無 prototype 屬性

  1. Function.prototype 原型是一個(gè)匿名函數(shù),但是它沒有 prototype 屬性
  2. 對象中使用 ES6 語法定義函數(shù) const obj = {say(){}} , say方法不具備
  3. 箭頭函數(shù)
  4. 不具備 prototype 屬性的函數(shù)是不能執(zhí)行 new 操作的

1.5 This 規(guī)律

在瀏覽器平臺(tái)下運(yùn)行 JS , 非函數(shù)當(dāng)中的 this 一般都指向 window 
因此這里討論的是函數(shù)執(zhí)行過程中的 this 
需要注意:ES6+ 的箭頭函數(shù)中是沒有自己 this 的,處理機(jī)制就是使用上下文當(dāng)中的 this 

1.5.1 this 是什么

  1. this 就是當(dāng)前函數(shù)執(zhí)行的主體(誰執(zhí)行了函數(shù)), 不等于執(zhí)行上下文或者當(dāng)前作用域
  2. syy 在拉勾教育講前端
    1. 講前端是一個(gè)動(dòng)作(函數(shù))
    2. 拉勾教育(執(zhí)行上下文)
    3. syy 主體,本次函數(shù)在當(dāng)前執(zhí)行上下文中執(zhí)行的 this 指向

1.5.2 常見 this 場景

  1. 事件綁定
  2. 普通函數(shù)
  3. 構(gòu)造函數(shù)
  4. 箭頭函數(shù)
  5. 基于 call bind apply 強(qiáng)制改變 this 指向

1.5.3 規(guī)律

  1. 事件綁定
    1. 不論是 DOM2 還是 DOM0 級事件綁定,事件觸發(fā)時(shí) this 一般都是被操作的元素
  2. 普通函數(shù)
    1. 函數(shù)執(zhí)行時(shí)查看前端是否有點(diǎn),如果有點(diǎn),則點(diǎn)前面的就是執(zhí)行主體, 沒有點(diǎn)就是 window , 嚴(yán)格模式下是 undefined
    2. 特殊情況
      1. 匿名函數(shù)中的 this 是 window 或者 undefined
      2. 回調(diào)函數(shù)中的 this 一般也是 window 或者 undefined
      3. 小括號語法
        1. 如果小括號只有一項(xiàng),則相當(dāng)于沒加
        2. 如果小括號當(dāng)中有多項(xiàng),則取出最后一項(xiàng),此時(shí)相當(dāng)于拷貝函數(shù),所以調(diào)用時(shí)主體是 window

1.5.4 練習(xí)

(function () {
  console.log(this)
})()

let arr = [1, 3, 5, 7]
obj = {
  name: '拉勾教育'
}
arr.map(function (item, index) {
  console.log(this)
}, obj)
------------------------------------------------------
//? 普通函數(shù)調(diào)用
let obj = {
  fn: function () {
    console.log(this, 111)
  }
}
let fn = obj.fn;
fn()  // window
obj.fn();  // obj
(10, fn, obj.fn)();
------------------------------------------------------
var a = 3, 
  obj = { a: 5 }
obj.fn = (function () { 
  this.a *= ++a
  return function (b) {
    this.a *= (++a) + b
    console.log(a)
  }
})();
var fn = obj.fn  
obj.fn(6)
fn(4)
console.log(obj.a, a)

二、異步編程與事件環(huán)

2.1 名詞說明

2.1.1 進(jìn)程與線程

  1. 進(jìn)程可以看做是一個(gè)應(yīng)用程序(例如打開瀏覽器或者瀏覽器打開一個(gè)界面)
  2. 線程是程序當(dāng)中具體做事情的人, 每個(gè)線程同一時(shí)刻只能做一件事情
  3. 一個(gè)進(jìn)程當(dāng)中可以包含多個(gè)線程

2.1.2 同步與異步

  1. 同步編程:一件一件的做事情,上一件沒有做完,下一件事情不會(huì)被處理(單線程)
  2. 異步編程:上一件事情沒有處理完,可以直接處理下一件事情(多線程)
  3. JS 基于單線程的 EventLoop 機(jī)制也可以實(shí)現(xiàn)異步編程

2.1.3 JS 中異步操作

  1. promise(then)
  2. aync await (generator)
  3. requestAnimationFrame
  4. 定時(shí)器操作
  5. ajax 網(wǎng)絡(luò)請求
  6. 事件綁定

2.1.4 JS 單線程

瀏覽器平臺(tái)下的 JS 代碼是由 JS 引擎執(zhí)行的, 所以它是單線程的

瀏覽器是多線程: GUI 渲染線程、JS引擎線程、事件觸發(fā)線程、定時(shí)器觸發(fā)線程、異步 http請求線程

  1. JS 中大部分代碼都是同步編程
  2. JS 可以基于單線程的 EventLoop (事件循環(huán)機(jī)制) 實(shí)現(xiàn)出異步效果

2.2 EventLoop 模型

事件循環(huán)模型圖

2.2.1 代碼執(zhí)行順序

  1. 瀏覽器加載界面之后會(huì)開啟一個(gè)線程來執(zhí)行 JS,稱之叫 JS引擎(主線程)
  2. JS引擎會(huì)自上而下的執(zhí)行 JS 代碼,此過程會(huì)遇到(定時(shí)器,網(wǎng)絡(luò)請求,事件綁定,promise....)
  3. 遇到上述代碼執(zhí)行之后,瀏覽器會(huì)開啟一個(gè) Event Queue(任務(wù)|事件)隊(duì)列 優(yōu)先級隊(duì)列結(jié)構(gòu)
  4. 在隊(duì)列當(dāng)中存在二個(gè)任務(wù)隊(duì)列:微任務(wù) microtask | 宏任務(wù) macrotask
  5. 最終會(huì)將遇到的異步任務(wù)存放到 Event Queue 隊(duì)列當(dāng)中(未執(zhí)行)
  6. 主線程會(huì)繼續(xù)向下執(zhí)行同步代碼,直到所有同步執(zhí)行完就會(huì)處理異步任務(wù)
  7. 進(jìn)入 Event Queue 當(dāng)中查找異步任務(wù),找到之后放入主線程中執(zhí)行(此時(shí)主線程又被占用)
  8. 執(zhí)行完一個(gè)異步任務(wù)之后,主線程再次空閑,此時(shí)再進(jìn)入 Event Queue 查找余下的異步任務(wù)

2.2.2 異步任務(wù)執(zhí)行順序

  1. 先執(zhí)行微任務(wù)(只要有微任務(wù)就不會(huì)處理宏任務(wù))
  2. 微任務(wù)(一般是誰先放置誰先執(zhí)行)
  3. 宏任務(wù)(一般是誰先到達(dá)的誰先執(zhí)行)

2.2.3 整體順序

  1. 同步任務(wù)
  2. 異步微任務(wù)
  3. 異步宏任務(wù)
- 如果同步任務(wù)執(zhí)行過程中遇到可執(zhí)行的異步任務(wù),此時(shí)依然需要等到同步任務(wù)執(zhí)行完
- 如果同步任務(wù)執(zhí)行完,還沒有可執(zhí)行的異步任務(wù),此時(shí)也只能等待 
- 不論何時(shí)放入的微任務(wù),只要異步微任務(wù)存在,就永遠(yuǎn)不會(huì)執(zhí)行異步宏任務(wù)

2.2.4 setTimeout

  1. 設(shè)置定時(shí)器這個(gè)操作是同步的
  2. 放置在 Event Queue 中的任務(wù)是異步宏任務(wù)
  3. 函數(shù)調(diào)用返回?cái)?shù)字,表示當(dāng)前是第幾個(gè)定時(shí)器
  4. 等待時(shí)間設(shè)置為 0 時(shí),也不是立即執(zhí)行,瀏覽器存在最快的反應(yīng)時(shí)間
  5. 定時(shí)器的等待時(shí)間到了之后,它的代碼可能還不會(huì)執(zhí)行(處于異步隊(duì)列中,同步任務(wù)還未完成執(zhí)行)

2.2.5 練習(xí)

setTimeout(() => {
  console.log('1')
}, 30)

console.log(2)

setTimeout(() => {
  console.log(3)
}, 20)

console.log(4)

console.time('AA')
// 消耗95ms
for (let i = 0; i < 88888888; i++) { }
console.timeEnd('AA')

console.log(5)

setTimeout(() => {
  console.log(6)
}, 18)

console.log(7)

setTimeout(() => {
  console.log(8)
}, 25)

console.log(9)
async function async1() {
  console.log('async1 執(zhí)行了')
  await async2()
  console.log('async1 結(jié)束了')
}

async function async2() {
  console.log('async2')
}

console.log('同步代碼執(zhí)行了')

setTimeout(() => {
  console.log('setTimeout')
}, 0)

async1()

new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => {
  console.log('promise2')
})

console.log('同步代碼結(jié)束了')
function fn1() {
  console.log('fn1 start')
  return new Promise((resolve) => {
    resolve('zcegg1')
  })
}
function fn2() {
  console.log('fn2 start')
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('zcegg2')
    }, 10)
  })
}
console.log('a')
setTimeout(async () => {
  console.log('b')
  await fn1()
  console.log('c')
}, 20)
for (let i = 0; i < 88888888; i++) { }  // 90ms  
console.log('d')
fn1().then((result) => {
  console.log('f')
})
fn2().then((result) => {
  console.log('g')
})
setTimeout(() => {
  console.log('h')
}, 0)
console.log('end')
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容