前端开发核心知识进阶

09cdbb60-7016-11e9-bc1a-1f859588b6cd

课程内容

开篇词:如何突破前端开发技术瓶颈

日本后现代主义作家村上春树写过一本富有哲理的书——《当我谈跑步时我谈些什么》。

书中,他谈到,跑步跟写作一样:都需要坚毅隐忍,追逐超越;都需要心无杂念,持之以恒。全书落笔之处,没有浮华旖旎,而是娓娓道来迷惘、失败和挣扎。

这本书名义上是在谈跑步,却是在个人创作低潮时期对突破的不断思考。仔细想来,这样的思考对于一位工程师也至关重要。

前端领域,入门相对简单,可是想要“更上一层楼”却难上加难,也就是我们常说的“职业天花板较低”,君不见——市场上高级/资深前端工程师凤毛麟角。这当然未必完全是坏事,一旦突破瓶颈,在技能上脱颖而出,便是更广阔的空间。那么,如何从夯实基础到突破瓶颈?

接下来就让我们一起来讨论下:当前端工程师需要进阶时,我们学些什么。

直面自我——前端工程师的焦虑和迷茫

说到进阶,我想先谈谈我们每个人内心的焦虑和迷茫,正视这种情绪是学习的第一步。对于每一个追求进步的人来说,瓶颈期总会在各个阶段“如约而至”。早在公元前某年,庄子就说过:

吾生也有涯,而知也无涯。以有涯随无涯,殆已!已而为知者,殆而已矣!

而现如今,信息爆炸时代,信息量呈几何级数增长,知识似乎唾手可得。那么“该学什么?”,“到底该怎么学?”,“学完之后又该做什么?”,大部分人都会在知识面前焦虑、迷茫。

同样地,对于有一定工作经验的工程师来说:

  • 我该如何避免相似的工作做三年,而不是具备了三年的工作经验?
  • 我该如何从繁杂而千篇一律的业务需求中提炼技术点并总结提高?
  • 我该如何为团队带来更大的价值,体现经验能力?

这些困扰对前端工程师来说貌似来得更加猛烈:前端技术发展备受瞩目,前端工程师变得越发重要的背后是相关技术的不断扩张和更新换代。因此,我们比以往任何时候都更需要主动学习。但据我观察,目前网络上的学习资料往往存在以下两个问题:

  • 过于碎片化,这类知识某种程度上只能成为缓解焦虑的精神鸦片
  • 追求短平快,大牛经验、快速搞定“面经题目”,渐渐演变成为跳槽加薪的兴奋剂

前端学习,尤其是进阶阶段的学习是一个系统曲折的过程。每个学习者所接触的知识内容和其背后的原理构建成了他的思维方式。短期速成的内容或者大量碎片化的知识很难帮助我们深度思考。坦白来说,我也是这些“学习资料”的搜集者,如果没有系统针对性地学习和反复的刻意练习,那么结果就是以为收藏的是知识,其实收藏的是“知道”。以为掌握了知识,其实只是囤积了一堆“知道”。

当 GitChat 联系我想要打造一门系统而全面的前端进阶课程时,我欣然接受。不光是因为想把自己在海外和 BAT 服务多年积累的经验分享给大家,也是想把长时间以来收藏的“干货”梳理一遍,系统性整理输出,和大家一起总结提高。最终,就是呈现在大家面前的这门《前端开发核心知识进阶》课程。

爱因斯坦说过:

只是学习他人的智慧并不足够,你需要自己想明白才行。花时间记录、通盘考虑和深入思考你学到的东西。

海伦·凯勒说过:

知识使人进步,而智慧使人得道。

希望这门课程不仅详述“知识”,更能体现编程“智慧”,能让所有订阅的朋友们一起思考,一起进步。

6b7210a0-5103-11e9-94b7-c74b6c916ba4

配图来自:电影《银河系漫游指南》海报

课程特色——基础和进阶,理论和实践

说到课程特色,我想一边聊聊前端开发的发展,一边说说我们课程的聚焦点。

前端的大航海时代,有旧工具淘汰,更有新力量崛起

记得我刚接触前端编程时,jQuery 风靡一时,其清新优雅的 DOM 操作、稳如磐石的兼容性处理、灵活高效的封装和链式调用,让人如沐春风。

