记录一些基础算法

开始补充一些算法和数据结构的知识

冒泡排序

const arr = [162, 6, 3, 8, 90, 234, 5, 7, 31, 45, 12, 1, 98, 23, 87, 40]
function sort(arr) {
  const len = arr.length - 1
  for (let x = 0, y = len; x < y; x++) {
    // 把第一个跟后面那个对比,如果比后面的数大,就交换位置,到最后就可以确定最大的数在最后
    // 也就是在 length 的位置
    // 在第一轮的时候,x=1,也就是遍历到倒数第二个就好,因为取到 i+1 的数
    for (let i = 0, l = len - x; i < l; i++) {
      const first = arr[i], second = arr[i+1]
      if (first > second) {
        [arr[i], arr[i+1]] = [second, first]
      }
    }
    // 假设没有第一层,最大的数已经在最后,那么第二轮就是要在剩下的数找到最大,并放到倒数第二位置
    // 也就是在 length-1 的位置
    // 此时 x=2,因为依然是当轮最后数(第二轮为length-1)的前一个
    // for (var i = 0, l = arr.length - 2; i < l; i++) {
    //  if (arr[i] > arr[i+1]) {
    //    [arr[i], arr[i+1]] = [arr[i+1], arr[i]]
    //  }
    // }
    // 第三轮 x=3
    // for (var i = 0, l = arr.length - 3; i < l; i++) {
    //  if (arr[i] > arr[i+1]) {
    //    [arr[i], arr[i+1]] = [arr[i+1], arr[i]]
    //  }
    // }
    // 第四轮 x=4
    // for (var i = 0, l = arr.length - 4; i < l; i++) {
    //  if (arr[i] > arr[i+1]) {
    //    [arr[i], arr[i+1]] = [arr[i+1], arr[i]]
    //  }
    // }
    // 所以循环个遍,就成了外面一层的循环
  }
  return arr
}
console.log(sort(arr))

二分法

就是在一个有序的序列中迅速找到要找的那个数的索引。一个一个遍历当然也可以找到,但是不确定性导致复杂度。
二分法就是中间砍一刀,这样数列就成了两半。看看中间值是不是,是那就刚好了。不是的话,判断目标数是在前一半还是后一半,如此这般的循环即可。

这种思想应该在现实生活中还是经常用到的,比如git bisect看看大佬的介绍)。
以及,让我想起以前 TVB 的《超级无敌掌门人》里面有个游戏叫“超级无敌开口中”,轮流在在100里面瞎说一个数,中了就受罚。特别是轮到说最后一个必中的数字时那种视死如归的神情,超搞笑。

function getRandomNum(numMin, numMax) {
  return Math.round( Math.random()*(numMax-numMin) + numMin )
}  
function getArr() {
  const arr = []
  for(let i = 1, l = 100; i <= l; i++) {
    arr.push(i)
  }
  return arr
}
// 随机生成 1 ~ 100 中的某数
const num = getRandomNum(1, 100)
// 生成 1 - 100 的数组
const arr = getArr()
function binarySearch(num, arr) {
  let start = arr.length
  let end = 0
  const getHalf = () => {
    let length = start + end
    return (length % 2 === 0) ? (length / 2) : (length + 1) / 2
  }
  // 取中值
  let half = getHalf()
  let current = arr[half]
  while (current !== num) {
    if (current > num) {
      start = half
    } else if (current < num) {
      end = half
    }
    half = getHalf()
    current = arr[half]
  }
  return half
}
const idx = binarySearch(num, arr)
console.log(idx, arr[idx], num)

斐波那契数列

具体的数学意义就不说了,我也不懂,总而言之是满足这样的一个数列:F(0)=0,F(1)=1,F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)

// 第一反应就是递归,最简单最明晰。但效率最低,其实就是每次算出来的值没存起来,导致每个 n 位的值传进去都要重新递归的算
function fibonacci(n) {
  if(n === 0 || n === 1) {
    return n
  }
  return fibonacci(n-1) + fibonacci(n-2)
}
// 改进一些,是个数列自然就可以用个数组来存
// 把之前获得 n 位的值存起来,下次要拿就直接取就好
function fibonacci2(n) {
  if(n === 0 || n === 1) {
    return n
  }
  const arr = [0, 1]
  for(let i = 2; i <= n; i++) {
    arr[i] = arr[i-1] + arr[i-2]
  }
  return arr[n]
}
// 改进一些,其实每次都是需要上两位就可以了,不需要用整个数组来存全部
function fibonacci3(n) {
  if(n === 0 || n === 1) {
    return n
  }
  let lastOne = 1 // n-1
  let lastTwo = 0 // n-2
  for(let i = 2; i <= n; i++) {
    var current = lastOne + lastTwo
    // n-2 和 n-1 分别获取后一位的值
    lastTwo = lastOne
    lastOne = current
  }
  return current
}
// 第一种递归的方法,在浏览器 40 以后基本快跑不出来了
// 第三种还是比第二种快那么些许
console.time('fibonacci')
console.log(fibonacci(40))
console.timeEnd('fibonacci')

