国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

做面試的不倒翁:一道事件循環(huán)題引發(fā)的血案

ispring / 2445人閱讀

摘要:通過(guò)查看的文檔可以發(fā)現(xiàn)整個(gè)分為個(gè)階段定時(shí)器相關(guān)任務(wù),中我們關(guān)注的是它會(huì)執(zhí)行和中到期的回調(diào)執(zhí)行某些系統(tǒng)操作的回調(diào)內(nèi)部使用執(zhí)行,一定條件下會(huì)在這個(gè)階段阻塞住執(zhí)行的回調(diào)如果或者關(guān)閉了,就會(huì)在這個(gè)階段觸發(fā)事件,執(zhí)行事件的回調(diào)的代碼在文件中。

這次我們就不要那么多前戲,直奔主題,我們的龍門陣正式開(kāi)始。

開(kāi)局一道題,內(nèi)容全靠吹。(此處應(yīng)有滑稽)

// 文件名: index.js
// 我們盡量模擬所有的異步場(chǎng)景,包括 timers、Promise、nextTick等等
setTimeout(() => {
  console.log("timeout 1");
}, 1);

process.nextTick(() => {
  console.log("nextTick 1");
});

fs.readFile("./index.js", (err, data) => {
  if(err) return;
  console.log("I/O callback");
  process.nextTick(() => {
      console.log("nextTick 2");
  });
});

setImmediate(() => {
  console.log("immediate 1");
  process.nextTick(() => {
      console.log("nextTick 3");
  });
});

setTimeout(() => {
  console.log("timeout 2");
  process.nextTick(() => {
    console.log("nextTick 4");
  });
}, 100);

new Promise((resolve, reject) => {
  console.log("promise run");
  process.nextTick(() => {
      console.log("nextTick 5");
  });
  resolve("promise then");
  setImmediate(() => {
      console.log("immediate 2");
  });
}).then(res => {
  console.log(res);
});

note: 上面的代碼執(zhí)行環(huán)境是 node v10.7.0,瀏覽器的事件循環(huán)和 node 還是有一點(diǎn)區(qū)別的,有興趣的可以自己找資料看一看。

好了,上面的代碼涉及到定時(shí)器、nextTick、Promise、setImmediate 和 I/O 操作。頭皮有點(diǎn)小發(fā)麻哈,大家想好答案了么?檢查一下吧!

promise run
nextTick 1
nextTick 5
promise then
timeout 1
immediate 1
immediate 2
nextTick 3
I/O callback
nextTick 2
timeout 2
nextTick 4

怎么樣?跟自己想的一樣么?不一樣的話,就聽(tīng)我慢慢道來(lái)。

event loop

在 Node.js 中,event loop 是基于 libuv 的。通過(guò)查看 libuv 的文檔可以發(fā)現(xiàn)整個(gè) event loop 分為 6 個(gè)階段:

timers: 定時(shí)器相關(guān)任務(wù),node 中我們關(guān)注的是它會(huì)執(zhí)行 setTimeout() 和 setInterval() 中到期的回調(diào)

pending callbacks: 執(zhí)行某些系統(tǒng)操作的回調(diào)

idle, prepare: 內(nèi)部使用

poll: 執(zhí)行 I/O callback,一定條件下會(huì)在這個(gè)階段阻塞住

check: 執(zhí)行 setImmediate 的回調(diào)

close callbacks: 如果 socket 或者 handle 關(guān)閉了,就會(huì)在這個(gè)階段觸發(fā) close 事件,執(zhí)行 close 事件的回調(diào)

   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