彼时,我幼稚地以为“这就是颠覆”,事实却是“这只是开始”——随着三大框架的崛起,技术更迭就像“暴风雨前的宁静”,jQuery 突然就被其他“先进的生产力”抛在背后了。于是我们看见:2018 年 9 月,Removing jQuery from GitHub.com frontend,各大平台技术进行“改朝换代”,引领开发潮流。

这还只是一个类库在前端浪潮中的兴衰。再想想 ES 语言规范的演进速度,HTML5 的扩张幅度,跨端从 Ionic 到 React Native 再到 Flutter,CSS 从基本布局模型到弹性盒模型再到原生 Grid 方案,构建工具从 Grunt 到 Gulp 再到 webpack/Rollup......

这门课程,我们在重视“亘古不变”的语言基础上,力求为大家介绍更先进的开发技术。比如服务端渲染,比如 HTTP 3.0,比如使用 Lerna、yarn workspaces 构建 monorepo 项目,比如框架演进和虚拟 DOM,等等。

点击了解更多《前端开发核心知识进阶》

18aca040-510a-11e9-adfc-e397a85e435c

配图来自:《海贼王》

前端开发有着与生俱来的混乱,需要我们披荆斩棘,在实践中勇往直前

前端三大方向 JavaScript、CSS、 HTML 的背后是无尽的碎片化场景。前端是最贴近用户的“战线”,它基因里就需要处理“大象万千”。同时,不管是跨平台还是语言特性,它都会让开发者感到迷茫:

  • “我们该使用哪些 HTML 标签以达到最佳的语义化?”
  • “我们该如何面对不同终端的诡异问题,并保证体验一致性?”
  • “我们如何写 JavaScript 做到 bug free?”
  • “this 乱七八糟,它到底指向谁?”
  • ……

这门课程,我们除了剖析理论,更加注重经验指导和最佳实践。

比如:

  • 《性能监控和错误收集与上报》是分析多种场景和业界解决方案的产出;
  • 《深入浅出模块化(含 tree shaking)》中,实战观摩 webpack 打包结果,对比 Rollup 解决方案,同时分析 tree shaking 的实施细节;
  • 《前端工程化背后的项目组织设计》探索究竟该如何组织架构代码,解放开发效率;
  • 《不可忽视的网络安全:单页应用鉴权设计》不去讲解 CSRF、XSS 等“死概念”,而是从鉴权角度出发,让读者对安全有一个立体认知。

891513d0-510f-11e9-adfc-e397a85e435c

配图来自:《七龙珠》

前端开发工程师有广阔的未来,但“打铁还需自身硬”

目前我们正在经历所谓的“资本寒冬”,不管是大厂、二线公司还是创业团队,“优化人员结构(裁员)”的新闻层出不穷。但是据我观察,“高级前端工程师”的招聘需求却“逆流而上”,具备高水平和经验的开发者无论在任何时候都备受追捧。因此,磨练技能、积累项目经验将是所有前端工程师的核心诉求。

作为内容贡献者,我也在思考如何让这门课程更有价值,真正帮助大家突破瓶颈,让读者感到“物有所值”,进而实现进阶。

这门课程中,我将穿插大量经典面试例题,其中既包括我作为 BAT 面试官的“私房题”,又涵盖我作为面试者遇见的“经典题”,以及和业界前辈讨论过的“开放题”。在平时开发和学习中,我也收藏积累了大量精品文章,会一并分享给大家。

从开发菜鸟到资深工程师,除了主观能动性以外,我个人认为成长过程中的一大瓶颈在于“不是每个人都能有机会接触到好项目”。这里的“好项目”是指类似“项目重构”、“类库迁移”、“复杂应用设计”、“疑难 bug 定位”等对开发者基础和设计能力有高要求的项目。

在这门课程中,我会插入大量代码设计模式、函数式、源码分析、组件设计和封装、开源库解读、项目代码组织等内容,也会手把手带领大家查阅 issue、changelog,从社区中汲取精华。构建更为真实的开发场景,直击实践中的高频痛点。

最后,也希望和每一位读者保持长线联系,一起讨论问题,共同进步。

e0049a50-5112-11e9-94b7-c74b6c916ba4

配图来自:《蜘蛛侠:平行宇宙》