玩一玩puppeteer

puppeteer官网上写着:

Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。Puppeteer 默认以 headless 模式运行,但是可以通过修改配置文件运行“有头”模式。

其实就是个浏览器,给了许多方法让我们可以控制这个浏览器,比如打开某某网页,输入什么字符,点击哪个按钮。看起来就像是用程序来模拟人工的行为。
这样就表示了它可以用来自动化测试。搭配 jest 或 mocha 之类的测试框架,进行e2e测试,模拟一个人去访问网站,进行一些操作。
当然不止这一个方案,还有 selenium 和在这之上的 night-watch,也是干 web 自动化测试的活。

我们项目上使用它来跑自动化测试的,我自己用它来做爬虫,奸笑~
可能也不叫爬虫,就是上新闻网站扫一下热搜,到图片库搜点图片之类的。

安装:

安装是个蛋疼的事,因为这个用到chromium,这玩意在谷歌仓库。所以如果不能科学上网,那就只能修改为淘宝源。

env puppeteer_download_host=https://npm.taobao.org/mirrors npm i puppeteer

npm config set puppeteer_download_host  https://npm.taobao.org/mirrors
npm i puppeteer

年代久远,记得都是可以的。不保证时效性…

我们项目一开始使用它来截图

const puppeteer = require('puppeteer')
;(async () => {
  const browser = await puppeteer.launch({
    headless: true // 无头就是不要弹出浏览器
  })
  const page = await browser.newPage()
  await page.goto('https://www.baidu.com')
  await page.setViewport({
    width: 1200,
    height: 800
  })
  await page.screenshot({
    path: 'puppeteer.png',
    fullPage: true
  })
  await browser.close()
})()

来看下用它来搜百度热搜,之所以对 puppeteer 感兴趣,就是同事写了一篇这个教程,里面就是演示了爬热搜。

const puppeteer = require('puppeteer')
;(async () => {
  const browser = await puppeteer.launch({
    headless: true // 无头就是不要弹出浏览器
  })
  const page = await browser.newPage()
  await page.goto('http://top.baidu.com/buzz?b=1&fr=topindex', {
    timeout: 0, // 设置超时时间为0
    waitUntil: 'domcontentloaded' // 有多个状态可设置,这里设置为 dom 渲染完成
  })
  // 有时候网速卡,加载慢,容易超时报错,所以上面把超时时间去掉
  // 同时网页渲染也需要时间,可以等一下,
  // 比如 await page.waitFor(1000) 等待1000ms
  // 比如我之前爬图片的时候,都知道图片是相当耗时的
  // 所以我写成等图片的 dom 可见即可,可见包括了它已经被赋予 src,不需要等到图片下载并渲染完成
  // 然后获取其 src,再通过别的方法下载
  // await page.waitForSelector(dom, { visible: true, timeout: 0 })
  const list = await page.evaluate((dom) => {  // 爬取内容
    // 这个 dom 需要去网站上看实际结构,很好找,跟 jQuery 一样
    const $doms = document.querySelectorAll('.mainBody .keyword a')
    const list = Array.from($doms).map(($dom) => {
      return {
        href: $dom.href,
        text: $dom.innerText
      }
    })
    return list
  })
  // 这个 list 就记录了热搜的标题和网址
  await page.close()
  await browser.close()
})()

对网页的抓取还有这个cheerio,在 node 里面像 jQuery 一样获取dom。
不过好像对单页面的网站比较麻烦。因为是抓了整个网页内容下来,再来分析页面。那对于 SPA 抓下来也是一个容器一个js,没有实体dom。所以具体看用途啦。

puppeteer还有很多牛的功能,这里只是用到了冰山一角,还能做更多有趣的事。

在海边

最近去了一趟深圳大鹏官湖村。典型的海边小镇,地方不大,沙滩挺大。有一个路口在滤镜加持下,居然有点像灌篮高手片头里,樱木在车站前的经典场景。







Promise的几个api用法

