7项重要储备

  1. Flow 基本语法
  2. 发布/订阅模式
  3. ES6+ 语法
  4. 原型链、闭包
  5. 函数柯里化
  6. event loop

接上讲

聊到了ES6的几个重要语法,加下来到第四点继续开始。

4.原型链、闭包

原型链

继承于我们前端来说绝对是非常熟悉也必须熟悉的一个高频必懂知识点。熟悉到只要是面试一定会有关于继承的问题;而且源码中继承的使用也随处可见。

可依旧有很多前端对继承的实现和应用没有一个整体的把握。追其原因无非有二:

  1. ECMAScript 继承的实现方法区别于其他基于类的实现继承的面向对象(Object Oriented)语言。
  2. 工作中即使对如何实现继承一知半解,也一点都不耽误写逻辑代码。

无论由于哪一个原因,必须尽快弄懂继承的实现和应用,否则就会流下了没有技术的泪水。

在讲 ECMAScript 继承的概念之前,我先说下类和原型的概念。

讲 ECMAScript 继承的概念之前,我先说下类的概念。(如果接触过 Java 或者是 C++ 的话,我们就知道 Java(C++)的继承都是基于类的继承)

类: 是面向对象(Object Oriented)语言实现信息封装的基础,称为类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。
类: 是描述了一种代码的组织结构形式。

原型

JavaScript 这门语言没有类的概念,所以 JavaScript 并非是基于类的继承,而是基于原型的继承。(主要是借鉴 Self 语言原型(prototype)继承机制)。

注意:ES6 中的 class 关键字和 OO 语言中的类的概念是不同的。ES6 的 class 其内部同样是基于原型实现的继承。
JavaScript 摒弃类转而使用原型作为实现继承的基础,是因为基于原型的继承相比基于类的继承上在概念上更为简单。
首先我们明确一点,类存在的目的是为了实例化对象,而 JavaScript 可以直接通过对象字面量语法轻松的创建对象。
每一个函数,都有一个 prototype 属性。 所有通过函数 new 出来的对象,这个对象都有一个 __proto__ 指向这个函数的 prototype。 当你想要使用一个对象(或者一个数组)的某个功能时:如果该对象本身具有这个功能,则直接使用;如果该对象本身没有这个功能,则去 __proto__ 中找。

prototype [显式原型]

prototype 是一个显式的原型属性,只有函数才拥有该属性。
每一个函数在创建之后都会拥有一个名为 prototype 的属性,这个属性指向函数的原型对象。( 通过 Function.prototype.bind 方法构造出来的函数是个例外,它没有 prototype 属性 )。
在控制台中打印 console.log(Array.prototype) 里面有很多方法。这些方法都以事先内置在 JavaScript 中,直接调用即可。两个特别的属性 constructor__proto__。这两个属性接下来要讲。

我们现在写一个 function noWork(){} 函数。

当写了一个 noWork 这个方法的时候,它自动创建了一个 prototype 指针属性(指向原型对象)。

而这个被指向的原型对象自动获得了一个 constructor (构造函数)。constructor 指向的是 noWork

noWork.prototype.constructor === noWork     // true
一个函数的原型对象的构造函数是这个函数本身

__proto__[隐式原型]

__proto__ 理解起来就会比 prototype 稍微复杂一点。这个过程很有趣。

其实这个属性指向了 `[[prototype]]`,但是 `[[prototype]]` 是内部属性,我们并不能访问到,所以使用 `__proto__` 来访问。
__proto__ 指向了创建该对象的构造函数的显式原型

我们现在还是使用 noWork 这个例子来说。我们发现 noWork 原型对象中还有另一个属性 __proto__

我们先打印这个属性:

发现这个 __proto__ 指向的是 Object.prototype!

  • 因为这个 __proto__.constructor 指向的是 Object
  • 我们知道:一个函数的原型对象的构造函数是这个函数本身
  • 所以这个 __proto__.constructor 指向的是 Object.prototype.constructor
  • 进而 __proto__ 指向的是 Object.prototype

至于为什么是指向 Object? 因为所有的引用类型默认都是继承 Object 。