课程简介——8 部分 36 个主题 50 课

  • 第一部分:JavaScript 基础强化(第 1-1~1-6 课)

    第二部分 JavaScript 语言进阶(第 2-1~2-6 课)

    第三部分 不可忽视的 HTML 和 CSS(第 3-1~3-3 课)

    其中前三部分介绍 JavaScript、 HTML、CSS ,对于一些热点、重点话题,比如“this 指向”、“闭包、作用域、执行上下文”,以及一些高频考察点进行了深入分析;对面向对象、原型与原型链、异步这些内容进行强化,从实现的角度,结合具体实例分析 Promise;结合 ES Next,从 ES 的发展来看语言的演进。

  • 第四部分 前端框架(第 4-1~4-7 课)

    接下来进入了前端框架部分,我们以 React 为主,分析框架对前端到底意味着什么,以及我们应该如何学习 React。事实上,对 React 的学习,不能只停留在“会用”的层面,其设计原理和思想演进,对于培养编程思维非常有益,也有利于学习者能从更高的角度看待问题。

  • 第五部分 前端工程化(第 5-1~5-8 课)

    资深程序员永远逃不开的工作之一就是“基础构建”、“项目架构”。前端工程化部分我们从代码组织谈起,从规范实施谈起,结合 webpack、Lerna 等工具,为大家还原一个真实的“基建”场景,共同探索学习。

  • 第六部分 性能优化(第 6-1~6-6 课)

    性能优化是理论和实践相结合的重要话题。

    我们需要大量理论知识:明白缓存策略,了解浏览器渲染特点,清楚 JavaScript 异步单线程对性能意味着什么,还要了解网络传输知识,等等。我们也需要大量实践经验:用得了 Chrome Devtool 分析火焰图,跑得出准确的 benchmark,知道防抖和节流的区别,懂得资源合并、拆分的利弊,了解业界最新性能优化方案,等等。

    这一部分,我们除了介绍重要大量知识点外,也会结合代码/项目实例来展开。

  • 第七部分 编程思维和算法(第 7-1~7-8 课)

    前端开发离不开编程基础,培养良好的编程思维,了解基本的算法知识,是每一个工程师所必须具备的。在这一部分,我们用 JavaScript 来描述多种设计模式,设计模式并不是纸上谈兵,实实在在地存在于我们的业务代码当中;手把手带大家用 JavaScript 处理各种数据,说到底前端还是处理数据,展示 UI;当然更少不了对一些常见算法的强化。

  • 第八部分 网络知识(第 8-1~8-5 课)

    作为一名前端开发者,不了解互联网传输的奥秘,不清楚网络细节是难以进阶的。网络知识关联着性能优化、前后端协作等核心环节,因此这一部分我们将重点强化网络基础。

  • 结束语:软技巧

    学习进步离不开社区的力量,“授人予鱼不如授人予渔”。在课程的最后,我会讲述我的学习方法:如何投身到社区当中,与广大开发者一起讨论;如何阅读前人的经典思想,站在巨人的肩上看得更远;如何解读开源库,从中汲取养分;如何在面试和述职当中,正确地表达汇报……

avatar

点击了解更多《前端开发核心知识进阶》

学习建议

建议大家在阅读课程过程中,手动执行文内代码,同时养成打断点或 console.log 调试的好习惯。此课程对应的代码仓库:https://github.com/HOUCe/lucas-gitchat-courses

分享交流

我们为本课程付费读者创建了《前端开发核心知识进阶》微信交流群,以方便更有针对性地讨论课程相关问题。(入群请到第1-2课末尾添加 GitChat 小助手伽利略的微信,注明「前端核心」。)

温馨提示:需购买才可入群哦,加小助手后需要截已购买的图来验证~

第1-1课:一网打尽 this,对执行上下文说 Yes

JavaScript 中的 this,因其灵活的指向、复杂的使用场景一直是面试中的热点,不论是初级还是中高级开发者,这都是一道必考题。这个概念虽然基础,但是非常重要,是否能深刻理解 this,是前端 JavaScript 中进阶的重要一环。this 指向多变,很多隐蔽的 bug 都缘于它。与此同时,this 强大灵活,如果能熟练驾驭,就会写出更简洁、优雅的代码。

社区上对于 this 的讲解虽然不少,但缺乏统一梳理。本节课,让我们直面 this 的方方面面,并通过例题真正领会与掌握 this

this 相关知识点如下:

be333ca0-4eca-11e9-b0b8-a9c8a3696845

this 到底指向谁

曾经在面试阿里某重点部门时,面试官从多个角度考察过我对 this 的理解:全局环境下的this、箭头函数的 this、构造函数的 thisthis 的显隐性和优先级,等等。尽管我能一一作答,可是最后的问题:请用一句话总结 this 的指向,注意只用一句话。 我却犯难了。

有一种广泛流传的说法是:

谁调用它,this 就指向谁。

也就是说,this 的指向是在调用时确定的。这么说没有太大的问题,可是并不全面。面试官要求我用更加规范的语言进行总结,那么他到底在等什么样的回答呢?

我们还要回到 JavaScript 中一个最基本的概念分析——执行上下文,这个概念,我们会在下一讲《老司机也会在闭包相关知识点翻车》中进行扩展。

事实上,调用函数会创建新的属于函数自身的执行上下文。执行上下文的调用创建阶段会决定 this 的指向。到此,我们可以得出的一个结论:

this 的指向,是在调用函数时根据执行上下文所动态确定的。

具体环节和规则,可以先“死记硬背”以下几条规律,后面再慢慢一一分析:

  • 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下 this 绑定到 undefined,否则绑定到全局对象 windowglobal
  • 一般构造函数 new 调用,绑定到新创建的对象上;
  • 一般由 call/apply/bind 方法显式调用,绑定到指定参数的对象上;
  • 一般由上下文对象调用,绑定在该对象上;
  • 箭头函数中,根据外层上下文绑定的 this 决定 this 指向。

当然,真实环境多样,我们来逐一梳理。

点击了解更多《前端开发核心知识进阶》

实战例题分析

例题组合 1:全局环境下的 this

这种情况相对简单直接,函数在浏览器全局环境中被简单调用,非严格模式下 this 指向 window;在 use strict 指明严格模式的情况下就是 undefined。我们来看例题,请描述打印结果:

function f1 () {    console.log(this)}function f2 () {    'use strict'    console.log(this)}f1() // windowf2() // undefined

这样的题目比较基础,但是需要候选人格外注意其变种,请再看一道题目:

const foo = {    bar: 10,    fn: function() {       console.log(this)       console.log(this.bar)    }}var fn1 = foo.fnfn1()

这里 this 仍然指向的是 window。虽然 fn 函数在 foo 对象中作为方法被引用,但是在赋值给 fn1 之后,fn1 的执行仍然是在 window 的全局环境中。因此输出 windowundefined,它们相当于:

console.log(window)console.log(window.bar)

还是上面这道题目,如果调用改变为:

const foo = {    bar: 10,    fn: function() {       console.log(this)       console.log(this.bar)    }}foo.fn()

将会输出:

{bar: 10, fn: ƒ}10

因为这个时候 this 指向的是最后调用它的对象,在 foo.fn() 语句中 this 指向 foo 对象。请记住:

在执行函数时,如果函数中的 this 是被上一级的对象所调用,那么 this 指向的就是上一级的对象;否则指向全局环境。

例题组合 2:上下文对象调用中的 this

如上结论,面对下题时我们便不再困惑:

const student = {    name: 'Lucas',    fn: function() {        return this    }}console.log(student.fn() === student)

最终结果将会返回 true

当存在更复杂的调用关系时,请看例题:

const person = {    name: 'Lucas',    brother: {        name: 'Mike',        fn: function() {            return this.name        }    }}console.log(person.brother.fn())

在这种嵌套的关系中,this 指向最后调用它的对象,因此输出将会是:Mike

到此,this 的上下文对象调用已经理解得比较清楚了。我们再看一道更高阶的题目:

const o1 = {    text: 'o1',    fn: function() {        return this.text    }}const o2 = {    text: 'o2',    fn: function() {        return o1.fn()    }}const o3 = {    text: 'o3',    fn: function() {        var fn = o1.fn        return fn()    }}console.log(o1.fn())console.log(o2.fn())console.log(o3.fn())

答案是:o1o1undefined,你答对了吗?

我们来一一分析。

  • 第一个 console 最简单,o1 没有问题。难点在第二个和第三个上面,关键还是看调用 this 的那个函数。
  • 第二个 consoleo2.fn(),最终还是调用 o1.fn(),因此答案仍然是 o1
  • 最后一个,在进行 var fn = o1.fn 赋值之后,是“裸奔”调用,因此这里的 this 指向 window,答案当然是 undefined

