您當前的位置:首頁 > 動漫

20道JS原理題助你面試一臂之力!

作者:由 愛前端不愛戀愛 發表于 動漫時間:2020-11-02

本文針對目前常見的面試題,實現了相應方法的核心原理,部分邊界細節未處理。後續也會持續更新,希望對你有所幫助。

1、實現一個call函式

// 思路:將要改變this指向的方法掛到目標this上執行並返回

Function

prototype

mycall

=

function

context

{

if

typeof

this

!==

‘function’

{

throw

new

TypeError

‘not funciton’

}

context

=

context

||

window

context

fn

=

this

let

arg

=

[。。。

arguments

]。

slice

1

let

result

=

context

fn

(。。。

arg

delete

context

fn

return

result

}

2、實現一個apply函式

// 思路:將要改變this指向的方法掛到目標this上執行並返回

Function

prototype

myapply

=

function

context

{

if

typeof

this

!==

‘function’

{

throw

new

TypeError

‘not funciton’

}

context

=

context

||

window

context

fn

=

this

let

result

if

arguments

1

])

{

result

=

context

fn

(。。。

arguments

1

])

}

else

{

result

=

context

fn

()

}

delete

context

fn

return

result

}

3、實現一個bind函式

// 思路:類似call,但返回的是函式

Function

prototype

mybind

=

function

context

{

if

typeof

this

!==

‘function’

{

throw

new

TypeError

‘Error’

}

let

_this

=

this

let

arg

=

[。。。

arguments

]。

slice

1

return

function

F

()

{

// 處理函式使用new的情況

if

this

instanceof

F

{

return

new

_this

(。。。

arg

。。。

arguments

}

else

{

return

_this

apply

context

arg

concat

(。。。

arguments

))

}

}

}

4、instanceof的原理

// 思路:右邊變數的原型存在於左邊變數的原型鏈上

function

instanceOf

left

right

{

let

leftValue

=

left

__proto__

let

rightValue

=

right

prototype

while

true

{

if

leftValue

===

null

{

return

false

}

if

leftValue

===

rightValue

{

return

true

}

leftValue

=

leftValue

__proto__

}

}

5、Object.create的基本實現原理

// 思路:將傳入的物件作為原型

function

create

obj

{

function

F

()

{}

F

prototype

=

obj

return

new

F

()

}

6、new本質

function

myNew

fun

{

return

function

()

{

// 建立一個新物件且將其隱式原型指向建構函式原型

let

obj

=

{

__proto__

fun

prototype

}

// 執行建構函式

fun

call

obj

。。。

arguments

// 返回該物件

return

obj

}

}

function

person

name

age

{

this

name

=

name

this

age

=

age

}

let

obj

=

myNew

person

)(

‘chen’

18

// {name: “chen”, age: 18}

7、實現一個基本的Promise

// 未新增非同步處理等其他邊界情況

// ①自動執行函式,②三個狀態,③then

class

Promise

{

constructor

fn

{

// 三個狀態

this

state

=

‘pending’

this

value

=

undefined

this

reason

=

undefined

let

resolve

=

value

=>

{

if

this

state

===

‘pending’

{

this

state

=

‘fulfilled’

this

value

=

value

}

}

let

reject

=

value

=>

{

if

this

state

===

‘pending’

{

this

state

=

‘rejected’

this

reason

=

value

}

}

// 自動執行函式

try

{

fn

resolve

reject

}

catch

e

{

reject

e

}

}

// then

then

onFulfilled

onRejected

{

switch

this

state

{

case

‘fulfilled’

onFulfilled

()

break

case

‘rejected’

onRejected

()

break

default

}

}

}

8、實現淺複製

// 1。 。。。實現

let

copy1

=

{。。。{

x

1

}}

// 2。 Object。assign實現

let

copy2

=

Object

assign

({},

{

x

1

})

9、實現一個基本的深複製

// 1。 JOSN。stringify()/JSON。parse()

let

obj

=

{

a

1

b

{

x

3

}}

JSON

parse

JSON

stringify

obj

))

// 2。 遞迴複製

function

deepClone

obj

{

let

copy

=

obj

instanceof

Array

[]

{}

for

let

i

in

obj

{

if

obj

hasOwnProperty

i

))

{

copy

i

=

typeof

obj

i

===

‘object’

deepClone

obj

i

])

obj

i

}

}

return

copy

}

10、使用setTimeout模擬setInterval

// 可避免setInterval因執行時間導致的間隔執行時間不一致

setTimeout

function

()

{

// do something

setTimeout

arguments

callee

500

},

500