event loop 的代碼在文件 deps/uv/src/unix/core.c 中。

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;

  // 確定 event loop 是否繼續(xù)
  r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);

  while (r != 0 && loop->stop_flag == 0) {
    uv__update_time(loop); // 更新時(shí)間
    uv__run_timers(loop); // timers 階段
    ran_pending = uv__run_pending(loop); // pending callbacks 階段
    uv__run_idle(loop); // idle 階段
    uv__run_prepare(loop); // prepare 階段

    timeout = 0;
    // 設(shè)置 poll 階段的超時(shí)時(shí)間,有以下情況超時(shí)時(shí)間設(shè)為 0,此時(shí) poll 不會(huì)阻塞
    // 1. stop_flag 不為 0
    // 2. 沒(méi)有活躍的 handles 和 request
    // 3. idle、pending callback、close 階段 handle 隊(duì)列不為空
    // 否則的話會(huì)將超時(shí)時(shí)間設(shè)置成距離當(dāng)前時(shí)間最近的 timer 的時(shí)間
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

    // poll 階段
    uv__io_poll(loop, timeout);
    // check 階段
    uv__run_check(loop);
    // close 階段
    uv__run_closing_handles(loop);

    if (mode == UV_RUN_ONCE) {
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
  }

  if (loop->stop_flag != 0)
    loop->stop_flag = 0;

  return r;
}
注冊(cè)加觸發(fā)

這一小節(jié)我們主要看看 Node 如何將我們寫(xiě)的定時(shí)器等等注冊(cè)到 event loop 中去并執(zhí)行的。

以 setTimeout 為例,首先我們進(jìn)到了 timers.js 這個(gè)文件中,找到了 setTimeout 函數(shù),我們主要關(guān)注這么兩句:

function setTimeout(callback, after, arg1, arg2, arg3) {
  // ...
  const timeout = new Timeout(callback, after, args, false);
  active(timeout);

  return timeout;
}

我們看到它 new 了一個(gè) Timeout 類,我們順著這條線索找到了 Timeout 的構(gòu)造函數(shù):

function Timeout(callback, after, args, isRepeat) {
  // ...
  this._onTimeout = callback;
  // ...
}

我們主要關(guān)注這一句,Node 將回調(diào)掛載到了 _onTimeout 這個(gè)屬性上。那么這個(gè)回調(diào)是在什么時(shí)候執(zhí)行的呢?我們?nèi)炙岩幌?_onTimeout(),我們可以發(fā)現(xiàn)是一個(gè)叫做 ontimeout 的方法執(zhí)行了回調(diào),好了,我們開(kāi)始順藤摸瓜,可以找到這么一條調(diào)用路徑 processTimers -> listOnTimeout -> tryOnTimeout -> ontimeout -> _onTimeout

最后的最后,我們?cè)谖募念^部發(fā)現(xiàn)了這么幾行代碼:

const {
  getLibuvNow,
  setupTimers,
  scheduleTimer,
  toggleTimerRef,
  immediateInfo,
  toggleImmediateRef
} = internalBinding("timers");
setupTimers(processImmediate, processTimers);

我們一看,setupTimers 是從 internalBinding("timers") 獲取的,我們?nèi)タ匆幌?internalBinding 就知道這就是 js 代碼和內(nèi)建模塊關(guān)聯(lián)的地方了。于是,我們順著這條線索往下找,我們?nèi)?src 目錄下去找叫 timers 的文件,果不其然,我們找到一個(gè)叫 timers.cc 的文件,同時(shí),找到了一個(gè)叫 SetupTimers 的函數(shù)。

void SetupTimers(const FunctionCallbackInfo& args) {
  CHECK(args[0]->IsFunction());
  CHECK(args[1]->IsFunction());
  auto env = Environment::GetCurrent(args);

  env->set_immediate_callback_function(args[0].As());
  env->set_timers_callback_function(args[1].As());
}

上面的 args[1] 就是我們傳遞的 processTimers,在這個(gè)函數(shù)中我們其實(shí)就完成了 processTimers 的注冊(cè),它成功的注冊(cè)到了 node 中。

那是如何觸發(fā)的回調(diào)呢?這里我們首先先看到 event loop 代碼中的 timers 階段執(zhí)行的函數(shù),然后跟進(jìn)去:

void uv__run_timers(uv_loop_t* loop) {
  struct heap_node* heap_node;
  uv_timer_t* handle;

  for (;;) {
    heap_node = heap_min(timer_heap(loop));
    if (heap_node == NULL)
      break;

    handle = container_of(heap_node, uv_timer_t, heap_node);
    if (handle->timeout > loop->time)
      break;

    uv_timer_stop(handle);
    uv_timer_again(handle);
    handle->timer_cb(handle);
  }
}

這段代碼我們將我們的目光放在 handle->timer_cb(handle) 這一行,這個(gè)函數(shù)是在哪兒定義的呢?我們?nèi)炙岩幌?timer_cb 發(fā)現(xiàn) uv_timer_start 中有這么一行代碼:

handle->timer_cb = cb;

所以我們知道是調(diào)用了 uv_timer_start 將回調(diào)函數(shù)掛載到了 handle 上。那么 cb 又是什么呢?其實(shí)你沿著代碼上去找就能發(fā)現(xiàn)其實(shí) cb 就是 timers_callback_function,眼熟對(duì)么?這就是我們上面注冊(cè)進(jìn)來(lái)觸發(fā)回調(diào)的函數(shù) processTimers。

恍然大悟,原來(lái)是這么觸發(fā)的回調(diào),現(xiàn)在還有個(gè)問(wèn)題,誰(shuí)去調(diào)用的 uv_timer_start 呢?這個(gè)問(wèn)題就簡(jiǎn)單了,我們通過(guò)源碼可以知道是 ScheduleTimer 這個(gè)函數(shù)調(diào)用了,是不是感覺(jué)很熟悉,對(duì),這個(gè)函數(shù)就是我們通過(guò) internalBinding 引進(jìn)來(lái)的 scheduleTimer 函數(shù)。

在這個(gè)地方就有點(diǎn)不一樣了?,F(xiàn)在最新的 tag 版本和 github 上 node 最新的代碼是有區(qū)別的,在一次 pr 中,將 timer_wrap.cc 重構(gòu)成了 timers.cc,并且移除了 TimerWrap 類,再說(shuō)下面的區(qū)別之前,先補(bǔ)充一下 timer 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu):

// 這是在有 TimeWrap 的版本
// 對(duì)應(yīng)的時(shí)間后面是一個(gè) timer 鏈表
refedLists = {
  1000: TimeWrap._list(TimersList(item<->item<->item<->item)),
  2000: TimeWrap._list(TimersList(item<->item<->item<->item)),
};
// 這是 scheduleTimer 的版本
refedLists = {
  1000: TimersList(item<->item<->item<->item),
  2000: TimersList(item<->item<->item<->item),
};

TimeWrap 的版本里,js 是通過(guò)調(diào)用實(shí)例化后的 start() 函數(shù)去調(diào)用了 uv_timer_start。

scheduleTimer 版本是注冊(cè)定時(shí)器的時(shí)候通過(guò)比較哪個(gè)定時(shí)器是最近要執(zhí)行的,從而將對(duì)應(yīng)時(shí)間的 timerList 注冊(cè)到 uv_timer 中去。

那么,為什么要這么改呢?是為了讓定時(shí)器和 Immediate 擁有更相似的行為,也就是將單個(gè) uv_timer_t handle 存在 Environment 上(Immediate 是有一個(gè) ImmediateQueue,這也是個(gè)鏈表)。

這里就只說(shuō)了一個(gè) timer,其他的大家就自己去看看吧,順著這個(gè)思路大家肯定會(huì)有所收獲的。

事件循環(huán)流程

在加載 node 的時(shí)候,將 setTimeout、setInterval 的回調(diào)注冊(cè)到 timerList,將 Promise.resolve 等 microTask 的回調(diào)注冊(cè)到 microTasks,將 setImmediate 注冊(cè)到 immediateQueue 中,將 process.nextTick 注冊(cè)到 nextTickQueue 中。

當(dāng)我們開(kāi)始 event loop 的時(shí)候,首先進(jìn)入 timers 階段(我們只看跟我們上面說(shuō)的相關(guān)的階段),然后就判斷 timerList 的時(shí)間是否到期了,如果到期了就執(zhí)行,沒(méi)有就下一個(gè)階段(其實(shí)還有 nextTick,等下再說(shuō))。

接下來(lái)我們說(shuō) poll 階段,在這個(gè)階段,我們先計(jì)算需要在這個(gè)階段阻塞輪詢的時(shí)間(簡(jiǎn)單點(diǎn)就是下個(gè) timer 的時(shí)間),然后等待監(jiān)聽(tīng)的事件。

下個(gè)階段是 check 階段,對(duì)應(yīng)的是 immediate,當(dāng)有 immediateQueue 的時(shí)候就會(huì)跳過(guò) poll 直接到 check 階段執(zhí)行 setImmediate 的回調(diào)。