Promise 应该大家都很熟悉了。大部分的库也都用上了Promise。
这文章主要记录下 Promise.all、Promise.allSettled、Promise.race 这几个的用法。

Promise.all:
参数是一个数组,数组元素是各个Promise。要数组里面所有的 Promise 都返回成功,那这个 Promise.all 的结果才会是成功。成功返回数组,就是元素成功的结果集合。失败的话返回单值,是元素里第一个失败的结果。

Promise.allSettled:
跟 Promise.all 参数一样,是一个数组,数组元素是各个Promise。不同的是这个不会因为数组元素的成败来返回成功失败。返回的是个数组,记录着元素 Promise 的各个结果,包括成败。

先准备四个小函数,然后再混起来用:

function PromiseSuccess0() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('PromiseSuccess0')
    }, 0)
  })
}
function PromiseSuccess1000() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('PromiseSuccess1000')
    }, 1000)
  })
}
function PromiseError2000() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('PromiseError2000')
    }, 2000)
  })
}
function PromiseError3000() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('PromiseError3000')
    }, 3000)
  })
}

Promise.all([PromiseSuccess0(), PromiseError3000(), PromiseError2000()])
 .then((data) => {
   console.log('success all-1: ', data)
 })
 .catch((error) => {
   console.error('error all-1: ', error)
    // error all-1:  PromiseError2000
 })

Promise.all([PromiseSuccess0(), PromiseSuccess1000()])
 .then((data) => {
   console.log('success all-2: ', data)
    // success all-2:  (2) ["PromiseSuccess0", "PromiseSuccess1000"]
 })
 .catch((error) => {
   console.error('error all-2: ', error)
 })

Promise.allSettled([PromiseError2000(), PromiseSuccess1000()])
  .then((data) => {
    console.log('success allSettled: ', data)
    // success allSettled:  
    // (2) [{…}, {…}]
    // 0: {status: "rejected", reason: "PromiseError2000"}
    // 1: {status: "fulfilled", value: "PromiseSuccess1000"}
  })
  .catch((error) => {
    console.error('error allSettled: ', error)
  })

Promise.race
顾名思义,竞赛。参数依然是个元素为 Promise 的数组。会返回第一个跑完的元素结果。话不多说,上码:

function load1000() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('load1000')
    }, 1000)
  })
}
function load3000() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('load3000')
    }, 3000)
  })
}
function errorTimer() {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     reject(new Error('timeout'))
   }, 2000)
 })
}

Promise.race([load1000(), errorTimer()])
 .then((data) => {
   console.log('success race-1: ', data)
   // success race-1:  load1000
 })
 .catch((error) => {
   console.error('error race-1: ', error)
 })

Promise.race([load3000(), errorTimer()])
 .then((data) => {
   console.log('success race-2: ', data)
 })
 .catch((error) => {
   console.error('error race-2: ', error)
   // error race-2:  Error: timeout
 })

具体的使用场景可以是,比如下载个东西,给其设定个时间限定,超过这个时间就报错。稍微封装一下:

function addTimer(fn, time) {
 const errorTimer = new Promise((resolve, reject) => {
   setTimeout(() => {
     reject(new Error('timeout'))
   }, time)
 })
 return Promise.race([fn(), errorTimer])
}

addTimer(load1000, 2000)
 .then((data) => {
   console.log('success addTimer: ', data)
 })
 .catch((error) => {
   console.error('error addTimer: ', error)
 })

webpack通过webpackJsonp来连接多文件依赖

webpack一般是打成一份文件,但有时也需要打成几份,生成的文件可以把其中的公共部分抽取出来,省点代码。
这里就免不了依赖文件顺序的问题。这里用了很巧妙的方法,来连接不同的文件,以及依赖的前置性问题。
相当用心思,这里稍微记录下。

先准备两份文件 foo.js,bar.js,util.js:

// foo.js
import util from './util'

console.log('foo')
console.log(util('foo'))

// bar.js
import util from './util'

console.log(util('bar'))
console.log('bar')

export default function () {
    return 'bar-function'
}

// util.js
console.log('util')

export default function (flag) {
    return 'util' + flag
}

webpack配置文件:

const { execSync } = require('child_process')
var webpack = require('webpack');

execSync(`rm -rf build`)