作用

  1. 显式原型:用来实现基于原型的继承与属性的共享。
  2. 隐式原型:构成原型链,同样用于实现基于原型的继承。 举个例子,当我们使用 noWork 这个对象中的 toString() 属性时,在noWork 中找不到,就会沿着 __proto__ 依次查找。

当我们使用 new 操作符时,生成的实例对象拥有了 __proto__属性。即在 new 的过程中,新对象被添加了 __proto__ 并且链接到构造函数的原型上。

new 的过程

  1. 新生成了一个对象
  2. 链接到原型
  3. 绑定 this
  4. 返回新对象

我们知道所有对象都可以通过原型链最终找到 Object.prototype ,虽然 Object.prototype 也是一个对象,但是这个对象却不是 Object 创造的,而是引擎自己创建了 Object.prototype 。 所以可以这样说:

所有实例都是对象,但是对象不一定都是实例。
首先引擎创建了 Object.prototype ,然后创建了 Function.prototype ,并且通过 __proto__ 将两者联系了起来。

这就是为什么 Function.prototype.bind() 没有 prototype 属性。因为 Function.prototype 是引擎创建出来的对象,引擎认为不需要给这个对象添加 prototype 属性。

闭包

先放一段 Vue 源码中的 once 函数。这就是闭包调用 —— 函数作为返回值:

/**
* Ensure a function is called only once.
*/
export function once (fn: Function): Function {
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}

这个函数的作用就是确保函数只调用一次。

为什么只会调用一次呢? 因为函数调用完成之后,其执行上下文环境不会被销毁,所以 called 的值依然在那里。
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。

给两段代码,如果你知道他们的运行结果,那么说明你是了解闭包的:

// 第一段
var num = 20;
function fun(){
var num = 10;
return function con(){
console.log( this.num )
}
}
var funOne = fun(); funOne(); // 20 // 第二段
var num = 20;
function fun(){
var num = 10;
return function con(){
console.log( num )
}
}
var funOne = fun(); funOne(); // 10

5.函数柯里化

"柯里化",就是把一个多参数的函数,转化为单参数函数。

看一道题目

如何使 add(2)(3)(4)(5)() 输出 14
依题意,有两个关键点要注意:

传入参数时,代码不执行输出结果,而是先记忆起来
当传入空的参数时,代表可以进行真正的运算

这可以用函数柯里化来解,即:

function add(num){
var sum=0;
sum= sum+num;
return function tempFun(numB){
if(arguments.length===0){
return sum;
}else{
sum= sum+ numB;
return tempFun;
}
}
}

我们经常这么写判断句

if( A ){
// code
}else if( B ){
// code
}

这个写法没什么问题,可是在重复的出现这种相同的判断的时候。这个就显得有点不那么智能了。这个时候函数柯里化就可以排上用场了。

这里利用柯里化的特点,通过 createPatchFunction 方法把一些参数提前保存,以便复用。

// 这样不用每次调用 patch 的时候都传递 nodeOps 和 modules
export function createPatchFunction (backend) {
// 省略好多代码
return function patch (oldVnode, vnode, hydrating, removeOnly) {
// 省略好多代码
}
}

6.event loop

四个概念汇总

  • 同步任务:即在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
  • 异步任务:指的是不进入主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
  • macrotask:主要场景有:主代码块、setTimeout、setInterval等
  • microtask:主要场景有:Promise、process.nextTick等。

