Phỏng vấn JavaScript người ta hỏi gì? – Phần 1 | Chanh’s blog

Phỏng vấn JavaScript người ta hỏi gì? – Phần 1

Published: 2018-10-23 20:19:18

Are these questions relevant?

Hình trên mình lấy từ một video trên youtube. Cái background chỉ là minh họa cho đoạn tweet mà anh speaker trong video đó thêm vào thôi.

Tác giả của tweet đó là anh Ried Draper (một kỹ sư ở GitHub) sau khi ngồi trơ mắt ngó một ứng viên bỏ dở buổi interview và bước đi không ngoảnh lại. Mình tạm dịch là:

“Đoạn code Fizz Buzz ưa thích của tôi vừa bỏ ra ngoài [giữa] buổi phỏng vấn”

Nếu bạn biết về Fizz Buzz problem thì có lẽ sẽ hiểu vì sao anh chàng đó bỏ về. Mình cùng đoán nhé. Anh ta là một Front-end developer với 5 năm kinh nghiệm, full tự học và không có bằng Computer Science gì. Hoặc cũng có thể anh ta chỉ là một Junior Developer 🤔?! Nhưng rất có thể hằng ngày anh ta chủ yếu chỉ làm việc với lib và framework, và tạo ra những tuyệt tác trong khi không hoàn toàn nắm rõ hay hiểu bản chất của đống tools đó.

Giống như phần lớn dân dev chúng ta thôi. Nên gặp phải những câu hỏi khá là basic như thế, những vấn đề mà ta chẳng mấy lúc hoặc chả gặp trong khi hằng ngày vẫn code đẹp và app vẫn chạy mượt mà, thì phần đông ai cũng thấy hơi nản: “Thằng cha này hỏi toàn ba thứ linh tinh!” 😂.

Phần 1 này sẽ tập trung vào những câu hỏi kiểu như vậy.

Mục lục

Fizz Buzz

Cho i chạy từ 1 tới 100. In fizz ra console nếu i chia hết cho 3, buzz nếu chia hết cho 5 và fizzbuzz nếu chia hết cho cả 3 và 5

Solution có thể tham khảo ở đây: https://gist.github.com/jaysonrowe/1592432.

Vậy mục đích của người phỏng vấn là gì? Bạn thấy đấy, câu hỏi tuy đơn giản nhưng có quá trời đáp án khác nhau. Quan điểm ở đây là Software Development không chỉ có cắm đầu code, mà còn nhiều thứ khác: clean code, optimization và quan trọng là cách giải quyết vấn đề.

Dạng câu hỏi này sẽ là chưa đủ để nhận diện một developer giỏi, nhưng nó sẽ nhận diện được những kẻ yếu. Vậy nên xài câu này là một hướng đi đúng đắn của nhà tuyển dụng để phân loại ứng viên. Càng rõ hơn khi yêu cầu họ viết code ra giấy và giới hạn chỉ trong 2-3 phút.

 

for

(i=

0

; i<

100

; )

console

.

log

((++i%

3

?

''

:

'Fizz'

) + (i%

5

?

''

:

'Buzz'

) || i)

JavaScript Hoisting

Về cơ bản, khi code được compile, tất cả variable khai báo sau từ khóa var sẽ được “hoiste” lên phía trên cùng của file .js (global) hay trên cùng của function (local). Bất kể bạn khai báo đống varibale đó ở đâu.

 

console

.

log

(myName);

var

myName =

"Lemon'"

;

var

myName;

console

.

log

(myName); myName =

"Lemon'"

;

Vậy, hỏi câu này làm gì? Nếu chăm đọc code người khác, bạn hẳn sẽ thấy nhiều người có thói quen declare biến trên cùng rồi phía dưới mới assign value sau. Đó là một best practice (maybe) trước cái concept “hoisting” này.

Lưu ý rằng “hoisting” cũng áp dụng cho cả letconst. Bạn có thể xem chi tiết ở bài viết này.

== vs ===

Javascript bà điên có hai kiểu so sánh bằng: === (!==) and the evil ==(!=). === là toán tử so sánh rất bình thường và an toàn: cùng type cùng value thì true không thì false. == thì đôi lúc sẽ giết ta khi 2 toán hạng khác type, bằng cách cố gắng ép về cùng 1 type để so sánh.

''

==

'0'

0

==

''

0

==

'0'

false

==

'false'

false

==

'0'

false

==

undefined

false

==

null

null

==

undefined

' \t\r\n '

==

0

Vậy làm sao cho nó an toàn? Theo mình thì nên xài === là an toàn nhất vì nó rõ ràng. Team đông thì nên xài một linter, thống nhất và đặt ra các rule để tránh các rủi ro chết chóc do JS si đa gây nên.

