多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
**概述** [[mozilla.org]](https://developer.mozilla.org/en-US/docs/Web/JavaScript) JavaScript是一种基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。 JavaScript 的标准是 ECMAScript 。截至 2012 年,所有的现代浏览器都完整支持 ECMAScript 5.1,旧式的浏览器至少支持 ECMAScript 3 标准。2015年6月17日,ECMA国际组织(Ecma International,一个欧洲标准化组织)发布了 ECMAScript 的第六版,该版本正式名称为ECMAScript 2015,但通常被称为 ECMAScript 6 或者ES6。自此,ECMAScript每年发布一次新标准。 与大多数编程语言不同,JavaScript 没有输入或输出的概念。它是一个在宿主环境(host environment)下运行的脚本语言,任何与外界沟通的机制都是由宿主环境提供的。浏览器是最常见的宿主环境,但在非常多的其他程序中也包含 JavaScript 解释器,如 Adobe Acrobat、Photoshop、SVG 图像、Yahoo! 的 Widget 引擎,以及 Node.js 之类的服务器端环境。JavaScript 的实际应用远不止这些,除此之外还有 NoSQL 数据库(如开源的 Apache CouchDB)、嵌入式计算机,以及包括 GNOME (注:GNU/Linux 上最流行的 GUI 之一)在内的桌面环境等等。 HTML是用来存储网页内容的,CSS是用来定义这些内容的显示样式的,而JavaScript是用来创造丰富的页面效果或者网页应用的。 但是,如果从浏览器的范畴去理解“JavaScript”这个术语,它包含了截然不同的两个方面。一个是JavaScript的核心语言(ECMAScript),另一个是DOM(文档对象模型)。 ---- 目录: [TOC] ---- ## X JavaScript 标准库 [参考内容:JavaScript 标准库](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects) JavaScript 标准库中的标准内置对象(Standard built-in objects)或称**全局的对象**( global objects ),不要和 "**全局对象**(global object)" 混淆。这里说的全局的对象是说在**全局作用域里的内的对象**。 "全局对象 (global object)” 是一个Global类的对象。可以在全局作用域里,用`this`访问(但只有在非严格模式下才可以,在严格模式下得到的是`undefined`)。实际上,全局作用域就是包含了全局对象的属性,还有它继承来的属性。 全局作用域中的其他对象可以由用户的脚本创建或由宿主程序提供。浏览器作为最常见的宿主程序,其所提供的宿主对象的说明可以在这里找到:[API 参考](https://developer.mozilla.org/zh-CN/docs/Web/API)。 ### global对象(全局对象) 《JS高级程序设计》中谈到,global对象可以说是ECMAScript中对特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。从某种意义上讲,它是一个终极的“兜底儿对象”,换句话说呢,就是不属于任何其他对象的属性和方法,最终都是它的属性和方法。我理解为,这个global对象呢,就是整个JS的“老祖宗”,找不到归属的那些“子子孙孙”都可以到它这里来认祖归宗。所有在全局作用域中定义的属性和函数,都是global对象的属性和方法,比如isNaN()、parseInt()以及parseFloat()等,实际都是它的方法;还有就是常见的一些特殊值,如:NaN、undefined等都是它的属性,以及一些构造函数Object、Array等也都是它的方法。总之,记住一点:global对象就是“老祖宗”,所有找不到归属的就都是它的。 ### window对象 前面说了global对象是“老祖宗”,那有人该问了,你把window对象置于何地呢?对,javascript这门语言现在这么红火很大部分原因是因为W3C将它作为写页面的官方脚本语言,但别忘了js它不仅仅可以用来写页面,也就是不仅仅可以用在浏览器中。window对象是相对于web浏览器而言的,它并不是ECMAScripta规定的内置对象,内置对象的定义是:“由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。”window对象是宿主对象也就是在一定的环境中才会生成的对象(这里也就是指浏览器),而global对象是在任何环境中都存在的。window对象具体也就是指浏览器打开的那个窗口。 ### document对象 简单来说,document对象是window对象的一个属性,是显示于窗口内的一个文档。而window对象则是一个顶层对象,它不是另一个对象的属性。document可以理解为文档,就是你的网页,而window是你的窗口,就是你的浏览器包含的。它们俩在没有框架的情况下可以说是等同的,在有框架的情况下就要区别对待了。 ## 1. JavaScript 技术概览 [概述](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/JavaScript_technologies_overview) HTML是用来存储网页内容的,CSS是用来定义这些内容的显示样式的,而JavaScript是用来创造丰富的页面效果或者网页应用的。 但是,如果从浏览器的范畴去理解“JavaScript”这个术语,它包含了截然不同的两个方面。一个是JavaScript的核心语言(ECMAScript),另一个是DOM(文档对象模型)。 ### X.1 JavaScript核心语言 (ECMAScript) JavaScript的核心语言是ECMAScript。ECMAScript是由ECMA TC39委员会进行标准化的一门编程语言,其最新版本是``ECMAScript 7.0``。 核心语言同样可以被用在非浏览器环境中,例如 ``node.js``。 **ECMAScript**([有关资源]()) 定义了: - 语法(解析规则,关键字,流程控制,对象初始化等) - 错误处理机制(throw, try/catch 以及用户自定义错误类型的能力) - 类型(布尔值,数字,字符串,函数,对象等) - 全局对象。在浏览器环境中,这个全局对象就是 window 对象,但是 ECMAScript 只定义与浏览器无关的 APIs(例如,parseInt, parseFloat, decodeURI, encodeURI 等) - 基于原型的继承机制 - 内置对象和函数(JSON,Math,Array.prototype 方法,Object introspection 方法等) - 严格模式 **浏览器支持** 截止2014年8月份,主流浏览器的最新版本都已经支持ECMAScript 5.1,但是旧版本的浏览器大都仅支持ECMAScript 3和ECMAScript 5的部分标准。[这里](http://kangax.github.io/compat-table/es5/)是一些关于ECMAScript 5的浏览器支持情况的资料。如今,主流浏览器的最新版本已经支持 ECMAScript 6 的大部分标准。 ECMA 委员会已经在 2015年6月17号,正式发布了第6版标准。 ### X.2 DOM API **WebIDL** [WebIDL (an interface definition language, )](https://heycam.github.io/webidl/)定义了ECMAScript和DOM技术之间的交互规范。 **DOM核心** 文档对象模型(DOM, Document Object Model)是用来表达HTML,XHTML及XML文档中的对象或与其进行交互的约定,它是跨平台的,并且与编程语言无关。通过调用DOM树上对象的方法可以操纵这些对象。文档对象模型核心是由W3C进行标准化的,它将HTML和XML文档抽象成对象,并在其上定义接口以及操纵这些对象的机制,这些定义都是与编程语言无关的。 - [DOM核心](https://dom.spec.whatwg.org/)中定义了文档结构,树模型,以及 DOM事件架构,包括:Node, Element, DocumentFragment, Document, DOMImplementation, Event, EventTarget等。 -[ DOM事件](https://w3c.github.io/uievents/)中包括DOM事件架构不太严格的定义以及一些特殊事件。 - [DOM元素遍历](https://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html) 以及 [DOM Range](https://dom.spec.whatwg.org/#ranges) 对象等其它内容。 从ECMAScript的角度来看,DOM规范中定义的对象被称作“宿主对象”。 **HTML DOM** [HTML](https://html.spec.whatwg.org/multipage/)是一种网页标记语言,它在DOM核心抽象概念之上,还定义了元素的含义,比如元素的``className``属性以及 ``document.body``对象等。 HTML规范同时还约束了元素之间的关系,例如无序列表ul元素中,只能以li元素作为子元素来表达列表项。还有就是禁止使用标准中未定义的元素和属性。 想了解更多关于[Document对象](https://developer.mozilla.org/zh-CN/docs/Web/API/document)、[Window对象](https://developer.mozilla.org/zh-CN/docs/Web/API/Window)以及其他[DOM元素](https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model)的信息?请访问Gecko DOM文档。 ### X.3 国际化API 由Ecma TC39进行标准化的ECMAScript国际化API规范是在ECMAScript语言规范之上额外增加的。国际化API为JavaScript提供了规则排序(字符串比较),数字格式化,日期时间格式化等功能,能够让应用根据实际需要选择语言或功能。本标准在2012年12月份审批通过,可以在[Intl对象](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)页面查看浏览器的实现情况。 ### X.4 其他值得关注的API - HTML标准中`Window`接口首次对setTimeout和setInterval函数进行定义。 - `XMLHttpRequest`:用来发起异步HTTP请求的API。 - `CSS 对象模型` :用于将CSS规则抽象成对象。 - `WebWorkers`: 是用来并行计算的API。 - `WebSockets`:是用来实现双向Socket通信的API。 - `Canvas 2D Context`:canvas元素的绘图API。 ### X.5 浏览器支持 自2011年6月以来,W3C,特别是WHATWG都在对旧特性进行细节定义,以提高互用性。浏览器也基于这些更加详细的规范提升它们的实现方式。 要实现跨浏览器的兼容性,一个常见的但可能不是最可靠的方式就是使用JavaScript库。这些库对于DOM特性进行抽象,以确保它们所提供的API在不同的浏览器上行为一致。被广泛采用的框架有[jQuery](http://jquery.com/),[prototype](http://prototypejs.org/)和[YUI](https://yuilibrary.com/)。 ## x.1 值属性 (Value properties) 这些全局属性返回一个简单值,这些值没有自己的属性和方法。 > > - Infinity > - NaN > - undefined > - null literal ## x.2 函数属性 (Function properties) 全局函数可以直接调用,不需要在调用时指定所属对象,执行结束后会将结果直接返回给调用者。 ### x.2.1 函数 [官方网页](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions) ### x.2.2 预定义函数 [官方网页](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions) > - eval() > - isFinite() > - isNaN() > - parseFloat() > - parseInt() > - decodeURI() > - decodeURIComponent() > - encodeURI() > - encodeURIComponent() > - uneval() (不再标准化) > - escape() (不再使用) > - unescape() (不再使用) ## x.3 基本对象 (Fundamental objects) 基本对象是定义或使用其他对象的基础。基本对象包括一般对象、函数对象和错误对象。 > > - Object > - Function > - Boolean > - Symbol > - Error > - EvalError > - InternalError > - RangeError > - ReferenceError > - SyntaxError > - TypeError > - URIError ## x.4 数字和日期对象 (Numbers and dates) 用来表示数字、日期和执行数学计算的对象。 > > - Number > - Math > - Date #### x.4.1 Number对象 #### x.4.2 Math对象 #### x.4.3 Date对象 [Date对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date)基于1970年1月1日(世界标准时间)起的毫秒数。 ~~~JavaScript var today = new Date(); var today = new Date(1453094034000); // by timestamp(accurate to the millimeter) var birthday = new Date('December 17, 1995 03:24:00'); var birthday = new Date('1995-12-17T03:24:00'); var birthday = new Date(1995, 11, 17); var birthday = new Date(1995, 11, 17, 3, 24, 0); var unixTimestamp = Date.now(); // in milliseconds ~~~ **构造函数** ~~~JavaScript new Date(); new Date(value); new Date(dateString); new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]); ~~~ > * 只能通过调用 Date 构造函数来实例化日期对象。以常规函数调用它(即不加 new 操作符)将会返回一个字符串,而不是一个日期对象。另外,不像其他JavaScript 类型,Date 对象没有`字面量格式`。 > * 当Date作为构造函数调用并传入多个参数时,如果数值大于合理范围时(如月份为13或者分钟数为70),相邻的数值会被调整。比如 new Date(2013, 13, 1)等于new Date(2014, 1, 1),它们都表示日期2014-02-01(注意月份是从0开始的)。其他数值也是类似,new Date(2013, 2, 1, 0, 70)等于new Date(2013, 2, 1, 1, 10),都表示时间2013-03-01T01:10:00。 > * 当Date作为构造函数调用并传入多个参数时,所定义参数代表的是当地时间。如果需要世界协调时,使用 new Date({{jsxref("Date.UTC()", "Date.UTC(...)")}}) 和相同参数 `value`:代表自1970年1月1日00:00:00 (世界标准时间) 起经过的毫秒数。 `dateString`:表示日期的字符串值。该字符串应该能被 [Date.parse()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) 方法识别(符合 [IETF-compliant RFC 2822 timestamps](https://tools.ietf.org/html/rfc2822#page-14) 或 [version of ISO8601](http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15))。 `year`:代表年份的整数值。为了避免2000年问题最好指定4位数的年份; 使用 1998, 而不要用 98. `month`:代表月份的整数值从0(1月)到11(12月)。 `day`:代表一个月中的第几天的整数值,从1开始。 `hour`:代表一天中的小时数的整数值 (24小时制)。 `minute`:分钟数。 `second`:秒数。 `millisecond`: 表示时间的毫秒部分的整数值。 **描述** * 如果没有输入任何参数,则Date的构造器会依据系统设置的当前时间来创建一个Date对象。 * 如果提供了至少两个参数,其余的参数均会默认设置为1(如果没有提供day参数)或者0。 * JavaScript的时间是由世界标准时间(UTC)1970年1月1日开始,用毫秒计时,一天由86,400,000毫秒组成。Date对象的范围是-100,000,000天至100,000,000天(等效的毫秒值)。 * JavaScript的Date对象为跨平台提供了统一的行为。时间属性可以在不同的系统中表示相同的时刻,而如果使用了本地时间对象,则反映当地的时间。 * JavaScript 的Date对象提供了数个UTC时间的方法,也相应提供了当地时间的方法。`UTC`,也就是我们所说的格林威治时间,指的是time中的世界时间标准。而`当地时间`则是指执行JavaScript的客户端电脑所设置的时间。 * 以一个函数的形式来调用JavaScript的Date对象(i.e., 不使用 new 操作符)会返回一个代表当前日期和时间的字符串。 **属性** `Date.prototype`:允许为 Date 实例对象添加属性。 `Date.length`:Date.length 的值是 7。这是该构造函数可接受的参数个数。 **方法** `Date.now()`:返回自 1970-1-1 00:00:00 UTC (世界标准时间)至今所经过的毫秒数。 `Date.parse()`:解析一个表示日期的字符串,并返回从 1970-1-1 00:00:00 所经过的毫秒数。 `Date.UTC()`:接受和构造函数最长形式的参数相同的参数(从2到7),并返回从 1970-01-01 00:00:00 UTC 开始所经过的毫秒数。 **Date.prototype方法** * Getter,19个 * Setter,16 * Conversion getter,13个 ### x.5 字符处理 (Text processing) 用来表示和操作字符串的对象。 > > - String > - RegExp ### x.6 可索引的集合对象 (Indexed collections) 按照索引值来排序的数据集合,包括数组和类型数组,以及类数组结构的对象。 > > - Array > - Int8Array > - Uint8Array > - Uint8ClampedArray > - Int16Array > - Uint16Array > - Int32Array > - Uint32Array > - Float32Array > - Float64Array ### x.7 使用键的集合对象 (Keyed collections) 这些集合对象在存储数据时会使用到键,支持按照插入顺序来迭代元素。 > > - Map > - Set > - WeakMap > - WeakSet ### x.8 结构化数据 (Structured data) > > - ArrayBuffer > - DataView > - JSON > - SharedArrayBuffer (实验开发中,不适用生产环境) > - Atomics (实验开发中,不适用生产环境) ### x.9 控制抽象对象 (Control abstraction objects) > > - Promise > - Generator > - GeneratorFunction > - AsyncFunction (实验开发中,不适用生产环境) ### x.11 反射 (Reflection) > > - Reflect > - Proxy ### x.12 国际化 (Internationalization) 为了支持多语言处理而加入ECMAScript的对象。 > > - Intl > - Intl.Collator > - Intl.DateTimeFormat > - Intl.NumberFormat ### x.13 WebAssembly > > - WebAssembly > - WebAssembly.Module > - WebAssembly.Instance > - WebAssembly.Memory > - WebAssembly.Table > - WebAssembly.CompileError > - WebAssembly.LinkError > - WebAssembly.RuntimeError ### x.14 其他 > > - arguments ### x.15 矢量集合 (Vector collections )(实验开发中,不适用生产环境) SIMD 矢量集合中的数据会被组织为一个数据序列。 ## x. 调试(console对象) [浏览器控制台(console对象)](http://blog.csdn.net/gz_jmark/article/details/22997349) console对象代表浏览器的JavaScript控制台。虽然它还不是标准,但是各大浏览器都原生支持,已经成为事实上的标准。 console对象主要有两个作用: - 显示网页代码运行时的错误信息。 - 提供了一个命令行接口,用来与网页代码互动。 ### x.1 console.log log方法用于在console窗口显示信息。 如果参数是普通字符串,log方法将字符串内容显示在console窗口。 ~~~js console.log("Hello World") // Hello World console.log("a","b","c") // a b c ~~~ 如果参数是格式字符串(使用了格式占位符),log方法将占位符替换以后的内容,显示在console窗口。 * %d, %i 整数 * %s 字符串 * %f 浮点数 * %o 对象的链接 * %c CSS格式字符串 ~~~js console.log("%c%s", "color: red; background: yellow; font-size: 24px;", "警告!"); ~~~ ### x.2 console.dir 输出对象的信息,用于显示一个对象的所有属性。 ### x.3 console.table 对于某些复合类型的数据,console.table方法可以将其转为表格显示。 ### x.4 console.clear 对console窗口进行清屏,光标回到第一行。 ### x.5 console.trace 当前执行的代码在堆栈中的调用路径。 ## xx. 基于原型(prototype)的编程范式 2018-02-01 陈皓(左耳朵耗子,极客时间)[编程范式游记(7)- 基于原型的编程范式 ](https://time.geekbang.org/column/article/2741) 基于原型(prototype)的编程其实也是面向对象编程的一种方式。没有 class 化的,直接使用对象。又叫,基于实例的编程。其主流的语言就是 JavaScript。 * 在基于类的语言中,一个新的实例通过类构造器和构造器可选的参数来构造,结果实例由类选定的行为和布局创建模型。 * 在基于原型的系统中构造对象有两种方法,通过复制已有的对象或者通过扩展空对象创建。很多基于原型的系统提倡运行时原型的修改,而基于类的面向对象系统只有动态语言允许类在运行时被修改(Common Lisp、Dylan、Objective-C、Perl、Python、Ruby 和 Smalltalk)。 每个对象都有一个` __proto__ `的属性,这个就是“原型”。 需要解释一下 JavaScript 的两个东西,一个是` __proto__`,另一个是 `prototype`. * `__proto__ `主要是安放在一个实际的对象中,用它来产生一个链接,一个原型链连,用于寻找方法名或属性,等等。 * `prototype` 是用 new 来创建一个对象时构造` __proto__ `用的。它是构造函数的一个属性。 在 JavaScript 中,对象有两种表现形式, 一种是 Object [(ES5 关于 Object 的文档)](http://www.ecma-international.org/ecma-262/5.1/#sec-15.2),一种是 Function [(ES5 关于 Object 的文档)](http://www.ecma-international.org/ecma-262/5.1/#sec-15.2)。 我们可以简单地认为,`__proto__ `是所有对象用于链接原型的一个指针,而 `prototype` 则是 Function 对象的属性,其主要是用来当需要 new 一个对象时让` __proto__ `指针所指向的地方。 对于超级对象 Function 而言, `Function.__proto__ `就是` Function.prototype`。 ~~~javascript // 一种构造函数写法 function Foo(y) { this.y = y; } // 修改 Foo 的 prototype,加入一个成员变量 x Foo.prototype.x = 10; // 修改 Foo 的 prototype,加入一个成员函数 calculate Foo.prototype.calculate = function (z) { return this.x + this.y + z; }; // 现在,我们用 Foo 这个原型来创建 b 和 c var b = new Foo(20); var c = new Foo(30); // 调用原型中的方法,可以得到正确的值 b.calculate(30); // 60 c.calculate(40); // 80 ~~~ 那么,在内存中的布局是怎么样的呢?大概是下面这个样子。 ![javascript中的prototype](https://box.kancloud.cn/e4a5053894b27759103976720d29ab80_627x392.png) 我们可以测试一下: ~~~javascript b.__proto__ === Foo.prototype, // true c.__proto__ === Foo.prototype, // true b.constructor === Foo, // true c.constructor === Foo, // true Foo.prototype.constructor === Foo, // true b.calculate === b.__proto__.calculate, // true b.__proto__.calculate === Foo.prototype.calculate // true ~~~ 这里需要说明的是—`Foo.prototype` 自动创建了一个属性 constructor,这是一个指向函数自己的一个 reference。这样一来,对于实例 b 或 c 来说,就能访问到这个继承的 constructor 了。 >[info]注: 上面示例和图示来源于[(JavaScript, The Core)](http://dmitrysoshnikov.com/ecmascript/javascript-the-core/) 一文。 “原型编程”中面向对象的编程玩法。 ~~~javascript //Person原型/对象 function Person(){} //Student原型/对象 var Student = function(){}; // 1. Student继承Person Student.prototype=Object.create(Person.prototype); //2. 修改构造函数Student.prototype.constructor Student.prototype.constructor=Student; //3. student对象实例 var student = new Student(); ~~~ ![](https://box.kancloud.cn/d47a4aca192ab7cad7cdc669e14afe7f_986x617.JPG) >[info]注:在 ECMAScript 标准的第四版开始寻求使 JavaScript 提供基于类的构造,且 ECMAScript 第六版有提供 "class"(类) 作为原有的原型架构之上的语法糖,提供构建对象与处理继承时的另一种语法。 这种在对象里面直接修改的玩法,虽然这个特性可以带来运行时的灵活性,我们可以在运行时修改一个 prototype,给它增加甚至删除属性和方法。但是其带来了执行的不确定性,也有安全性的问题,而代码还变得不可预测,这有点黑科技的味道了。因为这些不像静态类型系统,没有一个不可变的契约对代码的确定性有保证,所以,需要使用者来自己保证。 ## xx. 函数式编程(Functional Programming)的编程范式 [https://coolshell.cn/articles/17524.html](https://coolshell.cn/articles/17524.html) 我们把以前的过程式的编程范式叫做 Imperative Programming – 指令式编程,而把函数式的这种范式叫做 Declarative Programming – 声明式编程。 阶乘计算举例: ~~~javascript const fact = (n)=>n==0 ? 1 : n * fact(n-1); fact(5);//120 ~~~ > ### Javascript的箭头函数 [【参考文档】](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) ECMAScript2015 引入的箭头表达式。箭头函数其实都是匿名函数,其基本语法如下: ~~~Javascript (param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // 等于 : => { return expression; } // 只有一个参数时,括号才可以不加: (singleParam) => { statements } singleParam => { statements } //如果没有参数,就一定要加括号: () => { statements } ~~~ 下面是一些示例: ~~~Javascript var simple = a => a > 15 ? 15 : a; simple(16); // 15 simple(10); // 10 let max = (a, b) => a > b ? a : b; // Easy array filtering, mapping, ... var arr = [5, 6, 13, 0, 1, 18, 23]; var sum = arr.reduce((a, b) => a + b); // 66 var even = arr.filter(v => v % 2 == 0); // [6, 0, 18] var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46] ~~~ 上面前两个 simple 和 max 的例子都把箭头函数赋值给了一个变量,于是它就有了一个名字。有时候,某些函数在声明的时候就是调用的时候,尤其是函数式编程中,一个函数还对外返回函数的时候。比如下面的例子: ~~~javascript function MakePowerFn(power) { return function(base) { return Math.pow(base, power); } } power3 = MakePowerFn(3); //制造一个X的3次方的函数 power2 = MakePowerFn(2); //制造一个X的2次方的函数 console.log(power3(10)); //10的3次方 = 1000 console.log(power2(10)); //10的2次方 = 100 ~~~ 用箭头函数,可以写成: ~~~javascript MakePowerFn= power =>{ return base =>{ return Math.pow(base, power); } } ~~~ 如果用表达式的话,就不需要 { 和 }, 以及 return 语句,简化: ~~~javascript MakePowerFn= (power)=>( (base) =>(Math.pow(base, power)) ) ~~~ 去掉括号和换行,更进一步简化 ~~~javascript MakePowerFn= power=>base=>Math.pow(base, power) ~~~ >[danger] 自调用匿名函数是否可以用箭头函数改写?? ~~~javascript ;(function($,window,document,undefined){ //我们的代码。。 //blah blah blah... })(jQuery,window,document); ~~~ > ### 匿名函数的递归 函数式编程立志于用函数表达式消除有状态的函数,以及for/while循环,所以,在函数式编程的世界里是不应该用for/while循环的,而要改用递归(递归的性能很差,所以,一般是用尾递归来做优化,也就是把函数的计算的状态当成参数一层一层的往下传递,这样语言的编译器或解释器就不需要用函数栈来帮你保存函数的内部变量的状态了)。 一般来说,递归的代码就是函数自己调用自己,比如我们求阶乘的代码: ~~~javascript function fact(n){ return n==0 ? 1 : n * fact(n-1); } result = fact(5); ~~~ 对于匿名函数来说,我们可以把匿名函数当成一个参数传给另外一个函数,因为函数的参数有名字,所以就可以调用自己了。 把上述fact中自己调用自己的名字去掉,如下所示: ~~~javascript function fact(func, n) { return n==0 ? 1 : n * func(func, n-1); } fact(fact, 5); //输出120 ~~~ 然后,我们再把上面这个版本变成箭头函数的匿名函数版(还要用一个fact来保存这个匿名函数): ~~~javascript var fact = (func, n) => ( n==0 ? 1 : n * func(func, n-1) ) fact(fact, 5) ~~~ 接下来,我们要让匿名函数声明的时候,就自己调用自己。 ~~~javascript ( (func, x) => func(func, x) ) ( //函数体 (func, n) => ( n==0 ? 1 : n * func(func, n-1) ), //第一个调用参数 5 //第二调用参数 ); ~~~ 也就是说,我们要把`(func, n) => ( n==0 ? 1 : n * func(func, n-1) )` 这个函数当成调用参数,传给下面这个函数 `(func, x) => func(func, x)` > ### 动用高阶函数的递归 上面这个递归的匿名函数在自己调用自己,所以,代码中有hard code的实参。我们想实参去掉,如何去掉呢?需要一个函数做参数,然后返回这个函数的递归版本 ~~~javascript HighOrderFact = function(func){ return function(n){ return n==0 ? 1 : n * func(func)(n-1); }; }; ~~~ 调用函数时就为: ~~~javascript fact = HighOrderFact(HighOrderFact); fact(5); ~~~ 连起来写就是: ~~~javascript HighOrderFact ( HighOrderFact ) ( 5 ) ~~~ 但是,这样让用户来调用很不爽,所以,用一个函数把 HighOrderFact ( HighOrderFact ) 给代理一下: ~~~javascript fact = function ( hifunc ) { return hifunc ( hifunc ); } ( //调用参数是一个函数 function (func) { return function(n){ return n==0 ? 1 : n * func(func)(n-1); }; } ); fact(5); //于是我们就可以直接使用了 ~~~ 用箭头函数重构一下, ~~~javascript fact = (highfunc => highfunc ( highfunc ) ) ( func => n => n==0 ? 1 : n * func(func)(n-1) ); ~~~ > ### 查找数组的函数式编程实现 正常的版本 ~~~javascript //正常的版本 function find (x, y) { for ( let i = 0; i < x.length; i++ ) { if ( x[i] == y ) return i; } return null; } let arr = [0,1,2,3,4,5] console.log(find(arr, 2)) console.log(find(arr, 8)) ~~~ 开始变身。 * 先把for干掉,搞成递归版本: ~~~javascript function find (x, y, i=0) { if ( i >= x.length ) return null; if ( x[i] == y ) return i; return find(x, y, i+1); } ~~~ * 然后,写出带实参的匿名函数的版本(注:其中的if代码被重构成了 ?号表达式): ~~~javascript ( (func, x, y, i) => func(func, x, y, i) ) ( //函数体 (func, x, y, i=0) => ( i >= x.length ? null : x[i] == y ? i : func (func, x, y, i+1) ), //第一个调用参数 arr, //第二调用参数 2 //第三调用参数 ) ~~~ * 最后,引入高阶函数,去除实参: ~~~javascript const find = ( highfunc => highfunc( highfunc ) ) ( func => (x, y, i = 0) => ( i >= x.length ? null : x[i] == y ? i : func (func) (x, y, i+1) ) ); ~~~ 注:函数式编程装逼时一定要用const字符,这表示我写的函数里的状态是 immutable 的,天生骄傲! ## 4. 数组、字符串、对象及相互间的转换 ### x.1 数组 ~~~javascript var arr=['nihao.ww',1]; console.log(arr[0]);//输出:nihao.ww ~~~ ### x.2 字符串 ~~~javascript var str='nihao.ww'; console.log(str[0]);//输出:n ~~~ ### x.3 对象 ~~~javascript var arr=['nihao.ww',1]; var str='nihao.ww'; var obj={arr,str}; //上述3行代码可以合为: var obj={arr:['nihao.ww',1],str:'nihao.ww'}; //或 var obj={'arr':['nihao.ww',1],'str':'nihao.ww'}; console.log(obj.arr[0]);//输出:nihao.ww console.log(obj.str[0]);//输出:n ~~~ ### x.4 转换 * 数组转字符串 > toString()函数,无参数 ~~~javascript var arr=[1,'aaa',3]; var str=arr.toString(); var str=arr.toString(''); var str2=arr.toString('/+'); console.log(str);//输出:1,aaa,3 console.log(str1);//输出:1,aaa,3 console.log(str2);//输出:1,aaa,3 ~~~ > join()函数,有参数 ~~~javascript var arr=[1,'aaa',3]; var str=arr.join(); var str1=arr.join(''); var str2=arr.join('/+'); console.log(str);//输出:1,aaa,3 console.log(str1);//输出:1aaa3 console.log(str2);//输出:1/+aaa/+3 console.log(str[1]);//输出:, console.log(str1[1]);//输出:a console.log(str2[1]);//输出:/ ~~~ * 字符串转数组 > split()函数,有参数 ~~~javascript var str='nihao.ww'; var arr=str.split(); var arr1=str.split(''); var arr2=str.split('.'); console.log(arr);//输出:["nihao.ww"] console.log(arr1);//输出:["n", "i", "h", "a", "o", ".", "w", "w"] console.log(arr2);//输出:["nihao","ww"] console.log(arr[0]);//输出:nihao.ww console.log(arr1[0]);//输出:n console.log(arr2[0]);//输出:nihao ~~~ ## xx. 面向对象式编程(Object Orient Programming??)的编程范式 ## xx. 自调用匿名函数(javascript-self-invoking-functions) IIFE:immediately invoked function expressions?? 通常用于jQuery插件的开发?? 结构: ~~~javascript ;(function($,window,document,undefined){ //我们的代码。。 //blah blah blah... })(jQuery,window,document); //箭头函数的写法 (() => 'foobar')(); ~~~ >[info] 可视为由2个'()'组成。 > 第一个'()',包含的是一个匿名函数。作用是定义函数。 > 第二个'()',包含的是要传给匿名函数的参数,作用是将参数传递给定义好的函数并调用该函数。 JavaScript中无法用花括号方便地创建作用域,但函数却可以形成一个作用域,域内的代码是无法被外界访问的。如上所示,用自调用匿名函数包裹代码后的好处有: * 我们将自己的代码放入一个函数中,就不会污染全局命名空间,同时不会和别的代码冲突。 * 自调用匿名函数里面的代码会在第一时间执行,页面准备好后,上面的代码就将插件准备好了,以方便在后面的代码中使用插件。 * As soon as the function has been defined, it will immediately be called or invoked. I really like to use self invoking functions for doing initialization of things or adding event listeners or changing the layout of a page. In jQuery, usually a self invoked function was used instead of setting documment.ready * The primary benefit of self-invoking functions is that they execute only once and won’t fill the global namespace with all sorts of crud that lasts for the lifetime of the page. * It doesn’t seem like adding a few things to the global namespace would be a problm until you start getting into thousands of lines of javascript and having collisions between functions or variables. * Self invoking functions are commonly used for artificially creating namespaces which don’t natively exist in JavaScript. 代码示例: ~~~javascript var $ = "some value we don't care about"; // v=====normal plain old function (function ($) { // ^=======receives jQuery object as the $ parameter //majority of code here, where $ === jQuery... $('.myclass').do().crazy().things(); })(jQuery); // ^=======immediately invoked, and passed the jQuery object // out here, $ is undisturbed alert( $ ); // "some value we don't care about" ~~~ you can call jQuery with the $() notation inside that function, and outside that function if something else like prototype uses $() notation it doesn't matter?.. So really it just saves you from having to write jQuery() (or some other notation) every time when using no conflict. Essentially everything, that is in the majority of code here ... bit, is isolated from the rest of the code. It exists in separate scope.The code outside this structure cannot overwrite anything that is inside. In javascript the $ symbol is just another variable. jQuery uses it because it is a nice shorthand instead of having to type out jQuery each time, but so can any other code (including other libraries). To prevent conflict with other uses of a variable at the same scope (in this case $ at the "global" scope) it is common for code to be wrapped in a self-invoking function with the "conflict-free" variables passed as the function parameters. This then creates a new scope where your variable won't interfere with other uses of that variable. That way you can pass the full named variable & uses it with whatever name you want within the anonymous function. ## Promise模式(异步编程) ### Promise.then [[MDN文档,promise对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)] ### async/await [[MDN文档,async_function](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function)] [[MDN文档,await](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await)] **描述** 当调用一个 `async`函数时,会返回一个`Promise`对象(Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。)。当这个 `async`函数返回一个值时,`Promise`的 resolve 方法会负责传递这个值;当`async`函数抛出异常时,`Promise`的 reject 方法也会传递这个异常值。 `async`函数中可能会有`await`(await操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。)表达式,这会使`async`函数暂停执行,等待 `Promise`  的结果出来,然后恢复`async`函数的执行并返回解析值(resolved)。     注意, `await` 关键字仅仅在 `async` function中有效。如果在 `async function`函数体外使用 `await` ,只会得到一个语法错误。 `async`/`await`的用途是简化使用 promises 异步调用的操作,并对一组 `Promises`执行某些操作。正如`Promises`类似于结构化回调,`async`/`await`类似于组合生成器和 promises。 若希望同时`await`两个或者是更多的`Promise`对象,就必须使用`Promise.then`, ## 对象 在 Javascript 里,对象可以被看作是一组属性的集合。用[对象字面量语法](https://developer.mozilla.org/en/JavaScript/Guide/Values,_variables,_and_literals#Object_literals)来定义一个对象时,会自动初始化一组属性。 对象定义示例1 ~~~js var a = "Hello"; ~~~ a本身就会有a.substring这个方法,以及a.length这个属性,以及其它; 对象定义示例2 ~~~js var a = {}; ~~~ a就会自动有a.hasOwnProperty及a.constructor等属性和方法。 定义对象后,这些属性还可以被增减。属性的值可以是任意类型,包括具有复杂数据结构的对象。属性使用键来标识,它的键值可以是一个字符串或者符号值(Symbol)。 ECMAScript定义的对象中有两种属性:数据属性和访问器属性。 ### 数据属性 数据属性是键值对,并且每个数据属性拥有下列特性: **数据属性的特性(Attributes of a data property)** | 特性 | 数据类型 | 描述 | 默认值 | | --- | --- | --- | --- | | \[\[Value\]\] | 任何Javascript类型 | 包含这个属性的数据值。 | undefined | | \[\[Writable\]\] | Boolean | 如果该值为 `false,`则该属性的 \[\[Value\]\] 特性 不能被改变。 | true | | \[\[Enumerable\]\] | Boolean | 如果该值为 `true,`则该属性可以用 [for...in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) 循环来枚举。 | true | | \[\[Configurable\]\] | Boolean | 如果该值为 `false,`则该属性不能被删除,并且 除了 \[\[Value\]\] 和 \[\[Writable\]\] 以外的特性都不能被改变。 | true | ### 访问器属性 访问器属性有一个或两个访问器函数 (get 和 set) 来存取数值,并且有以下特性: 一个访问器属性的特性 | 特性 | 类型 | 描述 | 默认值 | | --- | --- | --- | --- | | \[\[Get\]\] | 函数对象或者 undefined | 该函数使用一个空的参数列表,能够在有权访问的情况下读取属性值。另见 [get](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/get)。 | undefined | | \[\[Set\]\] | 函数对象或者 undefined | 该函数有一个参数,用来写入属性值,另见 [set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/set)。 | undefined | | \[\[Enumerable\]\] | Boolean | 如果该值为 `true`,则该属性可以用 [for...in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) 循环来枚举。 | true | | \[\[Configurable\]\] | Boolean | 如果该值为 `false`,则该属性不能被删除,并且不能被转变成一个数据属性。 | true | >[warning] 注意:这些特性只有 JavaScript 引擎才用到,因此你不能直接访问它们。所以特性被放在两对方括号中,而不是一对。 ### "标准的" 对象, 和函数 一个 Javascript 对象就是键和值之间的映射。键是一个字符串(或者[`symbol`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol "Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:'new Symbol()'。")),值可以是任意类型的值。 这使得对象非常符合 [哈希表](http://en.wikipedia.org/wiki/Hash_table)。 函数是一个附带可被调用功能的常规对象。 ### 日期 当你想要显示日期时,毋庸置疑,使用内建的 [Date](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date) 对象。 ### 有序集: 数组和类型数组 [数组](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array "Array")是一种使用整数作为键(integer-key-ed)属性和长度(length)属性之间关联的常规对象。此外,数组对象还继承了 Array.prototype 的一些操作数组的便捷方法。例如, [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf "en/JavaScript/Reference/Global_Objects/Array/indexOf") (搜索数组中的一个值) or [`push`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push "en/JavaScript/Reference/Global_Objects/Array/push") (向数组中添加一个元素),等等。 这使得数组是表示列表或集合的最优选择。 [类型数组(Typed Arrays)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays)是ECMAScript Edition 6中新定义的 JavaScript 内建对象,提供了一个基本的二进制数据缓冲区的类数组视图。 ### 结构化数据: JSON JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,来源于 JavaScript 同时也被多种语言所使用。 JSON 用于构建通用的数据结构。参见 [JSON](https://developer.mozilla.org/en-US/docs/Glossary/JSON "JSON: JavaScript Object Notation (JSON) is a data-interchange format.  Although not a strict subset, JSON closely resembles a subset of JavaScript syntax. Though many programming languages support JSON, JSON is especially useful for JavaScript-based apps, including websites and browser extensions.") 以及 [`JSON`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON "JSON对象包含两个方法: 用于解析 JavaScript Object Notation  (JSON) 的 parse() 方法,以及将对象/值转换为 JSON字符串的 stringify() 方法。除了这两个方法, JSON这个对象本身并没有其他作用,也不能被调用或者作为构造函数调用。") 了解更多。 JSON采用完全独立于程序语言的文本格式,但是也使用了类C语言的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。 #### 1 JSON对象(object) 对象:对象在js中表示为“{}”括起来的内容,数据结构为 {key:value,key:value,...}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值。取值方法为 对象.key 获取属性值,这个属性值的类型可以是 数字、字符串、数组、对象几种。 ~~~JavaScript var val={"time":"2015-12-15 13:01:10","protocolVerion":"1.00","sensorId":"1234567890123456","signalValue":[{"D":"18.6","I1":"5","I2":"6","V1":"3.2","V2":"12","S1":"1","S2":"0","S3":"0","S4":"1","G":"112.23323,39.23434"}]} //将JSON对象转化为JSON字符1 var last=obj.toJSONString(); //将JSON对象转化为JSON字符2 var last=JSON.stringify(obj); //输出 alert(last); ~~~ #### 2 JSON字符串(string) 是由单引号包围的任意数量Unicode字符的集合,使用反斜线`\`转义。 ~~~JavaScript var str1 = '{ "name": "cxh", "sex": "man" }'; //由JSON字符串转换为JSON对象1 var obj = eval('(' + str + ')'); //由JSON字符串转换为JSON对象2 var obj = str.parseJSON(); //由JSON字符串转换为JSON对象3 var obj = JSON.parse(str); //读取对象属性 Alert(obj.name); Alert(obj.sex); ~~~ >[warning] 如果obj本来就是一个JSON对象,那么使用eval()函数转换后(哪怕是多次转换)还是JSON对象,但是使用parseJSON()函数处理后会有问题(抛出语法异常)。 ### 标准库中更多的对象 JavaScript 有一个内置对象的标准库。请查看[参考](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)来了解更多对象。 ## 参考文档 1. [《ECMAScript 6 入门》](http://es6.ruanyifeng.com/#README) 2. [[MDN文档,JavaScript索引](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Index)] 3. [[MDN,Javascript]](https://developer.mozilla.org/en-US/docs/Web/JavaScript) ## XMLHttpRequest [[Living Standard](https://xhr.spec.whatwg.org)] The XMLHttpRequest Standard defines an API that provides scripted client functionality for transferring data between a client and a server. ## 待整理 所有函数的最外层被称为全局作用域。 在全局作用域内定义的值可以在任意地方访问。