学习Vue源码前的几项必要储备(二)的更多相关文章

  1. 学习Vue源码前的几项必要储备(一)

    从接下来的一段时间里,Mg要进行阅读源码的工作.再阅读源码前,梳理一下准备工作. 7项重要储备 Flow 基本语法 发布/订阅模式 ES6+ 语法 原型链.闭包 函数柯里化 event loop 1. ...

  2. 一起学习vue源码 - Object的变化侦测

    作者:小土豆biubiubiu 博客园:www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d 简书:h ...

  3. 手牵手,从零学习Vue源码 系列二(变化侦测篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...

  4. 学习 vue 源码 -- 响应式原理

    概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...

  5. 手牵手,从零学习Vue源码 系列一(前言-目录篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 手牵手,从零学习Vue源码 系列三(虚拟DOM篇) 陆续更新中... 预计八月中旬更新 ...

  6. 一起学习vue源码 - Vue2.x的生命周期(初始化阶段)

    作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e8 ...

  7. Vue源码学习(一):调试环境搭建

    最近开始学习Vue源码,第一步就是要把调试环境搭好,这个过程遇到小坑着实费了点功夫,在这里记下来 一.调试环境搭建过程 1.安装node.js,具体不展开 2.下载vue项目源码,git或svn等均可 ...

  8. Vue源码详细解析:transclude,compile,link,依赖,批处理...一网打尽,全解析!

    用了Vue很久了,最近决定系统性的看看Vue的源码,相信看源码的同学不在少数,但是看的时候却发现挺有难度,Vue虽然足够精简,但是怎么说现在也有10k行的代码量了,深入进去逐行查看的时候感觉内容庞杂并 ...

  9. Vue源码解析(一):入口文件

    在学习Vue源码之前,首先要做的一件事情,就是去GitHub上将Vue源码clone下来,目前我这里分析的Vue版本是V2.5.21,下面开始分析: 一.源码的目录结构: Vue的源码都在src目录下 ...

随机推荐

  1. hdu 1709 The Balance(母函数)

    题意: 有一个天平.有N个砝码.重量分别是A1...AN. 问重量[1..S]中有多少种重量是无法利用这个天平和这些砝码称出来的. S是N个砝码的重量总和. 思路: 对于每一个砝码来说,有三种:不放, ...

  2. AppScan 10安装使用

    一.简介 AppScan是IBM的一款web安全扫描工具,具有利用爬虫技术进行网站安全渗透测试的能力,能够根据网站入口自动摸取网页链接进行安全扫描,提供了扫描.报告和修复建议等功能. appscan有 ...

  3. 基于Lucene的全文检索实践

    由于项目的需要,使用到了全文检索技术,这里将前段时间所做的工作进行一个实践总结,方便以后查阅.在实际的工作中,需要灵活的使用lucene里面的查询技术,以达到满足业务要求与搜索性能提升的目的. 一.全 ...

  4. 后台管理系统:vue&node&MongoDB(一)

    后台管理系统 使用工具: Vue    Node     Mongodb   Element-ui 一.后台(Node+Mongodb) 前期准备: 需要下载的包: mongooes -------- ...

  5. 新技能GET!在前端表格中花式使用异步函数的奥义

    背景 60年代时,操作系统中独立运行的单元通常是进程.但随着计算机技术的发展,人们发现在进程运行过程中,创建.撤销与切换都要花费较大的时空开销. 到了80年代为了解决这一问题,出现了更小的独立运行基本 ...

  6. 菜鸡的Java笔记 类图

    类图        1.如何实现类图的描述        2.时序图的使用                    从实际i的开发标准:应该在项目编写钱设计类图        而现在的开发大部分情况下, ...

  7. typing使用

    官方文档: typing 注: typing是python3.5(PEP484)开始的 可用于: 类型检查器.集成开发环境.静态检查器等, 但是不强制使用 使用方式 两种使用方式 别名: 先定义=, ...

  8. uni-app 微信小程序全局分享

    实际使用中,经常需要小程序分享到好友或者朋友圈,一般而言是一个个页面去设置. 官网分享介绍:https://uniapp.dcloud.io/api/plugins/share?id=onsharea ...

  9. [cf559E]Gerald and Path

    将所有线段的端点(即$a_{i}$和$a_{i}\pm l_{i}$)离散,并按照$a_{i}$从小到大排序 定义$f_{i,,j}$表示前$i$条线段在位置$j$之前最多能覆盖的长度(默认覆盖到$j ...

  10. [luogu5564]Say Goodbye

    两树相同的定义与题中相同,并定义两树完全相同当且仅当在不允许基环旋转的条件下相同 枚举基环的长度$l$,根据置换群的理论,答案即$\frac{1}{l}\sum_{i=1}^{l}f(i)$(其中$f ...