├── .gitignore ├── README.md ├── README1.md ├── SUMMARY.md ├── book.json ├── instanceof ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png └── index.md ├── prototype原型 ├── img │ ├── 1.png │ ├── 2.png │ └── 3.png └── index.md ├── this ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ └── 8.png ├── img1 │ └── 1.png ├── index.md └── index1.md ├── 一切都是对象 ├── img │ └── 11.png └── index.md ├── 上下文环境和作用域的关系 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png └── index.md ├── 从自由变量到作用域链 ├── img │ ├── 1.png │ ├── 2.png │ └── 3.png └── index.md ├── 作用域和上下文环境 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ └── 8.png └── index.md ├── 函数和对象的关系 └── index.md ├── 原型的灵活性 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png └── index.md ├── 完结 ├── img │ └── 1.png └── index.md ├── 执行上下文栈 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png └── index.md ├── 简介作用域 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png └── index.md ├── 简述执行上下文 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── img1 │ ├── 1.png │ └── 2.png ├── index.md └── index1.md ├── 继承 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png └── index.md ├── 闭包 ├── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png └── index.md └── 隐式原型 ├── img ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png └── 6.png └── index.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | _book 3 | 4 | 5 | data -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 书籍说明 2 | 本书是对[深入理解javascript原型和闭包](http://www.cnblogs.com/wangfupeng1988/p/3977924.html)系列博客的整理,该博客的作者是**王福朋**,在此感谢王老师。整理人:[wiliam](http://wiliam.top) 3 | 4 | 为什么会整理成电子书:最近学习JavaScript看到了原型和闭包的概念后,不是很理解,总是晕乎乎的,无意间看到了王老师写的[深入理解javascript原型和闭包](http://www.cnblogs.com/wangfupeng1988/p/3977924.html)教程,有醍醐灌顶之感,**王老师**讲的很透彻,所以有想要将这系列收藏做成自己的知识储备,以后可以随时随地的巩固消化。最后整理出来了本电子书,在此分享出来,也希望大家看后对javascript原型和闭包的概念有所收获。最后再次感谢**王老师**。 5 | 6 | # 版权说明 7 | 本书是对**王福朋**老师[深入理解javascript原型和闭包](http://www.cnblogs.com/wangfupeng1988/p/3977924.html)系列博客的整理。如果想要阅读原文章可以点击上面的链接。 8 | 9 | # 排版说明 10 | 针对**王老师**的补充章节,本人做了合并,补this合并到了this章节。补充:上下文环境和作用域的关系放到了【作用域】和【上下文环境】的下一章。针对**王老师**博客上的文字错误做了一定的校正,有可能还会存在不对的,欢迎指正,我会及时修改。 11 | 12 | 13 | 从下一页开始就是本书的真正内容了。 14 | -------------------------------------------------------------------------------- /README1.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 3 | 该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分——***原型和闭包***,当然,肯定少不了原型链和作用域链。帮你揭开javascript最神秘的面纱。 4 | 5 |   为什么要偏偏要讲这两个知识点? 6 | 7 |   这是我在这么多年学习javascript的经历中,认为最难理解、最常犯错的地方,学习这两个知识点,会让你对javascript有更深层次的理解,至少理解了原型和作用域,就不能再算是javascript菜鸟了。另外,这两方面也是javascript与其他语言不同的地方,学习这样的设计,有助于你开阔眼界,帮助你了解编程语言的设计思路。毕竟,你不能只把眼睛盯在一门语言上。 8 | 9 |   闲话不多讲,相信奔着这个话题来的朋友,也知道javascript原型和作用域的重要性。 10 | 11 |   最后说明:本系列教程我不是照搬的其他图书或者网络资料,而是全凭着我对知识的理解而一步一步写的。思路也是我一边写着一边想的。有什么不对的地方,欢迎指正。 12 |    13 |    14 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | 3 | * [ 说明](README1.md) 4 | 5 | * [一切都是对象](一切都是对象/index.md) 6 | * [函数和对象的关系](函数和对象的关系/index.md) 7 | * [prototype原型](prototype原型/index.md) 8 | * [隐式原型](隐式原型/index.md) 9 | * [instanceof](instanceof/index.md) 10 | * [继承](继承/index.md) 11 | * [原型的灵活性](原型的灵活性/index.md) 12 | * 简述执行上下文 13 | * [ 简述【执行上下文】上](简述执行上下文/index.md) 14 | * [ 简述【执行上下文】下](简述执行上下文/index1.md) 15 | * this 16 | * [this](this/index.md) 17 | * [补this](this/index1.md) 18 | * [执行上下文栈](执行上下文栈/index.md) 19 | * [简介作用域](简介作用域/index.md) 20 | * [作用域和上下文环境](作用域和上下文环境/index.md) 21 | * [上下文环境和作用域的关系](上下文环境和作用域的关系/index.md) 22 | * [从自由变量到作用域链](从自由变量到作用域链/index.md) 23 | * [闭包](闭包/index.md) 24 | * [完结](完结/index.md) -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "深入理解javascript原型和闭包", 3 | "description": "深入理解javascript原型和闭包", 4 | "author": "wiliam", 5 | "output.name": "site", 6 | "language": "zh-hans", 7 | "gitbook": "2.6.7", 8 | "root": ".", 9 | "structure": { 10 | "readme": "README.md" 11 | }, 12 | "links": { 13 | "sidebar": { 14 | "个人主页": "http://wiliam.top" 15 | } 16 | }, 17 | "sharing": { 18 | "weibo": true, 19 | "facebook": false, 20 | "twitter": false, 21 | "google": false, 22 | "instapaper": false, 23 | "vk": false, 24 | "all": [ 25 | "facebook", 26 | "google", 27 | "twitter", 28 | "weibo", 29 | "instapaper" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /instanceof/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/instanceof/img/1.png -------------------------------------------------------------------------------- /instanceof/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/instanceof/img/2.png -------------------------------------------------------------------------------- /instanceof/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/instanceof/img/3.png -------------------------------------------------------------------------------- /instanceof/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/instanceof/img/4.png -------------------------------------------------------------------------------- /instanceof/index.md: -------------------------------------------------------------------------------- 1 | # instanceof 2 | 又介绍一个老朋友——instanceof。 3 | 4 | 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/function,你不知道它到底是一个object对象,还是数组,还是new Number等等。 5 | 6 | 这个时候就需要用到instanceof。例如: 7 | 8 | ![PNG](img/1.png) 9 | 10 | 上图中,f1这个对象是被Foo创建,但是“f1 instanceof Object”为什么是true呢? 11 | 12 | 13 | 14 | 至于为什么过会儿再说,先把instanceof判断的规则告诉大家。根据以上代码看下图: 15 | 16 | ![PNG](img/2.png) 17 | 18 | Instanceof运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。 19 | 20 | Instanceof的判断队则是:**沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false**。 21 | 22 | 按照以上规则,大家看看“ f1 instanceof Object ”这句代码是不是true? 根据上图很容易就能看出来,就是true。 23 | 24 | 25 | 26 | 通过上以规则,你可以解释很多比较怪异的现象,例如: 27 | 28 | ![PNG](img/3.png) 29 | 30 | 这些看似很混乱的东西,答案却都是true,这是为何? 31 | 32 | 正好,这里也接上了咱们上一节说的“乱”。 33 | 34 | 上一节咱们贴了好多的图片,其实那些图片是可以联合成一个整体的,即: 35 | 36 | ![PNG](img/4.png) 37 | 38 | 看这个图片,千万不要嫌烦,必须一条线一条线挨着分析。如果上一节你看的比较仔细,再结合刚才咱们介绍的instanceof的概念,相信能看懂这个图片的内容。 39 | 40 | 看看这个图片,你也就知道为何上面三个看似混乱的语句返回的是true了。 41 | 42 | 问题又出来了。Instanceof这样设计,到底有什么用?到底instanceof想表达什么呢? 43 | 44 | 重点就这样被这位老朋友给引出来了——**继承**——**原型链**。 45 | 46 | 即,**instanceof表示的就是一种继承关系,或者原型链的结构**。请看下节分解。 47 | 48 | (注:本节的图片来源于http://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/figure1.jpg) -------------------------------------------------------------------------------- /prototype原型/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/prototype原型/img/1.png -------------------------------------------------------------------------------- /prototype原型/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/prototype原型/img/2.png -------------------------------------------------------------------------------- /prototype原型/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/prototype原型/img/3.png -------------------------------------------------------------------------------- /prototype原型/index.md: -------------------------------------------------------------------------------- 1 | # prototype原型 2 | 既typeof之后的另一位老朋友! 3 | 4 | prototype也是我们的老朋友,即使不了解的人,也应该都听过它的大名。如果它还是您的新朋友,我估计您也是javascript的新朋友。 5 | 6 | 在咱们的第一节[一切都是对象](../一切都是对象/index.md)中说道,函数也是一种对象。他也是属性的集合,你也可以对函数进行自定义属性。 7 | 8 | 不用等咱们去试验,javascript自己就先做了表率,人家就默认的给函数一个属性——prototype。对,每个函数都有一个属性叫做prototype。 9 | 10 | 这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。 11 | 12 | ![PNG](img/1.png) 13 | 14 | 如上图,SuperType是是一个函数,右侧的方框就是它的原型。 15 | 16 | 原型既然作为对象,属性的集合,不可能就只弄个constructor来玩玩,肯定可以自定义的增加许多属性。例如这位Object大哥,人家的prototype里面,就有好几个其他属性。 17 | 18 | ![PNG](img/2.png) 19 | 20 | 咦,有些方法怎么似曾相似? 21 | 22 | 对!别着急,之后会让你知道他们为何似曾相识。 23 | 24 | 接着往下说,你也可以在自己自定义的方法的prototype中新增自己的属性 25 | 26 | ``` 27 | function Fn() { } 28 | Fn.prototype.name = '王福朋'; 29 | Fn.prototype.getYear = function () { 30 | return 1988; 31 | }; 32 | ``` 33 | 看到没有,这样就变成了 34 | 35 | ![PNG](img/3.png) 36 | 37 | 没问题! 38 | 39 | 但是,这样做有何用呢? —— 解决这个问题,咱们还是先说说jQuery吧。 40 | ``` 41 | var $div = $('div'); 42 | $div.attr('myName', '王福朋'); 43 | ``` 44 | 以上代码中,$('div')返回的是一个对象,对象——被函数创建的。假设创建这一对象的函数是 myjQuery。它其实是这样实现的。 45 | ``` 46 | myjQuery.prototype.attr = function () { 47 | //…… 48 | }; 49 | $('div') = new myjQuery(); 50 | ``` 51 | 不知道大家有没有看明白。 52 | 53 | 如果用咱们自己的代码来演示,就是这样 54 | 55 | ``` 56 | function Fn() { } 57 | Fn.prototype.name = '王福朋'; 58 | Fn.prototype.getYear = function () { 59 | return 1988; 60 | }; 61 | 62 | var fn = new Fn(); 63 | console.log(fn.name); 64 | console.log(fn.getYear()); 65 | ``` 66 | 即,Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。 67 | 68 | 因为每个对象都有一个隐藏的属性——“__proto__”,这个属性引用了创建这个对象的函数的prototype。即:fn.__proto__ === Fn.prototype 69 | 70 | 这里的"__proto__"成为“隐式原型”,下回继续分解。 71 | -------------------------------------------------------------------------------- /this/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/1.png -------------------------------------------------------------------------------- /this/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/2.png -------------------------------------------------------------------------------- /this/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/3.png -------------------------------------------------------------------------------- /this/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/4.png -------------------------------------------------------------------------------- /this/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/5.png -------------------------------------------------------------------------------- /this/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/6.png -------------------------------------------------------------------------------- /this/img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/7.png -------------------------------------------------------------------------------- /this/img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img/8.png -------------------------------------------------------------------------------- /this/img1/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/this/img1/1.png -------------------------------------------------------------------------------- /this/index.md: -------------------------------------------------------------------------------- 1 | # this 2 | 接着上一节讲的话,应该轮到“执行上下文栈”了,但是这里不得不插入一节,把this说一下。因为this很重要,js的面试题如果不出几个与this有关的,那出题者都不合格。 3 | 4 | 其实,this的取值,分四种情况。我们来挨个看一下。 5 | 6 | 在此再强调一遍一个非常重要的知识点:**在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了**。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。 7 | 8 | ## 情况1:构造函数 9 | 所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等。 10 | 11 | ![PNG](img/1.png) 12 | 13 | 以上代码中,如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。 14 | 15 | 16 | 17 | 注意,以上仅限new Foo()的情况,即Foo函数作为构造函数的情况。如果直接调用Foo函数,而不是new Foo(),情况就大不一样了。 18 | 19 | ![PNG](img/2.png) 20 | 21 | 这种情况下this是window,我们后文中会说到。 22 | 23 | ## 情况2:函数作为对象的一个属性 24 | 如果函数作为对象的一个属性时,**并且作为对象的一个属性被调用时**,函数中的this指向该对象。 25 | 26 | ![PNG](img/3.png) 27 | 28 | 以上代码中,fn不仅作为一个对象的一个属性,而且的确是作为对象的一个属性被调用。结果this就是obj对象。 29 | 30 | **注意**,如果fn函数不作为obj的一个属性被调用,会是什么结果呢? 31 | 32 | ![PNG](img/4.png) 33 | 34 | 如上代码,如果fn函数被赋值到了另一个变量中,并没有作为obj的一个属性被调用,那么this的值就是window,this.x为undefined。 35 | 36 | ## 情况3:函数用call或者apply调用 37 | 当一个函数被call和apply调用时,this的值就取传入的对象的值。至于call和apply如何使用,不会的朋友可以去查查其他资料,本系列教程不做讲解。 38 | 39 | ![PNG](img/5.png) 40 | 41 | ## 情况4:全局 & 调用普通函数 42 | 在全局环境下,this永远是window,这个应该没有非议。 43 | ``` 44 | console.log(this === window); //true 45 | ``` 46 | 47 | 普通函数在调用时,其中的this也都是window。 48 | 49 | ![PNG](img/6.png) 50 | 51 | 以上代码很好理解。 52 | 53 | 不过下面的情况你需要注意一下: 54 | 55 | ![PNG](img/7.png) 56 | 57 | 函数f虽然是在obj.fn内部定义的,但是它仍然是一个普通的函数,this仍然指向window。 58 | 59 | 完了。 60 | 61 | 看到了吧,this有关的知识点还是挺多的,不仅多而且非常重要。 62 | 63 | 最后,既然提到了this,有必要把一个非常经典的案例介绍给大家,又是jQuery源码的。 64 | 65 | ![PNG](img/8.png) 66 | 67 | 以上代码是从jQuery中摘除来的部分代码。jQuery.extend和jQuery.fn.extend都指向了同一个函数,但是当执行时,函数中的this是不一样的。 68 | 69 | 执行jQuery.extend(…)时,this指向jQuery;执行jQuery.fn.extend(…)时,this指向jQuery.fn。 70 | 71 | 这样就巧妙的将一段代码同时共享给两个功能使用,更加符合设计原则。 72 | 73 | 74 | 75 | 好了,聊完了this。接着上一节继续说“执行上下文栈”。 76 | 77 | 注意:还有一部分this的内容本文中没有讲到,已经补充到这里:[补this](index1.md) -------------------------------------------------------------------------------- /this/index1.md: -------------------------------------------------------------------------------- 1 | # 补this 2 | 本文对[this](index.md)一篇进行补充 3 | 4 | 原文中,讲解了在javascript中this的各个情况,写完之后发现还落下一种情况,就此补充。 5 | 6 | 7 | 8 | 原文中this的其中一种情况是构造函数的,具体的内容可以参考原文,此处不再赘述。 9 | 10 | 要补充的内容是,在构造函数的prototype中,this代表着什么。 11 | 12 | ![PNG](img1/1.png) 13 | 14 | 如上代码,在Fn.prototype.getName函数中,this指向的是f1对象。因此可以通过this.name获取f1.name的值。 15 | 16 | **其实,不仅仅是构造函数的prototype,即便是在整个原型链中,this代表的也都是当前对象的值。** -------------------------------------------------------------------------------- /一切都是对象/img/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/一切都是对象/img/11.png -------------------------------------------------------------------------------- /一切都是对象/index.md: -------------------------------------------------------------------------------- 1 | # 一切都是对象 2 | 3 | “**一切都是对象**”这句话的重点在于如何去理解“**对象**”这个概念。 4 | 5 | ——当然,也不是所有的都是对象,值类型就不是对象。 6 | 7 | 首先咱们还是先看看javascript中一个常用的函数——typeof()。typeof应该算是咱们的老朋友,还有谁没用过它? 8 | 9 | typeof函数输出的一共有几种类型,在此列出: 10 | ``` 11 | function show(x) { 12 | 13 | console.log(typeof(x)); // undefined 14 | console.log(typeof(10)); // number 15 | console.log(typeof('abc')); // string 16 | console.log(typeof(true)); // boolean 17 | 18 | console.log(typeof(function () { })); //function 19 | 20 | console.log(typeof([1, 'a', true])); //object 21 | console.log(typeof ({ a: 10, b: 20 })); //object 22 | console.log(typeof (null)); //object 23 | console.log(typeof (new Number(10))); //object 24 | } 25 | show(); 26 | ``` 27 | 28 | 以上代码列出了typeof输出的集中类型标识,其中上面的四种(undefined, number, string, boolean)属于简单的**值类型**,不是对象。剩下的几种情况——函数、数组、对象、null、new Number(10)都是对象。他们都是**引用类型**。 29 | 30 | 判断一个变量是不是对象非常简单。值类型的类型判断用typeof,引用类型的类型判断用instanceof。 31 | ``` 32 | var fn = function () { }; 33 | console.log(fn instanceof Object); // true 34 | ``` 35 | 36 | 好了,上面说了半天对象,各位可能也经常在工作中应对对象,在生活中还得应对活生生的对象。有些个心理不正常或者爱开玩笑的单身人士,还对于系统提示的“找不到对象”耿耿于怀。那么在javascript中的对象,到底该如何定义呢? 37 | 38 | **对象——若干属性的集合。** 39 | 40 | java或者C#中的对象都是new一个class出来的,而且里面有字段、属性、方法,规定的非常严格。但是javascript就比较随意了——数组是对象,函数是对象,对象还是对象。对象里面的一切都是属性,只有属性,没有方法。那么这样方法如何表示呢?——方法也是一种属性。因为它的属性表示为**键值对**的形式。 41 | 42 | 而且,更加好玩的事,javascript中的对象可以任意的扩展属性,没有class的约束。这个大家应该都知道,就不再强调了。 43 | 44 | 先说个最常见的例子: 45 | 46 | ![PNG](img/11.png) 47 | 48 | 以上代码中,obj是一个自定义的对象,其中a、b、c就是它的属性,而且在c的属性值还是一个对象,它又有name、year两个属性。 49 | 50 | 这个可能比较好理解,那么函数和数组也可以这样定义属性吗?——当然不行,但是它可以用另一种形式,总之函数/数组之流,只要是对象,它就是属性的集合。 51 | 52 | 以函数为例子 53 | ``` 54 | var fn = function () { 55 | alert(100); 56 | }; 57 | fn.a = 10; 58 | fn.b = function () { 59 | alert(123); 60 | }; 61 | fn.c = { 62 | name: "王福朋", 63 | year: 1988 64 | }; 65 | ``` 66 | 67 | 上段代码中,函数就作为对象被赋值了a、b、c三个属性——很明显,这就是属性的集合吗。 68 | 69 | 你问:这个有用吗? 70 | 71 | 回答:可以看看jQuery源码! 72 | 73 | 在jQuery源码中,“jQuery”或者“$”,这个变量其实是一个函数,不信你可以叫咱们的老朋友typeof验证一下。 74 | 75 | ``` 76 | console.log(typeof ($)); // function 77 | console.log($.trim(" ABC ")); 78 | ``` 79 | 80 | 验明正身!的确是个函数。那么咱们常用的 $.trim() 也是个函数,经常用,就不用验了吧! 81 | 82 | 很明显,这就是在$或者jQuery函数上加了一个trim属性,属性值是函数,作用是截取前后空格。 83 | 84 | 85 | 86 | javascript与java/C#相比,首先最需要解释的就是弱类型,因为弱类型是最基本的用法,而且最常用,就不打算做一节来讲。 87 | 88 | 其次要解释的就是本文的内容——*一切(引用类型)都是对象,对象是**属性的集合***。最需要了解的就是对象的概念,和java/C#完全不一样。所以,切记切记! 89 | 90 | 91 | 92 | 最后,有个疑问。在typeof的输出类型中,function和object都是对象,为何却要输出两种答案呢?都叫做object不行吗?——当然不行。 93 | 94 | 具体原因,且听下回分解! -------------------------------------------------------------------------------- /上下文环境和作用域的关系/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/上下文环境和作用域的关系/img/1.png -------------------------------------------------------------------------------- /上下文环境和作用域的关系/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/上下文环境和作用域的关系/img/2.png -------------------------------------------------------------------------------- /上下文环境和作用域的关系/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/上下文环境和作用域的关系/img/3.png -------------------------------------------------------------------------------- /上下文环境和作用域的关系/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/上下文环境和作用域的关系/img/4.png -------------------------------------------------------------------------------- /上下文环境和作用域的关系/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/上下文环境和作用域的关系/img/5.png -------------------------------------------------------------------------------- /上下文环境和作用域的关系/index.md: -------------------------------------------------------------------------------- 1 | # 补充:上下文环境和作用域的关系 2 | 本系列用了大量的篇幅讲解了上下文环境和作用域,有些人反映这两个是一回儿事。本文就用一个小例子来说明一下,作用域和上下文环境绝对不是一回事儿。 3 | 4 | 5 | 6 | 再说明之前,咱们先用简单的语言来概括一下这两个的区别。 7 | 8 | **00 上下文环境:** 9 | 10 | 可以理解为一个看不见摸不着的对象(有若干个属性),虽然看不见摸不着,但确实实实在在存在的,因为所有的变量都在里面存储着,要不然咱们定义的变量在哪里存? 11 | 12 | 另外,对于函数来说,上下文环境是在调用时创建的,这个很好理解。拿参数做例子,你不调用函数,我哪儿知道你要给我传什么参数? 13 | 14 | **01 作用域:** 15 | 16 | 首先,它很抽象。第二,记住一句话:除了全局作用域,只有函数才能创建作用域。创建一个函数就创建了一个作用域,无论你调用不调用,函数只要创建了,它就有独立的作用域,就有自己的一个“地盘”。 17 | 18 | **02 两者:** 19 | 20 | 一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。 21 | 22 | 23 | 24 | 上面的文字不理解没关系,且看下面的例子。 25 | 26 | 第一,除了全局作用域外,每个函数都要创建一个作用域。作用域之间的变量是相互独立的。因此,全局作用域中的x和fn作用域中的x,两者毫无关系,互不影响,和平相处。 27 | 28 | ![png](img/1.png) 29 | 30 | 第二,程序执行之前,会生成全局上下文环境,并在程序执行时,对其中的变量赋值。 31 | 32 | ![png](img/2.png) 33 | 34 | 第三,程序执行到第17行,调用fn(5),会产生fn(5)的上下文环境,并压栈,并设置为活动状态。 35 | 36 | ![png](img/3.png) 37 | 38 | 第四,执行完第17行,fn(5)的返回值赋值给了f1。此时执行上下文环境又重新回到全局,但是fn(5)的上下文环境不能就此销毁,因为其中有闭包的引用(可翻看前面文章,此处不再赘述)。 39 | 40 | ![png](img/4.png) 41 | 42 | 第五,继续执行第18行,再次调用fn函数——fn(10)。产生fn(5)的上下文环境,并压栈,并设置为活动状态。**但是此时fn(5)的上下文环境还在内存中——一个作用域下同时存在两个上下文环境。** 43 | 44 | ![png](img/5.png) 45 | 46 | 讲到这里,重点已经讲出来了,之后的场景这里就不再赘述了。 47 | 48 | 目的还是希望大家能通过这个例子,来理清楚上下文环境和作用域的关系。当然,也不是非得像个学院派似的一字一文的把概念说出来,简单理解一下,对用闭包是有帮助的。 -------------------------------------------------------------------------------- /从自由变量到作用域链/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/从自由变量到作用域链/img/1.png -------------------------------------------------------------------------------- /从自由变量到作用域链/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/从自由变量到作用域链/img/2.png -------------------------------------------------------------------------------- /从自由变量到作用域链/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/从自由变量到作用域链/img/3.png -------------------------------------------------------------------------------- /从自由变量到作用域链/index.md: -------------------------------------------------------------------------------- 1 | # 从【自由变量】到【作用域链】 2 | 3 | 先解释一下什么是“自由变量”。 4 | 5 | 在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。如下图 6 | 7 | ![PNG](img/1.png) 8 | 9 | 如上程序中,在调用fn()函数时,函数体中第6行。取b的值就直接可以在fn作用域中取,因为b就是在这里定义的。而取x的值时,就需要到另一个作用域中取。到哪个作用域中取呢? 10 | 11 | **有人说过要到父作用域中取,其实有时候这种解释会产生歧义**。例如: 12 | 13 | ![PNG](img/2.png) 14 | 15 | 所以,不要在用以上说法了。相比而言,用这句话描述会更加贴切——**要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记**——其实这就是所谓的“静态作用域”。 16 | 17 | 对于本文第一段代码,在fn函数中,取自由变量x的值时,要到哪个作用域中取?——要到创建fn函数的那个作用域中取——无论fn函数将在哪里调用。 18 | 19 | 20 | 21 | 上面描述的只是跨一步作用域去寻找。 22 | 23 | 如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。 24 | 25 | 这个一步一步“跨”的路线,我们称之为——**作用域链**。 26 | 27 | 我们拿文字总结一下取自由变量时的这个“作用域链”过程:(假设a是自由量) 28 | 29 | 第一步,现在当前作用域查找a,如果有则获取并结束。如果没有则继续; 30 | 31 | 第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续; 32 | 33 | 第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域; 34 | 35 | 第四步,跳转到第一步。 36 | 37 | ![PNG](img/3.png) 38 | 39 | 以上代码中:第13行,fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。取b的值时,直接在fn作用域取出。取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了。 40 | 41 | 42 | 43 | 这一节看似很轻松的把作用域链引出来,并讲完了。之所有轻松是有前几节的基础,否则将很难解释。 44 | 45 | 接下来咱们开始正式说说一直期待依旧的朋友——闭包。敬请期待下一节。 -------------------------------------------------------------------------------- /作用域和上下文环境/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/1.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/2.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/3.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/4.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/5.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/6.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/7.png -------------------------------------------------------------------------------- /作用域和上下文环境/img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/作用域和上下文环境/img/8.png -------------------------------------------------------------------------------- /作用域和上下文环境/index.md: -------------------------------------------------------------------------------- 1 | # 【作用域】和【上下文环境】 2 | 上文简单介绍了作用域,本文把作用域和上下文环境结合起来说一下,会理解的更深一些。 3 | 4 | ![PNG](img/1.png) 5 | 6 | 如上图,我们在上文中已经介绍了,除了全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时确定。 7 | 8 | 下面我们将按照程序执行的顺序,一步一步把各个上下文环境加上。另外,对上下文环境不了解的朋友,可以去看看之前的两篇文章: 9 | * [简述【执行上下文】上](../简述执行上下文/index.md) 10 | * [简述【执行上下文】下](../简述执行上下文/index1.md) 11 | 12 | 第一步,在加载程序时,已经确定了全局上下文环境,并随着程序的执行而对变量就行赋值。 13 | 14 | ![PNG](img/2.png) 15 | 16 | 第二步,程序执行到第27行,调用fn(10),此时生成此次调用fn函数时的上下文环境,压栈,并将此上下文环境设置为活动状态。 17 | 18 | ![PNG](img/3.png) 19 | 20 | 第三步,执行到第23行时,调用bar(100),生成此次调用的上下文环境,压栈,并设置为活动状态。 21 | 22 | ![PNG](img/4.png) 23 | 24 | 第四步,执行完第23行,bar(100)调用完成。则bar(100)上下文环境被销毁。接着执行第24行,调用bar(200),则又生成bar(200)的上下文环境,压栈,设置为活动状态。 25 | 26 | ![PNG](img/5.png) 27 | 28 | 第五步,执行完第24行,则bar(200)调用结束,其上下文环境被销毁。此时会回到fn(10)上下文环境,变为活动状态。 29 | 30 | ![PNG](img/6.png) 31 | 32 | 第六步,执行完第27行代码,fn(10)执行完成之后,fn(10)上下文环境被销毁,全局上下文环境又回到活动状态。 33 | 34 | ![PNG](img/7.png) 35 | 36 | 结束了。像老太太的裹脚布——又臭又长! 37 | 38 | 最后我们可以把以上这几个图片连接起来看看。 39 | 40 | ![PNG](img/8.png) 41 | 42 | 连接起来看,还是挺有意思的。**作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值**。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。所以,**作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了**。 43 | 44 | 所以,**如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值**。 45 | 46 | 47 | 48 | 虽然本文很长,但是文字较少,图片居多,图片都有形象的展示,大家花十几分钟也能慢慢看完。但是,这节内容真的很重要。 49 | 50 | 以上代码中,咱们还没有设计到跨作用域取值的情况,即——自由变量。详细内容且听下回分解。 -------------------------------------------------------------------------------- /函数和对象的关系/index.md: -------------------------------------------------------------------------------- 1 | # 函数和对象的关系 2 | 上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断。 3 | ``` 4 | var fn = function () { }; 5 | console.log(fn instanceof Object); // true 6 | ``` 7 | 8 | 对!函数是一种对象,但是函数却不像数组一样——你可以说数组是对象的一种,因为数组就像是对象的一个子集一样。但是函数与对象之间,却不仅仅是一种包含和被包含的关系,函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑,咱们这一节就缕一缕。 9 | 10 | 11 | 12 | 还是先看一个小例子吧。 13 | ``` 14 | function Fn() { 15 | this.name = '王福朋'; 16 | this.year = 1988; 17 | } 18 | var fn1 = new Fn(); 19 | ``` 20 | 21 | 上面的这个例子很简单,它能说明:对象可以通过函数来创建。对!也只能说明这一点。 22 | 23 | 但是我要说——**对象都是通过函数创建的**——有些人可能反驳:不对!因为: 24 | ``` 25 | var obj = { a: 10, b: 20 }; 26 | var arr = [5, 'x', true]; 27 | ``` 28 | 29 | 但是不好意思,这个——真的——是一种——“快捷方式”,在编程语言中,一般叫做“语法糖”。 30 | 31 | 做“语法糖”做的最好的可谓是微软大哥,它把他们家C#那小子弄的不男不女从的,本想图个人见人爱,谁承想还得到处跟人解释——其实它是个男孩! 32 | 33 | 话归正传——其实以上代码的本质是: 34 | 35 | ``` 36 | //var obj = { a: 10, b: 20 }; 37 | //var arr = [5, 'x', true]; 38 | 39 | var obj = new Object(); 40 | obj.a = 10; 41 | obj.b = 20; 42 | 43 | var arr = new Array(); 44 | arr[0] = 5; 45 | arr[1] = 'x'; 46 | arr[2] = true; 47 | ``` 48 | 而其中的 Object 和 Array 都是函数: 49 | ``` 50 | console.log(typeof (Object)); // function 51 | console.log(typeof (Array)); // function 52 | ``` 53 | 所以,可以很负责任的说——**对象都是通过函数来创建的**。 54 | 55 | 56 | 57 | 现在是不是糊涂了—— 对象是函数创建的,而函数却又是一种对象——天哪!函数和对象到底是什么关系啊? 58 | 59 | 别着急!揭开这个谜底,还得先去了解一下另一位老朋友——prototype原型。 60 | 61 | 62 | 63 | 本系列文章不打算动辄几千字的长篇大论,咱们小步快跑,不至于看的太乏味。 64 | 65 | -------------------------------------------------------------------------------- /原型的灵活性/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/原型的灵活性/img/1.png -------------------------------------------------------------------------------- /原型的灵活性/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/原型的灵活性/img/2.png -------------------------------------------------------------------------------- /原型的灵活性/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/原型的灵活性/img/3.png -------------------------------------------------------------------------------- /原型的灵活性/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/原型的灵活性/img/4.png -------------------------------------------------------------------------------- /原型的灵活性/index.md: -------------------------------------------------------------------------------- 1 | # 原型的灵活性 2 | 在Java和C#中,你可以简单的理解class是一个模子,对象就是被这个模子压出来的一批一批月饼(中秋节刚过完)。压个啥样,就得是个啥样,不能随便动,动一动就坏了。 3 | 4 | 而在javascript中,就没有模子了,月饼被换成了面团,你可以捏成自己想要的样子。 5 | 6 | 7 | 8 | 首先,对象属性可以随时改动。 9 | 10 | 对象或者函数,刚开始new出来之后,可能啥属性都没有。但是你可以这会儿加一个,过一会儿在加两个,非常灵活。 11 | 12 | 在jQuery的源码中,对象被创建时什么属性都没有,都是代码一步一步执行时,一个一个加上的。 13 | 14 | ![PNG](img/1.png) 15 | 16 | 其次,如果继承的方法不合适,可以做出修改。 17 | 18 | ![PNG](img/2.png) 19 | 20 | 21 | 如上图,Object和Array的toString()方法不一样。肯定是Array.prototype.toString()方法做了修改。 22 | 23 | 同理,我也可以自定义一个函数,并自己去修改prototype.toString()方法。 24 | 25 | ![PNG](img/3.png) 26 | 27 | 最后,如果感觉当前缺少你要用的方法,可以自己去创建。 28 | 29 | 例如在json2.js源码中,为Date、String、Number、Boolean方法添加一个toJSON的属性。 30 | 31 | ![PNG](img/4.png) 32 | 33 | 如果你要添加内置方法的原型属性,最好做一步判断,如果该属性不存在,则添加。如果本来就存在,就没必要再添加了。 -------------------------------------------------------------------------------- /完结/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/完结/img/1.png -------------------------------------------------------------------------------- /完结/index.md: -------------------------------------------------------------------------------- 1 | # 完结 2 | 3 | 之前一共用15篇文章,把javascript的原型和闭包讲解完了。 4 | 5 |   首先,javascript本来就“不容易学”。不是说它有多难,而是学习它的人,往往都是在学会了其他语言之后,又学javascript。有其他语言的学习经历和实践经历,再加上自学javascript,边学边用,肯定会产生许多误解,走许多弯路。我就没少经历,也算是一种教训。 6 | 7 |   其次,原型和闭包又是一对难兄难弟,一来是他俩比较难懂,而来是他俩都或多或少的给初级开发人员带来许多BUG。不懂原型和闭包,你也可以开发javascript程序,但是你写不出高质量、符合设计原则的javascript程序。 8 | 9 | 因此,**还是强调基础,强调理论!理论和实践相结合不是一句空话**。 10 | 11 | 12 | 由此想到了我平时练习投篮。一般喜欢篮球的人都是周末和别人去一起打篮球玩,我之前也是如此。但是我从今年春天开始,每周另外抽出一小时时间,自己一个人去练习投篮。练习正确的投篮姿势,强迫自己在练习的时候使用正确姿势,时间长了就形成了肌肉记忆。 13 | 14 | 所以,**工作之余,切不可忘记充电**。 15 | 16 | ![PNG](img/1.png) 17 | 18 | 写这些东西,我也参考了好多资料,包括博客园、csdn好多技术专家的博客,各种javascript书籍,json.js源码,jQuery源码。但是我都没有生搬硬套里面的术语和段落。 19 | 20 | 自己写东西,不是抄袭,不是写毕业论文。一定要有自己的思考和总结。学习、工作中都是如此。 21 | 22 | 此前15篇文章中所有的描述、解释、例子包括图片,都体现了我自己的理解,大部分图片都是我自己画出来的。包括在写每一篇文章之前,我都要去思考,应该用何种方式去表达,才能让读者更容易理解和接收。 23 | 24 | 25 | 26 | 最后,看到好多文章都是长篇大论,恨不得用一篇文章解释完所有的内容。而我的文章都是小步快跑,看完一篇估计也就十分钟。这是我在看《明朝那些事儿》时发现的一个思路。这本书一节只有很少的内容,而且语言简单,演绎性很强,一节紧扣着一节,能黏住读者。 27 | 28 | 因此,我在写文章时,会先用一个大家都知道的概念引入正题,然后进一步解释。在一篇文章的最后,要抛出一个疑问引出下一节。 29 | 30 | 31 | 32 | 原型和闭包这个系列,正好填补了javascript教程一个空缺,即使《javascript高级程序设计》中,都没有将原型和闭包讲解的如此深入。希望大家多多支持吧。 33 | 34 | 35 | 36 | 就此,结束。 -------------------------------------------------------------------------------- /执行上下文栈/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/1.png -------------------------------------------------------------------------------- /执行上下文栈/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/2.png -------------------------------------------------------------------------------- /执行上下文栈/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/3.png -------------------------------------------------------------------------------- /执行上下文栈/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/4.png -------------------------------------------------------------------------------- /执行上下文栈/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/5.png -------------------------------------------------------------------------------- /执行上下文栈/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/6.png -------------------------------------------------------------------------------- /执行上下文栈/img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/7.png -------------------------------------------------------------------------------- /执行上下文栈/img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/8.png -------------------------------------------------------------------------------- /执行上下文栈/img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/执行上下文栈/img/9.png -------------------------------------------------------------------------------- /执行上下文栈/index.md: -------------------------------------------------------------------------------- 1 | # 执行上下文栈 2 | 继续上文的内容。 3 | 4 | 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个。 5 | 6 | 其实这是一个压栈出栈的过程——执行上下文栈。如下图: 7 | 8 | ![PNG](img/1.png) 9 | 10 | 可根据以下代码来详细介绍上下文栈的压栈、出栈过程。 11 | 12 | ![PNG](img/2.png) 13 | 14 | 如上代码。 15 | 16 | 在执行代码之前,首先将创建全局上下文环境。 17 | 18 | ![PNG](img/3.png) 19 | 20 | 然后是代码执行。代码执行到第12行之前,上下文环境中的变量都在执行过程中被赋值。 21 | 22 | ![PNG](img/4.png) 23 | 24 | 执行到第13行,调用bar函数。 25 | 26 | 跳转到bar函数内部,执行函数体语句之前,会创建一个新的执行上下文环境。 27 | 28 | ![PNG](img/5.png) 29 | 30 | 并将这个执行上下文环境压栈,设置为活动状态。 31 | 32 | ![PNG](img/6.png) 33 | 34 | 执行到第5行,又调用了fn函数。进入fn函数,在执行函数体语句之前,会创建fn函数的执行上下文环境,并压栈,设置为活动状态。 35 | 36 | ![PNG](img/7.png) 37 | 38 | 待第5行执行完毕,即fn函数执行完毕后,此次调用fn所生成的上下文环境出栈,并且被销毁(已经用完了,就要及时销毁,释放内存)。 39 | 40 | ![PNG](img/8.png) 41 | 42 | 同理,待第13行执行完毕,即bar函数执行完毕后,调用bar函数所生成的上下文环境出栈,并且被销毁(已经用完了,就要及时销毁,释放内存)。 43 | 44 | ![PNG](img/9.png) 45 | 46 | 好了,我很耐心的给大家介绍了一段简短代码的执行上下文环境的变化过程,一个完整的闭环。其中上下文环境的变量赋值过程我省略了许多,因为那些并不难,一看就知道。 47 | 48 | 49 | 50 | 讲到这里,我不得不很遗憾的跟大家说:其实以上我们所演示的是一种比较理想的情况。有一种情况,而且是很常用的一种情况,无法做到这样干净利落的说销毁就销毁。这种情况就是伟大的——闭包。 51 | 52 | 要说闭包,咱们还得先从自由变量和作用域说起。 -------------------------------------------------------------------------------- /简介作用域/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简介作用域/img/1.png -------------------------------------------------------------------------------- /简介作用域/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简介作用域/img/2.png -------------------------------------------------------------------------------- /简介作用域/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简介作用域/img/3.png -------------------------------------------------------------------------------- /简介作用域/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简介作用域/img/4.png -------------------------------------------------------------------------------- /简介作用域/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简介作用域/img/5.png -------------------------------------------------------------------------------- /简介作用域/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简介作用域/img/6.png -------------------------------------------------------------------------------- /简介作用域/index.md: -------------------------------------------------------------------------------- 1 | # 简介【作用域】 2 | 提到作用域,有一句话大家(有js开发经验者)可能比较熟悉:“javascript没有块级作用域”。所谓“块”,就是大括号“{}”中间的语句。例如if语句: 3 | 4 | ![PNG](img/1.png) 5 | 6 | 再比如for语句: 7 | 8 | ![PNG](img/2.png) 9 | 10 | 所以,我们在编写代码的时候,不要在“块”里面声明变量,要在代码的一开始就声明好了。以避免发生歧义。如: 11 | 12 | ![PNG](img/3.png) 13 | 14 | 其实,你光知道“javascript没有块级作用域”是完全不够的,你需要知道的是——**javascript除了全局作用域之外,只有函数可以创建的作用域**。 15 | 16 | 所以,**我们在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好。除了这两个地方,其他地方都不要出现变量声明。而且建议用“单var”形式**。 17 | 18 | jQuery就是一个很好的示例: 19 | 20 | ![PNG](img/4.png) 21 | 22 | 下面继续说作用域。作用域是一个很抽象的概念,类似于一个“地盘” 23 | 24 | ![PNG](img/5.png) 25 | 26 | 如上图,全局代码和fn、bar两个函数都会形成一个作用域。而且,**作用域有上下级的关系,上下级关系的确定就看函数是在哪个作用域下创建的**。例如,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。 27 | 28 | 29 | 30 | **作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突**。例如以上代码中,三个作用域下都声明了“a”这个变量,但是他们不会有冲突。各自的作用域下,用各自的“a”。 31 | 32 | 说到这里,咱们又可以拿出jquery源码来讲讲了。 33 | 34 | jQuery源码的最外层是一个自动执行的匿名函数: 35 | 36 | ![PNG](img/6.png) 37 | 38 | 39 | 为什么要这样做呢? 40 | 41 | 原因就是在jQuery源码中,声明了大量的变量,这些变量将通过一个函数被限制在一个独立的作用域中,而不会与全局作用域或者其他函数作用域的同名变量产生冲突。 42 | 43 | 全世界的开发者都在用jQuery,如果不这样做,很可能导致jQuery源码中的变量与外部javascript代码中的变量重名,从而产生冲突。 44 | 45 | 46 | 47 | 作用域这块只是很不好解释,咱们就小步快跑,一步一步慢慢展示给大家。 48 | 49 | 下一节将把作用域和执行上下文环境结合起来说一说。 50 | 51 | 可见,要理解闭包,不是一两句话能说清楚的。。。 -------------------------------------------------------------------------------- /简述执行上下文/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img/1.png -------------------------------------------------------------------------------- /简述执行上下文/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img/2.png -------------------------------------------------------------------------------- /简述执行上下文/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img/3.png -------------------------------------------------------------------------------- /简述执行上下文/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img/4.png -------------------------------------------------------------------------------- /简述执行上下文/img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img/5.png -------------------------------------------------------------------------------- /简述执行上下文/img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img/6.png -------------------------------------------------------------------------------- /简述执行上下文/img1/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img1/1.png -------------------------------------------------------------------------------- /简述执行上下文/img1/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiliam2015/jsprototype/933c75ff38211edc533d8f4accc3ae78589e3879/简述执行上下文/img1/2.png -------------------------------------------------------------------------------- /简述执行上下文/index.md: -------------------------------------------------------------------------------- 1 | # 简述【执行上下文】上 2 | 什么是“执行上下文”(也叫做“执行上下文环境”)?暂且不下定义,先看一段代码: 3 | 4 | ![PNG](img/1.png) 5 | 6 | 第一句报错,a未定义,很正常。第二句、第三句输出都是undefined,说明浏览器在执行console.log(a)时,已经知道了a是undefined,但却不知道a是10(第三句中)。 7 | 8 | 在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。可用下图模拟: 9 | 10 | ![PNG](img/2.png) 11 | 12 | 这是第一种情况。 13 | 14 | 15 | 下面还有。先来个简单的。 16 | 17 | ![PNG](img/3.png) 18 | 19 | 有js开发经验的朋友应该都知道,你无论在哪个位置获取this,都是有值的。至于this的取值情况,比较复杂,会专门拿出一篇文章来讲解。 20 | 21 | 与第一种情况不同的是:第一种情况只是对变量进行声明(并没有赋值),而此种情况直接给this赋值。这也是“准备工作”情况要做的事情之一。 22 | 23 | 24 | 25 | 下面还有。。。第三种情况。 26 | 27 | 在第三种情况中,需要注意代码注释中的两个名词——“函数表达式”和“函数声明”。虽然两者都很常用,但是这两者在“准备工作”时,却是两种待遇。 28 | 29 | ![PNG](img/4.png) 30 | 31 | 看以上代码。“函数声明”时我们看到了第二种情况的影子,而“函数表达式”时我们看到了第一种情况的影子。 32 | 33 | 没错。在“准备工作”中,对待函数表达式就像对待“ var a = 10 ”这样的变量一样,只是声明。而对待函数声明时,却把函数整个赋值了。 34 | 35 | 36 | 37 | 好了,“准备工作”介绍完毕。 38 | 39 | 我们总结一下,在“准备工作”中完成了哪些工作: 40 | * **变量、函数表达式——变量声明,默认赋值为undefined;** 41 | * **this——赋值;** 42 | * **函数声明——赋值;** 43 | 44 | **这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。** 45 | 46 | 47 | *这里插一句题外话:通过以上三种情况,你可能会联想到网上的有些考js语法的题目/面试题。的确,几乎每个js语法题中都有这种题目出现。之前你遇到这种题目是不是靠背诵来解决?背过了,隔几天又忘记了。——任何问题,都要去追根溯源,要知道这个问题是真正出自哪一块知识点,要真正去理解。光靠背诵是没用的。* 48 | 49 | 细心的朋友可能会发现,我们上面所有的例子都是在全局环境下执行的。 50 | 51 | 其实,javascript在执行一个代码段之前,都会进行这些“准备工作”来生成执行上下文。这个“代码段”其实分三种情况——全局代码,函数体,eval代码。 52 | 53 | 54 | 55 | 这里解释一下为什么代码段分为这三种。 56 | 57 | 所谓“代码段”就是一段文本形式的代码。 58 | 59 | 首先,全局代码是一种,这个应该没有非议,本来就是手写文本到