如果面试者回答顺利,可以紧接着追问,如果我们需要让:

console.log(o2.fn())

输出 o2,该怎么做?

一般开发者可能会想到使用 bind/call/apply 来对 this 的指向进行干预,这确实是一种思路。但是我接着问,如果不能使用 bind/call/apply,有别的方法吗?

这样可以考察候选人基础掌握的深度以及随机应变的思维能力。答案为:

const o1 = {    text: 'o1',    fn: function() {        return this.text    }}const o2 = {    text: 'o2',    fn: o1.fn}console.log(o2.fn())

还是应用那个重要的结论:this 指向最后调用它的对象,在 fn 执行时,挂到 o2 对象上即可,我们提前进行了赋值操作。

例题组合 3:bind/call/apply 改变 this 指向

上文提到 bind/call/apply,在这个概念上,比较常见的基础考察点是:bind/call/apply 三个方法的区别。

这样的问题相对基础,我们直接上答案:一句话总结,他们都是用来改变相关函数 this 指向的,但是 call/apply 是直接进行相关函数调用;bind 不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定了新的 this 指向,开发者需要手动调用即可。再具体的 call/apply 之间的区别主要体现在参数设定上,这里不再展开。

用代码来总结:

const target = {}fn.call(target, 'arg1', 'arg2')

相当于:

const target = {}fn.apply(target, ['arg1', 'arg2'])

相当于:

const target = {}fn.bind(target, 'arg1', 'arg2')()

具体基础用法这里不再科普,如果读者尚不清楚,需要自己补充一下知识点。

我们来看一道例题分析:

const foo = {    name: 'lucas',    logName: function() {        console.log(this.name)    }}const bar = {    name: 'mike'}console.log(foo.logName.call(bar))

将会输出 mike,这不难理解。但是对 call/apply/bind 的高级考察往往会结合构造函数以及组合式实现继承。实现继承的话题,我们会单独讲到。构造函数的使用案例,我们结合接下来的例题组合进行分析。

例题组合 4:构造函数和 this

这方面最直接的例题为:

function Foo() {    this.bar = "Lucas"}const instance = new Foo()console.log(instance.bar)

答案将会输出 Lucas。但是这样的场景往往伴随着下一个问题:new 操作符调用构造函数,具体做了什么?以下供参考:

  • 创建一个新的对象;
  • 将构造函数的 this 指向这个新对象;
  • 为这个对象添加属性、方法等;
  • 最终返回新对象。

以上过程,也可以用代码表述:

var obj  = {}obj.__proto__ = Foo.prototypeFoo.call(obj)

当然,这里对 new 的模拟是一个简单基本版的,更复杂的情况我们会在原型、原型链相关的第2-5课《面向对象和原型——永不过时的话题》中讲述。

需要指出的是,如果在构造函数中出现了显式 return 的情况,那么需要注意分为两种场景:

function Foo(){    this.user = "Lucas"    const o = {}    return o}const instance = new Foo()console.log(instance.user)

将会输出 undefined,此时 instance 是返回的空对象 o

function Foo(){    this.user = "Lucas"    return 1}const instance = new Foo()console.log(instance.user)

将会输出 Lucas,也就是说此时 instance 是返回的目标对象实例 this

结论:如果构造函数中显式返回一个值,且返回的是一个对象,那么 this 就指向这个返回的对象;如果返回的不是一个对象,那么 this 仍然指向实例。

例题组合 5:箭头函数中的 this 指向

首先我们再来温习一下相关结论。

结论:箭头函数使用 this 不适用以上标准规则,而是根据外层(函数或者全局)上下文来决定。

来看题目:

const foo = {      fn: function () {          setTimeout(function() {              console.log(this)        })    }  }  console.log(foo.fn())

这道题中,this 出现在 setTimeout() 中的匿名函数里,因此 this 指向 window 对象。如果需要 this 指向 foo 这个 object 对象,可以巧用箭头函数解决:

const foo = {      fn: function () {          setTimeout(() => {              console.log(this)        })    }  } console.log(foo.fn())// {fn: ƒ}

单纯箭头函数中的 this 非常简单,但是综合所有情况,结合 this 的优先级考察,这时候 this 指向并不好确定。请继续阅读。