11、js實現一個繼承方法// 借用建構函式繼承例項屬性

// 借用建構函式繼承例項屬性

function

Child

()

{

Parent

call

this

}

// 寄生繼承原型屬性

function

()

{

let

Super

=

function

()

{}

Super

prototype

=

Parent

prototype

Child

prototype

=

new

Super

()

})()

12、實現一個基本的Event Bus

// 元件通訊,一個觸發與監聽的過程

class

EventEmitter

{

constructor

()

{

// 儲存事件

this

events

=

this

events

||

new

Map

()

}

// 監聽事件

addListener

type

fn

{

if

this

events

get

type

))

{

this

events

set

type

fn

}

}

// 觸發事件

emit

type

{

let

handle

=

this

events

get

type

handle

apply

this

[。。。

arguments

]。

slice

1

))

}

}

// 測試

let

emitter

=

new

EventEmitter

()

// 監聽事件

emitter

addListener

‘ages’

age

=>

{

console

log

age

})

// 觸發事件

emitter

emit

‘ages’

18

// 18

13、實現一個雙向資料繫結

let

obj

=

{}

let

input

=

document

getElementById

‘input’

let

span

=

document

getElementById

‘span’

// 資料劫持

Object

defineProperty

obj

‘text’

{

configurable

true

enumerable

true

get

()

{

console

log

‘獲取資料了’

},

set

newVal

{

console

log

‘資料更新了’

input

value

=

newVal

span

innerHTML

=

newVal

}

})

// 輸入監聽

input

addEventListener

‘keyup’

function

e

{

obj

text

=

e

target

value

})

完整實現可前往之前寫的:這應該是最詳細的響應式系統講解了

https://

juejin。im/post/5d26e368

e51d4577407b1dd7

14、實現一個簡單路由

// hash路由

class

Route

{

constructor

(){

// 路由儲存物件

this

routes

=

{}

// 當前hash

this

currentHash

=

‘’

// 繫結this,避免監聽時this指向改變

this

freshRoute

=

this

freshRoute

bind

this

// 監聽

window

addEventListener

‘load’

this

freshRoute

false

window

addEventListener

‘hashchange’

this

freshRoute

false

}

// 儲存

storeRoute

path

cb

{

this

routes

path

=

cb

||

function

()

{}

}

// 更新

freshRoute

()

{

this

currentHash

=

location

hash

slice

1

||

‘/’

this

routes

this

currentHash

]()

}

}

15、實現懶載入

<

ul

>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/1。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/2。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/3。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/4。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/5。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/6。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/7。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/8。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/9。png”

alt

=

“”

><

/li>

<

li

><

img

src

=

“。/imgs/default。png”

data

=

“。/imgs/10。png”

alt

=

“”

><

/li>

<

/ul>

let

imgs

=

document

querySelectorAll

‘img’

// 可視區高度

let

clientHeight

=

window

innerHeight

||

document

documentElement

clientHeight

||

document

body

clientHeight

function

lazyLoad

()

{

// 滾動捲去的高度

let

scrollTop

=

window

pageYOffset

||

document

documentElement

scrollTop

||

document

body

scrollTop

for

let

i

=

0

i

<

imgs

length

i

++

{

// 圖片在可視區冒出的高度

let

x

=

clientHeight

+

scrollTop

-

imgs

i

]。

offsetTop

// 圖片在可視區內

if

x

>

0

&&

x

<

clientHeight

+

imgs

i

]。

height

{

imgs

i

]。

src

=

imgs

i

]。

getAttribute

‘data’

}

}

}

// addEventListener(‘scroll’, lazyLoad) or setInterval(lazyLoad, 1000)

16、rem實現原理

// 原始配置

function

setRem

()

{

let

doc

=

document

documentElement

let

width

=

doc

getBoundingClientRect

()。

width

let

rem

=

width

/

75

doc

style

fontSize

=

rem

+

‘px’

}

// 監聽視窗變化

addEventListener

“resize”

setRem

17、手寫實現AJAX

// 1。 簡單流程

// 例項化

let

xhr

=

new

XMLHttpRequest

()

// 初始化

xhr

open

method

url

async

// 傳送請求

xhr

send

data

// 設定狀態變化回撥處理請求結果

xhr

onreadystatechange

=

()

=>

{

if

xhr

readyStatus

===

4

&&

xhr

status

===

200

{

console

log

xhr

responseText

}

}

// 2。 基於promise實現

function

ajax

options