module.exports = {
    mode: 'development',
    devtool: 'none',//配置生成Source Maps,选择合适的选项
    entry:  { 
        foo: __dirname + "/foo.js",
        bar: __dirname + "/bar.js"
    },//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "[name].js"//打包后输出文件的文件名
    },
     optimization: {
        splitChunks: {
          chunks: 'all',
          minSize: 1,
          maxSize: 0,
          minChunks: 1,
          maxAsyncRequests: 6,
          maxInitialRequests: 4,
          automaticNameDelimiter: '~',
          automaticNameMaxLength: 30,
          cacheGroups: {
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10
            },
            default: {
              minChunks: 2,
              priority: -20,
              reuseExistingChunk: true
            }
          }
        }
      }
}

会在 dist 文件夹生成三个文件:foo.js,bar.js,defaultbarfoo.js
看得出 default-bar-foo.js 就是共用代码,这里就是 util.js,代码是:

// window["webpackJsonp"]是个数组,push了一个数组。元素1还是一个数组,里面只有一个值"default~bar~foo"
// 其实就是这份文件名字,或者说是这份chunk名字
// 而第二个值就又是各个module的集合。比如这里只有一个util.js,结合chunk名就很明显了,
// util.js就是foo.js和bar.js共同用到的。如果还有其他共用到的,那么还有多几个字段
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["default~bar~foo"],{
/***/ "./util.js":
/*!*****************!*\
  !*** ./util.js ***!
  \*****************/
/*! exports provided: default */
      // __webpack_exports__=module.exports
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
console.log('util')
/* harmony default export */ __webpack_exports__["default"] = (function (flag) {
    return 'util' + flag
});
/***/ })
}]);

如果平时有看过 webpack 生成的代码的话,就能很明显看出与入口文件打出来的内容很不一样,缺少了webpackBootstrap
看看 foo.js,(bar.js也长得差不多),几个名词先说一下:

module:每一份文件的js代码。比如a.js引入了b.js,把a.js作为entry。那么到时打出来的就是只有一份a.js(该输出文件就是一份chunk,输出文件名字可变)。里面有两个模module,a.js和b.js。
chunk:要输出的每一份文件就是一个chunk(一个或多个module组成一份chunk),一般是一个entry对应一个chunk。如果像这种抽离公共js的,也是一个chunk,也会多于entry数。

foo.js是
一个自执行的函数,传入一个json对象。只有一个key值,是”./foo.js”。相对应的value其实是一个函数模块,就是foo.js的代码内容。
打包出来的js靠全局变量维系,window[“webpackJsonp”]。在每个entry chunk的最上面都有webpack加载代码,而附属chunk则没有,只有看起来像数组push的代码,如default-bar-foo.js文件所示。

/******/ (function(modules) { // webpackBootstrap
/******/ 	// install a JSONP callback for chunk loading
                    // 这data就是window["webpackJsonp"] push进来的东西
/******/ 	function webpackJsonpCallback(data) {
/******/ 		var chunkIds = data[0];// chunkId="default~bar~foo"
/******/ 		var moreModules = data[1];// {'./util.js':f}
/******/ 		var executeModules = data[2]; // 暂不知何用
/******/
/******/ 		// add "moreModules" to the modules object,
/******/ 		// then flag all "chunkIds" as loaded and fire callback
/******/ 		var moduleId, chunkId, i = 0, resolves = [];
/******/ 		for(;i < chunkIds.length; i++) {
/******/ 			chunkId = chunkIds[i];
/******/ 			if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
                // resolves暂不知作何用
/******/ 				resolves.push(installedChunks[chunkId][0]);
/******/ 			}
              // 标记chunk安装
/******/ 			installedChunks[chunkId] = 0;
/******/ 		}
/******/ 		for(moduleId in moreModules) {
/******/ 			if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                // 把每个chunk的每份module记录在modules,用以调用运行
/******/ 				modules[moduleId] = moreModules[moduleId];
/******/ 			}
/******/ 		}
            // 当有parentJsonpFunction这方法适合,就执行这个方法,参数为每个chunk代码
            // window["webpackJsonp"]里面的元素其实就是非入口chunk代码块
            // parentJsonpFunction取自上一个window["webpackJsonp"]的push的方法,
            // 当第一个script的入口chunk,它的parentJsonpFunction就是普通数组的push
            // 同时在第一个script的入口chunk,把window["webpackJsonp"]的push改写为第一个script的入口chunk的webpackJsonpCallback方法
            // 也就是第二个script的入口chunk里,这个parentJsonpFunction就是第一个script的入口chunk的webpackJsonpCallback方法
            // 这样就把两份chunk的代码通过这种方式联系起来
            // 当push(改写后的push)了非入口chunk,可以通过这种联系一层层传递到各个chunk的webpackJsonpCallback方法
            // webpackJsonpCallback里面再调用checkDeferredModules方法,来检查并执行相应的模块代码
            // 这样一来无论script顺序怎样,只要调用webpackJsonpCallback方法,就能达到加载的模块(chunk即为模块合集)传递到各个入口chunk
/******/ 		if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ 		while(resolves.length) {
/******/ 			resolves.shift()();
/******/ 		}
/******/
/******/ 		// add entry modules from loaded chunk to deferred list
            // 看原注释是添加新的前置依赖
            // 暂不知为何是添加executeModules而不是一开始就写在deferredModules
/******/ 		deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/ 		// run deferred modules when all chunks ready
/******/ 		return checkDeferredModules();
/******/ 	};
          // 检查依赖到齐没有,并执行
          // 在主流程里会运行一次
          // 在webpackJsonpCallback里最后再运行一次,也就是每次有chunk的加载都会检查运行一次