例题组合 6:this 优先级相关

我们常常把通过 callapplybindnewthis 绑定的情况称为显式绑定;根据调用关系确定的 this 指向称为隐式绑定。

那么显式绑定和隐式绑定谁的优先级更高呢?

请看例题:

function foo (a) {    console.log(this.a)}const obj1 = {    a: 1,    foo: foo}const obj2 = {    a: 2,    foo: foo}obj1.foo.call(obj2)obj2.foo.call(obj1)

输出分别为 2、1,也就是说 callapply 的显式绑定一般来说优先级更高。

function foo (a) {    this.a = a}const obj1 = {}var bar = foo.bind(obj1)bar(2)console.log(obj1.a)

上述代码通过 bind,将 bar 函数中的 this 绑定为 obj1 对象。执行 bar(2) 后,obj1.a 值为 2。即经过 bar(2) 执行后,obj1 对象为:{a: 2}

当再使用 bar 作为构造函数时:

var baz = new bar(3)console.log(baz.a)

将会输出 3。我们看 bar 函数本身是通过 bind 方法构造的函数,其内部已经对将 this 绑定为 obj1,它再作为构造函数,通过 new 调用时,返回的实例已经与 obj1 解绑。 也就是说:

new 绑定修改了 bind 绑定中的 this,因此 new 绑定的优先级比显式 bind 绑定更高。

我们再看:

function foo() {    return a => {        console.log(this.a)    };}const obj1 = {    a: 2}const obj2 = {    a: 3}const bar = foo.call(obj1)console.log(bar.call(obj2))

将会输出 2。由于 foo()this 绑定到 obj1bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。

如果将 foo 完全写成箭头函数的形式:

var a = 123const foo = () => a => {    console.log(this.a)}const obj1 = {    a: 2}const obj2 = {    a: 3}var bar = foo.call(obj1)console.log(bar.call(obj2))

将会输出 123

这里我再“抖个机灵”,仅仅将上述代码的第一处变量 a 的赋值改为:

const a = 123const foo = () => a => {    console.log(this.a)}const obj1 = {    a: 2}const obj2 = {    a: 3}var bar = foo.call(obj1)console.log(bar.call(obj2))

答案将会输出为 undefined,原因是因为使用 const 声明的变量不会挂载到 window 全局对象当中。因此 this 指向 window 时,自然也找不到 a 变量了。关于 const 或者 let 等声明变量的方式不再本课的主题当中,我们后续也将专门进行介绍。

到这里,读者是否有“融会贯通”的感觉了呢?如果还有困惑,也不要灰心。进阶的关键就是基础,基础需要反复学习,“死记硬背”后才能慢慢体会。

开放例题分析

不知道实战例题分析是否已经把你绕晕了。事实上,this 的指向涉及的规范繁多,优先级也较为混乱。刻意刁难并不是很好的面试做法,一些细节候选人如果没有记住也不是太大的问题。作为面试官,我往往会另辟蹊径,出一些开放性题目。

其中,最典型的一道题目为:实现一个 bind 函数。

作为面试者,我也曾经在头条的面试流程中被问到模拟 bind。这道题并不新鲜,部分读者也会有自己的解答思路,而且社区上关于原生 bind 的研究也很多。但是,我们这里想强调的是,可能有一些细节被大家忽略了。在回答时,我往往先实现一个初级版本,然后根据 ES5-shim 源码进一步说明。

Function.prototype.bind = Function.prototype.bind || function (context) {    var me = this;    var args = Array.prototype.slice.call(arguments, 1);    return function bound () {        var innerArgs = Array.prototype.slice.call(arguments);        var finalArgs = args.concat(innerArgs);        return me.apply(context, finalArgs);    }}

这样的实现已经非常不错了。但是,就如同之前 this 优先级分析所示:bind 返回的函数如果作为构造函数,搭配 new 关键字出现的话,我们的绑定 this 就需要“被忽略”。

为了实现这样的规则,开发者就应该需要考虑如何区分这两种调用方式。具体来讲 bound 函数中就要进行 this instanceof 的判断。

另外一个细节是,函数具有 length 属性,表示形参的个数。上述实现方式形参的个数显然会失真。我们的实现就需要对 length 属性进行还原。可是难点在于:函数的 length 属性值是不可重写的。

