深入理解js原型和闭包笔记:
1.“一切皆是对象”,对象是属性的集合。
丨 函数也是对象,但是使用typeof时为什么函数返回function而
丨 不是object呢,js为何要对函数做这样的区分?函数和对象到底什么关系呢?
2.函数和对象的关系(对象是函数创建的,但同时函数又是一种对象)
丨 对象是通过函数创建的,那它的属性是如何通过函数构造的,又为什么说函数自身也是一种
丨对象呢,函数有什么对象的特征(对象的特质是属性的集合),难道函数也是属性的集合?它
丨有什么属性呢?
3.函数的属性:prototype原型(函数也是对象,都有一个叫做prototype原型的属性,这个属性的属性值也是一个对象,该对象中又默认包含一个constructor的属性,指向函数本身。函数所创建的对象所具有的属性定义来源于‘Fn.prototype.对象属性’)
丨但是我们在使用对象的属性时都是‘对象.属性名’这种方式,为什么不是 ‘对象.prototype.属性名’呢,
丨明明属性名是定义在prototype下的,原来对象都有一个隐藏原型 _proto_,‘对象._proto_’=== Fn.prototype 。
4.隐藏原型(每一个对象都有一个隐藏原型,指向创建该对象的函数的prototype ---别忘了每一个函数都有一个prototype属性哦)
丨那么问题来了,上面说函数的prototype属性值也是对象,那它的_proto_指向哪,prototype属性值对象是被Object函数创建的,
丨它的_proto_指向Object函数的prototype,那Object函数的prototype属性值的_proto_指向哪呢,Object函数的prototype
丨属性值的_proto_指向null。还有一个问题,上面函数也是对象,那函数的_proto_指向哪呢,函数是由Function函数创建来的,
丨所以函数的_proto_指向Function函数的prototype。那Function函数的_proto_指向哪呢,答案是指向Function自身的prototype,
丨因为Function被自身所创建,这也形成了一个环形引用。(只需要记住一个准则,对象的_proto_指向创建它的函数的prototype)。
丨是不是很乱,吼吼,一点也不乱。要是你和我一样都喜欢整洁干净感觉还乱,那么接下来我们借助instanceof来帮助我们理清
丨一下js内部“混乱的关系”。(下图为普通函数、Function及Object之间的关系图,这张图就是js原型链继承关系核心了,我们1~7
丨其实就是在讲解理清这张关系图)。
5.instanceof(A instanceof B的判断规则是:沿着A的_proto_这条线来找,同时沿着B的prototype的这条线来找,如果这两条线能找到同一个引用,即同一个对象,那么就返回true,如果找到终点还未重合,则返回false。Instanceof表示的是一种继承关系,或者原型链的结构。这时再对着图理一理是不是更加清楚了,原来他们指来指去就是在构造这样的继承关系)。
丨Instanceof这样设计,是想表达什么,是想表达我们接下来介绍的东东——继承——原型链。
6.原型链和继承(访问一个对象的属性时,先在基本属性中找,如果没有,在沿着_proto_这条链向上找,这就是原型链,而这种获取上级属性的方式就是继承)。
| 上面说了这么多函数、对象、原型、隐藏原型,以及之间如何相互指向构成js的继承和原型链的,那么我们也说一下这种原型的优
丨势——灵活性。
7.原型的灵活性(对象属性可以随时改动随时添加)如果你要添加内置方法的原型属性,最好做一步判断,如果该属性不存在则添加。如果本来就存在,就没必要添加了。
-----------------------------------------------------------------------------------------------------------------------------------
呵呵呵呵,终于看到分割线了,没错上面结束了。1~7介绍的就是js中的原型,从函数与对象的关系说起,介绍了js是如何通过函数和对象的原型、隐藏原型建立js内部的原型链和继承机制的。
8.【执行上下文环境】上(在执行代码之前,要把用到的所有变量先拿出来,有的直接赋值,有的先用undefined占个空)。先来介绍全局作用域的上下文都有哪些内容。
a.变量和函数表达式——变量声明,但是不赋值,先用undefined占空;
b.this——赋值;
c.函数声明——赋值,赋值函数体的内容;
丨那么函数体中的执行上下文环境有哪些呢?走起
9.【执行上下文环境】下(函数每被调用一次,都会产生一个新的执行上下文环境,那么产生的上下文环境都有什么呢,看下面)。
a.参数——赋值
b.arguments——赋值
c.*变量的取值作用域——赋值(函数在定义的时候不是在调用的时候就已经确定了函数体内*变量的作用域了)。至于“自有变量”和“作用域”,后面专门会讲。
丨那么我们又有了新的问题,在js执行的时候,会有数不清的函数调用,会产生很多个函数上下文环境,这么多上下文环境
丨该如何管理,以及如何销毁这些内存呢,我们接下来讲解“执行上下文栈”。不过在讲解“执行上下文栈”之前,我们先讲解一下this,
丨this还是挺重要的。
10.this(this到底取何值呢,this的取值是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。(而blog中仅仅是使用例子介绍了this取值的四种情况,难道又要死记硬背吗?具体原因却讲解的不够清楚,这完全不符合我们的宗旨。我的另一篇blog详细介绍了this取值的前因后果,说白了就是指向调用它的对象,但是这样说不是很精确,具体参考blog:https://www.cnblogs.com/lauzhishuai/p/9494196.html。在此我将这篇文章的精髓整理一下,让你彻底知道同时又理解this到底是如何取值的)。
一:
一句话:this的指向在定义时是确定不了的,只有在执行的时候才能确定this的指向,this指向最终调用它的对象(在被多个对象一层层调用出来时,this指向他上一级的对象,而不是最外层对象)。
这句话可以拆分成四句通俗易懂的话:a.首先如果调用this时没有什么东西"."出他来,this指向Window,指Window.
b.如果只有一个对象“.”出他来,this指向这个对象。(A.this)
c.如果通过多层对象一步步“.”出this(A.B.),那么this指向紧邻的上一级,此处即为B
d.注意一定是最终,即最后“.”出this的那一行代码中去找上面三种情况。
另外还有两种特殊情况:
二:
一种是函数使用apply、call、bind方法自行改变this的指向。
三:
另一种特殊情况:在碰到retrun时,如果返回的是一个对象,那么this指向的就是这个对象,如果返回的不是一个对象,那么this指向函数的实例。(注意在返回null时,虽然null是对象,但是此时this指向函数的实例)。
-----补充知识点:undefined是基本数据类型,表示未定义。null是一个对象表示空对象。
好了this就这么多,是不是一目了然了,this搞清出了让我们继续撤回我们本系列的主题,继续开始我们的闭包之旅。
11.执行上下文栈(就是一个压栈出栈的过程,保持活跃的只有一个执行上下文环境)
丨理想的执行上下文栈很简单,但是有一种很常见的情况并不能做到如此完美的压栈出栈,说销毁就销毁,这就是我们接下来要讲的——闭包,
丨但是在讲之前我们还要从“*变量”和“作用域”说起。
12.作用域(学过编程的都知道作用域,这里需要注意几点就行了:a.js中没有块级作用域,同时js除了全局作用域外只有函数可以创建作用域。那么我们在编程的时候,定义变量时最好只在全局代码前端和函数开头定义,以免产生混淆;b.作用域之间存在父级关系,这是下面的作用域链了,也没什么;c.作用域最大的作用就是隔离变量)
丨好了,这节没有什么可讲,我们继续,下面我们将作用域和执行上下文环境结合起来讲。
13.作用域和执行上下文环境(作用域只是一个“地盘”,是抽象的概念,没有变量,要通过作用域对应的执行上下文环境才能找到变量的取值,作用域在定义的时候已经确定了,而执行上下文只有在具体执行时才产生确定)。
|好了上面是作用域和执行上下文之间简单的关系,其实都理解,下面再讲讲跨作用域取值——即“*变量”和作用域链
14.*变量和作用域链(*变量:在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个*变量。而如果在当前作用域中找不到*变量的取值,那么就到定义该函数的上一级作用域中找,注意这里是定义而不是调用——其实这就是所谓的静态作用域。而如果在找不到则继续到它的上上创建父级作用域找,而这个寻找过程就产生了所谓的作用域链)。
丨好了,我们的一切知识都已储备好,终于轮到我们的“闭包”出场了,其实8~14讲的执行上下文环境、执行上下文栈、作用域、*变量对于
丨学过编程的人来讲基本上都是知道的,它就是你理解的那样,可能js中有几点特殊需要注意罢了,所以没有必要觉得“闭包”多厉害,因为基础
丨东西你早就懂了。
15.闭包(这篇系列的文章闭包讲的有点啰嗦,他结合上面的作用域链和执行上下文栈讲解了闭包,确实是这样,不过上面说的是原因,我们其实只需要一句话就可以知道闭包是什么,其中的变量如何取值:闭包就类似于一个背包,当函数作为返回值或者函数参数时,函数中包含的变量取值也会带过去,就像一个背包一样带着他们,它们的取值要到函数原来对应的作用域链中找)。
-----------------------------------------------------------------------------------------------------------------
好了,终于讲完了,js中的原型、闭包、this是不是现在理解的很透彻了。看完这篇总结,本系列文章中后面两篇的补充就没必要看了,在总结中早就含括在内了。这些知识是不是很简单,并没有你想想中的那么难,网上的很多资料杂七杂八,讲解的时候要不就是讲解的思路不清晰,要么就是只讲片面的知识点,本系列文章“小步快跑”,环环紧扣,很有逻辑和黏性的把所有的东西都讲解透了,对于不理解相关知识的人还是很值得看的。溜了溜了~~