/******/ 	function checkDeferredModules() {
/******/ 		var result;
/******/ 		for(var i = 0; i < deferredModules.length; i++) {
/******/ 			var deferredModule = deferredModules[i];
/******/ 			var fulfilled = true;
/******/ 			for(var j = 1; j < deferredModule.length; j++) {
                // depId="./foo.js","default~bar~foo"
/******/ 				var depId = deferredModule[j];
                // 是否已安装记录在installedChunks
/******/ 				if(installedChunks[depId] !== 0) fulfilled = false;
/******/ 			}
              // 全部安装完毕
/******/ 			if(fulfilled) {
                // 把 deferredModules 清空
/******/ 				deferredModules.splice(i--, 1);
                // deferredModule[0]本chunk的名字,这里是"./foo.js"
/******/ 				result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/ 			}
/******/ 		}
/******/
/******/ 		return result;
/******/ 	}
/******/
          // 先看同步代码,从这里开始看起
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// object to store loaded and loading chunks
/******/ 	// undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ 	// Promise = chunk loading, 0 = chunk loaded
          // 执行到这里,代表本身这个chunk也就是这个文件已加载
          // 这里同时也会标记别的chunk加载了没有,来判断前置依赖有了没有
/******/ 	var installedChunks = {
/******/ 		"foo": 0
/******/ 	};
/******/
/******/ 	var deferredModules = [];
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
            // 每个加载的模块结果缓存起来
            // 这里每个module就是未打包前的每一份js
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
            // 这里注意到具体模块去看,会把这个模块的导出值赋值到module.exports
            // 然后再 return 出去,所以 __webpack_require__ 模块会返回加载运行模块的结果
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
            // 标记这个 module 加载过
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/  // 部分 __webpack_require__.字段 这里未体现作用
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
          // 打上 ES6 
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
          // 初始化window["webpackJsonp"]为一个数组
/******/ 	var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
          // 把旧的window["webpackJsonp"]的push方法缓存起来,当然第一个就是数组的push方法
/******/ 	var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
          // 改写window["webpackJsonp"]的push方法为webpackJsonpCallback
/******/ 	jsonpArray.push = webpackJsonpCallback;
/******/ 	jsonpArray = jsonpArray.slice();
          // 把window["webpackJsonp"]的chunk传入webpackJsonpCallback执行一下
/******/ 	for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
          // 这里再次把旧的window["webpackJsonp"]的push方法赋值到parentJsonpFunction
          // parentJsonpFunction会存在在这个chunk的作用域,在这个chunk的webpackJsonpCallback里被调用
/******/ 	var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ 	// add entry module to deferred list
                    // 首位为本身chunk,后面为所依赖的chunk
                    // 所以可以得知靠着installedChunks和deferredModules这两个变量来判断到底所需的文件加载好了没有
                    // 以至于在script标签里的引入顺序会干扰到依赖加载从而影响执行
          // 注意这里是二维数组
/******/ 	deferredModules.push(["./foo.js","default~bar~foo"]);
/******/ 	// run deferred modules when ready
/******/ 	return checkDeferredModules();
/******/ })
/************************************************************************/
/******/ ({

/***/ "./foo.js":
/*!****************!*\
  !*** ./foo.js ***!
  \****************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
// 标记为 ES6 模块
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./util.js");

console.log('foo')
console.log(Object(_util__WEBPACK_IMPORTED_MODULE_0__["default"])('foo'))

/***/ })

/******/ });

通过全局的 webpackJsonp 这个数组来连接多份文件。改写了这个数组的 push 方法。让其在每次 push 的时候,都一层一层找上之前加载过的文件。
触发每份入口文件里的检查依赖(checkDeferredModules),来判断前置依赖是否准备好。好则开始执行。