那有同學(xué)就要問(wèn)了,nextTick 和 microTasks 去哪兒了?。縿e慌,聽(tīng)我慢慢道來(lái)。

process.nextTick 和 microTasks

現(xiàn)在我們有了剛剛找 timer 的經(jīng)驗(yàn),我們繼續(xù)去看看 nextTick 是怎么執(zhí)行的。

經(jīng)過(guò)排查我們能找到一個(gè)叫 _tickCallback 的函數(shù),它不斷的從 nextTickQueue 中獲取 nextTick 的回調(diào)執(zhí)行。

function _tickCallback() {
    let tock;
    do {
      while (tock = queue.shift()) {
        // ...
        const callback = tock.callback;
        if (tock.args === undefined)
          callback();
        else
          Reflect.apply(callback, undefined, tock.args);

        emitAfter(asyncId);
      }
      tickInfo[kHasScheduled] = 0;
      runMicrotasks();
    } while (!queue.isEmpty() || emitPromiseRejectionWarnings());
    tickInfo[kHasPromiseRejections] = 0;
  }

我們看到了什么?在將 nextTick 的回調(diào)執(zhí)行完之后,它執(zhí)行了 runMicrotasks。一切都真相大白了,microTasks 的執(zhí)行時(shí)機(jī)是當(dāng)執(zhí)行完所有的 nextTick 的回調(diào)之后。那 nextTick 又是在什么時(shí)候執(zhí)行的呢?

這就需要我們?nèi)フ?C++ 的代碼了,在 bootstrapper.cc 里找到了 BOOTSTRAP_METHOD(_setupNextTick, SetupNextTick),所以我們就要去找 SetupNextTick 函數(shù)。

void SetupNextTick(const FunctionCallbackInfo& args) {
  Environment* env = Environment::GetCurrent(args);
  // ...
  env->set_tick_callback_function(args[0].As());
  // ...
}

我們關(guān)注這一句,是不是很熟啊,跟上面 timer 一樣是吧,我們將 __tickCallback 注冊(cè)到了 node,在 C++ 中通過(guò) tick_callback_function 來(lái)調(diào)用這個(gè)函數(shù)。

我們通過(guò)查看源碼可以發(fā)現(xiàn)是 InternalCallbackScope 這個(gè)類調(diào)用 Close 函數(shù)的時(shí)候就會(huì)觸發(fā) nextTixk 執(zhí)行。

void InternalCallbackScope::Close() {
  if (closed_) return;
  closed_ = true;
  HandleScope handle_scope(env_->isolate());
  // ...
  if (!tick_info->has_scheduled()) {
    env_->isolate()->RunMicrotasks();
  }
  // ...
  if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) {
    return;
  }
  // ...
  if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
    failed_ = true;
  }
}

可能有同學(xué)有疑問(wèn)了,為啥在執(zhí)行 nextTick 上面還有 RunMicrotasks 呢?其實(shí)這是對(duì) event loop 的優(yōu)化,假如沒(méi)有 process.nextTick 就直接從 node 里面調(diào)用 RunMicrotasks 加快速度。

現(xiàn)在在 node.cc 里我們找到了調(diào)用 Close 的地方:

MaybeLocal InternalMakeCallback(Environment* env,
                                       Local recv,
                                       const Local callback,
                                       int argc,
                                       Local argv[],
                                       async_context asyncContext) {
  CHECK(!recv.IsEmpty());
  InternalCallbackScope scope(env, recv, asyncContext);

  scope.Close();

  return ret;
}

InternalMakeCallback() 則是在 async_wrap.ccAsyncWrap::MakeCallback() 中被調(diào)用。

找了半天,只找到了 setImmediate 注冊(cè)時(shí),注冊(cè)函數(shù)執(zhí)行回調(diào)運(yùn)行了這個(gè)函數(shù),沒(méi)有找到 timer 的。之前因?yàn)槭褂玫?TimeWrap,TimeWrap 繼承了 AsyncWrap,在執(zhí)行回調(diào)的時(shí)候調(diào)用了 MakeCallback(),問(wèn)題是現(xiàn)在移除了 TimeWrap,那是怎么調(diào)用的呢?我們會(huì)到 js 代碼,發(fā)現(xiàn)了這樣的代碼:

const { _tickCallback: runNextTicks } = process;
function processTimers(now) {
  runNextTicks();
}

一切都明了了,在移除了 TimeWrap 之后,將 _tickCallback 放到了這里執(zhí)行,所以我們剛剛在 C++ 里找不到。

其實(shí),每一個(gè)階段執(zhí)行完之后,都會(huì)去執(zhí)行 _tickCallback ,只是方式可能有點(diǎn)不同。

答案解析

好了,剛剛了解了關(guān)于 event loop 的一些情況,我們?cè)賮?lái)看看文章開(kāi)頭的那段代碼,我們一起來(lái)分析。

第一步

首先運(yùn)行 Promise 里的代碼,輸出了 promise run,然后 promise.resolve 將 then 放入 microTasks。

第二步

這里要提到的一點(diǎn)是 nextTick 在注冊(cè)之后,bootstrap 構(gòu)建結(jié)束后運(yùn)行SetupNextTick函數(shù),這時(shí)候就會(huì)清空 nextTickQueue 和 MicroTasks,所以輸出 nextTick 1、nextTick 5、promise then

第三步

在 bootstrap 之后便進(jìn)入了 event loop,第一個(gè)階段 timers,這時(shí) timeout 1 定時(shí)器時(shí)間到期,執(zhí)行回調(diào)輸出 timeout 1,timerList 沒(méi)有其他定時(shí)器了,去清空 nextTickQueue 和 MicroTasks,沒(méi)有任務(wù),這時(shí)繼續(xù)下階段,這時(shí)候有 immediate,所以跳過(guò) poll,進(jìn)入 check,執(zhí)行 immediate 回調(diào),輸出 immediate 1 和 immediate 2,并將 nextTick 3 推入 nextTickQueue,階段完成 immediateQueue 沒(méi)有需要處理的東西了,就去清空 nextTickQueue 和 MicroTasks 輸出 nextTick 3。

第四步

在這一輪,文件讀取完成,并且 timers 沒(méi)到期,進(jìn)入 poll 階段,超時(shí)時(shí)間設(shè)置為 timeout 2 的時(shí)間,執(zhí)行回調(diào)輸出 I/O callback,并且向 nextTickQueue 推入 nextTick 2。阻塞過(guò)程中沒(méi)有其他的 I/O 事件,去清空 nextTickQueue 和 MicroTasks,輸出 nextTick 2

第五步

這時(shí)候又到了 timers 階段,執(zhí)行 timeout 2 的回調(diào),輸出 timeout 2,將 nextTick 4 推入 nextTickQueue,這時(shí) timeList 已經(jīng)沒(méi)有定時(shí)器了,清空 nextTickQueue 和 MicroTasks 輸出 nextTick 4。

總結(jié)

不知道大家懂了沒(méi)有,整個(gè)過(guò)程其實(shí)還比較粗糙,在學(xué)習(xí)過(guò)程中也看了不少的源碼分析,但是 node 發(fā)展很快,很多分析已經(jīng)過(guò)時(shí)了,源碼改變了不少,但是對(duì)于理清思路還是很有作用的。

各位看官如果覺(jué)得還行、OK、有點(diǎn)用,歡迎來(lái)我 GitHub 給個(gè)小星星,我會(huì)很舒服的,哈哈。

文 / 小烜同學(xué)
天衣無(wú)縫的秘密是:做技術(shù),你快樂(lè)嗎?

編 / 熒聲

本文已由作者授權(quán)發(fā)布,版權(quán)屬于創(chuàng)宇前端。歡迎注明出處轉(zhuǎn)載本文。本文鏈接:https://knownsec-fed.com/2018...

想要訂閱更多來(lái)自知道創(chuàng)宇開(kāi)發(fā)一線的分享,請(qǐng)搜索關(guān)注我們的微信公眾號(hào):創(chuàng)宇前端(KnownsecFED)。歡迎留言討論,我們會(huì)盡可能回復(fù)。

歡迎點(diǎn)贊、收藏、留言評(píng)論、轉(zhuǎn)發(fā)分享和打賞支持我們。打賞將被完全轉(zhuǎn)交給文章作者。

感謝您的閱讀。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/98271.html

