[{"content":"原文链接：点击\n作者：Patrick Sexton\n什么是CSSOM？ CSSOM是CSS Object Model的缩写 大体上来说，CSSOM是一个建立在web页面上的 CSS 样式的映射 它和DOM类似，但是只针对CSS而不是HTML 浏览器将DOM和CSSOM结合来渲染web页面 CSSOM对于页面渲染起着至关重要的作用。\nCSSOM是critical rendering path的基本和核心，并且，它会有助于我们理解web性能优化中的原理。\nCSSOM是做什么的？ CSSOM将样式表中的规则映射到页面对应的元素上。\n虽然CSSOM采取了复杂的措施来做这件事，但是CSSOM最终的功能还是将样式映射到它们应该对应的元素上去。\n更确切地说，CSSOM识别tokens并把这些tokens转换成一个树结构上的对应的结点。所有结点以及它们所关联的页面中的样式就是所谓的CSS Object Model\nWeb浏览器使用CSSOM去渲染页面 Web浏览器为了展示页面，需要经历以下四个步骤：\n检查HTML并构建DOM 检查CSS并构建CSSOM Web浏览器将DOM和CSSOM结合，并构建出render tree 展示Web页面 从上述四个步骤可以看出，CSSOM对于Web页面的展示起着重要作用\n好消息 你不必为了优化你的Web页面而去了解CSSOM是怎样工作的，这里有几个关于CSSOM的关键点你需要知道，利用这些关键点可以优化页面的加载速度。\nCSSOM 阻止任何东西渲染 CSSOM 在加载一个新页面时必须重新构建 页面中CSS的加载和页面中javascript的加载是有关系的 让我们快速看一下这几个关键点，然后来看怎么优化我们的页面。\n1. CSSOM 阻止任何东西渲染 所有的CSS都是阻塞渲染的（意味着在CSS没处理好之前所有东西都不会展示）。\n真的原因是，如果浏览器在CSS检查之前展示了页面，那么每个页面都是没有样式的，等一会之后又突然有了样式，整个页面的体验就会很糟糕。\n由于CSSOM被用作创建render tree，那么如果不能高效的利用CSS会有一些严重的后果。\n主要的后果就是你的页面在加载时白屏。\n2. CSSOM 在加载一个新页面时必须重新构建 这可能对你来说，或明显或不明显，但这是一非常关键的点。\n这意味着即使你的CSS文件被缓存了，也并不意味着这个已经构建好了的CSSOM可以应用到每一个页面。\n当用户跳到你的另一个页面时（即使浏览器缓存了所有需要的CSS），CSSOM也必须重新构建一遍。\n也就是说，如果你的CSS文件写得很蹩脚，或者体积很大，这也会对你页面加载产生负面的影响。\n3. 页面中CSS的加载和页面中javascript的加载是有关系的 javascript的加载可能会阻塞CSSOM的构建\n简单来说，CSSOM是展示任何东西的必需品。在CSSOM构建之前，所有东西都不会展示，如果你阻塞了CSSOM的构建，CSSOM的构建就会消耗更长的时间，这就意味着页面的渲染也需要更长的时间。 如果你的javascript阻塞了CSSOM的构建，你的用户就会面对更长时间的白屏。\n影响CSSOM的一般优化方式 这里有几个首屏优化的最佳实践，可以帮助你让CSSOM构建地快一些（也就是让页面加载更快）\nOptimize your critical rendering path Make sure your pages are calling stylesheets before javascripts Ensure you are optimizing CSS delivery ","permalink":"https://xinyuli.blog/2017/02/16/%E6%96%87%E7%AB%A0/%E3%80%90%E8%AF%91%E3%80%91CSSOM%E4%BB%8B%E7%BB%8D/","summary":"\u003cp\u003e原文链接：\u003ca href=\"https://varvy.com/performance/cssom.html\"\u003e点击\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e作者：Patrick Sexton\u003c/p\u003e\n\u003ch2 id=\"什么是cssom\"\u003e什么是CSSOM？\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eCSSOM是CSS Object Model的缩写\u003c/li\u003e\n\u003cli\u003e大体上来说，CSSOM是一个建立在web页面上的 CSS 样式的映射\u003c/li\u003e\n\u003cli\u003e它和DOM类似，但是只针对CSS而不是HTML\u003c/li\u003e\n\u003cli\u003e浏览器将DOM和CSSOM结合来渲染web页面\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/cssom-defined.png\"\u003e\u003c/p\u003e\n\u003cp\u003eCSSOM对于页面渲染起着至关重要的作用。\u003c/p\u003e\n\u003cp\u003eCSSOM是\u003ca href=\"https://varvy.com/pagespeed/critical-render-path.html\"\u003ecritical rendering path\u003c/a\u003e的基本和核心，并且，它会有助于我们理解web性能优化中的原理。\u003c/p\u003e\n\u003ch2 id=\"cssom是做什么的\"\u003eCSSOM是做什么的？\u003c/h2\u003e\n\u003cp\u003eCSSOM将样式表中的规则映射到页面对应的元素上。\u003c/p\u003e\n\u003cp\u003e虽然CSSOM采取了复杂的措施来做这件事，但是CSSOM最终的功能还是将样式映射到它们应该对应的元素上去。\u003c/p\u003e\n\u003cp\u003e更确切地说，CSSOM识别tokens并把这些tokens转换成一个树结构上的对应的结点。所有结点以及它们所关联的页面中的样式就是所谓的CSS Object Model\u003c/p\u003e","title":"【译】CSSOM介绍"},{"content":"最近在复习一些JavaScript基础的东西，碰到js的原型和继承这一块总是感觉不悟其中精髓。因此，这篇文章算是从我自己理解的角度对js继承进行总结和剖析。\n什么是继承 大多数人使用继承不外乎是为了获得这两点好处，代码的抽象和代码的复用。 代码的抽象就不用说了，交通工具和汽车这类的例子数不胜数，在传统的OO语言中(比如Java)，代码的抽象更多的是使用接口(interface)来实现，而使用继承更多地是为了代码的复用(虽然现在强调使用组合而不是使用继承)。\n怎么复用的？打个比方，class A 继承了 class B，class A便拥有了class B 的public 和 protected类型的变量和方法，用最简单的方法去想，便是 class B 将 这些属性和方法直接copy给class A，这样便实现了继承。\n因此我们可以这样说，继承实际上是一种类与类之间的copy行为。\nJavaScript中的继承 在JavaScript中没有类的概念，只有对象。虽然现在人们经常使用class关键字，这让JavaScript看起来似乎是拥有了\u0026quot;类\u0026quot;，可表面看到的不一定是本质，class只是一块糖，嚼碎了才知道里面其实还是原型链那一套。因此，JavaScript中的继承只是对象与对象之间的继承。反观继承的本质，继承便是让子类拥有父类的一些属性和方法，那么在JavaScript中便是让一个对象拥有另一个对象的属性和方法。\n所以，这给我了我们一条十分清晰的思路，JavaScript中如何实现继承？只需让一个对象拥有另一个对象的属性和方法，这就实现了。\n利用Mixin 既然让一个对象拥有另一个对象的属性和方法，首先想到的便是利用Mixin的粗暴方式，直接将对象的属性和方法强制copy到另一个对象。\n就像这样\n1 2 3 4 5 6 7 function mixin(subObj, parentObj) { for (var prop in parentObj) { if (!(prop in subObj)) { subObj[prop] = parentObj[prop] } } } 当然也可以用ES6中的更优雅的Object.assign。\n这段代码就实现了最简单的从一个对象复制属性和方法到另一个对象。然而这种方法有一个缺陷，如果父对象的属性是引用类型，比如一个对象或者数组，那么修改子对象的时候势必会对父对象也造成修改，这显然不可接受。一种想法是采用深度克隆，然而又可能会有循环引用的问题。\n所以，这种继承方式，比较适合对简单对象的拓展，不太适合更复杂的继承。\n利用原型链 首先来说一下什么是原型，原型在JavaScript中，其实就是某个对象的一个属性。只不过这个属性很特殊，对于外界一般是不可见(在chrome中可以通过__proto__获取),我们一般把它叫作[[Prototype]]。这里和函数的prototype属性很相似但却是两个东西，后面会提到。\n那么什么是原型链呢，顾名思义就像这样：\nobj1.[[Prototype]] ===\u0026gt; obj2.[[Prototype]] ===\u0026gt; obj3.[[Prototype]]\u0026hellip;. ===\u0026gt; Object.prototype\n某一对象的原型属性中保存着另一个对象，以此类推，好像链子一样串起来。\n链的终点是Object.prototype对象，因此Object.prototype没有原型。当我们构建一个对象，这个对象的默认的原型就是Object.prototype\n在chrome中验证一下：\n1 2 var a = {} Object.prototype === a.__proto__ // true 那么我们如何用原型链实现继承呢？这要归功于JavaScript中的委托机制。\n当我们获取一个对象的某个属性时，比如a.b，会默认调用一个内置的[[Get]]方法，这个[[Get]]方法的算法就是：\n在当前对象里查找，找不到则委托给当前对象的[[Prototype]],再找不到则委托给[[Prototype]]的[[Prototype]]，直到Object.prototype中也没找到，则返回undefined。\n因此，我们想让对象a拥有对象b的属性和方法，即对象a继承对象b，只需要把b赋值给a的[[Prototype]]，利用属性查找的委托机制，实现了a也\u0026quot;拥有\u0026quot;了b的属性和方法，而且当a中有和b中的同名属性时，由于\u0026quot;屏蔽作用\u0026quot;，只有a中的属性会被优先获取到，实现了override，看起来相当完美。\nnew 和 \u0026ldquo;构造函数\u0026rdquo; 前面提到，[[Prototype]]是个内置隐藏属性，虽然在chrome可以通过__proto__访问，但是其设计本意是不可被读取和修改的，那么我们如何利用原型链来建立继承关系？\nJavaScript提供了new关键字。\n通常，在类似Java这样的OO语言中，new被用来实例化一个类，然而在JavaScript中，new仅仅是一个函数调用的方式！\nJavaScript中的函数也很奇怪，每一个函数都有一个默认的prototype属性，这个不同于对象的[[Prototype]]属性，函数的prototype是故意暴露出来的，而且这个属性还不为空，还有prototype还有另一个属性叫constructor，这个constructor竟然又引用回来了这个函数本身！于是我们看到的效果是这样的：\n用new来调用函数有什么不同的呢？new其实做了三件事：\n创建一个新对象 将这个新对象的[[Prototype]]连接到调用函数的prototype上 绑定调用函数的this并调用 用代码来表示就是：\n1 2 3 4 5 6 7 function New(fn) { var tmp = {} tmp.__proto__ = fn.prototype fn.call(tmp) return tmp } 可以看到，new帮我们把对象的[[Prototype]]连接到了函数的prototype上。\n到这儿，思路就清晰了，怎么让对象a和对象b的[[Prototype]]相连实现a继承b?\n只需把a的\u0026quot;构造函数\u0026quot;的[[Prototype]]连接到b就行了。\n来实现一下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function A() { } var b = { show: function() { console.log(\u0026#39;这是来自b的方法\u0026#39;) } } A.prototype = b // 这里修复了原先的 constructor A.prototype.constructor = A var a = new A() a.show() // 这是来自b的方法 更简单的Object.create ES5中提供的Object.create更简单粗暴，可以直接创建一个对象并将这个对象的[[Prototype]]指向传入的对象\n1 2 3 var b = {c: 1} var a = Object.create(b) console.log(a.c) // 1 模拟类继承 在JavaScript中没有类的概念，虽然从ES6开始拥有了class关键字，但其背后仍然是原型链作支撑，所以这里还是用最本质的原型来模拟\u0026quot;类\u0026quot;的继承。这才是JavaScript的本来面目！\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /** * 实现 A 继承 B */ function B(b) { this.b = b } function A(a, b) { // 调用B并绑定this B.call(this, b) this.a = a } A.prototype = Object.assign({}, B.prototype) A.prototype.constructor = A var c = new A(1, 2) console.log(c.a) // 1 // c 拥有了只有B的实例才拥有的 b 属性 console.log(c.b) // 2 总结 简单来说，继承即是copy和复用，JavaScript的继承其实就是利用原型链的查找和委托来实现属性和方法的复用，new关键字和\u0026quot;构造函数\u0026quot;只是连接原型链的工具，这样的工具还有Object.create。\n","permalink":"https://xinyuli.blog/2017/02/10/%E6%96%87%E7%AB%A0/%E8%B0%88%E4%B8%80%E8%B0%88JavaScript%E7%BB%A7%E6%89%BF/","summary":"\u003cp\u003e最近在复习一些JavaScript基础的东西，碰到js的原型和继承这一块总是感觉不悟其中精髓。因此，这篇文章算是从我自己理解的角度对js继承进行总结和剖析。\u003c/p\u003e\n\u003ch2 id=\"什么是继承\"\u003e什么是继承\u003c/h2\u003e\n\u003cp\u003e大多数人使用继承不外乎是为了获得这两点好处，\u003cem\u003e\u003cstrong\u003e代码的抽象\u003c/strong\u003e\u003c/em\u003e和\u003cem\u003e\u003cstrong\u003e代码的复用\u003c/strong\u003e\u003c/em\u003e。\n代码的抽象就不用说了，交通工具和汽车这类的例子数不胜数，在传统的OO语言中(比如Java)，代码的抽象更多的是使用接口(interface)来实现，而使用继承更多地是为了代码的复用(虽然现在强调使用组合而不是使用继承)。\u003c/p\u003e\n\u003cp\u003e怎么复用的？打个比方，class A 继承了 class B，class A便拥有了class B 的public 和 protected类型的变量和方法，用最简单的方法去想，便是 class B 将 这些属性和方法直接copy给class A，这样便实现了继承。\u003c/p\u003e\n\u003cp\u003e因此我们可以这样说，继承实际上是一种\u003cstrong\u003e类与类\u003c/strong\u003e之间的copy行为。\u003c/p\u003e","title":"谈一谈JavaScript继承"},{"content":"类理论 类/继承描述了一种代码组织形式，类和继承是对真实世界中事物的抽象和建模。\n类意味着复制，传统的类被实例化时，它的行为会被复制到实例中。类被继承时，行为也会被 复制到子类中\n\u0026ldquo;类设计模式\u0026rdquo; 类其实是一种软件设计模式，是一种可选的代码抽象。\nJavaScript中的\u0026quot;类\u0026quot; JavaScript中并没有真正的类，只有近似类的语法, 因为JavaScript并不会像类那样自动创建对象 的副本。\n类的机制 类是一张蓝图，为了获得真正可交互的对象必须按照类来建造(实例化)一个东西。\n类实例是由一个特殊的类方法构造的，被称为构造函数\n类的继承 定义好一个子类后，子类会包含父类行为的原始副本，但是也可以重写所有继承的行为甚至定义新行为\n多态 方法引用继承层次中高层的方法，这种技术被称作多态\n混入 由于其他语言中类表现出来的都是复制行为，因此JavaScript开发者也想出了一个方法来模拟类的 复制行为，这个方法就是混入\n显式混入 简单的显式混入的例子：\n1 2 3 4 5 6 7 8 9 function mixin(sourceObj, targetObj) { for (var key in sourceObj) { if (!(key in targetObj)) { targetObj[key] = sourceObj[key] } } return targetObj } 隐式混入 1 2 3 4 5 6 7 8 9 10 11 var Something = { cool: function() { ... } } var Another = { cool: function() { Something.function.cool.call(this) } } ","permalink":"https://xinyuli.blog/2016/12/29/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/This%20\u0026%20Object%20Prototypes/%E7%AC%AC%E5%9B%9B%E7%AB%A0%E6%B7%B7%E5%90%88%E5%AF%B9%E8%B1%A1%E7%B1%BB/","summary":"\u003ch2 id=\"类理论\"\u003e类理论\u003c/h2\u003e\n\u003cp\u003e类/继承描述了一种代码组织形式，类和继承是对真实世界中事物的抽象和建模。\u003c/p\u003e\n\u003cp\u003e类意味着复制，传统的类被实例化时，它的行为会被复制到实例中。类被继承时，行为也会被\n复制到子类中\u003c/p\u003e\n\u003ch3 id=\"类设计模式\"\u003e\u0026ldquo;类设计模式\u0026rdquo;\u003c/h3\u003e\n\u003cp\u003e类其实是一种软件设计模式，是一种可选的代码抽象。\u003c/p\u003e\n\u003ch3 id=\"javascript中的类\"\u003eJavaScript中的\u0026quot;类\u0026quot;\u003c/h3\u003e\n\u003cp\u003eJavaScript中并没有真正的类，只有近似类的语法, 因为JavaScript并不会像类那样自动创建对象\n的副本。\u003c/p\u003e","title":"第四章混合对象\"类\""},{"content":"语法 对象有两种定义形式：声明形式和构造形式\n声明形式:\n1 var obj = {} 构造形式:\n1 var obj = new Object() 类型 在JavaScript中共有6种基本类型（现在是7种）\nstring number boolean null undefined object 还有一种symbol类型\n简单基本类型并不是对象，虽然typeof null === 'object'，但是这个语言的一个bug\n原理是这样的，不同的对象在底层都表示为二进制，在JavaScript中二进制前3位为0的话会被判定为object类型，null的二 进制全是0，自然前三位也是0，所以执行typeof时会返回\u0026quot;object\u0026quot;\n内置对象 JavaScript中还有一些对象子类型，通常被称为内置对象\nString Number Boolean Object Function Array Date RegExp Error 这些对象从表现形式来说很像其他语言中的类型或者类，但在JavaScript中，只是一些内置函数，这些内置函数 可以当做构造函数来使用。\n装箱和拆箱操作 把字面量转换成对象的操作称作\u0026quot;装箱\u0026quot;，把对象转换成字面量的操作称作\u0026quot;拆箱\u0026quot;\n\u0026ldquo;装箱\u0026rdquo;\n1 2 var obj = new String(\u0026#39;hello\u0026#39;) typeof obj // \u0026#34;object\u0026#34; \u0026ldquo;拆箱\u0026rdquo;\n1 2 3 4 var obj = new String(\u0026#39;hello\u0026#39;) var str = obj.toString() typeof str // \u0026#34;string\u0026#34; \u0026ldquo;自动装箱\u0026rdquo;\n1 2 var str = \u0026#39;hello\u0026#39; str.length 属性 对象内的属性不是真正存储在对象内部，而是像指针一样，指向这些值的真正的存储位置\n属性描述符 从ES5开始，所有属性都具备了属性描述符，可以使用defineProperty来显式指定一些特性\nwritable\nwritable 决定是否可以修改属性的值\n如果writable设为false，修改属性的值在非严格模式下静默失败，严格模式下抛出TypeError\nconfigurable\nconfigurable决定是否可以通过defineProperty 来修改属性描述符\n如果configurable设为false，不管是否是严格模式，再次使用defineProperty都会抛出TypeError， 因此把configurable设为false是单向操作，无法撤销。\n除了无法修改，configurable设为false还会禁止删除这个属性。\nenumerable\nenumerable决定属性是否会出现在对象的属性枚举中，比如for..in中\n如果设为false，属性可以正常访问，但不会出现在枚举中。\n可计算属性名 ES6中增加了可计算属性名，可以在文字形式中使用[]包裹一个表达式当做属性名\n1 2 3 4 5 6 var prefix = \u0026#39;foo\u0026#39; var myObject = { [prefix + \u0026#39;bar\u0026#39;]: \u0026#39;hello\u0026#39; [prefix + \u0026#39;baz\u0026#39;]: \u0026#39;world\u0026#39; } 关于数组 如果试图向数组添加一个属性，但是属性名\u0026quot;看起来\u0026quot;像一个数组，那它会变成数值下标，因此会修改数组的内容而不是添加一个属性\n1 2 3 4 var arr = [\u0026#39;hello\u0026#39;] arr[\u0026#39;1\u0026#39;] = \u0026#39;hi\u0026#39; arr[1] // \u0026#39;hi\u0026#39; 不变性 所有方法创建的都是浅不变性，也就是说它们只会影响目标对象的直接属性，如果目标对象引用了其他对象，其它对象的内容不受影响。\n实现不变性的几种方法\n对象常量\n结合writable:false和configurable:false可以创建一个真正的常量属性\n禁止拓展\n如果想禁止一个对象添加新属性并保留已有属性，可以使用Object.preventExtensions(..)\n密封\nObject.seal(...)会创建一个密封对象，这个方法实际上会调用Object.preventExtensions，并将现有 对象的属性标记为configurable: false\n冻结\nObject.freeze(...)会创建一个冻结对象，这个方法实际上会调用Object.seal, 并将现有对象的属性标记为writable: false\n[[GET]] 和 [[PUT]] [[GET]]操作 对于属性的访问实际上是实现[[GET]]操作，默认的[[GET]]操作实际上是首先在当前对象中查找是否有名称相同的属性，如果找到就返回这个属性的值，否则 沿着原型链查找，如果还没有找到，返回undefined\n[[PUT]]操作 给对象的属性赋值会触发[[PUT]]操作，如果对象已经存在这个属性，[[PUT]]算法大致会检查下面这些内容\n属性是否是访问描述符，如果是并且存在setter就调用setter 属性描述符中writable是否是false，如果是，在非严格模式下静默失败，在严格模式下抛出TypeError异常 如果都不是，将该值设为属性的值 如果对象中不存在这个属性，，，之后再叙述,待补充\nGetter 和 Setter 当给一个对象的属性定义getter、setter或者两者都有时，这个属性会被定义为\u0026quot;访问描述符\u0026quot;，对于访问描述符来说， JavaScript会忽略它们的value和writable特性，取而代之的是关心set和get特性。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var myObject = { get a() { return 2 } } Object.defineProperty( myObject, \u0026#39;b\u0026#39;, { get: function() { return this.a * 2 } enumerable: true } ) myObject.a // 2 myObject.b // 4 注意，由于只定义了a的getter，所以对a进行赋值操作会被忽略。\n属性的存在性 in in操作符会检查属性是否在对象及其原型链中\nhasOwnProperty hasOwnProperty只会检查属性是否在本对象中\n注意： hasOwnProperty是存在于Object.prototype中的方法，所以通过Object.create(null)创建的对象调用会失败。\n遍历 ES6中的for..of可以用来直接遍历值, for..of首先会向被访问对象请求一个迭代器对象，然后通过调用迭代器对象的next()方法来遍历所有返回值。\n数组有内置的@@iterator，因此for..of可以直接应用在数组上。\n1 2 3 4 5 6 7 8 var a = [1,2,3] var it = a[Symbol.iterator]() it.next() // {value: 1, done: false} it.next() // {value: 2, done: false} it.next() // {value: 3, done: false} it.next() // {value: undefined, done: true} it.next() // {value: undefined, done: true} 因此我们我们可以自己定义迭代器，只要迭代器拥有next方法，并且返回值符合{value: ..., done: true/false}规范，就可以使用 for..of来迭代遍历。\n","permalink":"https://xinyuli.blog/2016/12/27/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/This%20\u0026%20Object%20Prototypes/%E7%AC%AC%E4%B8%89%E7%AB%A0%E5%AF%B9%E8%B1%A1/","summary":"\u003ch2 id=\"语法\"\u003e语法\u003c/h2\u003e\n\u003cp\u003e对象有两种定义形式：声明形式和构造形式\u003c/p\u003e\n\u003cp\u003e声明形式:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eobj\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e构造形式:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eobj\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"类型\"\u003e类型\u003c/h2\u003e\n\u003cp\u003e在JavaScript中共有6种基本类型（现在是7种）\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003estring\u003c/li\u003e\n\u003cli\u003enumber\u003c/li\u003e\n\u003cli\u003eboolean\u003c/li\u003e\n\u003cli\u003enull\u003c/li\u003e\n\u003cli\u003eundefined\u003c/li\u003e\n\u003cli\u003eobject\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e还有一种symbol类型\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e简单基本类型并不是对象\u003c/strong\u003e，虽然\u003ccode\u003etypeof null === 'object'\u003c/code\u003e，但是这个语言的一个bug\u003c/p\u003e","title":"第三章对象"},{"content":"绑定规则 this的绑定规则总结起来总共有四种：\n默认绑定 隐式绑定 显式绑定 new绑定 默认绑定 这条规则是无法应用其它绑定时的默认规则，思考以下代码：\n1 2 3 4 5 6 function foo() { console.log(this.a) } var a = 2 foo() // 2 foo()是直接使用不加任何修饰的函数引用进行调用的，因此只能应用默认绑定，在非严格模式下this默认绑定到了全局对象\n严格模式下则被绑定到undefined\n1 2 3 4 5 6 7 function foo() { \u0026#34;use strict\u0026#34; console.log(this.a) } var a = 2 foo() // TypeError 隐式绑定 另一条需要考虑的规则是调用位置是否有上下文对象，或者说是否被某个对象拥有或者包含，\n思考下面代码：\n1 2 3 4 5 6 7 8 9 10 function foo() { console.log(this.a) } var obj = { a: 2, foo: foo } obj.foo() // 2 调用位置obj.foo()会使用obj上下文来引用函数，因此可以说函数被调用时obj对象\u0026quot;拥有\u0026quot;或者\u0026quot;包含\u0026quot;它，当函数引用有 上下文对象时，隐式绑定会把函数调用中的this绑定到这个上下文对象。\n对象引用链只有最后一层在调用位置起作用， 举例来说\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function foo() { console.log(this.a) } var obj2 = { a: 42, foo: foo } var obj1 = { a: 2, obj2: obj2 } obj1.obj2.foo() // 42 隐式丢失问题 一个常见的问题就是被隐式绑定的函数会丢失绑定对象。\n例子：\n1 2 3 4 5 6 7 8 9 10 11 12 13 function foo() { console.log(this.a) } var obj = { a: 2, foo: foo } var bar = obj.foo var a = \u0026#34;global\u0026#34; bar() // global 虽然bar引用的是obj.foo，但是bar实际上引用的是foo函数本身，因此，此时的bar()是一个不带 任何修饰的函数调用，所以foo应用了默认绑定。\n显式绑定 JavaScript中所有的函数都具有的方法call和apply\n通过bar.call(obj)可以强制将foo的this绑定到obj上\n如果call和apply被传入了一个原始值(字符串类型，布尔类型或者数字类型)来当做this的绑定对象，这个原始值会被转换成 对象的形式(也就是 new String(\u0026hellip;)\u0026hellip;)，这通常称作\u0026quot;装箱\u0026quot;\n硬绑定 硬绑定的典型应用场景就是创建一个包裹函数，负责接收函数参数并返回值\n1 2 3 4 5 6 ... var bar = function() { return foo.apply(obj, arguments) } bar() // 2 或者构建一个简单的绑定辅助函数\n1 2 3 4 5 function bind(fn, obj) { return function() { fn.apply(obj, arguments) } } ES5中内置的bind方法 bind()会返回一个硬绑定的函数，无论在哪里调用(除了使用new)，这个this以及传入的arguments都会生效\nAPI调用上下文 许多函数提供了绑定this的参数，比如forEach(callback, thisArg)\n1 [1,2,3].forEach(foo, obj) new绑定 思考下面代码：\n1 2 3 4 5 6 function foo(a) { this.a = a } var bar = new foo(2) console.log(bar.a) // 2 使用new来调用foo时，构造的新对象会被绑定到foo(...)调用中的this上\n优先级 new绑定 \u0026gt; 显式绑定 \u0026gt; 隐式绑定 \u0026gt; 默认绑定\n绑定例外 被忽略的this 如果把null, undefined,作为this的绑定对象传入call``apply或者bind，这些值在调用时会被 忽略，实际应用的是默认绑定规则。\n间接引用 经典的例子：\n1 2 3 4 5 6 var a = 2 var o = { a: 3, foo: foo } var p = { a: 4 } o.foo() // 3 (p.foo = o.foo)() //2 赋值表达式p.foo = o.foo返回的是目标函数的引用，因此调用位置是foo()，这里会应用默认绑定。\n箭头函数 箭头函数不使用this的四种标准规则，而是根据外层的作用域来决定this,箭头函数的绑定无法修改，new 也不行。\n","permalink":"https://xinyuli.blog/2016/12/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/This%20\u0026%20Object%20Prototypes/%E7%AC%AC%E4%BA%8C%E7%AB%A0this%E5%85%A8%E9%9D%A2%E8%A7%A3%E6%9E%90/","summary":"\u003ch2 id=\"绑定规则\"\u003e绑定规则\u003c/h2\u003e\n\u003cp\u003ethis的绑定规则总结起来总共有四种：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e默认绑定\u003c/li\u003e\n\u003cli\u003e隐式绑定\u003c/li\u003e\n\u003cli\u003e显式绑定\u003c/li\u003e\n\u003cli\u003enew绑定\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"默认绑定\"\u003e默认绑定\u003c/h3\u003e\n\u003cp\u003e这条规则是无法应用其它绑定时的默认规则，思考以下代码：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e6\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003efoo\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003efoo\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e","title":"第二章this全面解析"},{"content":"为什么要用this this保存着上下文对象，避免显式地传递上下文对象\nthis到底是什么 this是在运行时绑定的，并不是在编写时绑定的，它的的上下文取决于函数调用时的各种条件，this的绑定和函数声明的位置没有任何关系， 只取决于函数的调用方式。\n","permalink":"https://xinyuli.blog/2016/12/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/This%20\u0026%20Object%20Prototypes/%E7%AC%AC%E4%B8%80%E7%AB%A0%E5%85%B3%E4%BA%8Ethis/","summary":"\u003ch2 id=\"为什么要用this\"\u003e为什么要用this\u003c/h2\u003e\n\u003cp\u003ethis保存着上下文对象，避免显式地传递上下文对象\u003c/p\u003e\n\u003ch2 id=\"this到底是什么\"\u003ethis到底是什么\u003c/h2\u003e\n\u003cp\u003ethis是在运行时绑定的，并不是在编写时绑定的，它的的上下文取决于函数调用时的各种条件，this的绑定和函数声明的位置没有任何关系，\n只取决于函数的调用方式。\u003c/p\u003e","title":"第一章关于this"},{"content":"实质问题, 闭包的定义 当函数访问并记住所在的词法作用域时，就产生了闭包，即使函数是在当前词法作用域之外执行。\n(个人理解)闭包并没有什么神奇的，闭包是词法作用域的特性，函数能够保存它本身的词法作用域的变量是肯定的， 即使这个函数嵌套在另一个函数里面也不例外。\n循环和闭包 闭包用来保存循环变量的值，每一次循环都创建一个副本。 如下代码：\n1 2 3 4 5 6 7 for(var i=1; i\u0026lt;=5; i++) { (function(j) setTimeout(function timer(){ console.log(j) }) {})(i) } 模块 可以利用闭包去构建模块，封装私有属性和方法，然后暴露给外部方案修改内部的属性。\n","permalink":"https://xinyuli.blog/2016/12/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/Scope%20\u0026%20Closures/%E7%AC%AC%E4%BA%94%E7%AB%A0%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%97%AD%E5%8C%85/","summary":"\u003ch2 id=\"实质问题-闭包的定义\"\u003e实质问题, 闭包的定义\u003c/h2\u003e\n\u003cp\u003e当函数访问并记住所在的词法作用域时，就产生了闭包，即使函数是在当前词法作用域之外执行。\u003c/p\u003e\n\u003cp\u003e(个人理解)闭包并没有什么神奇的，闭包是词法作用域的特性，函数能够保存它本身的词法作用域的变量是肯定的，\n即使这个函数嵌套在另一个函数里面也不例外。\u003c/p\u003e\n\u003ch2 id=\"循环和闭包\"\u003e循环和闭包\u003c/h2\u003e\n\u003cp\u003e闭包用来保存循环变量的值，每一次循环都创建一个副本。\n如下代码：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e7\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;=\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nx\"\u003esetTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e(){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ej\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{})(\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"模块\"\u003e模块\u003c/h2\u003e\n\u003cp\u003e可以利用闭包去构建模块，封装私有属性和方法，然后暴露给外部方案修改内部的属性。\u003c/p\u003e","title":"第五章作用域闭包"},{"content":"什么是提升 所有的声明（变量或者函数声明）会被移动到各自作用域的最顶端，这个过程被称作\u0026quot;提升\u0026quot;，提升的过程是发生在代码被执行之前的编译阶段， 无论作用域的声明出现在什么地方，都将在代码被执行之前首先处理。\nvar a = 2 问题 1 2 3 a = 2 var a console.log(a) // 2 可以看到，编译时变量a被提升，此处打印出2\n另一个例子\n1 2 console.log(a) // undefined var a = 2 以上代码等价于：\n1 2 3 var a console.log(a) a = 2 打印出a是undefined是显而易见的\n函数优先提升 函数声明和变量声明同时提升时，函数会被优先提升，而且出现在后面的函数声明可以覆盖前面的函数声明。\n考虑以下代码：\n1 2 3 4 5 6 7 8 9 10 foo() // 2 var foo function foo() { console.log(1) } foo = function() { console.log(2) } 函数声明foo被优先提升，因此函数调用foo()打印出2\n","permalink":"https://xinyuli.blog/2016/12/21/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/Scope%20\u0026%20Closures/%E7%AC%AC%E5%9B%9B%E7%AB%A0%E6%8F%90%E5%8D%87/","summary":"\u003ch2 id=\"什么是提升\"\u003e什么是提升\u003c/h2\u003e\n\u003cp\u003e所有的声明（变量或者函数声明）会被移动到各自作用域的最顶端，这个过程被称作\u0026quot;提升\u0026quot;，提升的过程是发生在代码被执行之前的编译阶段，\n无论作用域的声明出现在什么地方，都将在代码被执行之前首先处理。\u003c/p\u003e\n\u003ch2 id=\"var-a--2-问题\"\u003evar a = 2 问题\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// 2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e可以看到，编译时变量a被提升，此处打印出2\u003c/p\u003e\n\u003cp\u003e另一个例子\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e// undefined\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e以上代码等价于：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e打印出a是undefined是显而易见的\u003c/p\u003e","title":"第四章提升"},{"content":"函数作用域 JavaScript具有函数作用域，主要具有这几点好处:\n隐藏内部实现，符合软件设计中的最小特权原则。 规避冲突。函数作用域可以当做命名空间隐藏变量，避免暴露到全局作用域发生冲突。 块作用域 虽然函数作用域是JavaScript中最常见的作用域单元，但块作用域在JavaScript中也是存在的，只不过（个人理解）JavaScript中的块作用域只是针对块中的某个变量 来说的。\n举例： java中的块作用域如下，在if块中声明的所有变量都只在if块中有效\n1 2 3 4 5 if (true) { int i = 0; int j = 0; .... } JavaScript中的\u0026quot;块作用域\u0026quot;\n1 2 3 4 if (true) { let i = 0 var j = 0 } 在如上块中，我们只能说被let声明的变量i 具有块作用域的性质，并不能理解为整个if块都是块作用域，很显然被var声明的变量j在 块外部也是可以被访问到的。\nwith with是块作用域中的一个例子，with创建的新作用域只在with块中有效,并且只有被with的这个变量具有块作用域的性质。\ntry/catch try/catch中的err变量具有块作用域的性质，并且只有这个变量有这个性质。\nlet,const ES6中被let或者被const声明的变量具有块作用域的性质\n","permalink":"https://xinyuli.blog/2016/12/21/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/Scope%20\u0026%20Closures/%E7%AC%AC%E4%B8%89%E7%AB%A0%E5%87%BD%E6%95%B0%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%9D%97%E4%BD%9C%E7%94%A8%E5%9F%9F/","summary":"\u003ch2 id=\"函数作用域\"\u003e函数作用域\u003c/h2\u003e\n\u003cp\u003eJavaScript具有函数作用域，主要具有这几点好处:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e隐藏内部实现，符合软件设计中的最小特权原则。\u003c/li\u003e\n\u003cli\u003e规避冲突。函数作用域可以当做命名空间隐藏变量，避免暴露到全局作用域发生冲突。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"块作用域\"\u003e块作用域\u003c/h2\u003e\n\u003cp\u003e虽然函数作用域是JavaScript中最常见的作用域单元，但块作用域在JavaScript中也是存在的，只不过（个人理解）JavaScript中的块作用域只是针对块中的某个变量\n来说的。\u003c/p\u003e","title":"第三章函数作用域和块作用域"},{"content":"什么是词法作用域 简单来讲，词法作用域就是定义在词法阶段的作用域，换句话说，词法作用域是由你在写代码时将变量和块作用域写在哪里决定的。\n遮蔽效应 作用域查找会在找到第一个匹配的标识符时停止，在多层嵌套的作用域中可以定义同名的标识符，这叫做\u0026quot;遮蔽效应\u0026quot;\n欺骗词法 欺骗词法作用域会导致性能下降\neval eval 可以对一段包含代码的字符串进行演算，并借此来修改词法作用域。\n非严格模式下，eval会对其所在的词法作用域进行修改，严格模式下，其有自己的词法作用域，不会对所在的词法作用域造成修改。\n非严格模式:\n1 2 3 4 5 6 function foo(str) { eval(str) console.log(b) // b 等于 3 } var b = 2 foo(\u0026#34;var b = 3\u0026#34;) 严格模式下:\n1 2 3 4 5 6 7 function foo(str) { \u0026#34;use strict\u0026#34; eval(str) console.log(b) // b 等于 2 } var b = 2 foo(\u0026#34;var b = 3\u0026#34;) with with 本质上是将一个对象的引用当做作用域来处理，创建了一个新的词法作用域\n副作用 这两个机制的副作用是引擎无法在编译时对作用域查找进行优化，因为这两个机制是在运行时动态创建的。\n","permalink":"https://xinyuli.blog/2016/12/21/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/Scope%20\u0026%20Closures/%E7%AC%AC%E4%BA%8C%E7%AB%A0%E8%AF%8D%E6%B3%95%E4%BD%9C%E7%94%A8%E5%9F%9F/","summary":"\u003ch2 id=\"什么是词法作用域\"\u003e什么是词法作用域\u003c/h2\u003e\n\u003cp\u003e简单来讲，词法作用域就是定义在词法阶段的作用域，换句话说，词法作用域是由你在写代码时将变量和块作用域写在哪里决定的。\u003c/p\u003e\n\u003ch2 id=\"遮蔽效应\"\u003e遮蔽效应\u003c/h2\u003e\n\u003cp\u003e作用域查找会在找到第一个匹配的标识符时停止，在多层嵌套的作用域中可以定义同名的标识符，这叫做\u0026quot;遮蔽效应\u0026quot;\u003c/p\u003e\n\u003ch2 id=\"欺骗词法\"\u003e欺骗词法\u003c/h2\u003e\n\u003cp\u003e欺骗词法作用域会导致性能下降\u003c/p\u003e\n\u003ch3 id=\"eval\"\u003eeval\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eeval\u003c/code\u003e 可以对一段包含代码的字符串进行演算，并借此来修改词法作用域。\u003c/p\u003e\n\u003cp\u003e非严格模式下，eval会对其所在的词法作用域进行修改，严格模式下，其有自己的词法作用域，不会对所在的词法作用域造成修改。\u003c/p\u003e","title":"第二章词法作用域"},{"content":"编译原理 传统编译语言的流程中，编译会执行三个步骤： 分词/词法分析 这个阶段将代码分解成词法单元，比如var a = 2，分解成var、a、=、2 解析/语法分析 将词法单元转换成抽象语法树(AST) 代码生成 将抽象语法树转换成可执行代码 JavaScript的编译有什么不同 JavaScript的编译过程不是发生在构建之前，而是发生在执行前的几微秒或者更短的时候，因此，JavaScript不会像其它语言那样有大量的时间去做优化。\n理解作用域 三个角色 引擎：负责js编译和执行的整个过程 编译器：负责语法分析和代码生成 作用域：负责收集和维护由所有的声明标识符组成的一系列查询，有一套严格的规则 变量的赋值操作 变量的赋值操作会执行两个动作，首先编译器会在当前作用域中声明一个变量，如果这个变量之前没有声明过， 然后在运行时引擎会在作用域中查找该变量，如果能够找到就对它赋值。\nLHS 和 RHS LHS：赋值操作左侧查询，查找到该变量本身，以便进行赋值操作。如果LHS查询失败，在严格模式下会抛出TypeError，在非严格模式下，会创建一个全局变量。 RHS: 赋值操作右侧查询，查找某个变量的值。如果RHS查询失败，抛出ReferenceError 作用域嵌套 作用域是根据名称查找作用域的一套规则，在当前作用域没有找到某个变量时，引擎就会到外层作用域进行查找，直到最外层的全局作用域。\n什么是作用域 作用域是一套规则，这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找\n","permalink":"https://xinyuli.blog/2016/12/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript%E4%B8%8A%E5%8D%B7/Scope%20\u0026%20Closures/%E7%AC%AC%E4%B8%80%E7%AB%A0%E4%BD%9C%E7%94%A8%E5%9F%9F%E6%98%AF%E4%BB%80%E4%B9%88/","summary":"\u003ch2 id=\"编译原理\"\u003e编译原理\u003c/h2\u003e\n\u003ch3 id=\"传统编译语言的流程中编译会执行三个步骤\"\u003e传统编译语言的流程中，编译会执行三个步骤：\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e分词/词法分析\n这个阶段将代码分解成词法单元，比如var a = 2，分解成var、a、=、2\u003c/li\u003e\n\u003cli\u003e解析/语法分析\n将词法单元转换成抽象语法树(AST)\u003c/li\u003e\n\u003cli\u003e代码生成\n将抽象语法树转换成可执行代码\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"javascript的编译有什么不同\"\u003eJavaScript的编译有什么不同\u003c/h3\u003e\n\u003cp\u003eJavaScript的编译过程不是发生在构建之前，而是发生在执行前的几微秒或者更短的时候，因此，JavaScript不会像其它语言那样有大量的时间去做优化。\u003c/p\u003e","title":"第一章作用域是什么"},{"content":"要解决的问题 迭代器要解决的问题很简单很单纯，就是进行遍历操作。\n实现原理 基本所有语言都实现了迭代器，javascript也不例外，如Array.prototype.forEach，for..of..，for..in..，for 迭代器的种类分两种，内部迭代器，外部迭代器。\n内部迭代器 内部迭代器的迭代过程对外部是不可控的，内部自己实现迭代过程.比如forEach, jQuery中的$.each\n外部迭代器 外部迭代器就相对灵活，可以控制迭代过程，需要显式调用迭代操作.外部迭代器较内部迭代器来说更方便扩展。\n外部迭代器的一个实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // itObj 是被迭代对象，需要包含length属性 var Iterator = function(itObj) { this.obj = itObj this.currentItem = 0 this.length = itObj.length } Iterator.prototype.next = function() { if (this.currentItem \u0026lt; this.length) { this.currentItem++ } } Iterator.prototype.getCurrentItem = function() { return this.obj[this.currentItem] } Iterator.prototype.isDone = function() { return this.currentItem \u0026gt;= this.length } var it = new Iterator([1, 2, 3]) console.log(it.getCurrentItem()) // 1 it.next() console.log(it.getCurrentItem()) // 2 it.next() console.log(it.getCurrentItem()) // 3 it.next() console.log(it.getCurrentItem()) // 3 扩展我们的迭代器，实现两个数组的merge函数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 //两个迭代器 var it1 = new Iterator([1,3,4,6]) var it2 = new Iterator([2,3,5,7]) var merge = function(it1, it2) { var tmp = [] // 两个迭代器同时遍历 while(!it1.isDone() \u0026amp;\u0026amp; !it2.isDone()) { if (it1.getCurrentItem() \u0026gt; it2.getCurrentItem()) { tmp.push(it2.getCurrentItem()) it2.next() } else if (it1.getCurrentItem() \u0026lt; it2.getCurrentItem()) { tmp.push(it1.getCurrentItem()) it1.next() } else { tmp.push(it1.getCurrentItem()) it1.next() it2.next() } } // 如果it1没被遍历完，直接push while(!it1.isDone()) { tmp.push(it1.getCurrentItem()) it1.next() } // 如果it2没被遍历完，直接push while(!it2.isDone()) { tmp.push(it2.getCurrentItem()) it2.next() } return tmp } console.log(merge(it1, it2)) // [1, 2, 3, 4, 5, 6, 7] 可以看到用外部迭代器很容易地控制迭代过程，扩展功能\n实践中的应用 一个copy自书上的例子\n获取上传对象 未使用迭代器的版本\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 var getUploadObj = function() { try { return new ActiveXObject(\u0026#34;TXFTNActiveX.FTNUpload\u0026#34;); } catch (e) { // IE 上传控件 if (supportFlash()) { // supportFlash 函数未提供 var str = \u0026#39;\u0026lt;object type=\u0026#34;application/x-shockwave-flash\u0026#34;\u0026gt;\u0026lt;/object\u0026gt;\u0026#39;; return $(str).appendTo($(\u0026#39;body\u0026#39;)); } else { var str = \u0026#39;\u0026lt;input name=\u0026#34;file\u0026#34; type=\u0026#34;file\u0026#34;/\u0026gt;\u0026#39;; // 表单上传 return $(str).appendTo($(\u0026#39;body\u0026#39;)); } } } 我们看到其中夹杂着try...catch语句和大量的if语句，严重违反了开闭原则。 使用迭代器重构\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var getActiveUploadObj = function() { try { return new ActiveXObject(\u0026#34;TXFTNActiveX.FTNUpload\u0026#34;); //IE上传控件 } catch (e) { return false; } }; var getFlashUploadObj = function() { if (supportFlash()) { var str = \u0026#39;\u0026lt;object type=\u0026#34;application/x-shockwave-flash\u0026#34;\u0026gt;\u0026lt;/object\u0026gt;\u0026#39; return $(str).appendTo($(\u0026#39;body\u0026#39;)) } return false } var getFormUploadObj = function() { var str = \u0026#39;\u0026lt;input name=\u0026#34;file\u0026#34; type=\u0026#34;file\u0026#34; class=\u0026#34;ui-file\u0026#34; /\u0026gt;\u0026#39; return $(str).appendTo($(\u0026#39;body\u0026#39;)) } var iteratorUploadObj = function() { for (var i = 0, fn; fn = arguments[i++]) { var uploadObj = fn() if (uploadObj !== false) { return uploadObj } } } var uploadObj = iteratorUploadObj(getActiveUploadObj, getFlashUploadObj, getFormUploadObj) 重构代码之后，我们可以看到，获取不同上传对象的方法被隔离在各自的函数里互不干扰， try、catch 和 if 分支不再纠缠在一起，使得我们可以很方便地的维护和扩展代码。\n","permalink":"https://xinyuli.blog/2016/11/11/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F/","summary":"\u003ch2 id=\"要解决的问题\"\u003e要解决的问题\u003c/h2\u003e\n\u003cp\u003e迭代器要解决的问题很简单很单纯，就是进行遍历操作。\u003c/p\u003e\n\u003ch2 id=\"实现原理\"\u003e实现原理\u003c/h2\u003e\n\u003cp\u003e基本所有语言都实现了迭代器，javascript也不例外，如\u003ccode\u003eArray.prototype.forEach\u003c/code\u003e，\u003ccode\u003efor..of..\u003c/code\u003e，\u003ccode\u003efor..in..\u003c/code\u003e，\u003ccode\u003efor\u003c/code\u003e\n迭代器的种类分两种，内部迭代器，外部迭代器。\u003c/p\u003e\n\u003ch3 id=\"内部迭代器\"\u003e内部迭代器\u003c/h3\u003e\n\u003cp\u003e内部迭代器的迭代过程对外部是不可控的，内部自己实现迭代过程.比如\u003ccode\u003eforEach\u003c/code\u003e, jQuery中的\u003ccode\u003e$.each\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"外部迭代器\"\u003e外部迭代器\u003c/h3\u003e\n\u003cp\u003e外部迭代器就相对灵活，可以控制迭代过程，需要显式调用迭代操作.外部迭代器较内部迭代器来说更方便扩展。\u003c/p\u003e","title":"迭代器模式"},{"content":"解决哪一类问题 从字面意思上理解，代理模式解决对一个对象的直接访问，这种直接访问可能是\u0026quot;不方便\u0026quot;的，所谓\u0026quot;不方便\u0026quot;可能是直接访问成本比较大(在前端领域通常是要获取后台数据或者执行复杂的计算)，或者需要对原始对象进行一些修饰或处理。\n实现原理 实现一个代理对象，这个代理对象和原对象对外暴露相同的接口，用户直接操作代理对象，对于说对用户是透明的，用户并不知道有代理存在。\n虚拟代理 把一些昂贵的操作放在代理对象中进行，现在体会的还不是很深刻，具体例子没想好，以后补充。\n缓存代理 把昂贵操作产生的数据缓存在代理对象中，避免了重复操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 假设a*b是个昂贵的操作 function calculate(a, b) { return a*b } var proxyCalculate = (function() { var cache = {} return function(a, b) { var ab = \u0026#39;\u0026#39; + a + \u0026#39;_\u0026#39; + b if (ab in cache) { return cache[ab] } return cache[ab] = calculate(a, b) } })() var a = 99999, b = 99999 proxyCalculate(a, b) 实践中的应用 利用代理来做节流操作 直接copy书上的代码\n假设我们在做一个文件同步的功能，当我们选中一个 checkbox 的时候，它对应的文件就会被同 步到另外一台备用服务器上面，当我们选中 3 个 checkbox 的时候，依次往服务器发送了 3 次同步文件的请求, 可以预见，如此频繁的网络请求将会带来相当大的开销。\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;body\u0026gt; \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;1\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;1 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;2\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;2 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;3\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;3 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;4\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;4 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;5\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;5 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;6\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;6 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;7\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;7 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;8\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;8 \u0026lt;input type=\u0026#34;checkbox\u0026#34; id=\u0026#34;9\u0026#34;\u0026gt;\u0026lt;/input\u0026gt;9 \u0026lt;/body\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 var synchronousFile = function( id ){ console.log( \u0026#39;开始同步文件，id 为: \u0026#39; + id ); }; var proxySynchronousFile = (function(){ var cache = [], // 保存一段时间内需要同步的 ID timer; // 定时器 return function( id ){ cache.push( id ); if (timer){ // 保证不会覆盖已经启动的定时器 return; } timer = setTimeout(function(){ synchronousFile( cache.join( \u0026#39;,\u0026#39; ) ); clearTimeout( timer ); // 清空定时器 timer = null; cache.length = 0; // 清空 ID 集合 }, 2000 ); }// 2 秒后向本体发送需要同步的 ID 集合 })(); var checkbox = document.getElementsByTagName( \u0026#39;input\u0026#39; ); for ( var i = 0, c; c = checkbox[ i++ ]; ){ c.onclick = function(){ if (this.checked === true){ proxySynchronousFile( this.id ); } } } ","permalink":"https://xinyuli.blog/2016/11/10/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/","summary":"\u003ch2 id=\"解决哪一类问题\"\u003e解决哪一类问题\u003c/h2\u003e\n\u003cp\u003e从字面意思上理解，代理模式解决对一个对象的直接访问，这种直接访问可能是\u0026quot;不方便\u0026quot;的，所谓\u0026quot;不方便\u0026quot;可能是直接访问成本比较大(在前端领域通常是要获取后台数据或者执行复杂的计算)，或者需要对原始对象进行一些修饰或处理。\u003c/p\u003e\n\u003ch2 id=\"实现原理\"\u003e实现原理\u003c/h2\u003e\n\u003cp\u003e实现一个代理对象，这个代理对象和原对象对外暴露相同的接口，用户直接操作代理对象，对于说对用户是透明的，用户并不知道有代理存在。\u003c/p\u003e\n\u003ch3 id=\"虚拟代理\"\u003e虚拟代理\u003c/h3\u003e\n\u003cp\u003e把一些昂贵的操作放在代理对象中进行，现在体会的还不是很深刻，具体例子没想好，以后补充。\u003c/p\u003e\n\u003ch3 id=\"缓存代理\"\u003e缓存代理\u003c/h3\u003e\n\u003cp\u003e把昂贵操作产生的数据缓存在代理对象中，避免了重复操作\u003c/p\u003e","title":"代理模式"},{"content":"要解决的问题 当解决一个问题有多种方法时，选择使用哪种方法时就少不了要用大量的if语句进行判断，如果将这些方法的实现和判断语句放在一起实现就会产生问题， 比如增加一种的新的方法时，就不得不再去写一条if语句，这不符合开闭原则，也不方便复用，因此策略模式主要解决的是算法的实现和算法的使用的过度耦合问题。\n实现原理 将每个算法的实现封装起来，并可以相互替换(因为它们有共同的目的)，通过统一的上下文调用，一方面解决了耦合问题，另一方面增强了代码的复用能力\n实践中的应用 表单的验证 表单验证涉及到多个判断语句，一般我们会这样实现\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var form = document.getElementById(\u0026#39;myform\u0026#39;) form.onsubmit = function() { if (form.username.value === \u0026#39;\u0026#39;) { alert(\u0026#39;请输入用户名\u0026#39;) return false } if (form.password.value === \u0026#39;\u0026#39;) { alert(\u0026#39;请输入密码\u0026#39;) return false } if (form.password.value.length \u0026lt; 8) { alert(\u0026#39;密码不小于8位\u0026#39;) return false } } 当增加新的字段或者变换新的验证规则时就不得不去修改if语句或者增加新的if语句，而且当别的表单也需要这样验证时没法复用。 所以采用策略模式来实现\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // 策略对象，封装一系列的策略 var rules = { NOT_EMPTY: function(value, errorMsg) { value = \u0026#39;\u0026#39; + value if (value === \u0026#39;\u0026#39;) { return errorMsg } }, NOT_LESS: function(value, length, errorMsg) { value = \u0026#39;\u0026#39; + value if (value.length \u0026lt; length) { return errorMsg } } } // 验证器的实现 var Validator = function(rules) { this.rules = rules // 保存着验证规则 this.cache = [] } // 添加规则 Validator.prototype.add = function(value, rule, errorMsg) { var ruleArray = rule.split(\u0026#39;:\u0026#39;) var self = this if (ruleArray.length === 1) { // 利用闭包来保存外部的值 this.cache.push(function() { return self.rules[rule](value, errorMsg) }) } else if (ruleArray.length === 2) { this.cache.push(function() { return self.rules[ruleArray[0]](value, ruleArray[1], errorMsg) }) } } Validator.prototype.start = function() { var errorMsg = \u0026#39;\u0026#39; for (var r = 0; r \u0026lt; this.cache.length; r++) { var errorMsg = this.cache[r]() if (errorMsg) { return errorMsg } } } var form = document.getElementById(\u0026#39;myform\u0026#39;) form.onsubmit = function() { var v = new Validator(rules) v.add(form.username.value, \u0026#39;NOT_EMPTY\u0026#39;, \u0026#39;请输入用户名\u0026#39;) v.add(form.password.value, \u0026#39;NOT_LESS:8\u0026#39;, \u0026#39;密码不少于8位\u0026#39;) var errorMsg = v.start() if (errorMsg) { alert(errorMsg) return false } } 根据上述代码，如果我们添加新的验证规则，只需要在rules添加新的验证函数就可，如果添加新的字段，只需要add进Validator中即可，实现了算法的实现与使用的分离，符合开闭原则。\n","permalink":"https://xinyuli.blog/2016/11/09/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/","summary":"\u003ch2 id=\"要解决的问题\"\u003e要解决的问题\u003c/h2\u003e\n\u003cp\u003e当解决一个问题有多种方法时，选择使用哪种方法时就少不了要用大量的if语句进行判断，如果将这些方法的实现和判断语句放在一起实现就会产生问题，\n比如增加一种的新的方法时，就不得不再去写一条if语句，这不符合开闭原则，也不方便复用，因此策略模式主要解决的是\u003cem\u003e算法的实现\u003c/em\u003e和\u003cem\u003e算法的使用\u003c/em\u003e的过度耦合问题。\u003c/p\u003e\n\u003ch2 id=\"实现原理\"\u003e实现原理\u003c/h2\u003e\n\u003cp\u003e将每个算法的实现封装起来，并可以相互替换(因为它们有共同的目的)，通过统一的上下文调用，一方面解决了耦合问题，另一方面增强了代码的复用能力\u003c/p\u003e\n\u003ch2 id=\"实践中的应用\"\u003e实践中的应用\u003c/h2\u003e\n\u003ch3 id=\"表单的验证\"\u003e表单的验证\u003c/h3\u003e\n\u003cp\u003e表单验证涉及到多个判断语句，一般我们会这样实现\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eform\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;myform\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eform\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eonsubmit\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eform\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eusername\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nx\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;请输入用户名\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eform\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nx\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;请输入密码\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eform\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003epassword\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nx\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;密码不小于8位\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e","title":"策略模式"},{"content":"解决的问题 保证实例只有一个，避免多个实现，从全局来看，这个实例的状态是唯一的。\n实现原理 设置一个变量来记录实例，通过检测该变量是否为空来决定是否创建实例\n非透明单例 所谓非透明就是用户在使用前必须知道这是一个单例类，不使用new来获取实例\n1 2 3 4 5 6 7 8 9 10 11 12 13 var Single = function() {} Single.instance = null Single.getInstance = function() { if (this.instance) { return this.instance } return this.instance = new Single() } var a = Single.getInstance() var b = Single.getInstance() a === b //true 透明单例 其优点是创建实例时对用户透明，虽然多次new，但实际指向一个实例\n1 2 3 4 5 6 7 8 9 10 11 12 var Single = (function() { var instance = null return function() { if (instance) { return instance } return instance = this } })() var a = new Single() var b = new Single() a === b // true 使用代理模式实现单例 上述实现单例的缺点是 单例的判断和实例的实现耦合在一起 ，特别是在构造函数传参数的时候。因此，不符合单一职责原则， 采用代理的方式来将单例的判断和实例的实现解耦。\n1 2 3 4 5 6 7 8 9 10 11 12 13 // 真正的构造函数，负责真正的实例实现 var _Single = function(option) { this.name = option.name } // 代理构造函数,主要负责单例判断 var Single = （function() { var instance = null return function(option) { if (instance) return instance return instance = new _Single(option) } })() 实践中的应用 个人理解，单例模式主要是用于约束实例状态的唯一性，避免存在多个实例从而有多个状态。 因此，单例更多用于系统层面的全局功能，比如模态窗口。\n","permalink":"https://xinyuli.blog/2016/11/08/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/","summary":"\u003ch2 id=\"解决的问题\"\u003e解决的问题\u003c/h2\u003e\n\u003cp\u003e保证实例只有一个，避免多个实现，从全局来看，这个实例的状态是唯一的。\u003c/p\u003e\n\u003ch2 id=\"实现原理\"\u003e实现原理\u003c/h2\u003e\n\u003cp\u003e设置一个变量来记录实例，通过检测该变量是否为空来决定是否创建实例\u003c/p\u003e\n\u003ch3 id=\"非透明单例\"\u003e非透明单例\u003c/h3\u003e\n\u003cp\u003e所谓非透明就是用户在使用前必须知道这是一个单例类，不使用new来获取实例\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eSingle\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eSingle\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003einstance\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eSingle\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003egetInstance\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003einstance\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003einstance\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003ethis\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003einstance\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nx\"\u003eSingle\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eSingle\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eSingle\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e  \u003cspan class=\"c1\"\u003e//true\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e","title":"单例模式"}]