Undeclared vs undefined vs null

undeclared

Không khai báo trước khi xài

const

bar = foo +

5

;

undefined

  • Khai báo nhưng không có value nào cả (nhưng mà const a; thì báo lỗi chứ a không phải là undefined nhé).
  • Function không trả về gì hết
  • Truy xuất value của một property không có trong object hay tại một index vượt quá length của array.

    const

    foo = {

    xx

    :

    1

    }; foo.

    xxx

    ;

    const

    bars = [

    1

    ,

    2

    ]; bars[

    2

    ];

null

null là một value. Value đó không là gì cả, nhưng nó là một value 😖.

Kiểm tra undefined và null

 

let

foo;

console

.

log

(

typeof

foo);

console

.

log

(

typeof

bar):

console

.

log

(foo ===

undefined

);

const

baz =

'undefined'

;

console

.

log

(baz ===

undefined

);

const

foo =

null

;

console

.

log

(

typeof

foo);

console

.

log

(foo ===

null

);

if

(!foo) {

Kiểm tra một array có tồn tại hoặc empty không

if

(!

Array

.

isArray

(array) || !array.

length

) { }

Trình bày về Scope và Context trong JavaScript

Scope

Scope trong JS cũng như các ngôn ngữ khác, có Global và Local scope. Global scope tồn tại cho đến khi ứng dụng bị tắt. Local scope tồn tại cho đến khi function chạy xong. ES6 giới thiệu letconst mang local scope đến với block statement.

if

(

true

) {

var

name =

'Lemon'

;

let

likes =

'Coding'

;

const

skills =

'JavaScript and C#'

; }

console

.

log

(name);

console

.

log

(likes);

console

.

log

(skills);

Context

Context! Hmm, là một khái niệm rất quan trọng cần phải nắm nếu không muốn sấp mặt với Javascript. Về cơ bản thì context là value của this. Trong “global scope” thì context luôn là window object.

class

User

{

logName

(

) {

console

.

log

(

this

); } } (

new

User

).

logName

();

function

logFunction

(

) {

console

.

log

(

this

); }

new

logFunction

();

Phải cẩn thận với arrow function nhé.

const

foo = {

a

:

function

(

) {

console

.

log

(

this

); },

b

:

() =>

{

console

.

log

(

this

); } } foo.

a

(); foo.

b

();

Đọc thêm về Scope và Context ở đây.

Closure là gì?

Hãy bắt đầu với một ví dụ (được lấy từ một bài viết trên Kipalog), từ đó bạn sẽ dễ trình bày hơn với interviewer.

function

outer

(

x

) {

function

inner

(

y

) {

return

x + y; }

return

inner; } fn_inner =

outer

(

3

); result =

fn_inner

(

5

); result1 =

outer

(

3

)(

5

);

Về cơ bản, sau khi execute xong, outer() trả về một function fn_inner() và đồng thời một bao đóng (closure) được tạo ra gói cái context bao gồm con số 3 đó lại. Hay nói cách khác là đóng gói biến x tại thời điểm outer() được gọi.

Vậy khi nào thì xài closure? Trường hợp tiêu biểu nhất là khi cần assign các hàm eventHandler cho nhiều element một lúc.

const

addHandlers =

function

(

nodes

) {

const

helper =

function

(

i

) {

return

function

(

e

) {

alert

(i); }; };

for

(

let

i =

0

; i < nodes.

length

; ++i) { nodes[i].

onclick

=

helper

(i); } };

Nói một cách hàn lâm hơn: Một trong những ứng dụng mạnh mẽ của closure là sử dụng hàm outer() như một “function factory”.

Bind, Call và Apply

Tôi đi code dạo đã viết về 3 thứ đó khá là thú vị và sinh động dễ hiểu rồi. Mình chỉ muốn lưu ý một chút là cái bind đó rất quan trọng khi bạn code ReactJS. Nó đảm bảo value của this là đúng trong callback khi handle event, chi tiết ở đây.

Event Loop, Call Stack và Callback Queue là gì?

Riêng câu này xứng đáng có một bài viết riêng. Và nó đây: Javascript hoạt động như thế nào?

Lời kết

Dù bạn không thể trả lời hay chỉ trả lời được phần nào đó thôi trong mớ câu hỏi trên, thì mình tin rằng, những kiến thức đó rất bổ ích. Chúng sẽ giúp bạn tránh những lỗi ngớ ngẩn hay gặp và thiết kế chương trình được tốt hơn.

Phần 1 tới đây thôi. Mời các bạn ghé đọc tiếp phần 2.

References