相關(guān)文章

  • 一道JS面試引發(fā)血案

    摘要:項(xiàng)目組長(zhǎng)給我看了一道面試別人的面試題。打鐵趁熱,再來(lái)一道題來(lái)加深下理解。作者以樂(lè)之名本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVbur0z?w=600&h=400); 剛?cè)肼毿鹿?,屬于公司萌新一枚,一天下午?duì)著屏幕看代碼架構(gòu)時(shí)。BI項(xiàng)目組長(zhǎng)給我看了一道面試別人的JS面試題。 雖然答對(duì)了,但把理由說(shuō)錯(cuò)了,照樣不及格。 ...

    fantix 評(píng)論0 收藏0
  • 一道JS面試引發(fā)"血案",透過(guò)現(xiàn)象尋本質(zhì),再?gòu)谋举|(zhì)看現(xiàn)象

    摘要:一看這二逼就是周杰倫的死忠粉看看控制臺(tái)輸出,確實(shí)沒(méi)錯(cuò)就是對(duì)象。從根本上來(lái)說(shuō),作用域是基于函數(shù)的,而執(zhí)行環(huán)境是基于對(duì)象的例如全局執(zhí)行環(huán)境即全局對(duì)象。全局對(duì)象全局屬性和函數(shù)可用于所有內(nèi)建的對(duì)象。全局對(duì)象只是一個(gè)對(duì)象,而不是類。 覺(jué)得本人寫(xiě)的不算很爛的話,可以登錄關(guān)注一下我的GitHub博客,博客會(huì)堅(jiān)持寫(xiě)下去。 今天同學(xué)去面試,做了兩道面試題,全部做錯(cuò)了,發(fā)過(guò)來(lái)給我看,我一眼就看出來(lái)了,因?yàn)?..

    QiShare 評(píng)論0 收藏0
  • Deep in JS - 收藏集 - 掘金

    摘要:今天同學(xué)去面試,做了兩道面試題全部做錯(cuò)了,發(fā)過(guò)來(lái)給道典型的面試題前端掘金在界中,開(kāi)發(fā)人員的需求量一直居高不下。 排序算法 -- JavaScript 標(biāo)準(zhǔn)參考教程(alpha) - 前端 - 掘金來(lái)自《JavaScript 標(biāo)準(zhǔn)參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡(jiǎn)介 算法實(shí)現(xiàn) 選擇排序 簡(jiǎn)介 算法實(shí)現(xiàn) ... 圖例詳解那道 setTimeout 與循環(huán)閉包的經(jīng)典面...

    enali 評(píng)論0 收藏0
  • 一道面試引發(fā)思考 --- Event Loop

    摘要:想必面試題刷的多的同學(xué)對(duì)下面這道題目不陌生,能夠立即回答出輸出個(gè),可是你真的懂為什么嗎為什么是輸出為什么是輸出個(gè)這兩個(gè)問(wèn)題在我腦邊縈繞。同步任務(wù)都好理解,一個(gè)執(zhí)行完執(zhí)行下一個(gè)。本文只是我對(duì)這道面試題的一點(diǎn)思考,有誤的地方望批評(píng)指正。 想必面試題刷的多的同學(xué)對(duì)下面這道題目不陌生,能夠立即回答出輸出10個(gè)10,可是你真的懂為什么嗎?為什么是輸出10?為什么是輸出10個(gè)10?這兩個(gè)問(wèn)題在我腦...

    betacat 評(píng)論0 收藏0
  • 一道面試引發(fā)思考

    摘要:下面我們來(lái)使用面向?qū)ο箢悎D這里就不再畫(huà)了首先面試題中所提到的我們都可以看成類,比如停車場(chǎng)是一個(gè)類吧,它里面的車位是一個(gè)類吧,攝像頭,屏幕。。。 以下是某場(chǎng)的一道面試題(大概): 1、一個(gè)停車場(chǎng),車輛入場(chǎng)時(shí),攝像頭記錄下車輛信息2、屏幕上顯示所接收的車輛的信息情況(車牌號(hào))以及各層車位的車位余量3、停車場(chǎng)一共四層車位,其中的三層都為普通車位,還有一層為特殊車位(體現(xiàn)在停車計(jì)費(fèi)價(jià)格上面的不...

    Apollo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<