{

// 請求地址

const

url

=

options

url

// 請求方法

const

method

=

options

method

toLocaleLowerCase

()

||

‘get’

// 預設為非同步true

const

async

=

options

async

// 請求引數

const

data

=

options

data

// 例項化

const

xhr

=

new

XMLHttpRequest

()

// 請求超時

if

options

timeout

&&

options

timeout

>

0

{

xhr

timeout

=

options

timeout

}

// 返回一個Promise例項

return

new

Promise

((

resolve

reject

=>

{

xhr

ontimeout

=

()

=>

reject

&&

reject

‘請求超時’

// 監聽狀態變化回撥

xhr

onreadystatechange

=

()

=>

{

if

xhr

readyState

==

4

{

// 200-300 之間表示請求成功,304資源未變,取快取

if

xhr

status

>=

200

&&

xhr

status

<

300

||

xhr

status

==

304

{

resolve

&&

resolve

xhr

responseText

}

else

{

reject

&&

reject

()

}

}

}

// 錯誤回撥

xhr

onerror

=

err

=>

reject

&&

reject

err

let

paramArr

=

[]

let

encodeData

// 處理請求引數

if

data

instanceof

Object

{

for

let

key

in

data

{

// 引數拼接需要透過 encodeURIComponent 進行編碼

paramArr

push

encodeURIComponent

key

+

‘=’

+

encodeURIComponent

data

key

]))

}

encodeData

=

paramArr

join

‘&’

}

// get請求拼接引數

if

method

===

‘get’

{

// 檢測url中是否已存在 ? 及其位置

const

index

=

url

indexOf

‘?’

if

index

===

-

1

url

+=

‘?’

else

if

index

!==

url

length

-

1

url

+=

‘&’

// 拼接url

url

+=

encodeData

}

// 初始化

xhr

open

method

url

async

// 傳送請求

if

method

===

‘get’

xhr

send

null

else

{

// post 方式需要設定請求頭

xhr

setRequestHeader

‘Content-Type’

‘application/x-www-form-urlencoded;charset=UTF-8’

xhr

send

encodeData

}

})

}

18、實現拖拽

window

onload

=

function

()

{

// drag處於絕對定位狀態

let

drag

=

document

getElementById

‘box’

drag

onmousedown

=

function

e

{

var

e

=

e

||

window

event

// 滑鼠與拖拽元素邊界的距離 = 滑鼠與可視區邊界的距離 - 拖拽元素與邊界的距離

let

diffX

=

e

clientX

-

drag

offsetLeft

let

diffY

=

e

clientY

-

drag

offsetTop

drag

onmousemove

=

function

e

{

// 拖拽元素移動的距離 = 滑鼠與可視區邊界的距離 - 滑鼠與拖拽元素邊界的距離

let

left

=

e

clientX

-

diffX

let

top

=

e

clientY

-

diffY

// 避免拖拽出可視區

if

left

<

0

{

left

=

0

}

else

if

left

>

window

innerWidth

-

drag

offsetWidth

{

left

=

window

innerWidth

-

drag

offsetWidth

}

if

top

<

0

{

top

=

0

}

else

if

top

>

window

innerHeight

-

drag

offsetHeight

{

top

=

window

innerHeight

-

drag

offsetHeight

}

drag

style

left

=

left

+

‘px’

drag

style

top

=

top

+

‘px’

}

drag

onmouseup

=

function

e

{

this

onmousemove

=

null

this

onmouseup

=

null

}

}

}

19、實現一個節流函式

// 思路:在規定時間內只觸發一次

function

throttle

fn

delay

{

// 利用閉包儲存時間

let

prev

=

Date

now

()

return

function

()

{

let

context

=

this

let

arg

=

arguments

let

now

=

Date

now

()

if

now

-

prev

>=

delay

{

fn

apply

context

arg

prev

=

Date

now

()

}

}

}

function

fn

()

{

console

log

‘節流’

}

addEventListener

‘scroll’

throttle

fn

1000

))

20、實現一個防抖函式

// 思路:在規定時間內未觸發第二次,則執行

function

debounce

fn

delay

{

// 利用閉包儲存定時器

let

timer

=

null

return

function

()

{

let

context

=

this

let

arg

=

arguments

// 在規定時間內再次觸發會先清除定時器後再重設定時器

clearTimeout

timer

timer

=

setTimeout

function

()

{

fn

apply

context

arg

},

delay

}

}

function

fn

()

{

console

log

‘防抖’

}

addEventListener

‘scroll’

debounce

fn

1000

))

原作者姓名: 陳煜侖

原出處:掘金

原文連結:20道JS原理題助你面試一臂之力!

標簽: imgs  li  PNG  xhr  function