这样的内容一般属于“超纲”范畴,但在面试中能够很好地体现面试者平时的积累,以及对源码的阅读和思考,显然是加分项。对此进一步的理解,读者可参考我的原创文章:从一道面试题,到“我可能看了假源码”。这篇文章发表于 2017 年年初,可以说是现在很多研究 bind 相关文章的启蒙。

总结

通过本课的学习,我们看到 this 纷繁多象,确实不容易彻底掌握。本节尽可能系统地进行讲解、说明,例题尽可能地覆盖更多 case。与此同时,需要读者在阅读之外继续进行消化与吸收。只有“记死”,才能“用活”。请读者随时关注课程,接下来会更新更多大家感兴趣的话题,让我们一起进步。

点击了解更多《前端开发核心知识进阶》

分享交流

请大家留言分享开发实践中遇到的 this 相关的难忘 bug。阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者 LucasHC 提问。你的分享不仅帮助他人,更会提升自己。

你也可以说说自己最想了解的主题,课程内容会根据部分读者的意见和建议迭代和完善。

此外,我们为本课程付费读者创建了《前端开发核心知识进阶》微信交流群,以方便更有针对性地讨论课程相关问题。(入群请到第1-2课末尾添加 GitChat 小助手伽利略的微信,注明「前端核心」。)

温馨提示:需购买才可入群哦,加小助手后需要截已购买的图来验证~

第1-2课:老司机也会在闭包相关知识点翻车(上)
第1-3课:老司机也会在闭包相关知识点翻车(下)
第1-4课:我们不背诵 API,只实现 API
第1-5课:JavaScript 知识图谱和高频考点梳理
第1-6课:其他基础题库
第2-1课:异步不可怕“死记硬背”+ 实践拿下(上)
第2-2课:异步不可怕“死记硬背”+ 实践拿下(下)
第2-3课:你以为我真的想让你手写 Promise 吗(上)
第2-4课:你以为我真的想让你手写 Promise 吗(下)
第2-5课:面向对象和原型——永不过时的话题
第2-6课:究竟该如何学习与时俱进的 ES
第3-1课:前端面试离不开的“面子工程”
第3-2课:进击的 CSS 和 HTML
第3-3课:响应式布局和 Bootstrap 的实现分析
第4-1课:触类旁通多种框架
第4-2课:你真的懂 React 吗
第4-3课:揭秘 React 真谛:组件设计
第4-4课:揭秘 React 真谛:数据状态管理
第4-5课:React 的现状与未来
第4-6课:同构应用中你所忽略的细节
第4-7课:从框架和类库,我们该学到什么
第5-1课:深入浅出模块化(含 tree shaking)(上)
第5-2课:深入浅出模块化(含 tree shaking)(下)
第5-3课:webpack 工程师 > 前端工程师(上)
第5-4课:webpack 工程师 > 前端工程师(下)
第5-5课:前端工程化背后的项目组织设计(上)
第5-6课:前端工程化背后的项目组织设计(下)
第5-7课:代码规范工具及背后技术设计(上)
第5-8课:代码规范工具及背后技术设计(下)
第6-1课:性能监控和错误收集与上报(上)
第6-2课:性能监控和错误收集与上报(下)
第6-3课:性能优化问题,老司机如何解决(上)
第6-4课:性能优化问题,老司机如何解决(下)
第6-5课:以 React 为例,说说框架和性能(上)
第6-6课:以 React 为例,说说框架和性能(下)
第7-1课:揭秘前端设计模式(上)
第7-2课:揭秘前端设计模式(下)
第7-3课:剖析无处不在的数据结构
第7-4课:古老又新潮的函数式
第7-5课:那些年常考的前端算法(上)
第7-6课:那些年常考的前端算法(中)
第7-7课:那些年常考的前端算法(下)
第7-8课:分析一道“微信”面试题
第8-1课:离不开的网络基础
第8-2课:缓存谁都懂,一问都哑巴(上)
第8-3课:缓存谁都懂,一问都哑巴(下)
第8-4课:HTTP 的深思:我从何而来,去向何处
第8-5课:不可忽视的前端安全 - 单页应用鉴权设计
结束语:大话社区和一名技术者的自我修养

阅读全文: http://gitbook.cn/gitchat/column/5c91c813968b1d64b1e08fde

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页