├── html-senior ├── index1.css ├── index1.html └── exercises.md ├── ts-senior ├── index.html └── exercises.md ├── es5-senior ├── article │ ├── index.md │ ├── javascript-object.md │ ├── js-inheritance.md │ ├── img │ │ ├── object.png │ │ ├── stage1.png │ │ ├── stage2.png │ │ ├── stage3.png │ │ ├── stage4.png │ │ ├── stage5.png │ │ ├── stage6.jpg │ │ ├── stage7.jpg │ │ ├── stage8.jpg │ │ ├── basic-data.png │ │ ├── callstack.jpg │ │ ├── callstack.png │ │ ├── callstack1.bmp │ │ ├── 201914223356.png │ │ ├── prototype-img.png │ │ ├── 20181217224906.png │ │ ├── 20181217225601.png │ │ ├── 20181217230419.png │ │ ├── 20181217233505.png │ │ ├── 20181217234302.png │ │ ├── 20181218212309.png │ │ ├── 20181218212401.png │ │ ├── 20190101163046.png │ │ ├── 20190101172107.png │ │ ├── 20190101172135.png │ │ ├── 20190101173728.png │ │ ├── 20190101173808.png │ │ ├── 20190101233923.png │ │ ├── 20190101234057.png │ │ ├── 20190101234151.png │ │ ├── object-function1.jpg │ │ ├── prototype-console.png │ │ └── JavaScript-object.svg │ ├── excellent-articles-collection.md │ ├── javascript-oop.md │ ├── async-and-sync.md │ ├── js-variable-type.md │ ├── call-stack.md │ ├── javascript-iife.md │ ├── this.md │ └── prototype-chain.md ├── index.html ├── index3.html ├── index2.html ├── demo1.js ├── index.js ├── essence1.md ├── essence2.md └── exercises.md ├── algorithm ├── examination │ ├── test1.js │ └── test.md └── index.html ├── computer-network └── readme.md ├── ide ├── github-AQ.md ├── npm-console.md ├── mysql.md └── idea.md ├── css-senior ├── article │ ├── box-model.md │ ├── selector.md │ ├── img │ │ ├── timg-xyz.jpg │ │ ├── timg-alpha.jpg │ │ ├── timg-beta.jpg │ │ └── timg-gamma.jpg │ ├── css-mathematics.md │ ├── css-animation.md │ └── css3d.md └── overview.md ├── es6-senior ├── img │ ├── 20180608150600435.png │ ├── 20180608150616891.png │ ├── 20180608150633718.png │ ├── 20180608150651453.png │ ├── 20180608150706213.png │ ├── 20180608150723503.png │ ├── 20180608150750678.png │ └── es6-generalization.png ├── exercises.md ├── index.html └── index.js ├── functional-programming ├── article │ ├── img │ │ ├── fam.png │ │ ├── bind.png │ │ ├── fmap.png │ │ ├── half.png │ │ ├── just2.png │ │ ├── maybe.png │ │ ├── recap.png │ │ ├── justadd3.png │ │ ├── normal2.png │ │ ├── RACStream.png │ │ ├── fmap_just.png │ │ ├── monad_chain.png │ │ ├── pointfree1.png │ │ ├── pointfree2.png │ │ ├── pointfree3.png │ │ ├── pointfree4.png │ │ ├── pointfree5.png │ │ ├── value_apply.png │ │ ├── fmap_nothing.png │ │ ├── applicative_just.png │ │ └── apply_justadd3_just2.png │ ├── 2019-1-17.md │ ├── 2019-1-18.1.md │ ├── 2019-1-21.md │ ├── demo.js │ ├── 2019-1-19.1.md │ ├── 2019-1-13.md │ ├── 2019-1-22.md │ ├── 2019-1-18.md │ ├── 2019-1-19.md │ └── 2019-1-20.md ├── js │ ├── demo.2.js │ ├── index.1.html │ ├── exercises.js │ └── demo.1.js └── readme.md ├── es5.1-new-feature ├── exercises.md ├── article │ ├── asi-translation.md │ └── ASI.md └── javascript-qa.md ├── javascript-essence └── chapter-1.js ├── php └── overview.md ├── fe-qa ├── FrontEnd-QA.md └── article │ └── 2019-1-8.md ├── README.md └── question-bank └── JavaScript └── 2019-01-02.md /html-senior/index1.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-senior/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /es5-senior/article/index.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /algorithm/examination/test1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /computer-network/readme.md: -------------------------------------------------------------------------------- 1 | # 计算机网络原理 2 | -------------------------------------------------------------------------------- /ide/github-AQ.md: -------------------------------------------------------------------------------- 1 | # github 常见问题答疑 2 | 3 | 最近遇到一件事情,心态崩了。 -------------------------------------------------------------------------------- /css-senior/article/box-model.md: -------------------------------------------------------------------------------- 1 | # CSS盒模型 2 | 3 | ![alt text](./img/css-box-model.svg "Title") 4 | -------------------------------------------------------------------------------- /css-senior/article/selector.md: -------------------------------------------------------------------------------- 1 | # CSS选择器 2 | 3 | ![alt text](./img/css-selector.svg "Title") 4 | -------------------------------------------------------------------------------- /es5-senior/article/javascript-object.md: -------------------------------------------------------------------------------- 1 | # JavaScript面向对象编程之对象 2 | 3 | ![alt text](./img/JavaScript-object.svg "Title") -------------------------------------------------------------------------------- /es5-senior/article/js-inheritance.md: -------------------------------------------------------------------------------- 1 | # JavaScript继承方式详解 2 | 3 | ## 继承本意 4 | 5 | 初识面向对象编程范式继承特性的同学,可能会感叹一句为什么要有继承这种东西啊。 -------------------------------------------------------------------------------- /ide/npm-console.md: -------------------------------------------------------------------------------- 1 | 2 | npm list -g --depth 0 3 | 4 | npm install -g typings 5 | 6 | npm uninstall -g 利用npm 7 | -------------------------------------------------------------------------------- /css-senior/article/img/timg-xyz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/css-senior/article/img/timg-xyz.jpg -------------------------------------------------------------------------------- /es5-senior/article/img/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/object.png -------------------------------------------------------------------------------- /es5-senior/article/img/stage1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage1.png -------------------------------------------------------------------------------- /es5-senior/article/img/stage2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage2.png -------------------------------------------------------------------------------- /es5-senior/article/img/stage3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage3.png -------------------------------------------------------------------------------- /es5-senior/article/img/stage4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage4.png -------------------------------------------------------------------------------- /es5-senior/article/img/stage5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage5.png -------------------------------------------------------------------------------- /es5-senior/article/img/stage6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage6.jpg -------------------------------------------------------------------------------- /es5-senior/article/img/stage7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage7.jpg -------------------------------------------------------------------------------- /es5-senior/article/img/stage8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/stage8.jpg -------------------------------------------------------------------------------- /css-senior/article/img/timg-alpha.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/css-senior/article/img/timg-alpha.jpg -------------------------------------------------------------------------------- /css-senior/article/img/timg-beta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/css-senior/article/img/timg-beta.jpg -------------------------------------------------------------------------------- /css-senior/article/img/timg-gamma.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/css-senior/article/img/timg-gamma.jpg -------------------------------------------------------------------------------- /es5-senior/article/img/basic-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/basic-data.png -------------------------------------------------------------------------------- /es5-senior/article/img/callstack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/callstack.jpg -------------------------------------------------------------------------------- /es5-senior/article/img/callstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/callstack.png -------------------------------------------------------------------------------- /es5-senior/article/img/callstack1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/callstack1.bmp -------------------------------------------------------------------------------- /es6-senior/img/20180608150600435.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150600435.png -------------------------------------------------------------------------------- /es6-senior/img/20180608150616891.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150616891.png -------------------------------------------------------------------------------- /es6-senior/img/20180608150633718.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150633718.png -------------------------------------------------------------------------------- /es6-senior/img/20180608150651453.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150651453.png -------------------------------------------------------------------------------- /es6-senior/img/20180608150706213.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150706213.png -------------------------------------------------------------------------------- /es6-senior/img/20180608150723503.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150723503.png -------------------------------------------------------------------------------- /es6-senior/img/20180608150750678.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/20180608150750678.png -------------------------------------------------------------------------------- /es6-senior/img/es6-generalization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es6-senior/img/es6-generalization.png -------------------------------------------------------------------------------- /es5-senior/article/img/201914223356.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/201914223356.png -------------------------------------------------------------------------------- /es5-senior/article/img/prototype-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/prototype-img.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181217224906.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181217224906.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181217225601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181217225601.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181217230419.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181217230419.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181217233505.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181217233505.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181217234302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181217234302.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181218212309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181218212309.png -------------------------------------------------------------------------------- /es5-senior/article/img/20181218212401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20181218212401.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101163046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101163046.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101172107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101172107.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101172135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101172135.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101173728.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101173728.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101173808.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101173808.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101233923.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101233923.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101234057.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101234057.png -------------------------------------------------------------------------------- /es5-senior/article/img/20190101234151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/20190101234151.png -------------------------------------------------------------------------------- /functional-programming/article/img/fam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/fam.png -------------------------------------------------------------------------------- /es5-senior/article/img/object-function1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/object-function1.jpg -------------------------------------------------------------------------------- /es5-senior/article/img/prototype-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/es5-senior/article/img/prototype-console.png -------------------------------------------------------------------------------- /functional-programming/article/img/bind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/bind.png -------------------------------------------------------------------------------- /functional-programming/article/img/fmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/fmap.png -------------------------------------------------------------------------------- /functional-programming/article/img/half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/half.png -------------------------------------------------------------------------------- /functional-programming/article/img/just2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/just2.png -------------------------------------------------------------------------------- /functional-programming/article/img/maybe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/maybe.png -------------------------------------------------------------------------------- /functional-programming/article/img/recap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/recap.png -------------------------------------------------------------------------------- /functional-programming/article/img/justadd3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/justadd3.png -------------------------------------------------------------------------------- /functional-programming/article/img/normal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/normal2.png -------------------------------------------------------------------------------- /functional-programming/article/2019-1-17.md: -------------------------------------------------------------------------------- 1 | # JavaScript函数式编程指南 2 | 3 | 很多函数式编程的文章教授的都是函数式编程技巧。诸如像函数组合、管道、高阶函数等等。本文另辟蹊径,给大家展示的如何将每天编写的命令式、非功能性的代码,转换成功能性代码。 -------------------------------------------------------------------------------- /functional-programming/article/img/RACStream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/RACStream.png -------------------------------------------------------------------------------- /functional-programming/article/img/fmap_just.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/fmap_just.png -------------------------------------------------------------------------------- /functional-programming/article/img/monad_chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/monad_chain.png -------------------------------------------------------------------------------- /functional-programming/article/img/pointfree1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/pointfree1.png -------------------------------------------------------------------------------- /functional-programming/article/img/pointfree2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/pointfree2.png -------------------------------------------------------------------------------- /functional-programming/article/img/pointfree3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/pointfree3.png -------------------------------------------------------------------------------- /functional-programming/article/img/pointfree4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/pointfree4.png -------------------------------------------------------------------------------- /functional-programming/article/img/pointfree5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/pointfree5.png -------------------------------------------------------------------------------- /functional-programming/article/img/value_apply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/value_apply.png -------------------------------------------------------------------------------- /css-senior/article/css-mathematics.md: -------------------------------------------------------------------------------- 1 | # CSS数学基础,从入门到放弃 2 | 3 | 不仅是CSS中,其实对于软件开发工程师来说,数学是离不开的工具, 4 | 是设计软件的灵魂。鉴于CSS所需要的CSS数学知识并不庞大,我们把 5 | 目光主要集中在三角函数和线性代数两方面。 6 | -------------------------------------------------------------------------------- /functional-programming/article/img/fmap_nothing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/fmap_nothing.png -------------------------------------------------------------------------------- /functional-programming/article/img/applicative_just.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/applicative_just.png -------------------------------------------------------------------------------- /es6-senior/exercises.md: -------------------------------------------------------------------------------- 1 | # ES6课程笔记 2 | 3 | **这是ES6核心内容的思维导图** 4 | ![这是ES6核心内容的思维导图](./img/es6-generalization.png "Title") 5 | 6 | ## ES6在企业中的应用 7 | 8 | author @颜海镜 9 | -------------------------------------------------------------------------------- /functional-programming/article/img/apply_justadd3_just2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinshao/yideng-note/HEAD/functional-programming/article/img/apply_justadd3_just2.png -------------------------------------------------------------------------------- /ide/mysql.md: -------------------------------------------------------------------------------- 1 | MySQL--启动和关闭MySQL服务 2 | 1.Windows下 3 | 4 | 启动服务 5 | mysqld --console 6 | 或  net start mysql 7 | 关闭服务 8 | mysqladmin -uroot shudown 9 | 或 net stop mysql 10 | 11 | 2.Linux下 12 | 13 | 启动服务 14 | service mysql start 15 | 关闭服务 16 | service mysql stop 17 | 重启服务 18 | service restart stop -------------------------------------------------------------------------------- /es5.1-new-feature/exercises.md: -------------------------------------------------------------------------------- 1 | ## ESMAScript新增语法 2 | 3 | > 本章内容分为上下两部分,每小节内容总结如下 4 | > 1. ECMAScript5.1简介 5 | > 2. 浏览器支持 6 | > 3. 严格模式 7 | > 4. JSON格式 8 | > 5. 添加对象 9 | > 6. 额外的数组 10 | > 7. Function.prototype.bind 11 | > 8. JavaScript this的使用 12 | > 9. JavaScript作用域和闭包 13 | > 10. 按值传递和按引用传递 14 | -------------------------------------------------------------------------------- /html-senior/index1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /es5-senior/article/excellent-articles-collection.md: -------------------------------------------------------------------------------- 1 | ## JavaScript语言相关优秀文章集锦 2 | 3 | > 有关于网络上太多关于JavaScript的优秀博客,作为自学的一种方式,因此创建该文档。 4 | 5 | 作者:木易杨说/网易高级前端工程 6 | 有关于JavaScript面试重点难点多了详细的梳理,掘金和github上均有连载 7 | 8 | 掘金 9 | https://juejin.im/post/5bf3d20ff265da61776b95da 10 | 11 | github https://github.com/yygmind/blog 12 | 13 | 好书推荐 14 | JavaScript设计模式与开发实践 -------------------------------------------------------------------------------- /es6-senior/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /css-senior/article/css-animation.md: -------------------------------------------------------------------------------- 1 | # CSS已经很酷炫的动效! 2 | 3 | 惯例的高清导图 4 | ![alt text](./img/CSSDynamic.svg "CSS 动效") 5 | 看不清的同学,可以鼠标右击,点击<在新标签页中打开图片>,就可以得到高清大图。 6 | 7 | ## 简介 8 | 9 | CSS3已经问世不短,随着广大开发人员的热衷挖掘,已经把CSS3的动效发挥到极致,也产生了很多惊艳的作品,接下里我们就简单的学习一些CSS动效的相关知识。 10 | 11 | ## 如何创建动画 12 | 13 | 通过@keyframes规则创建动画,@keyframes规则内指定一个CSS样式和动画逐步从目前的样式更改新的样式 14 | 15 | 将@keyframes绑定到选择器,规定动画的名称和动画的时长 16 | 17 | -------------------------------------------------------------------------------- /javascript-essence/chapter-1.js: -------------------------------------------------------------------------------- 1 | function fun(n, o) { 2 | console.log(o); 3 | return { 4 | fun: function (m) { 5 | return fun(m, n) 6 | } 7 | } 8 | } 9 | 10 | var a = fun(0); 11 | a.fun(1); 12 | a.fun(2); 13 | var b = fun(0).fun(1).fun(2).fun(3); 14 | var c = fun(0).fun(1); 15 | c.fun(2); c.fun(3); 16 | 17 | if (!("userName in window")) { 18 | var userName = "shaogucheng" 19 | } 20 | console.info(userName); -------------------------------------------------------------------------------- /ts-senior/exercises.md: -------------------------------------------------------------------------------- 1 | # typescript的前世今生 2 | 3 | ## node.js 4 | 5 | 有模有样的后端语言 6 | 又是一个新玩具 7 | 大量的闭包、回调、内存浪费、全站崩溃 8 | 面向过程的观点无法改变 9 | 对于继承、或者接口等一听就迷糊 10 | 11 | 名门正派 12 | 13 | 正规语言的心经 14 | 15 | TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发。2012年十月份,微软发布了首个公开版本的TypeScript,2013年6月19日,在经历了一个预览版之后微软正式发布了正式版TypeScript 0.9,向未来的TypeScript 1.0版迈进了很大一步。 16 | 17 | 1. 强类型的编程语言 显式声明字符串 18 | 2. 常量、变量、作用域、this、可空类型、真实数组、结构、枚举 19 | 3. 面向对象、类、继承、多态、接口、命名空间、变量的修饰、构造函数、访问器(Get、Set)、静态属性 20 | 4. 委托、泛型、反射、集合(动态数组(ArrayList/Hashtable/SortedList/Stack/Queue))、匿名方法、拆箱 21 | -------------------------------------------------------------------------------- /es6-senior/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | // var a = "shaogucheng"; 6 | // var b = `This is cool boy ${a}`; 7 | 8 | // console.info(b); 9 | 10 | // function add([x, y]) { 11 | // return x + y; 12 | // } 13 | 14 | // console.info(add([10, 2, 3, 4])); 15 | 16 | let a = 1; 17 | let b = 2; 18 | let c = 3; 19 | 20 | // let [a, b, c] = [1, 2, 3]; 21 | // Uncaught SyntaxError: 22 | // Identifier 'a' has already been declared 23 | 24 | let [foo, [[bar], baz]] = [1, [[2], 3]]; 25 | console.log(foo) 26 | console.log(bar) 27 | console.log(baz) 28 | 29 | let [head, ...tail] = [1, 2, 3, 4]; 30 | console.log(head); 31 | console.log(tail); 32 | 33 | console.log(...[1, 2, 3]); -------------------------------------------------------------------------------- /es5-senior/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 20 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /es5-senior/index3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

产品一

9 |

产品二

10 |

产品三

11 |

产品四

12 |

产品五

13 | 14 | 31 | 32 | tongbu 33 | -------------------------------------------------------------------------------- /functional-programming/js/demo.2.js: -------------------------------------------------------------------------------- 1 | class Wrapper { 2 | constructor(value) { 3 | this._value = value; 4 | } 5 | 6 | map(f) { 7 | return f(this._value); 8 | }; 9 | 10 | toString() { 11 | // return `Wrapper ( ${this.value} )`; 12 | return 'Wrapper (' + this._value + ')'; 13 | } 14 | } 15 | 16 | const wrap = (val) => new Wrapper(val); 17 | 18 | const wrappedValue = wrap('Get Functional'); 19 | wrappedValue.map(R.identity); 20 | // console.info(wrappedValue); 21 | console.info(wrappedValue.map(R.identity)); 22 | // wrappedValue.toString(); 23 | 24 | wrappedValue.map(console.log); 25 | wrappedValue.map(R.toUpper); 26 | wrappedValue.toString(); 27 | console.info(wrappedValue.map(R.toUpper)); 28 | console.info(wrappedValue.toString()); -------------------------------------------------------------------------------- /es5.1-new-feature/article/asi-translation.md: -------------------------------------------------------------------------------- 1 | # 自动分号插入 (Automatic Semicolon Insertion 简称 ASI) 2 | 3 | 某些ECMAScript语句(空语句、变量语句、表达式语句、do-while语句、continue语句、break语句、return语句和throw 语句)必须以分号结束。这样的分号可能总是显式地出现在源文本中。然而,为了方便起见,在某些情况下,可以从源文本中省略这些分号。描述这些情况的方法是,在这些情况下,分号自动插入源代码令牌流。 4 | 5 | ## 1. 分号自动插入规则 6 | 7 | 分号插入有三条基本规则: 8 | 9 | 1. 当从左到右解析程序时,遇到语法的任何生成都不允许的令牌(称为冒犯令牌),如果下列条件中的一个或多个为真,则在冒犯令牌之前自动插入分号: 10 | * 违规令牌通过至少一个LineTerminator与前一令牌分离。 11 | * 令人不快的令牌是`}`。 12 | 2. 当从左到右解析程序时,遇到令牌输入流的末尾,并且解析器无法将输入令牌流解析为单个完整的ECMAScript Program时,则在输入流的末尾自动插入分号。 13 | 3. 当从左到右解析程序时,会遇到语法的某些生成所允许的令牌,但是该生成是受限制的产物,并且令牌将是紧跟在受限制的产物内的注释“[这里没有LineTerminator]”之后的终端或非终端的第一个令牌(以及重构这样的令牌称为受限令牌,并且受限令牌被至少一个LineTerminator与前一个令牌分离,然后在受限令牌之前自动插入分号。 14 | 15 | 然而,前面的规则还有一个附加的重写条件:如果分号随后被解析为空语句,或者该分号将成为for语句头部中的两个分号之一,则分号永远不会自动插入(参见12.6.3)。 16 | -------------------------------------------------------------------------------- /php/overview.md: -------------------------------------------------------------------------------- 1 | # PHP基础与实战 2 | 3 | PHP基于yii框架的图书管理系统的设计与开发 4 | 5 | 1、准备工作:学习PHP基础知识,能够用PHP写一些demo代码 6 | 2、大致了解yii框架,安装和运行yii框架模板。 7 | 3、熟悉MySQL的知识,能够根据需求设计出表模型,完成建表和模拟数据插入 8 | 4、熟悉yii框架的MVC开发模式,通过增删查改的开发熟悉。 9 | 10 | ## yii的MVC 11 | 12 | yii开发比较重要的几个文档: 13 | 1. [Yii 2.0 权威指南](https://www.yiichina.com/doc/guide/2.0) 14 | 2. [Yii 2.0 类参考手册(Yii Framework 2.0 API文档)](https://www.yiichina.com/doc/api/2.0) 15 | 16 | yii是一个基于MVC框架的PHP框架,在实际的开发过程中,除了数据库的设计和操作之外, 17 | 主要就是MVC三层代码的编写。这其中我认为比较重要的几点: 18 | 1. 对于yii提供的现成的面向对象接口的理解 19 | 2. yii框架对于数据库操作的封装 20 | 21 | ## yii接口 22 | 23 | 1. [活动记录(Active Record)][1] 24 | 2. [控制器(Controller)][2] 25 | 3. [模板][3] 26 | 27 | [1]:https://www.yiichina.com/doc/guide/2.0/db-active-record 28 | [2]:https://www.yiichina.com/doc/guide/2.0/structure-controllers 29 | [3]:https://www.yiichina.com/doc/guide/2.0/structure-views -------------------------------------------------------------------------------- /functional-programming/js/index.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 27 | 28 | -------------------------------------------------------------------------------- /functional-programming/article/2019-1-18.1.md: -------------------------------------------------------------------------------- 1 | Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate. Instead the definitions merely compose other functions, among which are combinators that manipulate the arguments. Tacit programming is of theoretical interest, because the strict use of composition results in programs that are well adapted for equational reasoning.[1] It is also the natural style of certain programming languages, including APL and its derivatives,[2] and concatenative languages such as Forth. The lack of argument naming gives point-free style a reputation of being unnecessarily obscure, hence the epithet "pointless style".[1] 2 | 3 | UNIX scripting uses the paradigm with pipes. 4 | 5 | For example, a sequence of operations in an applicative language such as the following: -------------------------------------------------------------------------------- /css-senior/overview.md: -------------------------------------------------------------------------------- 1 | # CSS大纲 2 | 3 | 鉴于以往学习过程中对于CSS的知识没有过多重视,导致后面的工作中,遇到非常多的麻烦,特下定决定,对于CSS来一次系统性的学习。 4 | 5 | 下面思维导图CSS知识的大纲思维导图,参照大纲,我们能大概知道CSS的知识体系中包含了哪些知识。 6 | 7 | 看不清的同学,可以鼠标右击,点击<在新标签页中打开图片>,就可以得到高清大图。 8 | ![alt text](./article/img/CSSoverview.svg "Title") 9 | 10 | 每一部分的知识动作了充分的解析,点击链接即可阅读。 11 | 12 | * [CSS选择器,基础和足够强大的存在!][1] 13 | * [CSS不得不深入掌握的盒模型][2] 14 | * [CSS3构建3D吸睛世界!][3] 15 | * [CSS已经很酷炫的动效!][4] 16 | * [CSS数学基础,从入门到放弃][5] 17 | 18 | * CSS响应式布局设计与实践 19 | 20 | [1]: https://github.com/Martin-Shao/yideng-note/blob/master/css-senior/article/selector.md 21 | [2]: https://github.com/Martin-Shao/yideng-note/blob/master/css-senior/article/box-model.md 22 | [3]: https://github.com/Martin-Shao/yideng-note/blob/master/css-senior/article/css3d.md 23 | [4]: https://github.com/Martin-Shao/yideng-note/blob/master/css-senior/article/css-animation.md 24 | [5]: https://github.com/Martin-Shao/yideng-note/blob/master/css-senior/article/css-mathematics.md -------------------------------------------------------------------------------- /functional-programming/js/exercises.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 柯里化改造前的加法运算函数 3 | * O -> Original (原始) 4 | */ 5 | function addO(x, y) { 6 | return x + y; 7 | } 8 | 9 | /** 10 | * 柯里化改造,ES5写法 11 | * F -> ES Five (原始) 12 | */ 13 | function addF(x) { 14 | return function (y) { 15 | return x + y; 16 | } 17 | } 18 | console.info(add(1)(2)); 19 | 20 | /** 21 | * 柯里化改造,ES6写法 22 | * S -> ES Six (原始) 23 | */ 24 | var addS = x => (y => x + y); 25 | console.info(addS(4)(5)); 26 | 27 | 28 | (new Functor(2)).map(function (two) { 29 | return two + 2; 30 | }); 31 | // Functor(4) 32 | 33 | (new Functor('flamethrowers')).map(function (s) { 34 | return s.toUpperCase(); 35 | }); 36 | // Functor('FLAMETHROWERS') 37 | 38 | (new Functor('bombs')).map(_.concat(' away')).map(_.prop('length')); 39 | // Functor(10) 40 | 41 | class Functor { 42 | constructor(val) { 43 | this.val = val; 44 | } 45 | 46 | map(f) { 47 | return new Functor(f(this.val)); 48 | } 49 | } 50 | 51 | Functor.of = function(val) { 52 | return new Functor(val); 53 | }; 54 | 55 | Functor.of(2).map(function (two) { 56 | return two + 2; 57 | }); 58 | // Functor(4) -------------------------------------------------------------------------------- /es5-senior/article/javascript-oop.md: -------------------------------------------------------------------------------- 1 | ## 面向对象编程范式简介 2 | 3 | JavaScript其实是一门支持多范式编程的语言(支持函数式编程和面相对象编程),但其实,JavaScript在面向对象编程方面,并没有像Java、C++语言那样规范严谨,一方面是由于语言设计之初的仓促,另一方面也跟JavaScript前期发展、不受重视有关系。 4 | 5 | 在学习JavaScript面向对象编程时就不得不简单介绍下面向对象编程范式了。 6 | 7 | ### 面向对象编程由来 8 | 9 | 在面向对象编程范式没有出现,面向过程编程大行其道,它是一种以过程为中心的编程思想。就是分析出解决问题所需要的步骤,然后采用分支循环用函数把这些步骤一步一步实现,使用的时候一个一个一次调用就可以了。面向过程编程虽然也可以解决问题,但是也存在着重用性差、可维护性差、开发过程复杂等缺点。 10 | 11 | 尤其是随着时间的发展,软件开发中出现的问题越加突出: 12 | * 软件复杂庞大 13 | * 需求的不断变更 14 | * 很多软件维护阶段困难重重 15 | 16 | 为解决以上问题,面向对象编程应运而生。面向对象编程具有很好的可读性、可维护性和可扩展性,并且保证了代码的高内聚低耦合。具备以上优点的面向对象编程具备四大特性: 17 | * 抽象 18 | * 封装 19 | * 继承 20 | * 多态 21 | 22 | 并且延伸出七大设计原则和24种设计模式。 23 | 24 | ### 面向对象编程四大基本特性简介: 25 | 26 | **抽象**:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对类进行实例化得到对象。 27 | 28 | **封装**:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等) 29 | 30 | **继承**:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。 31 | 32 | **多态**:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。 33 | 34 | 抽象、封装、继承和多态是面向对象的基础。在面向对象四大基础特性之上,我们在做面向对象编程设计时还需要遵循有一些基本的设计原则。 35 | 36 | **七大设计原则**: 37 | SOLID原则(单一职责原则、开放关闭原则、里氏替换原则、接口隔离原则和依赖倒置原则) 38 | 迪米特法则 39 | 组合优于继承原则(合成复用原则)。 40 | 41 | 在遵循这些面向对象设计原则基础上,前辈们总结出一些解决不同问题场景的设计模式,以四人帮的gof23最为知名。 42 | 24种设计模式(gof23+1) 43 | 44 | ## JavaScript之对象 45 | -------------------------------------------------------------------------------- /es5-senior/index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

产品一

9 |

产品二

10 |

产品三

11 |

产品四

12 |

产品五

13 | 14 | 52 | 53 | -------------------------------------------------------------------------------- /es5.1-new-feature/javascript-qa.md: -------------------------------------------------------------------------------- 1 | # JavaScript与QA工程师 2 | 3 | ## 测试核心概念 4 | 5 | 1. 单元测试 6 | 2. 性能测试 7 | 3. 安全测试 8 | 4. 功能测试 9 | 10 | ## 单元测试 11 | 12 | * 正确性:测试可以验证代码的正确性,在上线前做到心里有底 13 | * 自动化:当然手工也可以测试,通过console可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行 14 | * 解释性:测试用例用于测试接口、模块的重要性,那么在测试用例中就会涉及如何使用这些API。其他开发人员如果使用这些API,那阅读测试用例是一种很好地途径,有时比文档说明更清晰 15 | * 驱动开发,指导设计:代码被测试的前提是代码本身的可测试性,那么要保证代码的可测试性,就需要在开发中注意API的设计,TDD将测试前移就是起到这么一个作用 16 | * 保证重构:互联网行业产品迭代速度很快,迭代后必然存在代码重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆的进行重构 17 | 18 | * 目的:单元测试能够让开发者明确知道代码结果 19 | * 原则:单一职责、接口抽象、层次分离 20 | * 断言库:保证最小单元是否正常运行检测方法 21 | * 测试风格:测试驱动开发(Test-Driven Development,TDD)、(Behavior Driven Development,BDD)行为驱动开发均是敏捷开发方法论。 22 | 23 | TDD关注所有的功能是否被实现(每一个功能都必须有对应的测试用例),suite配合test利用assert('tobi' == user.name); 24 | BDD关注整体行为是否符合整体预期,编写的每一行代码都有目的提供一个全面的测试用例集。expect/should,describe配合it利用自然语言expect(1).toEqual(fn())执行结果。 25 | 26 | ## 单元测试框架 27 | 28 | ## 单元测试运行流程 29 | 30 | ## 自动化单元测试 31 | 32 | ## 报告和单测覆盖率检查 33 | 34 | ## 性能测试 35 | 36 | ### 基准测试 37 | 38 | * 面向切面编程AOP无侵入式统计 39 | * Benchmark基准测试方法,它并不是简单地统计执行多少次测试代码后对比时间,它对测试有着严格的抽样过程。执行多少次取决于采样到数据能否完成统计。根据统计次数计算方差。 40 | 41 | ### 压力测试 42 | 43 | * 对网络接口做压力测试需要检查的几个常用指标有吞吐率、响应时间和并发数,这些指标反映了服务器并发处理能力。 44 | * PV网站当日访问人数UV独立访问人数。PV每天几十万甚至上百万就需要考虑压力测试。换算公式QPS=PV/tps:1000000/10*60*60=27.7 45 | 46 | ## 安全测试 47 | 48 | ### 安全漏洞检查 49 | 50 | * XSS 51 | * SQL 52 | * CSRF 53 | 54 | 功能测试 55 | 56 | 用户真实性检查 57 | 58 | JsLint&JsHint 59 | -------------------------------------------------------------------------------- /functional-programming/js/demo.1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Functional Programming in JavaScript 3 | * Chapter 01 4 | * Magical -run- function in support of listing 1.1 5 | * 6 | */ 7 | 8 | // -run- with two functions 9 | var run2 = function (f, g) { 10 | return function (x) { 11 | return f(g(x)); 12 | }; 13 | }; 14 | 15 | // -run- with three functions 16 | var run3 = function (f, g, h) { 17 | return function (x) { 18 | return f(g(h(x))); 19 | }; 20 | }; 21 | 22 | // Test this magical function 23 | var add1 = function (x) { return x + 1 }; 24 | var mult2 = function (x) { return x * 2 }; 25 | var square = function (x) { return x * x }; 26 | var negate = function (x) { return -x }; 27 | 28 | var double = run2(add1, add1); 29 | console.log(double(2)); 30 | 31 | var testRun = run3(negate, square, mult2); 32 | console.info(testRun(2)); 33 | 34 | var run = (...functions) => x => { 35 | functions.reverse().forEach(func => x = func(x)); 36 | return x; 37 | }; 38 | 39 | // 第一种实现 40 | const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args))); 41 | // 第二种实现 42 | const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x); 43 | 44 | const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x); 45 | var echoDocument = document.writeln.bind(document); 46 | var h1 = function (text) { 47 | return "

" + text + "

"; 48 | } 49 | var printMessage = compose(echoDocument, h1); 50 | printMessage("Hello World"); -------------------------------------------------------------------------------- /functional-programming/article/2019-1-21.md: -------------------------------------------------------------------------------- 1 | 2 | # FP编程基础之——纯函数(Pure Functions) 3 | 4 | 当我们的程序变得庞大的时候, 将不可避免地引发一些bugs。我们不能保证杜绝bug产生, 但是我们可以通过某些编程方式来减少一些错误的发生。 5 | 6 | 纯函数就是其中一种,它也是函数式编程中一部分。那它为什么可以起到减少bug的作用呢, 原因就在于能被称之为纯函数而制定的一些原则,我们来简单看下 7 | 8 | 3个原则: 9 | * 变量都只在函数作用域内获取, 作为的函数的参数传入 10 | * 不会产生副作用(side effects), 不会改变被传入的数据或者其他数据 11 | * 相同的输入保证相同的输出(same input -> same ouput) 12 | 13 | ## 关于same input -> same ouput 14 | 15 | 比如以下这个pureAdd函数就是纯函数, x和y都是函数参数,处在函数作用域内 16 | 17 | ```js 18 | function pureAdd(x, y) { 19 | return x + y; 20 | } 21 | ``` 22 | 23 | 但是一下就是这个impureAdd就是不是 24 | 25 | ```js 26 | let x = 1; 27 | function impureAdd(y) { 28 | return x + y; 29 | } 30 | ``` 31 | 32 | 因为函数内需要的x需要从函数外部的去获取, 这样的也就导致了函数的相同的输入不能保证有相同的输出, 如下 33 | 34 | ```js 35 | let x = 1; 36 | console.log(impureAdd(3)) // 4 37 | let x = 2; 38 | console.log(impureAdd(3)) // 5 39 | ``` 40 | 41 | 还有纯函数不得改变传入的值, 比较容易出错的就是引用类型作为参数传入 42 | 43 | ```js 44 | function mutateObject(obj) { 45 | obj['newkey'] = 'newValue'; 46 | } 47 | var o = {}; 48 | mutateObject(o); 49 | console.log(o) //{newkey: "newValue"} 50 | ``` 51 | 52 | 还有使用一些mutator methods如数组的 `push` , `shift` , `splice` 等等 53 | 54 | ```js 55 | function firstThree(arr) { 56 | return arr.splice(0,3); 57 | } 58 | ``` 59 | 60 | 纯函数的一些优点: 61 | * 容易可测试(testable) => 因为相同的输入必定是相同的输出,因此结果可以缓存(cacheable) 62 | * 自我记录(Self documenting) => 因为需要的变量都是参数,参数命名良好的情况下即便很久以后再去看这个函数依旧可以很容易知道这个函数需要哪些参数 63 | * 因为不用担心有副作用(side-effects),因此可以更好地工作 -------------------------------------------------------------------------------- /functional-programming/readme.md: -------------------------------------------------------------------------------- 1 | # JavaScript函数式编程超指南 2 | 3 | ![alt](./article/img/JavaScript--FP.svg) 4 | 5 | 学习函数式编程范式,相比较面向对象编程范式的另一个新天地,也是一种十分实用的编程方式。 6 | 7 | 对于学习JavaScript函数式编程作用的疑惑的解答: 8 | 在复杂的Web应用中,底层的JavaScript代码实现细节很可能会使整个系统交织混杂在一起。函数式编程风格,提供了一种松耦合的应用组件组织方式,使系统在宏观上更易于设计、交互和维护。 9 | 10 | 所以,作为一个有梦想不甘当咸鱼的前端工程师来说,摆脱整天沉溺于丑陋的业务代码中,学习函数式编程是一条光明大道。 11 | 12 | 本文最佳的食用方式,以上是JavaScript函数式编程学习的引言,也是我写这个系列文章的初衷。具体的知识脉络将以章节文章的形式展现。下面是章节文章的目录,鉴于时间的关系,文章将逐步完善,请进来的同学点击已完成的文章阅读。 13 | 14 | * [函数式编程——原来你也是编程范式][1] 15 | * [函数式编程基础——范畴论][2] 16 | * [FP编程基础之——纯函数][3] 17 | * [FP编程基础之——函数柯里化][4] *未完成* 18 | * [FP编程基础之——偏函数][5] *未完成* 19 | * [FP编程基础之——函数组合][6] *未完成* 20 | 21 | 下面是我认为FP中稍微有难度的知识点。 22 | 23 | * [FP编程进阶之——Point-Free][7] 24 | * [FP编程进阶之——惰性求值、惰性函数、惰性链][8] *未完成* 25 | * [FP编程进阶之——高阶函数][9] *未完成* 26 | * [FP编程进阶之——递归和尾调用优化][10] *未完成* 27 | * [FP编程进阶之——容器、函子][11] *未完成* 28 | * [FP编程进阶之——Monad][12] *未完成* 29 | * [FP编程进阶之——Functor、Applicative 和 Monad][13] 30 | 31 | [1]: https://github.com/Martin-Shao/yideng-note/blob/master/functional-programming/article/2019-1-22.md 32 | [2]: https://github.com/Martin-Shao/yideng-note/blob/master/functional-programming/article/2019-1-18.md 33 | [3]: https://github.com/Martin-Shao/yideng-note/blob/master/functional-programming/article/2019-1-21.md 34 | [7]: https://github.com/Martin-Shao/yideng-note/blob/master/functional-programming/article/2019-1-19.md 35 | [13]: https://github.com/Martin-Shao/yideng-note/blob/master/functional-programming/article/2019-1-20.md -------------------------------------------------------------------------------- /es5-senior/article/async-and-sync.md: -------------------------------------------------------------------------------- 1 | 于是,这个问题又回到了最开始的起点:JavaScript是单线程的。 2 | 3 | JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。 4 | 5 | JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 6 | 7 | 所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。 8 | 9 | 为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。 10 | 11 | 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是就有一个概念,任务队列。 12 | 13 | 如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。 14 | 15 | JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。 16 | 17 | 于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 18 | 19 | 具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。) 20 | 21 | (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。 22 | 23 | (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 24 | 25 | (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 26 | 27 | (4)主线程不断重复上面的第三步。 28 | 29 | 下图就是主线程和任务队列的示意图。 30 | 31 | 只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。 32 | 33 | "任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。 34 | 35 | "任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。 36 | 37 | 所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。 38 | 39 | "任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。 -------------------------------------------------------------------------------- /fe-qa/FrontEnd-QA.md: -------------------------------------------------------------------------------- 1 | # JavaScript与QA工程师 2 | 3 | 作为前端工程师来说,测试必不可少的环节,但却又是很多开发的同学容易忽视的环节。 4 | 本片文文章将带领同学们由浅入深的学习前端测试相关知识。 5 | 6 | 首先是思维导图 7 | ![alt text](./article/img/JavaScript&QA.svg "Title") 8 | 9 | ## 为什么要做单元测试 10 | 11 | * 正确性:测试可以验证代码的正确性,在上线前做到心里有底 12 | * 自动化:当然手动也可以测试,通过console可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头再来,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行。 13 | * 解释性:测试用例用于测试接口、模块的重要性,那么在测试用例中就会涉及如何使用这些API。其他开发人员如果使用这些API,那阅读测试用例是一种很好地途径,有时比文档说明更清晰。 14 | * 驱动开发,指导设计:代码被测试的前提是代码本身的可测试性,那么要保证代码的可测试性,就需要在开发中注意API的设计,TDD将测试前移就是起到这么一个作用。 15 | * 保证重构:互联网行业产品迭代速度很快,迭代后必然存在diamante重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆地进行重构。 16 | 17 | ## 如何写单元测试用例 18 | 19 | * 测试代码时,只考虑测试,不考虑内部实现 20 | * 数据尽量模拟现实,越靠近现实越好 21 | * 充分考虑数据的边界条件 22 | * 对重点、复杂、核心代码,重点测试 23 | * 利用AOP(beforeEach、afterEach),减少测试代码数量,避免无用功能 24 | * 测试、功能开发相结合,有利于设计和代码重构 25 | 26 | ## TDD(Test-driven development): 27 | 28 | 其基本思路是通过测试来推动整个开发的进行。 29 | 30 | 单元测试的首要目的不是为了能够编写出大覆盖率的全部通过的测试代码,而是需要从使用者(调用者)的角度出发,尝试函数逻辑的各种可能性,进而辅助性增强代码质量 31 | 32 | 测试是手段而不是目的。测试的主要目的不是证明代码正确,而是帮助发现错误,包括低级的错误 33 | 34 | 测试要快。快速运行、快速编写 35 | 36 | 测试代码保持简洁 37 | 38 | 不会忽略失败的测试。一旦团队开始接受1个测试的构建失败,那么他们渐渐地适应2、3、4或者更多的失败。在这种情况下,测试集就不再起作用 39 | 40 | 需要注意的是: 41 | 42 | 一定不能误解了TDD的核心目的! 43 | 44 | 测试不是为了覆盖率和正确率 45 | 46 | 而是作为实例,告诉开发人员要编写什么代码 47 | 48 | 红灯(代码还不完善,测试挂)-> 绿灯(编写代码,测试通过)-> 重构(优化代码并保证测试通过) 49 | 50 | TDD的过程是: 51 | 52 | 需求分析,思考实现。考虑如何“使用”产品代码,是一个实例方法还是一个类方法,是从构造函数传参还是从方法调用传参,方法的命名,返回值等。这时其实就是在做设计,而且设计以代码来体现。此时测试为红 53 | 54 | 实现代码让测试为”绿灯“ 55 | 56 | 重构,然后重复测试 57 | 58 | 最终符合所有要求即: 59 | 60 | 每个概念都被清晰的表达 61 | 62 | 代码中无自我重复 63 | 64 | 没有多余的东西 65 | 66 | 通过测试 67 | 68 | ## BDD(Behavior-driven development): 69 | 70 | 行为驱动开发(BDD),重点是通过与利益相关者(简单说就是客户)的讨论,取得对预期的软件行为的认识,其重点在于沟通 71 | 72 | BDD过程是: 73 | 74 | 从业务的角度定义具体的,以及可衡量的目标 75 | 76 | 找到一种可以达到设定目标的、对业务最重要的那些功能的方法 77 | 78 | 然后像故事一样描述出一个个具体可执行的行为。其描述方法基于一些通用词汇,这些词汇具有准确无误的表达能力和一致的含义。例如,expect, should, assert 79 | 80 | 寻找合适语言及方法,对行为进行实现 81 | 82 | 测试人员检验产品运行结果是否符合预期行为。最大程度的交付出符合用户期望的产品,避免表达不一致带来的问题 83 | -------------------------------------------------------------------------------- /algorithm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /es5-senior/demo1.js: -------------------------------------------------------------------------------- 1 | function timeout(ms) { 2 | return new Promise((resolve, reject) => { 3 | setTimeout(resolve, ms, 'done'); 4 | }); 5 | } 6 | 7 | timeout(100).then((value) => { 8 | console.log(value); 9 | }); 10 | 11 | const getJSON = function (url) { 12 | const promise = new Promise(function (resolve, reject) { 13 | const handler = function () { 14 | if (this.readyState !== 4) { 15 | return; 16 | } 17 | if (this.status === 200) { 18 | resolve(this.response); 19 | } else { 20 | reject(new Error(this.statusText)); 21 | } 22 | }; 23 | const client = new XMLHttpRequest(); 24 | client.open("GET", url); 25 | client.onreadystatechange = handler; 26 | client.responseType = "json"; 27 | client.setRequestHeader("Accept", "application/json"); 28 | client.send(); 29 | 30 | }); 31 | 32 | return promise; 33 | }; 34 | 35 | getJSON("/posts.json").then(function (json) { 36 | console.log('Contents: ' + json); 37 | }, function (error) { 38 | console.error('出错了', error); 39 | }); 40 | 41 | var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10'; 42 | var result; 43 | 44 | var XHR = new XMLHttpRequest(); 45 | XHR.open('GET', url, true); 46 | XHR.send(); 47 | 48 | XHR.onreadystatechange = function () { 49 | if (XHR.readyState == 4 && XHR.status == 200) { 50 | result = XHR.response; 51 | console.log(result); 52 | 53 | // 伪代码 54 | var url2 = 'http:xxx.yyy.com/zzz?ddd=' + result.someParams; 55 | var XHR2 = new XMLHttpRequest(); 56 | XHR2.open('GET', url, true); 57 | XHR2.send(); 58 | XHR2.onreadystatechange = function () { 59 | // ... 60 | } 61 | } 62 | } 63 | 64 | const promise = new Promise(function (resolve, reject) { 65 | // ... some code 66 | 67 | if (/* 异步操作成功 */) { 68 | resolve(value); 69 | } else { 70 | reject(error); 71 | } 72 | }); 73 | 74 | promise.then(function (value) { 75 | // success 76 | }, function (error) { 77 | // failure 78 | }); -------------------------------------------------------------------------------- /css-senior/article/css3d.md: -------------------------------------------------------------------------------- 1 | # CSS3构建3D吸睛世界! 2 | 3 | 本文章来源自预科班课程--CSS3构建3D的世界。需要说明的是,该节知识已经不是基础的CSS知识科普,而是CSS3D和动画的高级应用了。希望阅读过该文章的同学也能做出属于你的CSS3D酷炫动画。 4 | 5 | 老规矩的思维导图: 6 | ![alt text](./img/CSS3Dworld.svg "CSS3构建3D吸睛世界!") 7 | 8 | ## 陀螺仪 9 | 10 | 陀螺仪又叫角速度传感器,是不同于加速度计(G-sensor)的,他的测量物理量是偏转、倾斜时的转动角速度。在手机上,仅用加速度计没办法测量或重构出完整的3D动作,测不到转动的动作的,G-sensor只能检测轴向的线性动作。但陀螺仪则可以对转动、偏转的动作做很好的测量,这样就可以精确分析判断出使用者的实际动作。而后根据动作,可以对手机做相应的操作! 11 | 12 | 设备方向定义了三种旋转: 13 | 14 | Alpha:以Z轴为轴的旋转为alpha。其范围为0到360度,当前指向表示为z。 15 | Beta:以X轴为轴的旋转为beta。其范围为-180到180度,当前指向表示为x。 16 | Gamma:以Y轴为轴的旋转为gamma。其范围为-90到90度,当前指向表示为y。 17 | 18 | ![alt text](./img/timg-xyz.jpg "Title") 19 | ![alt text](./img/timg-alpha.jpg "Title") 20 | ![alt text](./img/timg-beta.jpg "Title") 21 | ![alt text](./img/timg-gamma.jpg "Title") 22 | 23 | ### 获取罗盘校准 24 | 25 | ```js 26 | window.addEventListener("compassneedscalibration", function (event) { 27 | showMessage('您的罗盘需要校准,请将设备沿数字8方向移动。'); 28 | event.preventDefault(); 29 | }, true); 30 | ``` 31 | 32 | ### 获取重力加速度 33 | 34 | ```js 35 | 36 | ``` 37 | 38 | ### 重力加速度 39 | 40 | ### 摇一摇 41 | 42 | ## CSS 3D模型 43 | 44 | ### 球面投影 45 | 46 | ### 比较 47 | 48 | ### 效果 49 | 50 | ### 公式 51 | 52 | ### 效果11 53 | 54 | 美滋滋 55 | 56 | ### 淘宝造物节 57 | 58 | ## 集合Touch事件 59 | 60 | ## CSS高级实用技巧 61 | 62 | css3开发常备核心技能 63 | 64 | * 早起的双飞翼布局+CSS HACK 65 | * 基于移动端的PX与REM转换兼容方案 66 | * 弹性盒模型与Reset的选择 67 | * 自制的CON-FONT与常用字体排版 68 | * CSS代码检测与团队项目规范 69 | * CSS绘制特殊图形 高级技巧 70 | * BFC IFC GFC FFC 71 | 72 | ### 双飞翼布局 73 | 74 | position 75 | float 76 | 负边距 77 | 等高 78 | 盒模型 79 | 清除浮动 80 | 81 | ### 从双飞翼布局到CSS布局 82 | 83 | 作为全栈的伪前端工程师,一直以来对于CSS这块的知识都有所欠缺。但每次遇到样式问题也十分尴尬, 84 | 痛定思痛之后,决定把这块的知识彻底梳理出来。 85 | 86 | 学习布局是对于CSS知识整体运用的一种有效的方式。 87 | 88 | CSS布局基础知识:定位、尺寸、浮动。 89 | 90 | CSS中有个重要的概念就是定位:`position` ,掌握position属性值的用法和特性是十分有必要的。 91 | 总的来说,position属性有六个属性值:static、relative、absolute、fixed、sticky和inherit。 92 | * static(默认):元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分;行内元素则会创建一个或多个行框,置于其父元素中。 93 | * relative:元素框相对于之前正常文档流中的位置发生偏移,并且原先的位置仍然被占据。发生偏移的时候,可能会覆盖其他元素。 94 | * absolute:元素框不再占有文档流位置,并且相对于包含块进行偏移(所谓的包含块就是最近一级外层元素position不为static的元素) 95 | * fixed:元素框不再占有文档流位置,并且相对于视窗进行定位 96 | * sticky:(这是css3新增的属性值)粘性定位,官方的介绍比较简单,或许你不能理解。其实,它就相当于relative和fixed混合。最初会被当作是relative,相对于原来的位置进行偏移;一旦超过一定阈值之后,会被当成fixed定位,相对于视口进行定位。 97 | 98 | 学习CSS可以借助一下几个网站:W3school 99 | * [W3school](http://www.w3school.com.cn/cssref/pr_class_position.asp) 100 | * [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/position) 101 | 102 | 偏移量:top、right、bottom、left四个属性。 103 | 104 | 这里的尺寸指的是盒模型的尺寸。 105 | 106 | 今天学习内容: 107 | 108 | icon-font 109 | 110 | BFC IFC GFC FFC -------------------------------------------------------------------------------- /functional-programming/article/demo.js: -------------------------------------------------------------------------------- 1 | function partial(func, ...argsBound) { 2 | return function (...args) { // (*) 3 | return func.call(this, ...argsBound, ...args); 4 | } 5 | } 6 | 7 | // Usage: 8 | let user = { 9 | firstName: "John", 10 | say(time, phrase) { 11 | console.info(`[${time}] ${this.firstName}: ${phrase}!`); 12 | } 13 | }; 14 | 15 | // add a partial method that says something now by fixing the first argument 16 | user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes()); 17 | 18 | user.sayNow("Hello"); 19 | // Something like: 20 | // [10:00] Hello, John! 21 | 22 | const partail = (fn, args) => 23 | (...moreArgs) => 24 | fn(...args, ...moreArgs) 25 | 26 | const partail = function (fn, args) { 27 | return function (...moreArgs) { 28 | return fn(...args, ...moreArgs) 29 | } 30 | } 31 | 32 | const add3 = (a, b, c) => a + b + c; 33 | const fivePlus = partail(add3, 2, 3); 34 | fivePlus(4) 35 | const add1More = add3.bind(null, 2, 3) 36 | 37 | 38 | function add(a, b) { 39 | return a + b; 40 | } 41 | console.log(add(1, 2));//结果3 42 | console.log(add(1, 3));//结果4 43 | console.log(add(1, 4));//结果5 44 | console.log(add(1, 5));//结果6 45 | console.log(add(1, 6));//结果7 46 | console.log(add(1, 7));//结果8 47 | 48 | function add(a, b, c, d, e) { 49 | return a + b + c + d + e; 50 | } 51 | console.log(add(1, 2, 3, 4, 5)); 52 | console.log(add(1, 2, 3, 1, 2)); 53 | console.log(add(1, 2, 3, 3, 5)); 54 | console.log(add(1, 2, 3, 2, 11)); 55 | console.log(add(1, 2, 3, 3, 8)); 56 | console.log(add(1, 2, 3, 7, 5)); 57 | 58 | //入参函数 59 | function add(a, b) { 60 | return a + b; 61 | } 62 | //生产偏函数的工厂,接受一个入参函数,返回一个新的函数,用于接受剩余的参数 63 | function partial(fn, a) { 64 | return function (b) { 65 | return fn(a, b); 66 | } 67 | } 68 | 69 | //入参函数 70 | function add(a, b) { 71 | return a + b; 72 | } 73 | //生产偏函数的工厂 74 | function partial(fn, a) { 75 | return function (b) { 76 | return fn(a, b); 77 | } 78 | } 79 | var parAdd = partial(add, 1);//变量parAdd接受返回的新函数 80 | console.log(parAdd(2));//在调用的时候传入剩余的参数 81 | console.log(parAdd(3));//在调用的时候传入剩余的参数 82 | console.log(parAdd(4));//在调用的时候传入剩余的参数 83 | console.log(parAdd(5));//在调用的时候传入剩余的参数 84 | 85 | function add(a, b, c, d, e) { 86 | return a + b + c + d + e; 87 | } 88 | function partial(fn, a, b, c) { 89 | return function (d, e) { 90 | return fn(a, b, c, d, e); 91 | } 92 | } 93 | var parAdd = partial(add, 1, 2, 3); 94 | console.log(parAdd(2, 1)); 95 | console.log(parAdd(3, 7)); 96 | console.log(parAdd(4, 8)); 97 | 98 | 99 | function add(a, b) { 100 | return a + b; 101 | } 102 | var obj = {}; 103 | obj.parAdd = add.bind(obj, 1); 104 | console.log(obj.parAdd(2));//结果3 105 | 106 | var foo = function (a) { 107 | return function (b) { 108 | return a * a + b * b; 109 | } 110 | } 111 | 112 | var bar = function (a, b) { 113 | return a * a + b * b; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /algorithm/examination/test.md: -------------------------------------------------------------------------------- 1 | # JavaScript中关于算法的题目合集 2 | 3 | 会搜集日常中有意思关于算法的题目 4 | 5 | ## JavaScript中奖list数据转为tree数组 6 | 7 | 完成 convert(list) 函数,实现将 list 转为 tree 8 | 业务上经常要用到数组转树形结构的函数,比如从后台服务器拿到list数据 9 | 10 | ```js 11 | 12 | var nodes = [ 13 | { "id": 1, "pId": 0, "name": "父节点1 - 展开", "open": true }, 14 | { "id": 11, "pId": 1, "name": "父节点11 - 折叠" }, 15 | { "id": 12, "pId": 1, "name": "父节点12 - 折叠" }, 16 | { "id": 13, "pId": 1, "name": "父节点13 - 没有子节点" }, 17 | { "id": 2, "pId": 0, "name": "父节点2 - 折叠" }, 18 | { "id": 21, "pId": 2, "name": "父节点21 - 展开", "open": true }, 19 | { "id": 22, "pId": 2, "name": "父节点22 - 折叠" }, 20 | { "id": 23, "pId": 2, "name": "父节点23 - 折叠" }, 21 | { "id": 3, "pId": 0, "name": "父节点3 - 没有子节点" } 22 | ]; 23 | 24 | ``` 25 | 26 | 但是我们想要的是树形文档结构的数组树,如下: 27 | 28 | ```js 29 | var tree = [ 30 | { 31 | "id": 1, 32 | "pId": 0, 33 | "name": "父节点1 - 展开", 34 | "open": true, 35 | "children": [ 36 | { 37 | "id": 11, 38 | "pId": 1, 39 | "name": "父节点11 - 折叠" 40 | }, 41 | { 42 | "id": 12, 43 | "pId": 1, 44 | "name": "父节点12 - 折叠" 45 | }, 46 | { 47 | "id": 13, 48 | "pId": 1, 49 | "name": "父节点13 - 没有子节点" 50 | } 51 | ] 52 | }, 53 | { 54 | "id": 2, 55 | "pId": 0, 56 | "name": "父节点2 - 折叠", 57 | "children": [ 58 | { 59 | "id": 21, 60 | "pId": 2, 61 | "name": "父节点21 - 展开", 62 | "open": true 63 | }, 64 | { 65 | "id": 22, 66 | "pId": 2, 67 | "name": "父节点22 - 折叠" 68 | }, 69 | { 70 | "id": 23, 71 | "pId": 2, 72 | "name": "父节点23 - 折叠" 73 | } 74 | ] 75 | }, 76 | { 77 | "id": 3, 78 | "pId": 0, 79 | "name": "父节点3 - 没有子节点" 80 | }] 81 | ``` 82 | 83 | 实现convert函数: 84 | 85 | ```js 86 | /* 87 | * @param list {object[]}, 入参数组 88 | * @param parentKey {string} 当前节点字段 89 | * @param currentKey {string} 父节点字段 90 | * @param rootValue {any} 根节点标识 91 | * @return object 92 | */ 93 | function convert(list, parentKey, currentKey, rootValue) { 94 | // your code here 95 | } 96 | ``` 97 | 98 | 完成代码如下: 99 | 100 | ```js 101 | 102 | function listToTree(data, options) { 103 | options = options || {}; 104 | var ID_KEY = options.idKey || 'id'; 105 | var PARENT_KEY = options.parentKey || 'pId'; 106 | var CHILDREN_KEY = options.childrenKey || 'children'; 107 | 108 | var tree = [], 109 | childrenOf = {}; 110 | var item, id, parentId; 111 | 112 | for (var i = 0, length = data.length; i < length; i++) { 113 | item = data[i]; 114 | id = item[ID_KEY]; 115 | parentId = item[PARENT_KEY] || 0; 116 | childrenOf[id] = childrenOf[id] || []; 117 | item[CHILDREN_KEY] = childrenOf[id]; 118 | if (parentId != 0) { 119 | childrenOf[parentId] = childrenOf[parentId] || []; 120 | console.info(parentId); 121 | childrenOf[parentId].push(item); 122 | } else { 123 | tree.push(item); 124 | } 125 | }; 126 | 127 | return tree; 128 | } 129 | ``` -------------------------------------------------------------------------------- /functional-programming/article/2019-1-19.1.md: -------------------------------------------------------------------------------- 1 | # FP编程基础之——偏函数 2 | 3 | ## 引言 4 | 5 | 本文是对于函数式编程中 偏函数、偏应用函数,以及偏函数与柯里化区别的解析的文章。 6 | 7 | > 关键字:偏函数、偏应用函数、偏函数应用、柯里化 8 | 9 | ## 偏函数定义 10 | 11 | 维基百科中对偏函数 (Partial application) 的定义为: 12 | 13 | > In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity. 14 | 15 | **翻译成中文: 16 | 在计算机科学中,局部应用是指固定一个函数的一些参数,然后产生另一个更小元的函数。** 17 | 18 | 那么什么是元?元是指函数参数的个数,比如一个带有两个参数的函数被称为二元函数。 19 | 20 | 举个简单的例子: 21 | 22 | ```js 23 | function add(a, b) { 24 | return a + b; 25 | } 26 | 27 | // 执行 add 函数,一次传入两个参数即可 28 | add(1, 2) // 3 29 | 30 | // 假设有一个 partial 函数可以做到局部应用 31 | var addOne = partial(add, 1); 32 | 33 | addOne(2) // 3 34 | ``` 35 | 36 | 个人觉得翻译成“局部应用”或许更贴切些,以下全部使用“局部应用”。 37 | 38 | 柯里化与局部应用 39 | 如果看过上一篇文章《JavaScript专题之柯里化》,实际上你会发现这个例子和柯里化太像了,所以两者到底是有什么区别呢? 40 | 41 | 其实也很明显: 42 | 43 | 柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。 44 | 45 | 局部应用则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。 46 | 47 | 如果说两者有什么关系的话,引用 functional-programming-jargon 中的描述就是: 48 | 49 | > Curried functions are automatically partially applied. 50 | 51 | partial 52 | 我们今天的目的是模仿 underscore 写一个 partial 函数,比起 curry 函数,这个显然简单了很多。 53 | 54 | 也许你在想我们可以直接使用 bind 呐,举个例子: 55 | 56 | ```js 57 | function add(a, b) { 58 | return a + b; 59 | } 60 | 61 | var addOne = add.bind(null, 1); 62 | 63 | addOne(2) // 3 64 | ``` 65 | 66 | 然而使用 bind 我们还是改变了 this 指向,我们要写一个不改变 this 指向的方法。 67 | 68 | 第一版 69 | 根据之前的表述,我们可以尝试着写出第一版: 70 | 71 | ```js 72 | // 第一版 73 | // 似曾相识的代码 74 | function partial(fn) { 75 | var args = [].slice.call(arguments, 1); 76 | return function() { 77 | var newArgs = args.concat([].slice.call(arguments)); 78 | return fn.apply(this, newArgs); 79 | }; 80 | }; 81 | ``` 82 | 83 | 我们来写个 demo 验证下 this 的指向: 84 | 85 | ```js 86 | function add(a, b) { 87 | return a + b + this.value; 88 | } 89 | 90 | // var addOne = add.bind(null, 1); 91 | var addOne = partial(add, 1); 92 | 93 | var value = 1; 94 | var obj = { 95 | value: 2, 96 | addOne: addOne 97 | } 98 | obj.addOne(2); // ??? 99 | // 使用 bind 时,结果为 4 100 | // 使用 partial 时,结果为 5 101 | ``` 102 | 103 | 第二版 104 | 然而正如 curry 函数可以使用占位符一样,我们希望 partial 函数也可以实现这个功能,我们再来写第二版: 105 | 106 | ```js 107 | // 第二版 108 | var _ = {}; 109 | 110 | function partial(fn) { 111 | var args = [].slice.call(arguments, 1); 112 | return function() { 113 | var position = 0, len = args.length; 114 | for(var i = 0; i < len; i++) { 115 | args[i] = args[i] === _ ? arguments[position++] : args[i] 116 | } 117 | while(position < arguments.length) args.push(argumetns[position++]); 118 | return fn.apply(this, args); 119 | }; 120 | }; 121 | ``` 122 | 123 | 我们验证一下: 124 | 125 | ```js 126 | var subtract = function(a, b) { return b - a; }; 127 | subFrom20 = partial(subtract, _, 20); 128 | subFrom20(5); 129 | ``` 130 | 131 | 写在最后 132 | 值得注意的是:underscore 和 lodash 都提供了 partial 函数,但只有 lodash 提供了 curry 函数。 133 | 134 | JavaScript专题系列预计写二十篇左右,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特点是研(chao)究(xi) underscore 和 jQuery 的实现方式。 135 | 136 | 如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。 137 | 138 | ```haskell 139 | class Functor f where 140 | fmap :: (a -> b) -> f a -> f b 141 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 这是一灯学堂的笔记项目,详细的纪录每节课程的笔记。 2 | 3 | ## 预科班 4 | 5 | 第一部分是预科班的笔记内容,在正式开课之前需要全面预习和梳理的前端知识点。预科班课程中有部分讲课重复,所以梳理过程中,先按照课程体系梳理,然后按照知识体系梳理。 6 | 7 | **下面是按照课程体系的思维导图** 8 | 右击可查看高清大图! 9 | ![alt text](./images/预科班课程体系.svg "Title") 10 | 11 | **下面是按照知识体系的思维导图** 12 | 右击可查看高清大图! 13 | ![alt text](./images/预科班知识体系.svg "Title") 14 | 上图是知识体系思维导图的概要,详细内容请参照每一章内容的详细笔记 15 | 16 | ** 各部分课程详细笔记 17 | * [你不知道的html][1] 18 | * [CSS基础与高级][2] 19 | * [ES5基础与核心知识][3] 20 | * [JavaScript与QA测试工程师][6] 21 | * [ES6编程风格与精粹][4] *未完成* 22 | * [PHP与MySQL入门到实践][5] *未完成* 23 | * [JavaScript函数式编程超指南][7] 24 | 25 | [1]: https://github.com/Martin-Shao/yideng-note/blob/master/html-senior/exercises.md 26 | [2]: https://github.com/Martin-Shao/yideng-note/blob/master/css-senior/overview.md 27 | [3]: https://github.com/Martin-Shao/yideng-note/blob/master/es5-senior/exercises.md 28 | [4]: https://github.com/Martin-Shao/yideng-note/blob/master/es6-senior/exercises.md 29 | [5]: https://github.com/Martin-Shao/yideng-note 30 | [6]: https://github.com/Martin-Shao/yideng-note/blob/master/fe-qa/FrontEnd-QA.md 31 | [7]: https://github.com/Martin-Shao/yideng-note/tree/master/functional-programming 32 | 33 | ## 每周课程表: 34 | 35 | ## 第一周——前端基础 36 | 37 | ## 第二周——服务端开发入门 38 | 39 | * Nodejs学习介绍 40 | * Nodejs基础API 41 | 1. 走近Nodejs 42 | 2. Nodejs入门 43 | 3. Nodejs环境及npm深入 44 | 4. Nodejs回调函数 45 | 5. Nodejs事件驱动机制 46 | 6. Nodejs模块化 47 | 7. Nodejs函数 48 | 8. Nodejs路由 49 | 9. 全局方法和工具 50 | 10. 文件系统 51 | 52 | * Express入门必看 53 | 1. Express介绍 54 | 2. Express中间件 55 | 3. Express路由 56 | 4. Express错误处理 57 | 5. Express模板引擎 58 | 59 | * KOA1入门必看 60 | 1. KOA介绍 61 | 2. KOA应用 62 | 3. Context(上下文) 63 | 4. 请求 64 | 5. 输出 65 | 66 | * Node实战小项目 67 | 1. Express入门实战 68 | 2. Express+PHP实战 69 | 3. KOA1实战 70 | 4. KOA2实战 71 | 5. 爬虫实战之Robots协议 72 | 6. 配置爬虫系统和开发环境 73 | 7. 爬虫代码实战 74 | 8. 数据推送至Comet 75 | 9. 数据推送至WebSocket 76 | 10. 数据推送之SSE 77 | 78 | * 大话Nodejs72般变化【深度实践课】 79 | 1. 【实战】Nginx的反向代理与负载均衡 80 | 2. 【实战】Nodejs线上部署【上】 81 | 3. 【实战】Nodejs线上部署【下】 82 | 4. 腾讯地图H5 Nodejs架构搭建 83 | 5. 如何阅读Nodejs源代码 84 | 6. JavaWebNET用框架核心思想 85 | 7. 前后端分离与用户验证 86 | 8. 【实战】走近Hapi的世界 87 | 9. 【实战】30分钟快速上手Egg 88 | 89 | * 大话Nodejs72般变化【直播课】 90 | 1. HTTP协议那些事 91 | 2. 大规模Nodejs项目架构与优化 92 | 3. 第二周实战+作业讲解【上】 93 | 4. 第二周实战+作业讲解【下】 94 | 95 | ## 第三周——前端工程化入门 96 | 97 | * 常用前端构建工具入门【上】 98 | 1. Webpack学习注意事项 99 | 2. Webpack入门介绍 100 | 3. Webpack1入门与实践 101 | 4. Webpack2&3核心技巧 102 | 5. Webpack4快速入门 103 | 6. Rollup的介绍和使用 104 | 7. Gulp的介绍和使用 105 | 8. Gulp的安装 106 | 9. Gulp的使用 107 | 10. Grunt的介绍和使用 108 | 109 | * 常用前端构建工具入门【下】 110 | 1. Grunt的安装 111 | 2. Grunt的应用 112 | 3. Bower的安装与应用 113 | 4. Bower的安装 114 | 5. Bower的应用 115 | 6. Yeoman的介绍 116 | 7. Yeoman的安装 117 | 8. Yeoman的应用 118 | 9. Browserify介绍与应用 119 | 10. Parcel初体验 120 | 121 | * 前端工程化那些事【深度实践课】 122 | 1. 前端架构那些事儿 123 | 2. Webpack从入门到放弃 124 | 3. 前端工程化预备知识【上】 125 | 4. 前端工程化预备知识【下】 126 | 5. FIS从入门到放弃【上】 127 | 6. FIS从入门到放弃【下】 128 | 129 | * 前端工程化【直播课】 130 | 1. 从小到大论前端项目持续集成 131 | 2. 前端工程化Linux预备知识 132 | 3. 第三周实战+作业讲解 133 | 4. 第三周实战+作业讲解 134 | 135 | 136 | ## 第四周——前端性能优化与工程化 137 | 138 | * 性能优化常用技术手段 139 | 1. 雅虎军规 140 | 2. 面向切面的概念解读 141 | 3. 面向切面代码实战 142 | 4. Nginx服务器缓存策略 143 | 144 | * 前端工程师高级调试 145 | 1. 整体课程概要 146 | 2. 断点以及捕捉事件绑定 147 | 3. Audits和Chrome性能插件 148 | 4. Timeliness掌控帧渲染模式 149 | 5. Profiles法分析具体问题 150 | 151 | * 前端性能优化【深入提升】 152 | 1. 网红平台性能优化 153 | 2. 性能优化黑科技 Google AMP 154 | 3. Web高性能动画及渲染原理 155 | 156 | * 前端性能优化与工程化【直播课】 157 | 1. 前端性能优化必备服务器知识 158 | 2. Webpack实战MPA 159 | 3. 前端架构与性能优化那些事儿 160 | 4. 第四周考试+作业讲解 161 | 5. 前端架构师启蒙课第一讲 162 | 163 | ## 第五周——CSS深度实践 164 | -------------------------------------------------------------------------------- /html-senior/exercises.md: -------------------------------------------------------------------------------- 1 | # 你不知道的HTML 2 | 3 | ## 1.开发工具 4 | 5 | 推荐 Sublime Text (最主要的优点:轻量、性能高) 6 | 7 | 插件推荐: 8 | Emmet 9 | 概括地说,Emmet(译者注:前身就是以前大名鼎鼎的Zen Coding,这个如果你没听说和使用过,就悲哀了)是一个可以让你更快更高效地编写HTML和CSS,节省你大量时间的插件。怎么使用?你只需按约定的缩写形式书写而不用写整个代码,然后按“扩展”键,这些缩写就会自动扩展为对应的代码内容。 10 | 11 | Material Theme 12 | 主题插件,界面看起来清爽优化。 13 | 14 | Comic Sans MS 15 | 这个是英文字体,一个看起来舒服的英文字体,能够让开发在写代码时,心情更加愉悦。比如说,后面这段调皮的代码: 16 | console.info("Hello World"); 17 | 18 | 备注:统一编辑器的好处 19 | 20 | 1)作为团队开发,统一的编辑器可以尽量减少团队的沟通的成本。 21 | 2)好的编辑器能够提高团队的开发效率,无论从写代码的角度,还是审美的角度。 22 | 23 | ## 前端跨域请求解决方案 24 | 25 | 1. 什么是同源 26 | 2. 浏览器不同的域名不能访问对应的cookie但是内部的表单没有限制 27 | 3. 同源策略限制的对象(跨域) 28 | 4. 如何设置同源策略(hosts) 29 | 5. 怎么突破同源策略 30 | 31 | ### 同源策略 32 | 33 | 先来说说什么是源? 34 | 源(origin)就是协议、域名和端口号。 35 | 36 | 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。 37 | 38 | 同源就是: 39 | * 协议相同 40 | * 域名相同 41 | * 端口号相同 42 | 43 | 以上url中的源就是:`http://www.company.com:80` 44 | 若地址里面的协议、域名和端口号均相同则属于同源。 45 | 以下是相对于 `http://www.a.com/test/index.html` 的同源检测 46 | 47 | * `http://www.a.com/dir/page.html` ----成功 48 | * `http://www.child.a.com/test/index.html` ----失败,域名不同 49 | * `https://www.a.com/test/index.html` ----失败,协议不同 50 | * `http://www.a.com:8080/test/index.html` ----失败,端口号不同 51 | 52 | 不受同源策略限制的: 53 | 1. 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。 54 | 2. 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的` 87 | 88 | 89 | 90 | ``` 91 | 92 | ## html语义化 93 | 94 | ### 1、什么是HTML语义化? 95 | 96 | <基本上都是围绕着几个主要的标签,像标题(H1~H6)、列表(li)、强调(strong em)等等> 97 | 98 | 根据内容的结构化(内容语义化),选择合适的标签(代码语义化)便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析。 99 | 100 | ### 2、为什么要语义化? 101 | 102 | 1. 为了在没有CSS的情况下,页面也能呈现出很好地内容结构、代码结构:为了裸奔时好看; 103 | 2. 用户体验:例如title、alt用于解释名词或解释图片信息、label标签的活用; 104 | 3. 有利于SEO:和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息:爬虫依赖于标签来确定上下文和各个关键字的权重; 105 | 4. 方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备)以意义的方式来渲染网页; 106 | 5. 便于团队开发和维护,语义化更具可读性,是下一步吧网页的重要动向,遵循W3C标准的团队都遵循这个标准,可以减少差异化。 107 | 108 | ### 3、写HTML代码时应注意什么? 109 | 110 | 1. 尽可能少的使用无语义的标签div和span; 111 | 2. 在语义不明显时,既可以使用div或者p时,尽量用p, 因为p在默认情况下有上下间距,对兼容特殊终端有利; 112 | 3. 不要使用纯样式标签,如:b、font、u等,改用css设置。 113 | 4. 需要强调的文本,可以包含在strong或者em标签中(浏览器预设样式,能用CSS指定就不用他们),strong默认样式是加粗(不要用b),em是斜体(不用i); 114 | 5. 使用表格时,标题要用caption,表头用thead,主体部分用tbody包围,尾部用tfoot包围。表头和一般单元格要区分开,表头用th,单元格用td; 115 | 6. 表单域要用fieldset标签包起来,并用legend标签说明表单的用途; 116 | 7. 每个input标签对应的说明文本都需要使用label标签,并且通过为input设置id属性,在lable标签中设置for=someld来让说明文本和相对应的input关联起来。 117 | 118 | ### 4、HTML5新增了哪些语义标签 119 | 120 | 在HTML 5出来之前,我们用div来表示页面章节,但是这些div都没有实际意义。(即使我们用css样式的id和class形容这块内容的意义)。这些标签只是我们提供给浏览器的指令,只是定义一个网页的某些部分。但现在,那些之前没“意义”的标签因为因为html5的出现消失了,这就是我们平时说的“语义”。 121 | -------------------------------------------------------------------------------- /es5-senior/article/js-variable-type.md: -------------------------------------------------------------------------------- 1 | # JavaScript基础之变量类型 2 | 3 | ## JavaScript变量类型思维导图 4 | 5 | ![alt text](./img/js-variable-type.svg "Title") 6 | 7 | ## 概述 8 | 9 | > ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型。 10 | > 基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象。 11 | > 当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值。 12 | > 到目前为止,我们看到的大多数引用类型值都是Object类型的实例。 13 | >

-------《JavaScript高级程序设计》

14 | 15 | 备注:**严谨的来说,JavaScript只有七种数据类型:Number、String 、Boolean、Null、Undefined、Symbol、Object** 16 | 17 | ## 常见的基本数据类型 18 | 19 | **Number、String 、Boolean、Null和Undefined。** 20 | 基本数据类型是按值访问的,因为可以直接操作保存在变量中的实际值。示例: 21 | 22 | ``` js 23 | var a = 10 24 | var b = a; 25 | b = 20; 26 | console.log(a); // 10值 27 | ``` 28 | 29 | 上面,b获取的是a值得一份拷贝,虽然,两个变量的值相等,但是两个变量保存了两个不同的基本数据类型值。 30 | b只是保存了a复制的一个副本。所以,b的改变,对a没有影响。 31 | 下图演示了这种基本数据类型赋值的过程: 32 | 33 | ![alt text](./img/basic-data.png "Title") 34 | 35 | 常见的引用类型数据 36 | 也就是对象类型Object type,比如:Object 、Array 、Function 、Data等。 37 | javascript的引用数据类型是保存在堆内存中的对象。与其他语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。 38 | 所以,引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。 39 | 40 | ``` js 41 |   var obj1 = new Object(); 42 |   var obj2 = obj1; 43 |   obj2.name = "我有名字了"; 44 |   console.log(obj1.name); // 我有名字了 45 | ``` 46 | 47 | 说明这两个引用数据类型指向了同一个堆内存对象。obj1赋值给onj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2, 48 | 但是实际上他们共同指向了同一个堆内存对象。实际上改变的是堆内存对象。 49 | 下面我们来演示这个引用数据类型赋值过程: 50 | 51 | ![alt text](./img/object.png "Title") 52 | 53 | ## 总结区别 54 | 55 | ### A.声明变量时不同的内存分配: 56 | 57 | 1. 原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。 58 | 2. 引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。 59 | 3. 这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。 60 | 61 | ### B.不同的内存分配机制也带来了不同的访问机制 62 | 63 | 1. 在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时, 64 | 2. 首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。 65 | 3. 而原始类型的值则是可以直接访问到的。 66 | 67 | ### C.复制变量时的不同 68 | 69 | 1. 原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。 70 | 2. 引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量, 71 | 也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。 72 | (这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。多了一个指针 73 | 74 | ### D.参数传递的不同(把实参复制给形参的过程) 75 | 76 | 首先我们应该明确一点:**ECMAScript中所有函数的参数都是按值来传递的。** 77 | 但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还是因为内存分配时的差别。   78 | 1. 原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。 79 | 2. 引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心! 80 | 因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。 81 | 82 | ## null和undefined区别 83 | 84 | 在JavaScript中存在这样两种原始类型:Null与Undefined。这两种类型常常会使JavaScript的开发人员产生疑惑,在什么时候是Null,什么时候又是Undefined? 85 | Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。 86 | Null类型也只有一个值,即null。null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。 87 | 88 | ``` js 89 | var oValue; 90 | console.info(oValue == undefined); //output "true" 91 | ``` 92 | 93 | 这段代码显示为true,代表oVlaue的值即为undefined,因为我们没有初始化它。 94 | 95 | ``` js 96 | console.info(null == document.getElementById('notExistElement')); 97 | ``` 98 | 99 | 当页面上不存在id为"notExistElement"的DOM节点时,这段代码显示为"true",因为我们尝试获取一个不存在的对象。 100 | 101 | ``` js 102 | alert(typeof undefined); //output "undefined" 103 | alert(typeof null); //output "object" 104 | ``` 105 | 106 | 第一行代码很容易理解,undefined的类型为Undefined;第二行代码却让人疑惑,为什么null的类型又是Object了呢?其实这是JavaScript最初实现的一个错误,后来被ECMAScript沿用下来。在今天我们可以解释为,null即是一个不存在的对象的占位符,但是在实际编码时还是要注意这一特性。 107 | 108 | ``` js 109 | alert(null == undefined); //output "true" 110 | ``` 111 | 112 | ECMAScript认为undefined是从null派生出来的,所以把它们定义为相等的。但是,如果在一些情况下,我们一定要区分这两个值,那应该怎么办呢?可以使用下面的两种方法。 113 | 114 | ``` js 115 | alert(null === undefined); //output "false" 116 | alert(typeof null == typeof undefined); //output "false" 117 | ``` 118 | 119 | 使用typeof方法在前面已经讲过,null与undefined的类型是不一样的,所以输出"false"。而===代表绝对等于,在这里null === undefined输出false。 120 | 121 | ## 变量 122 | -------------------------------------------------------------------------------- /es5-senior/index.js: -------------------------------------------------------------------------------- 1 | // function fn() { 2 | // this.i = 0; 3 | // this.j = 0; 4 | // this.x = 0; 5 | 6 | // var interval1 = setInterval(function () { 7 | // console.info('Function...', this.i++); 8 | // }, 500); 9 | 10 | // var interval2 = setInterval(function () { 11 | // console.info('bind***', this.x++); 12 | // }.bind(this), 500); 13 | 14 | // var interval3 = setInterval(() => { 15 | // console.info('Arrow function$$$', this.j++); 16 | // }, 500); 17 | 18 | // setTimeout(function () { 19 | // clearInterval(interval1); 20 | // clearInterval(interval2); 21 | // clearInterval(interval3); 22 | // }, 5000); 23 | // } 24 | // fn(); 25 | 26 | // var obj = { 27 | // user: "shaowei is cool", 28 | // getName: function () { 29 | // return this.user; 30 | // } 31 | // } 32 | 33 | // var getNameFn = obj.getName; 34 | // console.info(getNameFn()); 35 | 36 | this.m = 100; 37 | var obj = { 38 | m: 1000, 39 | test: function() { 40 | console.log(this.m); 41 | return function() { 42 | console.log(this.m); 43 | }; 44 | } 45 | }(obj.test())(); 46 | 47 | window.Glog = (function(msg) { 48 | console.log(msg); 49 | })( 50 | // this was added before the main closure. 51 | 52 | function(win) { 53 | //the former closure that contains the main javascript logic; 54 | } 55 | )(window); 56 | 57 | var s = { 58 | p: function() { 59 | return function() { 60 | console.log("enna"); 61 | }; 62 | } 63 | }(s.p())(); 64 | 65 | var test = (function(param) { 66 | console.log("test", param); 67 | return function(param) { 68 | console.log("inner", param); 69 | }; 70 | })(test())(); 71 | 72 | var s = { 73 | p: function() { 74 | return function() { 75 | console.log("enna"); 76 | }; 77 | } 78 | }; 79 | s.p()(); 80 | 81 | var s = { 82 | p: function() { 83 | return function() { 84 | console.log("enna"); 85 | }; 86 | } 87 | }(s.p())(); 88 | 89 | var person = { 90 | name: "shaogucheng", 91 | age: 18, 92 | job: "Software Engineer", 93 | sayName: function() { 94 | console.info(this.name); 95 | } 96 | }; 97 | 98 | function Person(name, age, job) { 99 | this.name = name; 100 | this.age = age; 101 | this.sayName = function() { 102 | console.info(this.name); 103 | }; 104 | } 105 | 106 | function Person() {} 107 | 108 | Person.prototype.language = "Chinese"; 109 | Person.prototype.gender = "unknown"; 110 | Person.prototype.name = "Legend of the Dragon"; 111 | Person.prototype.sayName = function() { 112 | console.info(this.name); 113 | }; 114 | 115 | var person1 = new Person(); 116 | var person2 = new Person(); 117 | person1.sayName(); // => Legend of the Dragon 118 | person2.sayName(); // => Legend of the Dragon 119 | console.info(person1.sayName === person2.sayName); // => true 120 | 121 | function Car(color) { 122 | this.color = color; 123 | } 124 | Car.prototype.sail = function() { 125 | console.info(this.color); 126 | }; 127 | 128 | function BWM(color) { 129 | Car.call(this, color); 130 | } 131 | 132 | var prototype = Object.create(Car.prototype); 133 | prototype.constructor = BWM; 134 | BWM.prototype = prototype; 135 | 136 | function a() { 137 | console.info(10); 138 | } 139 | var a; 140 | console.info(a); 141 | a(); 142 | a = 3; 143 | console.info(a); 144 | a = 6; 145 | a(); 146 | 147 | var a = { n: 1 }; 148 | var b = a; 149 | a.x = { n: 2 }; 150 | console.info(a); 151 | console.info(b); 152 | 153 | a.x = a = { n: 2 }; 154 | console.info(a); 155 | console.info(b); 156 | console.info(b.x === a); 157 | 158 | var a = { n: 1 }; 159 | a = a.x = { n: 2 }; 160 | console.info(a); 161 | 162 | a = { n: 2 }; 163 | a.x = a; 164 | 165 | a.x = a = { n: 2 }; 166 | 167 | Obeject.defineO; 168 | 169 | a.x = a = { n: 2 }; 170 | 171 | let a = [{ a: 1 }, { b: 2 }]; 172 | let b = a; 173 | b[0].a= 2; 174 | 175 | console.info(a); 176 | console.info(b); 177 | 178 | 179 | var a = [{a:1}]; 180 | var b = [{a:1}]; 181 | b[0].a = 2; 182 | console.info(a); 183 | console.info(b); 184 | 185 | // ExecutionContext = { 186 | // ThisBinding = , 187 | // LexicalEnvironment = { ... }, 188 | // VariableEnvironment = { ... }, 189 | // } 190 | 191 | {/* GlobalExectionContext = { 192 | LexicalEnvironment: { 193 | EnvironmentRecord: { 194 | Type: "Object", 195 | // 标识符绑定在这里 196 | outer: 197 | } 198 | } */} 199 | 200 | {/* FunctionExectionContext = { 201 | LexicalEnvironment: { 202 | EnvironmentRecord: { 203 | Type: "Declarative", 204 | // 标识符绑定在这里 205 | outer: 206 | } 207 | } */} 208 | -------------------------------------------------------------------------------- /ide/idea.md: -------------------------------------------------------------------------------- 1 | 2 | The keyboard shortcut `Ctrl+K` enables you to quickly invoke the `Commit Changes` dialog. 3 | This dialog shows all modifications in project, gives summary information of file status and suggests improvements before check-in. 4 | 5 | 6 | It is very easy to toggle between find and replace functionality. 7 | When you perform search and replace in a file, pressing `Ctrl+F` shows the search pane. Pressing `Ctrl+R` adds field, where you can type the replace string. 8 | While in the `Find in Path` dialog, you can switch to replace by pressing `Ctrl+Shift+R`. Same way, press `Ctrl+Shift+F` to hide the `Replace with` field, and switch to mere search. 9 | 10 | If you are working on a large project, with numerous TODO items, filter them by scopes. 11 | Use the Scope-Based tab in the TODO tool window to show only those items that pertain to the scope of interest. 12 | 13 | `TODO` tool window lets you preview each of the encountered TODO items - just click the preview button on the toolbar. 14 | 15 | If a method signature has been changed, IntelliJ IDEA highlights the tags that ran out of sync with the documentation comment and suggests a quick fix: 16 | 17 | When working with a lengthy list of tasks, you don't need to delete them one by one. Select several tasks, using `Shift` or `Control/Command` keys, click the right arrow, and then click `Remove`. 18 | 19 | If there are too many run/debug configurations of the same type, you can group them into folders, and thus distinguish them visually. 20 | 21 | In the `Live Templates` settings, use speed search to find templates with certain text in the template abbreviation, body or description. 22 | Start typing the desired text, and the list of available templates will shrink to show matching templates only: 23 | 24 | You can avoid escaping backslashes in your regular expressions. Start typing a regular expression, then press `Alt+Enter` and choose `Edit RegExp`. The regular expression opens in a separate tab in the editor, where you can type backslashes as is. 25 | All changes are synchronized with the original regular expression, and escapes are presented automatically. When ready, just press `Esc` to close the regular expression editor. 26 | 27 | Speed up HTML, XML or CSS development with `Emmet`. 28 | Enable this framework in the corresponding page of the `Editor | Emmet` node (`Settings/Preferences`): 29 | 30 | To view which line separators style is used in the current file, look at the `Status Bar`: 31 | 32 | You do not need to open a file in the editor to change its line separator style. Use the `Project tool window` instead: select one or more files, or folders, point to `File | Line Separators` on the main menu, and then choose the desired line ending style. 33 | For a directory, new line separator applies recursively. 34 | 35 | If you place the caret at certain symbol and press `Ctrl+Alt+Shift+T`, you will see the list of refactorings applicable to the current context. 36 | 37 | You want your bookmarks and breakpoints to be always at hand, so that you can easily navigate through them? 38 | They are visible in the `Favorites` tool window, which you can dock or float as required: 39 | 40 | You can drag an external file from the Explorer or Finder, and drop it onto the `Favorites` tool window. 41 | 42 | Tune the IntelliJ IDEA tool windows layout to make better use of your screen. 43 | Toggle between the vertical and side-by-side placement of the tool windows by Ctrl+Click/Cmd+Click on the splitter: 44 | 45 | You don't need to leave IntelliJ IDEA to work with your favorite shell. Just click the Terminal tool window button, and enjoy using the embedded local terminal. 46 | 47 | 48 | For the embedded local terminal, you can define your favorite shell, default tab name, and other settings. Choose File | Settings on the main menu, and then open the page Terminal. 49 | 50 | Enable the horizontal scrolling with the mouse wheel by holding the Shift key. 51 | 52 | IntelliJ IDEA allows you to search through the classes, files, tool windows, actions, settings, and symbols of your project by double pressing the Shift key. 53 | 54 | When using autopopup Code Completion, you can select the first item using Ctrl+句点. The selected name is automatically entered in the editor followed by dot. 55 | 56 | When an autopopup completion is active, Ctrl+向下箭头 and Ctrl+向上箭头 will close it and move the caret down or up in the editor. 57 | 58 | When in the Code Completion lookup, you can ease the search by filtering the list with the help of the "camel words" prefixes. 59 | 60 | Pressing the same shortcut after you have invoked Smart-type Completion when there's an array of expected type in context will suggest to get an element from this array. 61 | 62 | Pressing the same shortcut after you have invoked Smart-type Completion when a collection type is expected will search for arrays with same component type and suggest to convert them using Arrays.asList() call. 63 | 64 | Pressing the same shortcut after you have invoked Smart-type Completion will search for chained expressions which have expected type. 65 | 66 | When using Code Completion, you can accept the currently highlighted selection in the popup list with the Ctrl+Shift+Enter, IntelliJ IDEA will not just insert the selected string, but also will do its best to turn current code construct into syntactically correct one (balance parentheses, add missing braces and semicolons, etc.) -------------------------------------------------------------------------------- /es5-senior/article/call-stack.md: -------------------------------------------------------------------------------- 1 | # JavaScript与执行栈 2 | 3 | JavaScript执行机制的探索离不开三个核心要素:执行上下文、执行环境栈、作用域链。 4 | 5 | 学习和使用JavaScript这门语言眼看着也有了两年时光,一直以来对于JavaScript的运行机制不甚了解, 6 | 一方面是自己对于语言底层的东西很感兴趣(作为一个非计算机专业同学的坚持),另一方面,这些知识也给开发带来更多的助力(虽然表面上看上去没怎么用到。) 7 | 8 | 首先明确几个概念: 9 | * `EC`:函数执行环境(或执行上下文),Execution Context 10 | * `GEC`:全局执行环境(或全局执行上下文),Global Execution Context 11 | * `ECS`:执行环境栈,Execution Context Stack 12 | * `VO`:变量对象,Variable Object 13 | * `AO`:活动对象,Active Object 14 | * `GO`:全局对象,Global Object 15 | * `scope chain`:作用域链 16 | * `Lexical Environment`:词法环境 17 | * `Variable Environment`:变量环境 18 | * `Environment Record`:环境记录 19 | * `outer Lexical Environment`:外部词法环境 20 | * `Object Environment Record`:对象环境记录 21 | * `Declarative Environment Record`:声明性环境记录 22 | * `Function Environment Record`:函数环境记录 23 | * `Global Environment Records`:全局环境记录 24 | * `Module Environment Record`:模块环境记录 25 | * ``: 26 | * ``: 27 | * ``: 28 | * ``: 29 | * ``: 30 | 31 | ## 一切从执行栈开始 32 | 33 | 关于执行栈,这其实并不是JavaScript专有的知识 ,计算机语言的执行几乎都是依赖于执行栈的。 34 | 35 | > 执行栈是计算机科学中存储有关正在运行的子程序的消息的栈。经常被用于存放子程序的返回地址。在调用任何子程序时,主程序都必须暂存子程序运行完毕后应该返回到的地址。因此,如果被调用的子程序还要调用其他的子程序,其自身的返回地址就必须存入执行栈,在其自身运行完毕后再行取回。在递归程序中,每一层次递归都必须在执行栈上增加一条地址,因此如果程序出现无限递归(或仅仅是过多的递归层次),执行栈就会产生栈溢出。 36 | 37 | 关于执行栈的称呼或者说别名有很多种: 38 | * 执行栈(Execution stack) 39 | * 调用栈(Call stack) 40 | * 控制栈(Control stack) 41 | * 运行时栈(Run-time stack) 42 | * 机器栈(Machine stack) 43 | 44 | 以上名词所要表达的都是一个意思,下文中指定使用 **执行栈** 表达 45 | 46 | JavaScript执行栈示意图 47 | ![alt text](./img/callstack.png "JavaScript call stack ") 48 | ![alt text](./img/callstack1.bmp "JavaScript call stack ") 49 | 50 | ### **功能** 51 | 52 | 调用栈的主要功能是存放返回地址。除此之外,调用栈还用于存放: 53 | * 本地变量:子程序的变量可以存入调用栈,这样可以达到不同子程序间变量分离开的作用。 54 | * 参数传递:如果寄存器不足以容纳子程序的参数,可以在调用栈上存入参数。 55 | * 环境传递:有些语言(如Pascal与Ada)支持“多层子程序”,即子程序中可以利用主程序的本地变量。这些变量可以通过调用栈传入子程序。 56 | 57 | ## JavaScript执行上下文、函数堆栈、提升的概念 58 | 59 | 60 | 61 | ```js 62 | let a = 20; 63 | const b = 30; 64 | var c; 65 | 66 | function multiply(e, f) { 67 | var g = 20; 68 | return e * f * g; 69 | } 70 | // 这是标记 71 | multiply(20, 30); 72 | ``` 73 | 74 | ```js 75 | ExecutionContext = { 76 | ThisBinding = , 77 | LexicalEnvironment = { ... }, 78 | VariableEnvironment = { ... }, 79 | } 80 | ``` 81 | 82 | ```js 83 | GlobalExectionContext = { 84 | LexicalEnvironment: { 85 | EnvironmentRecord: { 86 | Type: "Object", 87 | // 标识符绑定在这里 88 | outer: 89 | } 90 | } 91 | ``` 92 | 93 | ```js 94 | FunctionExectionContext = { 95 | LexicalEnvironment: { 96 | EnvironmentRecord: { 97 | Type: "Declarative", 98 | // 标识符绑定在这里 99 | outer: 100 | } 101 | } 102 | ``` 103 | 104 | ```js 105 | let a = 20; 106 | const b = 30; 107 | var c; 108 | 109 | function multiply(e, f) { 110 | var g = 20; 111 | return e * f * g; 112 | } 113 | 114 | c = multiply(20, 30); 115 | ``` 116 | LexicalEnvironment 组件和 VariableEnvironment 组件的区别在于前者用于存储函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var )绑定。 117 | ```js 118 | GlobalExectionContext = { 119 | 120 | ThisBinding: , 121 | 122 | LexicalEnvironment: { 123 | EnvironmentRecord: { 124 | Type: "Object", 125 | // 标识符绑定在这里 126 | a: < uninitialized >, 127 | b: < uninitialized >, 128 | multiply: < func > 129 | } 130 | outer: 131 | }, 132 | 133 | VariableEnvironment: { 134 | EnvironmentRecord: { 135 | Type: "Object", 136 | // 标识符绑定在这里 137 | c: undefined, 138 | } 139 | outer: 140 | } 141 | } 142 | ``` 143 | 144 | ```js 145 | FunctionExectionContext = { 146 | 147 | ThisBinding: , 148 | 149 | LexicalEnvironment: { 150 | EnvironmentRecord: { 151 | Type: "Declarative", 152 | // 标识符绑定在这里 153 | Arguments: {0: 20, 1: 30, length: 2}, 154 | }, 155 | outer: 156 | }, 157 | 158 | VariableEnvironment: { 159 | EnvironmentRecord: { 160 | Type: "Declarative", 161 | // 标识符绑定在这里 162 | g: undefined 163 | }, 164 | outer: 165 | } 166 | } 167 | ``` 168 | 169 | ```js 170 | // 全局词法环境 171 | GlobalEnvironment = { 172 | outer: null, // 全局环境的外部环境引用为null 173 | // 全局环境记录,抽象为一个声明式环境记录和一个对象式环境记录的封装 174 | GlobalEnvironmentRecord: { 175 | // 全局this绑定值指向全局对象,即ObjectEnvironmentRecord的binding object 176 | [[GlobalThisValue]]: ObjectEnvironmentRecord[[BindingObject]], 177 | // 声明式环境记录,全局除了函数和var,其他声明绑定于此 178 | DeclarativeEnvironmentRecord: { 179 | y: 20, 180 | z: 30, 181 | Person: <> 182 | }, 183 | // 对象式环境记录的,绑定对象为全局对象,故其中的绑定可以通过访问全局对象的属性来获得 184 | ObjectEnvironmentRecord: { 185 | // 全局函数声明和var声明 186 | x: 10, 187 | foo: <>, 188 | // 内置全局属性 189 | isNaN: <>, 190 | isFinite: <>, 191 | parseInt: <>, 192 | parseFloat: <>, 193 | Array: <>, 194 | Object: <> 195 | // 其他内置全局属性不一一列举 196 | } 197 | } 198 | } 199 | 200 | // foo函数词法环境 201 | fooFunctionEnviroment = { 202 | outer: GlobalEnvironment, // 外部词法环境引用指向全局环境 203 | FunctionEnvironmentRecord: { 204 | [[ThisValue]]: GlobalEnvironment, // foo函数全局调用,故this绑定指向全局环境 205 | // 其他函数代码内的绑定 206 | a: 10 207 | } 208 | } 209 | ``` 210 | 211 | -------------------------------------------------------------------------------- /fe-qa/article/2019-1-8.md: -------------------------------------------------------------------------------- 1 | # 前端测试实践 2 | 3 | 1. 第一步新建文件夹 --> `yideng-note` 4 | 2. 执行 `npm init -y` ,建立好 `package.json` 文件之后,可以测试一下 `npm run test` 命令 5 | 3. 安装karma,执行 `npm install karma --save-dev` 6 | 7 | 在package.json文件中配置执行命令 8 | 9 | ```js 10 | "script": { 11 | "test": "karma init" 12 | } 13 | ``` 14 | 15 | 接下里测试一下配置是否生效 16 | 17 | ```bash 18 | npm run test 19 | ``` 20 | 21 | Which testing framework do you want to use? 22 | Press tab to list possible options. Enter to move to the next question. 23 | => jasmine 24 | 25 | Do you want to use Require.js? 26 | This will add Require.js pkugin. 27 | => no 28 | 29 | Do you want to capture any browsers automatically ? 30 | Press tab to list possible options. Enter empty string to move to the next question. 31 | => PhantomJS 32 | => 33 | 34 | What is the location of your source and test files ? 35 | You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js". 36 | Enter empty string to move to the next question. 37 | => 38 | 39 | Should any of the files included by the previous patterns be excluded ? 40 | You can use glob patterns, eg. "**/*.swp". 41 | Enter empty string to move to the next question. 42 | => 43 | 44 | Do you want Karma to watch all the files and run the tests on change ? 45 | Press tab to list possible options. 46 | => no 47 | 48 | 然后会生成一个karma的配置文件 karma.conf.js 49 | 50 | ```js 51 | "scripts": { 52 | "test": "karma init", 53 | "unit": "karma start" 54 | } 55 | ``` 56 | 57 | 这个配置文件的门道,串行和并行的配置方法 58 | 59 | 编写测试脚本 60 | 61 | 配置karma配置文件 62 | 63 | 启动脚本 64 | 65 | 如果报错的的化,执行以下命令,安装两个包 66 | 67 | ```bash 68 | npm install karma-jasmine --save-dev 69 | npm install jasmine-core --save-dev 70 | #也可以这样执行 71 | npm install karma-jasmine jasmine-core --save-dev 72 | ``` 73 | 74 | 如果还报错,还需要安装 75 | 76 | ```bash 77 | npm install phantom --save-dev 78 | npm install karma-phantomjs-launcher --save-dev 79 | ``` 80 | 81 | 如果还报错,还需要安装 82 | 83 | 接下来就可以运行测试脚本了,简单的测试算是通过了。 84 | 85 | 但是仍然有一个问题,代码逻辑有分支的情况下,存在测试覆盖率问题。 86 | 87 | 测试过程 88 | 生成测试结果 89 | 90 | 安装 `karma-coverage` 91 | 92 | ```bash 93 | npm install karma-coverage --save-dev 94 | ``` 95 | 96 | 然后添加配置 97 | 98 | 添加之后报错了,然后发现生成测试报告的存放路径配置错误 99 | 100 | 然后介绍 e2e,怎么做e2e 101 | 102 | 安装 selenium-webdriver 103 | 104 | ```bash 105 | npm install selenium-webdriver 106 | ``` 107 | 108 | 要退出 109 | 推出的重要性 110 | 111 | package.json是有声明周期的,比如prod前面还有一个preprod 112 | 113 | 发现一个问题,在windows系统环境下,使用e2e测试,启动脚本不能使用通配符 `*` 114 | 115 | e2e测试工具介绍,首先介绍一个经典的案列 nightwatch 116 | jest(react推出的)工具 117 | 118 | rize工具 119 | 120 | npm install puppeteer rize --save-dev 121 | npm install rize --save-dev 122 | 123 | npm i puppeteer --ignore-scripts --save-dev 124 | 取代了phantomJS 125 | 126 | ```bash 127 | d:\code\yideng\yideng-test>npm install puppeteer rize --save-dev 128 | 129 | > puppeteer@1.11.0 install d:\code\yideng\yideng-test\node_modules\puppeteer 130 | > node install.js 131 | 132 | ERROR: Failed to download Chromium r609904! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download. 133 | { Error: read ECONNRESET 134 | at _errnoException (util.js:1024:11) 135 | at TLSWrap.onread (net.js:615:25) code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' } 136 | npm WARN yideng-test@1.0.0 No description 137 | npm WARN yideng-test@1.0.0 No repository field. 138 | npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents): 139 | npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) 140 | 141 | npm ERR! code ELIFECYCLE 142 | npm ERR! errno 1 143 | npm ERR! puppeteer@1.11.0 install: `node install.js` 144 | npm ERR! Exit status 1 145 | npm ERR! 146 | npm ERR! Failed at the puppeteer@1.11.0 install script. 147 | npm ERR! This is probably not a problem with npm. There is likely additional logging output above. 148 | 149 | npm ERR! A complete log of this run can be found in: 150 | npm ERR! C:\Users\shaowei\AppData\Roaming\npm-cache\_logs\2019-01-08T15_13_52_344Z-debug.log 151 | ``` 152 | 153 | ```bash 154 | > yideng-test@1.0.0 e2e d:\code\yideng\yideng-test 155 | > node ./e2e/github.spec.js 156 | 157 | d:\code\yideng\yideng-test\node_modules\rize\dist\utils\error.js:27 158 | throw error; 159 | ^ 160 | 161 | Expected text "xx.js" cannot be found. 162 | at Object. (d:\code\yideng\yideng-test\e2e\github.spec.js:9:4) 163 | at Module._compile (module.js:635:30) 164 | at Object.Module._extensions..js (module.js:646:10) 165 | at Module.load (module.js:554:32) 166 | at tryModuleLoad (module.js:497:12) 167 | at Function.Module._load (module.js:489:3) 168 | at Function.Module.runMain (module.js:676:10) 169 | at startup (bootstrap_node.js:187:16) 170 | npm ERR! code ELIFECYCLE 171 | npm ERR! errno 1 172 | npm ERR! yideng-test@1.0.0 e2e: `node ./e2e/github.spec.js` 173 | npm ERR! Exit status 1 174 | npm ERR! 175 | npm ERR! Failed at the yideng-test@1.0.0 e2e script. 176 | npm ERR! This is probably not a problem with npm. There is likely additional logging output above. 177 | 178 | npm ERR! A complete log of this run can be found in: 179 | npm ERR! C:\Users\shaowei\AppData\Roaming\npm-cache\_logs\2019-01-09T05_05_03_648Z-debug.log 180 | ``` 181 | 182 | 介绍f2etest 183 | [https://github.com/alibaba/f2etest](https://github.com/alibaba/f2etest) 184 | [http://shaofan.org/f2etest/](http://shaofan.org/f2etest/) 185 | [http://shaofan.org/ui-recorder/](http://shaofan.org/ui-recorder/) 186 | [https://f2etest.net/](https://f2etest.net/) 187 | 188 | UI自动化测试 189 | 190 | 最早做UI走查的框架PhantomCSS 191 | 现在我们要用的backstopJS工具,首先要安装这个工具 192 | 193 | 接口测试 194 | 要先安装axios 195 | 要安装mocha 196 | 要安装express 197 | -------------------------------------------------------------------------------- /es5.1-new-feature/article/ASI.md: -------------------------------------------------------------------------------- 1 | # Automatic Semicolon Insertion (ASI) 2 | 3 | [source](https://www.ecma-international.org/ecma-262/5.1/#sec-7.9) 4 | 5 | Certain ECMAScript statements (empty statement, variable statement, expression statement, **do-while** statement, **continue** statement, **break** statement, **return** statement, and **throw** statement) must be terminated with semicolons. Such semicolons may always appear explicitly in the source text. For convenience, however, such semicolons may be omitted from the source text in certain situations. These situations are described by saying that semicolons are automatically inserted into the source code token stream in those situations. 6 | 7 | ## 1. Rules of Automatic Semicolon Insertion 8 | 9 | There are three basic rules of semicolon insertion: 10 | 11 | 1. When, as the program is parsed from left to right, a token (called the offending token) is encountered that is not allowed by any production of the grammar, then a semicolon is automatically inserted before the offending token if one or more of the following conditions is true: 12 | * The offending token is separated from the previous token by at least one LineTerminator. 13 | * The offending token is }. 14 | 2. When, as the program is parsed from left to right, the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete ECMAScript Program, then a semicolon is automatically inserted at the end of the input stream. 15 | 3. When, as the program is parsed from left to right, a token is encountered that is allowed by some production of the grammar, but the production is a restricted production and the token would be the first token for a terminal or nonterminal immediately following the annotation “[no LineTerminator here]” within the restricted production (and therefore such a token is called a restricted token), and the restricted token is separated from the previous token by at least one LineTerminator, then a semicolon is automatically inserted before the restricted token. 16 | 17 | However, there is an additional overriding condition on the preceding rules: a semicolon is never inserted automatically if the semicolon would then be parsed as an empty statement or if that semicolon would become one of the two semicolons in the header of a for statement ([see 12.6.3](https://www.ecma-international.org/ecma-262/5.1/#sec-12.6.3)). 18 | 19 | **NOTE** The following are the only restricted productions in the grammar: 20 | 21 | > *PostfixExpression* : 22 | >  LeftHandSideExpression [no LineTerminator here] ++ 23 | >  LeftHandSideExpression [no LineTerminator here] -- 24 | > 25 | > *ContinueStatement* : 26 | >  **continue** [no LineTerminator here] Identifier ; 27 | > 28 | > *BreakStatement* : 29 | >  **break** [no LineTerminator here] Identifier ; 30 | > 31 | > *ReturnStatement* : 32 | >  **return** [no LineTerminator here] Expression ; 33 | > 34 | > *ThrowStatement* : 35 | >  **throw** [no LineTerminator here] Expression ; 36 | 37 | The practical effect of these restricted productions is as follows: 38 | 39 | When a ++ or -- token is encountered where the parser would treat it as a postfix operator, and at least one LineTerminator occurred between the preceding token and the ++ or -- token, then a semicolon is automatically inserted before the ++ or -- token. 40 | 41 | When a continue, break, return, or throw token is encountered and a LineTerminator is encountered before the next token, a semicolon is automatically inserted after the continue, break, return, or throw token. 42 | 43 | The resulting practical advice to ECMAScript programmers is: 44 | 45 | A postfix ++ or -- operator should appear on the same line as its operand. 46 | 47 | An Expression in a return or throw statement should start on the same line as the return or throw token. 48 | 49 | An Identifier in a break or continue statement should be on the same line as the break or continue token. 50 | 51 | ## 2. Examples of Automatic Semicolon Insertion 52 | 53 | The source 54 | 55 | `{ 1 2 } 3` 56 | 57 | is not a valid sentence in the ECMAScript grammar, even with the automatic semicolon insertion rules. In contrast, the source 58 | 59 | ```js 60 | { 1 61 | 2 } 3 62 | ``` 63 | 64 | is also not a valid ECMAScript sentence, but is transformed by automatic semicolon insertion into the following: 65 | 66 | ```js 67 | { 1 68 | ;2 ;} 3; 69 | ``` 70 | 71 | which is a valid ECMAScript sentence. 72 | 73 | The source 74 | 75 | ```js 76 | for (a; b 77 | ) 78 | ``` 79 | 80 | is not a valid ECMAScript sentence and is not altered by automatic semicolon insertion because the semicolon is needed for the header of a for statement. Automatic semicolon insertion never inserts one of the two semicolons in the header of a for statement. 81 | 82 | The source 83 | 84 | ```js 85 | return 86 | a + b 87 | ``` 88 | 89 | is transformed by automatic semicolon insertion into the following: 90 | 91 | ```js 92 | return; 93 | a + b; 94 | ``` 95 | 96 | NOTE The expression a + b is not treated as a value to be returned by the return statement, because a LineTerminator separates it from the token return. 97 | 98 | The source 99 | 100 | ```js 101 | a = b 102 | ++c 103 | ``` 104 | 105 | is transformed by automatic semicolon insertion into the following: 106 | 107 | ```js 108 | a = b; 109 | ++c; 110 | ``` 111 | 112 | NOTE The token ++ is not treated as a postfix operator applying to the variable b, because a LineTerminator occurs between b and ++. 113 | 114 | The source 115 | 116 | ```js 117 | if (a > b) 118 | else c = d 119 | ``` 120 | 121 | is not a valid ECMAScript sentence and is not altered by automatic semicolon insertion before the else token, even though no production of the grammar applies at that point, because an automatically inserted semicolon would then be parsed as an empty statement. 122 | 123 | The source 124 | 125 | ```js 126 | a = b + c 127 | (d + e).print() 128 | ``` 129 | 130 | is not transformed by automatic semicolon insertion, because the parenthesised expression that begins the second line can be interpreted as an argument list for a function call: 131 | 132 | `a = b + c(d + e).print()` 133 | 134 | In the circumstance that an assignment statement must begin with a left parenthesis, it is a good idea for the programmer to provide an explicit semicolon at the end of the preceding statement rather than to rely on automatic semicolon insertion. -------------------------------------------------------------------------------- /functional-programming/article/2019-1-13.md: -------------------------------------------------------------------------------- 1 | # JavaScript函数式编程 2 | 3 | ## 一、函数式编程基础——范畴论 4 | 5 | 函数式编程的理论基础就是范畴论,虽然不得不成为我们对于数学,总是保佑复杂的心态,但是不可否认的,他确实是科学之母。 6 | 7 | * 范畴论的目的是:规范化数学构造。 8 | * 方法为:使用带标签的有向图。 9 | * 研究内容:各种数学结构之间的关系。 10 | 11 | ### 什么是范畴 12 | 13 | 范畴就是一系列之间存在关系的对象所组成的一个“集合”。这里对象之间的关系就是态射。范畴由以下部分组成: 14 | 15 | 1. 一系列的对象(object). 16 | 2. 一系列的态射(morphism). 17 | 3. 一个组合(composition)操作符,用点(.)表示,用于将态射进行组合。 18 | 19 | ### 函子(functor) 20 | 21 | 函子是范畴之间的map关系。可以理解为范畴之间的态射。 22 | 23 | ## 二、纯函数 24 | 25 | 我们先用数学概念去理解对于函数的定义: 26 | 函数 f 的概念就是,对于输入 x 产生一个输出 y = f(x)。这便是一种最简单的纯函数。引出纯函数的定义就是: 27 | **纯函数的定义是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。** 28 | 29 | 对于上面的总结我们可以拆分成三部分用代码去理解: 30 | 1. 只要每次给定相同的输入值,就一定会得到相同的输出值; 31 | 2. 不会改变原始输入参数,或是外部的环境,所以没有副作用; 32 | 3. 不依頼其他外部的状态,变量或常量。 33 | 34 | 下面我们通过代码的例子去逐一解释上面的内容 35 | 36 | ```js 37 | // 理解1:只要每次给定相同的输入值,就一定会得到相同的输出值 38 | 39 | var arr = [1,2,3,4,5]; 40 | 41 | // Array.slice是纯函数,对于固定的输入,输出总是固定的 42 | // 这很函数式 43 | arr.slice(0,3); //=> [1,2,3] 44 | arr.slice(0,3); //=> [1,2,3] 45 | 46 | // Array.splice是不纯的,对于固定的输入,输出不是固定的 47 | // 这不函数式 48 | arr.splice(0,3); //=> [1,2,3] 49 | arr.splice(0,3); //=> [4,5] 50 | arr.splice(0,3); //=> [] 51 | 52 | //它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。 53 | ``` 54 | 55 | ```js 56 | // 理解2:不会改变原始输入参数,或是外部的环境,所以没有副作用; 57 | 58 | ``` 59 | 60 | ```js 61 | // 理解3:不依頼其他外部的状态,变量或常量 62 | //不是纯函数 63 | var min = 18; 64 | var checkage = age => age > min; 65 | 66 | //这很函数式 67 | var checkage = age => age > 18; 68 | ``` 69 | 70 | 在不纯的版本中,checkage 这个函数的行为不仅取决于输入的参数 age,还取决于一个外部的变量 min,换句话说,这个函数的行为需要由外部的系统环境决定。对于大型系统来说,这种对于外部状态的依赖是造成系统复杂性大大提高的主要原因。 71 | 72 | 可以注意到,纯的 checkage 把关键数字 18 硬编码在函数内部,扩展性比较差,我们可以在后面的柯里化中看到如何用优雅的函数式解决这种问题。 73 | 74 | 纯函数不仅可以有效降低系统的复杂度,还有很多很棒的特性,比如可缓存性: 75 | 76 | ```js 77 | import _ from 'lodash'; 78 | var sin = _.memorize(x => Math.sin(x)); 79 | 80 | //第一次计算的时候会稍慢一点 81 | var a = sin(1); 82 | 83 | //第二次有了缓存,速度极快 84 | var b = sin(1); 85 | ``` 86 | 87 | ## 三、函数的柯里化 88 | 89 | 函数柯里化(curry)的定义很简单:传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。 90 | 91 | 比如对于加法函数 var add = (x, y) => x + y ,我们可以这样进行柯里化: 92 | 93 | ```js 94 | //比较容易读懂的ES5写法 95 | var add = function(x){ 96 | return function(y){ 97 | return x + y 98 | } 99 | } 100 | 101 | //ES6写法,也是比较正统的函数式写法 102 | var add = x => (y => x + y); 103 | 104 | //试试看 105 | var add2 = add(2); 106 | var add200 = add(200); 107 | 108 | add2(2); // =>4 109 | add200(50); // =>250 110 | ``` 111 | 112 | 对于加法这种极其简单的函数来说,柯里化并没有什么大用处。 113 | 114 | 还记得上面那个 checkage 的函数吗?我们可以这样柯里化它: 115 | 116 | ```js 117 | var checkage = min => (age => age > min); 118 | var checkage18 = checkage(18); 119 | checkage18(20); 120 | // =>true 121 | ``` 122 | 123 | 事实上柯里化是一种“预加载”函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法: 124 | 125 | ```js 126 | import { curry } from 'lodash'; 127 | 128 | //首先柯里化两个纯函数 129 | var match = curry((reg, str) => str.match(reg)); 130 | var filter = curry((f, arr) => arr.filter(f)); 131 | 132 | //判断字符串里有没有空格 133 | var haveSpace = match(/\s+/g); 134 | 135 | haveSpace("ffffffff"); 136 | //=>null 137 | 138 | haveSpace("a b"); 139 | //=>[" "] 140 | 141 | filter(haveSpace, ["abcdefg", "Hello World"]); 142 | //=>["Hello world"] 143 | ``` 144 | 145 | ## 四、函数组合 146 | 147 | 学会了使用纯函数以及如何把它柯里化之后,我们会很容易写出这样的“包菜式”代码: 148 | 149 | ```js 150 | h(g(f(x))); 151 | ``` 152 | 153 | 虽然这也是函数式的代码,但它依然存在某种意义上的“不优雅”。为了解决函数嵌套的问题,我们需要用到“函数组合”: 154 | 155 | ```js 156 | //两个函数的组合 157 | var compose = function(f, g) { 158 | return function(x) { 159 | return f(g(x)); 160 | }; 161 | }; 162 | 163 | //或者 164 | var compose = (f, g) => (x => f(g(x))); 165 | 166 | var add1 = x => x + 1; 167 | var mul5 = x => x * 5; 168 | 169 | compose(mul5, add1)(2); 170 | // =>15 171 | ``` 172 | 173 | 我们定义的compose就像双面胶一样,可以把任何两个纯函数结合到一起。当然你也可以扩展出组合三个函数的“三面胶”,甚至“四面胶”“N面胶”。 174 | 175 | 这种灵活的组合可以让我们像拼积木一样来组合函数式的代码: 176 | 177 | ```js 178 | var first = arr => arr[0]; 179 | var reverse = arr => arr.reverse(); 180 | 181 | var last = compose(first, reverse); 182 | 183 | last([1,2,3,4,5]); 184 | // =>5 185 | ``` 186 | 187 | ## 五、Point Free 188 | 189 | 有了柯里化和函数组合的基础知识,下面介绍一下Point Free这种代码风格。 190 | 191 | 细心的话你可能会注意到,之前的代码中我们总是喜欢把一些对象自带的方法转化成纯函数: 192 | 193 | ```js 194 | var map = (f, arr) => arr.map(f); 195 | 196 | var toUpperCase = word => word.toUpperCase(); 197 | 198 | // 这就是有参的,因为有word 199 | var snakeCase = word => word.toLowerCase().replace(/\s+/ig, '_'); 200 | 201 | // 这是pointfree 202 | var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase); 203 | 204 | ``` 205 | 206 | 从另一个角度看,有参的函数的目的是得到一个数据,而pointfree的函数的目的是得到另一个函数。 所以,如下的方程,虽然也有参,也可以认为是pointfree的。 207 | 208 | 这种做法是有原因的。 209 | 210 | ```js 211 | const titlesForYear = year => 212 | pipe( 213 | filter(publishedInYear(year)), 214 | map(book => book.title) 215 | ) 216 | ``` 217 | 218 | Point Free这种模式现在还暂且没有中文的翻译,有兴趣的话可以看看这里的英文解释: 219 | 220 | 用中文解释的话大概就是,不要命名转瞬即逝的中间变量,比如: 221 | 222 | ```js 223 | //这不Piont free 224 | var f = str => str.toUpperCase().split(' '); 225 | ``` 226 | 227 | 这个函数中,我们使用了 str 作为我们的中间变量,但这个中间变量除了让代码变得长了一点以外是毫无意义的。下面改造一下这段代码: 228 | 229 | ```js 230 | var toUpperCase = word => word.toUpperCase(); 231 | var split = x => (str => str.split(x)); 232 | 233 | var f = compose(split(' '), toUpperCase); 234 | 235 | f("abcd efgh"); 236 | // =>["ABCD", "EFGH"] 237 | ``` 238 | 239 | 这种风格能够帮助我们减少不必要的命名,让代码保持简洁和通用。当然,为了在一些函数中写出Point Free的风格,在代码的其它地方必然是不那么Point Free的,这个地方需要自己取舍。 240 | 241 | 六、声明式与命令式代码 242 | 命令式代码的意思就是,我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。 243 | 244 | 而声明式就要优雅很多了,我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。 245 | 246 | ```js 247 | //命令式 248 | var CEOs = []; 249 | for(var i = 0; i < companies.length; i++){ 250 | CEOs.push(companies[i].CEO) 251 | } 252 | 253 | //声明式 254 | var CEOs = companies.map(c => c.CEO); 255 | ``` 256 | 257 | 命令式的写法要先实例化一个数组,然后再对 companies 数组进行for循环遍历,手动命名、判断、增加计数器,就好像你开了一辆零件全部暴露在外的汽车一样,虽然很机械朋克风,但这并不是优雅的程序员应该做的。 258 | 259 | 声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了。 260 | 261 | 函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。 262 | 263 | 相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些不干净的副作用。在复杂的系统中,这对于程序员的心智来说是极大的负担。 264 | 265 | ## 七、尾声 266 | 267 | 任何代码都是要有实际用处才有意义,对于JS来说也是如此。然而现实的编程世界显然不如范例中的函数式世界那么美好,实际应用中的JS是要接触到ajax、DOM操作,NodeJS环境中读写文件、网络操作这些对于外部环境强依赖,有明显副作用的“很脏”的工作。 268 | 269 | 这对于函数式编程来说也是很大的挑战,所以我们也需要更强大的技术去解决这些“脏问题”。我会在下一篇文章中介绍函数式编程的更加高阶一些的知识,例如Functor、Monad等等概念。 270 | 271 | 浏览器对尾调用有优化但是浏览器没有给实践 272 | 273 | 所以尾递归减少了栈侦记录,手动我们进行了优化,但是最终优化是覆盖当前侦,可惜浏览器并没有。 -------------------------------------------------------------------------------- /es5-senior/essence1.md: -------------------------------------------------------------------------------- 1 | # JavaScript语言精粹 2 | 3 | ## 数据类型 4 | 5 | JavaScript是弱类型语言,但并不是没有类型,JavaScript可以识别7种不同的类型: 6 | 1. boolean 7 | 2. number 8 | 3. string 9 | 4. null 10 | 5. underfined 11 | 6. Symbol 12 | 7. object 13 | 14 | typeof false 15 | typeof .2 16 | typeof NaN 17 | typeof ' ' 18 | typeof undefined 19 | typeof Symbol() 20 | typeof new Date() 21 | typeof [ ] 22 | 23 | typeof alert 24 | 25 | typeof null 26 | typeof not_defined_var 27 | 28 | ## 变量 29 | 30 | 在应用程序中,使用变量来为值命名。变量的名称称为 identifiers 31 | 32 | ### 声明 33 | 34 | 1. 使用关键字 var :函数作用域 35 | 2. 使用关键字 let :块作用域(block scope local variable) 36 | 3. 直接使用:全局作用域 37 | 38 | ``` js 39 | var global_var = 1; 40 | 41 | function fn() { 42 | var fn_var = 2; 43 | 44 | if(fn_var > 10) { 45 | let block_var = 3; 46 | global__var2 = 4; 47 | } 48 | } 49 | ``` 50 | 51 | 只声明不赋值,变量的默认值是 underfined 52 | 53 | const 关键字可以声明不可变变量,同样是块作用域。对不可变的理解在对象上的理解需要注意。 54 | 55 | ``` js 56 | const num = 1; 57 | const obj = { 58 | prop: 'value' 59 | }; 60 | 61 | num = 2; // 62 | obj['prop'] = 'value2'; 63 | 64 | obj = [ ] 65 | ``` 66 | 67 | ### 变量提升 68 | 69 | JavaScript中可以引用稍后声明的变量,而不会引发异常,这一概念称为变量声明提升(hoisting) 70 | 71 | ``` js 72 | console.log(a); 73 | var a = 2; 74 | 75 | // 等同于 76 | 77 | var a; 78 | console.log(a); 79 | a = 2; 80 | ``` 81 | 82 | ## 函数 83 | 84 | 一个函数就是一个可以被外部代码调用(或者函数本身递归调用)的子程序 85 | 86 | ### 定义函数 87 | 88 | 1. 函数声明 89 | 2. 函数表达式 90 | 3. Function构造函数 91 | 4. 箭头函数 92 | 93 | ``` js 94 | 95 | function fn( ) { 96 | // ... 97 | } 98 | 99 | var fn = function( ) { 100 | // ... 101 | } 102 | 103 | var fn = new Function(arg1, arg2, arg3, ... arg, funcBody) 104 | 105 | var fn = (param) => { } 106 | ``` 107 | 108 | ### arguments 109 | 110 | arguments对象存放着函数的所有参数,类似数组但不是数组 111 | 112 | ``` js 113 | function foo() { 114 | return arguments; 115 | } 116 | foo(1, 2, 3); 117 | ``` 118 | 119 | ### rest 120 | 121 | ``` js 122 | function foo(...args) { 123 | return args; 124 | } 125 | 126 | foo(1, 2, 3); 127 | 128 | function fn(a, b, ...args) { 129 | return args; 130 | } 131 | 132 | fn(1, 2, 3, 4, 5); 133 | ``` 134 | 135 | ### default 136 | 137 | ``` js 138 | function fn(a = 2, b = 3) { 139 | return a + b; 140 | } 141 | 142 | fn(2, 3); 143 | fn(2); 144 | fn(); 145 | ``` 146 | 147 | ## 对象 148 | 149 | JavaScript中对象是可变 键值集合 (keyed collections) 150 | 151 | ### 定义对象 152 | 153 | 1. 字面量 154 | 2. 构造函数 155 | 156 | var obj = { 157 | prop: 'value', 158 | fn: function() {} 159 | }; 160 | 161 | var date = new Date(); 162 | 163 | ## 构造函数 164 | 165 | 构造函数和普通函数并没有区别,使用new关键字调用就是构造函数,使用构造函数可以实例化一个对象。 166 | 167 | 函数的返回值有两种可能 168 | 1. 显示调用return 返回return后表达式的求值 169 | 2. 没有调用return返回undefined 170 | 171 | ``` js 172 | function People(name, age) { 173 | this.name = name; 174 | this.age = age; 175 | } 176 | 177 | var people = new People('Byron', 26); 178 | ``` 179 | 180 | 构造函数返回值 181 | 1. 没有返回值 182 | 2. 返回简单数据类型 183 | 3. 对象类型 184 | 185 | 前两种情况构造函数返回构造对象的实例,实例化对象正是利用的这个特性 186 | 187 | 第三种构造函数和普通函数表现一致,返回return后表达式的结果。 188 | 189 | ### prototype 190 | 191 | 1. 每个函数都有一个prototype 的对象属性,对象内有一个constructor属性,默认指向函数本身 192 | 2. 每个对象都有一个__proto__的属性,属相指向其父类型的prototype 193 | 194 | 继承 195 | 196 | label statement 197 | 198 | ``` js 199 | loop: 200 | for (var i = 0; i < 10; i++) { 201 | for () { 202 | console.log(j); 203 | if (j === 1) { 204 | break loop; 205 | } 206 | } 207 | } 208 | 209 | console.log(i); 210 | ``` 211 | 212 | 语句与表达式 213 | 214 | ``` js 215 | var x = {a: 1}; 216 | 217 | {a: 1} 218 | 219 | {a:1, b: 2} 220 | ``` 221 | 222 | 语句优先原则,JavaScript进行语法分析时,当遇到语法冲突,优先解释为语句(注意不是表达式), 223 | 224 | `{a: 1}` 可以解释成一个对象的语法树,也可以理解成这是一个语句块, 225 | 226 | 立即执行函数 227 | 228 | ``` js 229 | !function(){}() 230 | +function(){}() 231 | -function(){}() 232 | 233 | (function(){})() 234 | 235 | ``` 236 | 237 | ## 高阶函数 238 | 239 | 高阶函数是把函数当做参数或者返回值是函数的函数 240 | 241 | ### 回调函数 242 | 243 | ```js 244 | [1, 2, 3, 4].forEach(function(item) { 245 | console.log(item); 246 | }); 247 | ``` 248 | 249 | ## 闭包 250 | 251 | 闭包由两部分组成 252 | 1. 函数 253 | 2. 环境:函数创建时作用域内的局部变量 254 | 255 | ```js 256 | function makeCounter(init) { 257 | var init = init || 0; 258 | 259 | return function() { 260 | return ++init; 261 | } 262 | } 263 | 264 | var counter = makeCounter(10); 265 | console.log(counter()); 266 | console.log(counter()); 267 | ``` 268 | 269 | 典型错误 270 | 271 | ```js 272 | // 并不能得出想要的结果 273 | // 因为闭包,i值被保留 274 | // 事件绑定是在for循环执行之后的 275 | for (var i = 0; i < doms.length; i++) { 276 | doms.eq(i).on('click', function (ev) { 277 | console.log(i); 278 | }); 279 | } 280 | 281 | for (var i = 0; i < doms.length; i++) { 282 | (function (i) { 283 | doms.eq(i).on('click', function (ev) { 284 | console.log(i); 285 | }); 286 | })(i); 287 | } 288 | ``` 289 | 290 | ## 惰性函数 291 | 292 | ```js 293 | function eventBinderGenerator() { 294 | if(window.addEventListener) { 295 | return function(element, type, handler) { 296 | element.addEventListener(type, handler, false); 297 | } 298 | } else { 299 | return function(element, type, handler) { 300 | element.attanchEvent('on' + type, handler.bind(element, window.event)); 301 | } 302 | } 303 | } 304 | 305 | var addEvent = eventBinderGenerator(); 306 | ``` 307 | 308 | ## 柯里化 309 | 310 | 一种允许使用部分参数生成函数的方式 311 | 312 | ```js 313 | function isType(type) { 314 | return function(obj) { 315 | return Object.prototype.toString.call(obj) === '[object' + type +']'; 316 | } 317 | } 318 | 319 | var isNumber = isType('Number'); 320 | 321 | console.log(isNumber(1)); 322 | console.log(isNumber('s')); 323 | 324 | var isArray = isType('Array'); 325 | 326 | console.log(isArray(1)); 327 | console.log(isArray([1, 2, 3])); 328 | 329 | function f(n) { 330 | return n * n; 331 | } 332 | 333 | function g(n) { 334 | return n * 2; 335 | } 336 | 337 | console.log(f(g(5))); 338 | 339 | function pipe(f, g) { 340 | return function() { 341 | return f.call(null, g.apply(null, arguments)); 342 | } 343 | } 344 | 345 | var fn = pipe(f, g); 346 | 347 | console.log(fn(5)); 348 | ``` 349 | 350 | 你可能会问,Underscore 和 Lodash 已经这么流行了,为什么还要学习好像雷同的 Ramda 呢? 351 | 352 | ## 尾递归 353 | 354 | 1. 尾调用是指某个函数的最后一步是调用另一个函数 355 | 2. 函数调用自身,称为递归 356 | 3. 如果尾调用自身,就称为尾递归 357 | 358 | 递归很容易发生"栈溢出”错误(stack overflow) 359 | 360 | ```js 361 | function factorial(n) { 362 | if(n === 1) return 1; 363 | return n * factorial(n - 1); 364 | } 365 | 366 | factorial(5) 367 | ``` 368 | 369 | ## 反柯里化 370 | 371 | ### 柯里化减少参数 372 | -------------------------------------------------------------------------------- /functional-programming/article/2019-1-22.md: -------------------------------------------------------------------------------- 1 | # 函数式编程——原来你也是编程范式 2 | 3 | ## 编程范式有点抽象 4 | 5 | 在学习面向对象编程或者是函数式编程的时候,总会提到“编程范式”这四个字,这篇文章正式为了解释“编程范式”,以及它和函数式编程的关系。 6 | 7 | 首先,**编程范式(Programming Paradigm)是某种编程语言的典型编程风格或者说是编程方式。** 8 | 9 | 随着编程方法学和软件工程学的深入,特别是OO思想的普及,范式(Paradigm)以及编程范式等术语渐渐出现在人们面前。面向对象编程(OOP)常常被誉为是一种革命性的思想,正因为它不同于其他的各种编程范式。编程范式也许是学习任何一门编程语言时要理解的最重要的术语。 10 | 托马斯.库尔提出“科学的革命”的范式论后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词。编程范式一般包括三个方面,以OOP为例: 11 | 1. 学科的逻辑体系——规则范式:如 类/对象、继承、动态绑定、方法改写、对象替换等等机制。 12 | 2. 心理认知因素——心理范式:按照面向对象编程之父Alan Kay的观点,“计算就是模拟”。OO范式极其重视隐喻(metaphor)的价值,通过拟人化,按照自然的方式模拟自然。 13 | 3. 自然观/世界观——观念范式:强调程序的组织技术,视程序为松散耦合的对象/类的组合,用继承机制将类组织成一个层次结构,把程序运行视为相互服务的对象之间的对话。 14 | 15 | 需要再次提醒的是:编程范式是编程语言的一种分类方式,它并不针对某种编程语言。就编程语言而言,一种语言可以适用多种编程范式。例如JavaScript、Java对于面向对象编程和函数式编程都是支持的。 16 | 17 | ## 主流编程范式 18 | 19 | 按照不同的分类标准,对于编程范式的分类也略有不同。 20 | 计算机科学中主流的: 21 | * 面向对象编程 22 | * 面向过程编程 23 | * 泛型编程 24 | 25 | 工程业务框架中特有的: 26 | * 事件驱动编程 27 | * 并发编程,分布式编程。 28 | 29 | 三个主流 30 | * 声明式编程 31 | * 命令式编程 32 | * 函数式编程 33 | 34 | **备注:**面向过程编程也就是命令式编程。 35 | 36 | 下面我们将着重介绍面向过程编程、面向对象编程、函数式编程 37 | 38 | ## 面向过程编程(Procedure Oriented) 39 | 40 | 简单的理解:面向过程编程以过程为中心的编程思想,是具体化的,流程化的。解决一个问题,需要一步一步分析需要怎样,然后需要怎样,一步一步实现的。 41 | 42 | 面向过程编程,也称为命令式编程,应该是最原始的、也是我们最熟悉的一种传统编程方式。从本质上讲,它是“冯.诺依曼机”运行机制的抽象,它的编程思想方式源于计算机指令的顺序排列。(也就是说:过程化语言模拟的是计算机机器的系统构造,而并不是基于语言的使用者的个人能力和倾向。这一点我们应该都很清楚,比如我们最早曾经使用过的单片机的汇编语言。) 43 | 44 | ### 过程化编程的步骤是: 45 | 46 | 首先,我们必须将待解问题的解决方案抽象为一系列概念化的步骤。 47 | 48 | 然后通过编程的方式将这些步骤转化为程序指令集(算法),而这些指令按照一定的顺序排列,用来说明如何执行一个任务或解决一个问题。这就意味着,程序员必须要知道程序要完成什么,并且告诉计算机如何来进行所需的计算工作,包括每个细节操作。简言之,就是将计算机看作一个善始善终服从命令的装置。 49 | 50 | 所以在过程化编程中,把待解问题规范化、抽象为某种算法是解决问题的关键步骤。其次,才是编写具体算法和完成相应的算法实现问题的正确解决。当然,程序员对待解问题的抽象能力也是非常重要的因素,但这本身已经与编程语言无关了。程序流程图是过程化语言进行程序编写的有效辅助手段。 51 | 尽管现存的计算机编程语言很多,但是人们把所有支持过程化编程范式的编程语言都被归纳为过程化编程语言。例如机器语言、汇编语言、BASIC、COBOL、C 、FORTRAN等等许多第三代编程语言都被归纳为过程化语言。 52 | 53 | 过程化语言特别适合解决线性(或者说按部就班)的算法问题。它强调“自上而下(自顶向下)”、“精益求精”的设计方式。这种方式非常类似我们的工作和生活方式,因为我们的日常活动都是按部就班的顺序进行的。 54 | 55 | 过程化语言趋向于开发运行较快且对系统资源利用率较高的程序。过程化语言非常的灵活并强大,同时有许多经典应用范例,这使得程序员可以用它来解决多种问题。 56 | 57 | 过程化语言的不足之处就是它不适合某些种类问题的解决,例如那些非结构化的具有复杂算法的问题。问题出现在,过程化语言必须对一个算法加以详尽的说明,并且其中还要包括执行这些指令或语句的顺序。实际上,给那些非结构化的具有复杂算法的问题给出详尽的算法是极其困难的。 58 | 59 | 广泛引起争议和讨论的地方是:无条件分支,或goto语句,它是大多数过程式编程语言的组成部分,反对者声称:goto语句可能被无限地滥用;它给程序设计提供了制造混 乱的机会。目前达成的共识是将它保留在大多数语言中,对于它所具有的危险性,应该通过程序设计的规定将其最小化。 60 | 61 | ## 面向对象编程 62 | 63 | 在面向对象编程范式没有出现,面向过程编程大行其道。上面我们介绍过:面向过程编程就是分析出解决问题所需要的步骤,然后采用分支循环用函数把这些步骤一步一步实现,使用的时候一个一个一次调用就可以了。面向过程编程虽然也可以解决问题,但是也存在着重用性差、可维护性差、开发过程复杂等缺点。 64 | 尤其是随着时间的发展,软件开发中出现的问题越加突出: 65 | 66 | * 软件复杂庞大 67 | * 需求的不断变更 68 | * 很多软件维护阶段困难重重 69 | 70 | 为解决以上问题,面向对象编程应运而生。面向对象编程具有很好的可读性、可维护性和可扩展性,并且保证了代码的高内聚低耦合。具备以上优点的面向对象编程具备四大特性: 71 | * 抽象 72 | * 封装 73 | * 继承 74 | * 多态 75 | 76 | 面向对象的程序设计包括了三个基本概念:封装性、继承性、多态性。面向对象的程序语言通过类、方法、对象和消息传递,来支持面向对象的程序设计范式。 77 | 1. 对象 78 | 世间万事万物都是对象。 79 | 面向对象的程序设计的抽象机制是将待解问题抽象为面向对象的程序中的对象。利用封装使每个对象都拥有个体的身份。程序便是成堆的对象,彼此通过消息的传递,请求其它对象 进行工作。 80 | 2. 类 81 | 每个对象都是其类中的一个实体。 82 | 物以类聚——就是说明:类是相似对象的集合。类中的对象可以接受相同的消息。换句话说:类包含和描述了“具有共同特性(数据元素)和共同行为(功能)”的一组对象。 83 | 比如:苹果、梨、橘子等等对象都属于水果类。 84 | 3. 封装 85 | 封装(有时也被称为信息隐藏)就是把数据和行为结合在一个包中,并对对象的使用者隐藏数据的实现过程。信息隐藏是面向对象编程的基本原则,而封装是实现这一原则的一种方 式。 86 | 封装使对象呈现出“黑盒子”特性,这是对象再利用和实现可靠性的关键步骤。 87 | 4. 接口 88 | 每个对象都有接口。接口不是类,而是对符合接口需求的类所作的一套规范。接口说明类应该做什么但不指定如何作的方法。一个类可以有一个或多个接口。 89 | 5. 方法 90 | 方法决定了某个对象究竟能够接受什么样的消息。面向对象的设计有时也会简单地归纳为“将消息发送给对象”。 91 | 6. 继承 92 | 继承的思想就是允许在已存在类的基础上构建新的类。一个子类能够继承父类的所有成员,包括属性和方法。 93 | 继承的主要作用:通过实现继承完成代码重用;通过接口继承完成代码被重用。继承是一种规范的技巧,而不是一种实现的技巧。 94 | 7. 多态 95 | 多态提供了“接口与实现分离”。多态不但能改善程序的组织架构及可读性,更利于开发出“可扩充”的程序。 96 | 继承是多态的基础。多态是继承的目的。 97 | 合理的运用基于类继承的多态、基于接口继承的多态和基于模版的多态,能增强程序的简洁性、灵活性、可维护性、可重用性和可扩展性。 98 | 99 | 面向对象技术一方面借鉴了哲学、心理学、生物学的思考方式,另一方面,它是建立在其他编程技术之上的,是以前的编程思想的自然产物。 100 | 如果说结构化软件设计是将函数式编程技术应用到命令式语言中进行程序设计,面向对象编程不过是将函数式模型应用到命令式程序中的另一途径,此时,模块进步为对象,过程龟缩到class的成员方法中。OOP的很多技术——抽象数据类型、信息隐藏、接口与实现分离、对象生成功能、消息传递机制等等,很多东西就是结构化软件设计所拥有的、或者在其他编程语言中单独出现。但只有在面向对象语言中,他们才共同出现,以一种独特的合作方式互相协作、互相补充。 101 | 102 | ## 函数式编程 103 | 104 | 函数式编程属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。 105 | 106 | ### 特点 107 | 108 | 1. 闭包和高阶函数 109 | 函数编程支持函数作为第一类对象,有时称为闭包或者仿函数(functor)对象。实质上,闭包是起函数的作用并可以像对象一样操作的对象。与此类似,FP 语言支持高阶函数。高阶函数可以用另一个函数(间接地,用一个表达式) 作为其输入参数,在某些情况下,它甚至返回一个函数作为其输出参数。这两种结构结合在一起使得可以用优雅的方式进行模块化编程,这是使用 FP 的最大好处。 110 | 111 | 2. 惰性计算 112 | 除了高阶函数和仿函数(或闭包)的概念,FP 还引入了惰性计算的概念。在惰性计算中,表达式不是在绑定到变量时立即计算,而是在求值程序需要产生表达式的值时进行计算。延迟的计算使您可以编写可能潜在地生成无穷输出的函数。因为不会计算多于程序的其余部分所需要的值,所以不需要担心由无穷计算所导致的 out-of-memory 错误。一个惰性计算的例子是生成无穷 Fibonacci 列表的函数,但是对第n个Fibonacci 数的计算相当于只是从可能的无穷列表中提取一项。 113 | 114 | 3. 递归 115 | FP 还有一个特点是用递归做为控制流程的机制。例如,Lisp 处理的列表定义为在头元素后面有子列表,这种表示法使得它自己自然地对更小的子列表不断递归。 116 | 函数式编程具有五个鲜明的特点。 117 | 118 | 4. 函数是"第一等公民" 119 | 所谓"第一等公民"(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。 120 | 121 | 5. 只用"表达式",不用"语句" 122 | "表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。 123 | 原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。"语句"属于对系统的读写操作,所以就被排斥在外。 124 | 当然,实际应用中,不做I/O是不可能的。因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。 125 | 126 | 6. 没有"副作用" 127 | 所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。 128 | 函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。 129 | 130 | 7. 不修改状态 131 | 上一点已经提到,函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。 132 | 在其他类型的语言中,变量往往用来保存"状态"(state)。不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递归。下面的代码是一个将字符串逆序排列的函数,它演示了不同的参数如何决定了运算所处的"状态"。 133 | 134 | 8. 引用透明性 135 | 函数程序通常还加强引用透明性,即如果提供同样的输入,那么函数总是返回同样的结果。就是说,表达式的值不依赖于可以改变值的全局状态。这使您可以从形式上推断程序行为,因为表达式的意义只取决于其子表达式而不是计算顺序或者其他表达式的副作用。这有助于验证正确性、简化算法,甚至有助于找出优化它的方法。 136 | 137 | 9. 副作用 138 | 副作用是修改系统状态的语言结构。因为 FP 语言不包含任何赋值语句,变量值一旦被指派就永远不会改变。而且,调用函数只会计算出结果 ── 不会出现其他效果。因此,FP 语言没有副作用。 139 | 140 | ### 优点 141 | 142 | 1. 代码简洁,开发快速 143 | 函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。 144 | Paul Graham在《黑客与画家》一书中写道:同样功能的程序,极端情况下,Lisp代码的长度可能是C代码的二十分之一。 145 | 如果程序员每天所写的代码行数基本相同,这就意味着,"C语言需要一年时间完成开发某个功能,Lisp语言只需要不到三星期。反过来说,如果某个新功能,Lisp语言完成开发需要三个月,C语言需要写五年。"当然,这样的对比故意夸大了差异,但是"在一个高度竞争的市场中,即使开发速度只相差两三倍,也足以使得你永远处在落后的位置。" 146 | 147 | 2. 接近自然语言,易于理解 148 | 函数式编程的自由度很高,可以写出很接近自然语言的代码。 149 | 前文曾经将表达式 `(1 + 2) * 3 - 4` ,写成函数式语言: 150 | `subtract(multiply(add(1,2), 3), 4)` 151 | 对它进行变形,不难得到另一种写法: 152 | `add(1,2).multiply(3).subtract(4)` 153 | 这基本就是自然语言的表达了。再看下面的代码,大家应该一眼就能明白它的意思吧: 154 | `merge([1,2],[3,4]).sort().search("2")` 155 | 因此,函数式编程的代码更容易理解。 156 | 157 | 3. 更方便的代码管理 158 | 函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。 159 | 160 | 4. 代码的热升级 161 | 函数式编程没有副作用,只要保证接口不变,内部实现是外部无关的。所以,可以在运行状态下直接升级代码,不需要重启,也不需要停机。Erlang语言早就证明了这一点,它是瑞典爱立信公司为了管理电话系统而开发的,电话系统的升级当然是不能停机的。 162 | 163 | 5. 易于"并发编程" 164 | 函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。 165 | 请看下面的代码: 166 | 167 | ```js 168 | var s1 = Op1(); 169 | var s2 = Op2(); 170 | var s3 = concat(s1, s2); 171 | ``` 172 | 173 | 由于s1和s2互不干扰,不会修改变量,谁先执行是无所谓的,所以可以放心地增加线程,把它们分配在两个线程上完成。其他类型的语言就做不到这一点,因为s1可能会修改系统状态,而s2可能会用到这些状态,所以必须保证s2在s1之后运行,自然也就不能部署到其他线程上了。 174 | 多核CPU是将来的潮流,所以函数式编程的这个特性非常重要。 -------------------------------------------------------------------------------- /question-bank/JavaScript/2019-01-02.md: -------------------------------------------------------------------------------- 1 | # 2019年1月2日 JavaScript第一次检测 2 | 3 | ## 1.请写出弹出值,并解释为什么。 4 | 5 | ```js 6 | console.info(a); 7 | a(); 8 | var a = 3; 9 | function a() { 10 | console.info(10) 11 | } 12 | console.info(a); 13 | a = 6; 14 | a(); 15 | ``` 16 | 17 | ```js 18 | var a = 3; 19 | function a() { 20 | console.info(10) 21 | } 22 | a(); 23 | ``` 24 | 25 | 变量提升,提升到当前作用域的顶端。 26 | 27 | 涉及知识点: 28 | 1. JavaScript中存在变量提升和函数提升现象; 29 | 2. 函数提升的优先级大于变量提升。 30 | 3. 函数提升的过程中会把函数声明转为函数表达式。 31 | 32 | ```js 33 | function test() { 34 | console.info(1); 35 | } 36 | function init() { 37 | if(false) { 38 | function test() { 39 | console.info(2) 40 | } 41 | } 42 | test(); 43 | } 44 | init(); 45 | ``` 46 | 47 | 变量提升的过程的理解,以及每个版本浏览器对于变量提升不同的解释。 48 | 49 | ## 2.请写出如下输出值,并写出把注释掉的代码取消注释的值,并解释为什么。 50 | 51 | ```js 52 | this.a = 20; 53 | var test = { 54 | a: 40, 55 | init: () => { 56 | console.info(this.a); 57 | function go() { 58 | // this.a = 60; 59 | console.info(this.a); 60 | } 61 | go.prototype.a = 50; 62 | return go; 63 | } 64 | } 65 | // var p = test.init(); 66 | // p(); 67 | new (test.init())(); 68 | ``` 69 | 70 | 基础知识: 71 | this的指向问题,先看下面这个代码 72 | 73 | 自动补全分号问题 74 | 逻辑运算符和小括号 75 | 76 | ```js 77 | function test() { 78 | console.info(this); 79 | } 80 | var obj = { 81 | f: test 82 | } 83 | (obj.f)(); 84 | (false || obj.f)(); 85 | ``` 86 | 87 | ## 3.请写出如下点击li的输出值,并用三种正确输出li里的数字。 88 | 89 | ```html 90 | 91 |
    92 |
  • 1
  • 93 |
  • 2
  • 94 |
  • 3
  • 95 |
  • 4
  • 96 |
  • 5
  • 97 |
  • 6
  • 98 |
99 | 108 | ``` 109 | 110 | 答案: 111 | 112 | JavaScript异步队列和同步执行栈 113 | 114 | ```js 115 | // 这种做法比较巧妙的运用this 116 | var list_li = document.getElementsByTagName("li"); 117 | console.info(list_li); 118 | for (var i = 0; i < list_li.length; i++) { 119 | list_li[i].onclick = function () { 120 | console.info(this.innerHTML); 121 | } 122 | } 123 | // 闭包的正经运用 124 | var list_li = document.getElementsByTagName("li"); 125 | console.info(list_li); 126 | for (var i = 0; i < list_li.length; i++) { 127 | (function (i) { 128 | list_li[i].onclick = function () { 129 | console.info(i + 1); 130 | } 131 | })(i); 132 | } 133 | 134 | // let、const的块级作用域 135 | var list_li = document.getElementsByTagName("li"); 136 | console.info(list_li); 137 | for (let i = 0; i < list_li.length; i++) { 138 | list_li[i].onclick = function () { 139 | console.info(i + 1); 140 | } 141 | } 142 | 143 | // 事件代理,但是有点偏题 144 | window.onclick = function (event) { 145 | var event = event || window.event; 146 | var target = event.target || event.srcElement; 147 | if (target.nodeName.toLowerCase() === 'li') { 148 | console.info(target.innerHTML) 149 | } 150 | } 151 | ``` 152 | 153 | ## 4.写出输出值,并解释为什么。 154 | 155 | ```js 156 | function test(m) { 157 | m = { v: 5 } 158 | } 159 | var m = { k: 5 }; 160 | test(m); 161 | console.info(m.v); 162 | ``` 163 | 164 | 这道题目的变种是这样的: 165 | 166 | ```js 167 | function test(m) { 168 | m.v = 5 169 | } 170 | var m = { k: 5 }; 171 | test(m); 172 | console.info(m.v); 173 | ``` 174 | 175 | 理解按值传递和重新开辟内存空间 176 | 177 | ## 5.请写出代码执行结果,并解释为什么。 178 | 179 | ```js 180 | function yideng() { 181 | console.info(1); 182 | } 183 | (function () { 184 | if (false) { 185 | function yideng() { 186 | console.info(2); 187 | } 188 | } 189 | yideng(); 190 | })(); 191 | ``` 192 | 193 | 涉及知识点: 194 | 1. JavaScript深刻理解函数提升 195 | 2. 函数提升的过程中会把函数声明转为函数表达式 196 | 197 | ## 6.(编程题)请用一句话算出0~100分数之间学生的学生等级,如90~100输出为1等生、80~90为2等生以此类推。不允许使用if switch等。 198 | 199 | ## 7.请用一句话遍历变量a。(禁止用for已知var a = "abc") 200 | 201 | ```js 202 | var a = "abcd"; 203 | Object.keys(a).forEach(function (key) { 204 | console.log(key, a[key]); 205 | }); 206 | 207 | Array.prototype.slice.call 208 | 209 | [...a].forEach 210 | ``` 211 | 212 | ## 8.(编程题)请在下面写出JavaScript面向对象编程的混合式继承。并写出ES6版本的继承。要求:汽车是父类,Cruze是子类。父类有颜色、价格属性,有售卖的方法。Cruze子类实现父类颜色是红色,价格是140000,售卖方法实现输出如下语句:将红色的Cruze卖给了小王,价格是14万。 213 | 214 | ```js 215 | function Car(color, sale) { 216 | this.color = color; 217 | this.sale = sale; 218 | } 219 | Car.prototype.sell = function () { 220 | console.info(`There a ${this.color} car is selling.`); 221 | } 222 | 223 | function Cruze(color, sale) { 224 | Car.call(this, color, sale); 225 | } 226 | 227 | function inherite(surperType, subType) { 228 | let prototype = Object.create(surperType.prototype); 229 | prototype.constructor = subType; 230 | subType.prototype = prototype; 231 | } 232 | 233 | inherite(Car, Cruze); 234 | 235 | var cruze = new Cruze('red', 140000); 236 | cruze.sell(); 237 | ``` 238 | 239 | ## 9.请你写出如何利用ECMAScript6/7(小demo)优化多步异步嵌套的代码? 240 | 241 | ``` js 242 | function runAsync1() { 243 | var p = new Promise(function (resolve, reject) { 244 | //做一些异步操作 245 | setTimeout(function () { 246 | console.log('异步任务1执行完成'); 247 | resolve('随便什么数据1'); 248 | }, 1000); 249 | }); 250 | return p; 251 | } 252 | function runAsync2() { 253 | var p = new Promise(function (resolve, reject) { 254 | //做一些异步操作 255 | setTimeout(function () { 256 | console.log('异步任务2执行完成'); 257 | resolve('随便什么数据2'); 258 | }, 2000); 259 | }); 260 | return p; 261 | } 262 | function runAsync3() { 263 | var p = new Promise(function (resolve, reject) { 264 | //做一些异步操作 265 | setTimeout(function () { 266 | console.log('异步任务3执行完成'); 267 | resolve('随便什么数据3'); 268 | }, 2000); 269 | }); 270 | return p; 271 | } 272 | 273 | runAsync1() 274 | .then(function (data) { 275 | console.log(data); 276 | return runAsync2(); 277 | }) 278 | .then(function (data) { 279 | console.log(data); 280 | return runAsync3(); 281 | }) 282 | .then(function (data) { 283 | console.log(data); 284 | }); 285 | ``` 286 | 287 | ## 10.(仔细思考)写出如下代码的执行结果,并解释为什么。 288 | 289 | ```js 290 | var length = 10; 291 | function fn() { 292 | console.info(this.length); 293 | } 294 | var yideng = { 295 | length: 5, 296 | method: function (fn) { 297 | fn(); 298 | arguments[0](); 299 | } 300 | }; 301 | yideng.method(fn, 1); 302 | ``` -------------------------------------------------------------------------------- /es5-senior/article/javascript-iife.md: -------------------------------------------------------------------------------- 1 | # JavaScript中立即执行函数详解 2 | 3 | 本文来自网上一篇很好的译文。 4 | 译文 :[http://www.cnblogs.com/zichi/p/4401755.html](http://www.cnblogs.com/zichi/p/4401755.html) 5 | 原文 :[http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife](http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife) 6 | 7 | ## 我们要说的到底是什么? 8 | 9 | 在javascript中,每一个函数在被调用的时候都会创建一个执行上下文,在该函数内部定义的变量和函数只能在该函数内部被使用,而正是因为这个上下文,使得我们在调用函数的时候能创建一些私有变量。 10 | 11 | ``` js 12 | // makeCounter函数返回的是一个新的函数,该函数对makeCounter里的局部变量i享有使用权 13 | function makeCounter() { 14 | // i只是makeCounter函数内的局部变量 15 | var i = 0; 16 | return function () { 17 | console.log(++i); 18 | }; 19 | } 20 | // 注意counter和counter2是不同的实例,它们分别拥有自己范围里的i变量 21 | // 注意这里有闭包,变量没有被回收。 22 | var counter = makeCounter(); 23 | counter(); // 1 24 | counter(); // 2 25 | var counter2 = makeCounter(); 26 | counter2(); // 1 27 | counter2(); // 2 28 | i; 29 | // Uncaught ReferenceError: i is not defined 30 | // 报错,i没有定义,它只是makeCounter内部的局部变量 31 | ``` 32 | 33 | 很多情况下我们并不需要像以上代码一样初始化很多实例,甚至有时候并不需要返回值。 34 | 35 | ### 问题的核心 36 | 37 | 现在我们定义了一个函数(`function foo(){}` 或者 `var foo = function(){}`),函数名后加上一对小括号即可完成对该函数的调用,比如下面的代码: 38 | 39 | ``` js 40 | var foo = function(){ /* code */ }; 41 | foo(); 42 | ``` 43 | 44 | 接着我们来看下面的代码: 45 | 46 | ``` js 47 | function(){ /* code */ }(); // SyntaxError: Unexpected token ( 48 | ``` 49 | 50 | 报错了,这是为何?这是因为在javascript代码解释时,当遇到function关键字时,会默认把它当做是一个函数声明,而不是函数表达式,如果没有把它显视地表达成函数表达式,就报错了,因为函数声明需要一个函数名,而上面的代码中函数没有函数名。(以上代码,也正是在执行到第一个左括号(时报错,因为(前理论上是应该有个函数名的。) 51 | 52 | ### 一波未平一波又起 53 | 54 |   有意思的是,如果我们给它函数名,然后加上()立即调用,同样也会报错,而这次报错原因却不相同: 55 | 56 | ``` js 57 | function foo(){ /* code */ }(); // SyntaxError: Unexpected token ) 58 | ``` 59 | 60 | 为什么会这样?在一个表达式后面加上括号,表示该表达式立即执行;而如果是在一个语句后面加上括号,该括号完全和之前的语句不搭嘎,而只是一个分组操作符,用来控制运算中的优先级(小括号里的先运算)。所以以上代码等价于 61 | 62 | ```js 63 | function foo(){ /* code */ } 64 | (); // SyntaxError: Unexpected token ) 65 | ``` 66 | 67 | 相当于先声明了一个叫foo的函数,之后进行()内的表达式运算,但是()(分组操作符)内的表达式不能为空,所以报错。(以上代码,也就是执行到右括号时,发现表达式为空,所以报错)。 68 | 69 |   如果想要了解更多,可以参考[ECMA-262-3 in detail. Chapter 5. Functions](http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/#question-about-surrounding-parentheses) 70 | 71 | ## 立即执行函数 72 | 73 | 看到这里,相信你一定迫不及待地想知道究竟如何做了吧,其实很简单,只需要用括号全部括起来即可,比如下面这样: 74 | 75 | ```js 76 | (function(){ /* code */ }()); 77 | ``` 78 | 79 | 为什么这样就能立即执行并且不报错呢?因为在javascript里,括号内部不能包含语句,当解析器对代码进行解释的时候,先碰到了(),然后碰到function关键字就会自动将()里面的代码识别为函数表达式而不是函数声明。 80 | 81 | 而立即执行函数并非只有上面的一种写法,写法真是五花八门: 82 | 83 | ``` js 84 | // 最常用的两种写法 85 | (function () { /* code */ }()); // 老道推荐写法 86 | (function () { /* code */ })(); // 当然这种也可以 87 | [function () { /* code */ }()]; 88 | 89 | // 括号和JS的一些操作符(如 = && || ,等)可以在函数表达式和函数声明上消除歧义 90 | // 如下代码中,解析器已经知道一个是表达式了,于是也会把另一个默认为表达式 91 | // 但是两者交换则会报错 92 | var i = function () { return 10; }(); 93 | true && function () { /* code */ }(); 94 | 0, function () { /* code */ }(); 95 | 96 | // 如果你不怕代码晦涩难读,也可以选择一元运算符 97 | ! function () { /* code */ }(); 98 | ~ function () { /* code */ }(); 99 | - function () { /* code */ }(); 100 | + function () { /* code */ }(); 101 | 102 | // 还有一些关键字 103 | delete function( ) { } ( ); 104 | typeof function( ) { } ( ); 105 | void function( ) { } ( ); 106 | 107 | // 你也可以这样 108 | new function () { /* code */ } 109 | new function () { /* code */ }() // 带参数 110 | ``` 111 | 112 | 无论何时,给立即执行函数加上括号是个好习惯 113 |   通过以上的介绍,我们大概了解通过()可以使得一个函数表达式立即执行。 114 | 115 |   有的时候,我们实际上不需要使用()使之变成一个函数表达式,啥意思?比如下面这行代码,其实不加上()也不会保错: 116 | 117 | ```js 118 | var i = function(){ return 10; }(); 119 | ``` 120 | 121 | 但是我们依然推荐加上(): 122 | 123 | ```js 124 | var i = (function(){ return 10; }()); 125 | ``` 126 | 127 | 为什么?因为我们在阅读代码的时候,如果function内部代码量庞大,我们不得不滚动到最后去查看function(){}后是否带有()来确定i值是个function还是function内部的返回值。所以为了代码的可读性,请尽量加上()无论是否已经是表达式。 128 | 129 | 立即执行函数与闭包的暧昧关系 130 |   立即执行函数能配合闭包保存状态。 131 | 132 |   像普通的函数传参一样,立即执行函数也能传参数。如果在函数内部再定义一个函数,而里面的那个函数能引用外部的变量和参数(闭包),利用这一点,我们能使用立即执行函数锁住变量保存状态。 133 | 134 | ```js 135 | // 并不会像你想象那样的执行,因为i的值没有被锁住 136 | // 当我们点击链接的时候,其实for循环已经执行完了 137 | // 于是在点击的时候i的值其实已经是elems.length了 138 | var elems = document.getElementsByTagName( 'a' ); 139 | 140 | for ( var i = 0; i < elems.length; i++ ) { 141 | 142 | elems[ i ].addEventListener( 'click', function(e){ 143 | e.preventDefault(); 144 | alert( 'I am link #' + i ); 145 | }, 'false' ); 146 | 147 | } 148 | 149 | 150 | // 这次我们得到了想要的结果 151 | // 因为在立即执行函数内部,i的值传给了lockedIndex,并且被锁在内存中 152 | // 尽管for循环结束后i的值已经改变,但是立即执行函数内部lockedIndex的值并不会改变 153 | var elems = document.getElementsByTagName( 'a' ); 154 | 155 | for ( var i = 0; i < elems.length; i++ ) { 156 | 157 | (function( lockedInIndex ){ 158 | 159 | elems[ i ].addEventListener( 'click', function(e){ 160 | e.preventDefault(); 161 | alert( 'I am link #' + lockedInIndex ); 162 | }, 'false' ); 163 | 164 | })( i ); 165 | 166 | } 167 | 168 | 169 | // 你也可以这样,但是毫无疑问上面的代码更具有可读性 170 | var elems = document.getElementsByTagName( 'a' ); 171 | 172 | for ( var i = 0; i < elems.length; i++ ) { 173 | 174 | elems[ i ].addEventListener( 'click', (function( lockedInIndex ){ 175 | return function(e){ 176 | e.preventDefault(); 177 | alert( 'I am link #' + lockedInIndex ); 178 | }; 179 | })( i ), 'false' ); 180 | 181 | } 182 | ``` 183 | 184 | 其实上面代码的lockedIndex也可以换成i,因为两个i是在不同的作用域里,所以不会互相干扰,但是写成不同的名字更好解释。以上便是立即执行函数+闭包的作用。 185 | 186 | 我为什么更愿意称它是“立即执行函数”而不是“自执行函数” 187 |   IIFE的称谓在现在似乎已经得到了广泛推广(不知道是不是原文作者的功劳?),而原文写于10年,似乎当时流行的称呼是自执行函数(Self-executing anonymous function),接下去作者开始为了说明立即执行函数的称呼好于自执行函数的称呼开始据理力争,有点咬文嚼字,不过也蛮有意思的,我们来看看作者说了些什么。 188 | 189 | ```js 190 | // 这是一个自执行函数,函数内部执行的是自己,递归调用 191 | function foo() { foo(); } 192 | 193 | // 这是一个自执行匿名函数,因为它没有函数名 194 | // 所以如果要递归调用自己的话必须用arguments.callee 195 | var foo = function() { arguments.callee(); }; 196 | 197 | // 这可能也算是个自执行匿名函数,但仅仅是foo标志引用它自身 198 | // 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数 199 | var foo = function() { foo(); }; 200 | 201 | // 有些人叫它自执行匿名函数,尽管它没有执行自己,只是立即执行而已 202 | (function(){ /* code */ }()); 203 | 204 | // 给函数表达式添加了标志名称,可以方便debug 205 | // 但是一旦添加了标志名称,这个函数就不再是匿名的了 206 | (function foo(){ /* code */ }()); 207 | 208 | // 立即执行函数也可以自执行,不过不常用罢了 209 | (function(){ arguments.callee(); }()); 210 | (function foo(){ foo(); }()); 211 | ``` 212 | 213 | 我的理解是作者认为自执行函数是函数内部调用自己(递归调用),而立即执行函数就如字面意思,该函数立即执行即可。其实现在也不用去管它了,就叫IIFE好了。 214 | 215 | 最后的旁白:模块模式 216 |   立即执行函数在模块化中也大有用处。用立即执行函数处理模块化可以减少全局变量造成的空间污染,构造更多的私有变量。 217 | 218 | ```js 219 | // 创建一个立即执行的匿名函数 220 | // 该函数返回一个对象,包含你要暴露的属性 221 | // 如下代码如果不使用立即执行函数,就会多一个属性i 222 | // 如果有了属性i,我们就能调用counter.i改变i的值 223 | // 对我们来说这种不确定的因素越少越好 224 | 225 | var counter = (function(){ 226 | var i = 0; 227 | 228 | return { 229 | get: function(){ 230 | return i; 231 | }, 232 | set: function( val ){ 233 | i = val; 234 | }, 235 | increment: function() { 236 | return ++i; 237 | } 238 | }; 239 | }()); 240 | 241 | // counter其实是一个对象 242 | 243 | counter.get(); // 0 244 | counter.set( 3 ); 245 | counter.increment(); // 4 246 | counter.increment(); // 5 247 | 248 | counter.i; // undefined i并不是counter的属性 249 | i; // ReferenceError: i is not defined (函数内部的是局部变量) 250 | ``` 251 | -------------------------------------------------------------------------------- /es5-senior/essence2.md: -------------------------------------------------------------------------------- 1 | # 闭包、作用域、原型链 2 | 3 | ```js 4 | if(!('userName' in window)) { 5 | var userName = 'shaogucheng'; 6 | } 7 | console.log(userName); 8 | ``` 9 | 10 | ```js 11 | var obj = { 12 | user: 'shaogucheng', 13 | getName: function() { 14 | return this.user; 15 | } 16 | } 17 | var getNameFn = obj.getName; 18 | 19 | console.log(getNameFn()); 20 | console.log(obj.getName()); 21 | ``` 22 | 23 | ## 作用域 24 | 25 | 作用域有大有小: 26 | 1. 程序级 27 | 2. 文件级 28 | 3. 函数级 29 | 4. 块级 30 | 31 | ## JavaScript的作用域 32 | 33 | * 全局作用域 34 | * 函数作用域 35 | * 块级作用域(ES6) 36 | 37 | ```js 38 | var global = 1; 39 | function doSomething() { 40 | var inner = 2; 41 | globalA = 3; 42 | } 43 | doSomething(); 44 | console.log(global); 45 | console.log(globalA); 46 | console.log(inner); 47 | ``` 48 | 49 | ## JavaScript的作用域链 50 | 51 | 什么是作用域链? 52 | 53 | 执行环境(execution context,为简单起见,有时也称为“环境”)是JavaScript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。 54 | 55 | 全局执行环境是最外围的一个执行环境。根据ECMAscript实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或者浏览器——时才会销毁)。 56 | 57 | 每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAscript程序中的执行流正是由这个方便的机制控制着。 58 | 当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的是保证执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在的环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)。 59 | 60 | 在JavaScript中,函数也是对象,函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性[[scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。 61 | 62 | ```js 63 | var x = 1; 64 | function foo() { 65 | var y = 1 + x; 66 | return function() { 67 | var z = 1 + y; 68 | return z; 69 | } 70 | } 71 | foo()(); 72 | ``` 73 | 74 | ```js 75 | var test = 'aaa'; 76 | function doSomething() { 77 | console.log(test); // 内部变量提升,覆盖全局变量 78 | var test = 'bbb'; // 变量声明且赋值,这里覆盖了全局变量 79 | console.log(test) // 有值的变量 80 | } 81 | doSomething(); 82 | console.log(test); // 访问的是全局变量 83 | ``` 84 | 85 | 执行顺序 86 | 1. 声明函数 87 | 2. 调用函数 88 | 3. 声明变量 89 | 4. console.log(test) 90 | 5. test变量赋值为bbb 91 | 6. console.log(test); 92 | 93 | ## JavaScript中的this关键字 94 | 95 | this指向哪里? 96 | 97 | 在JavaScript中,this指向函数执行时的当前对象。 98 | 99 | this的使用场景 100 | 101 | * 普通函数中: 102 | * 严格模式:undefined 103 | * 非严格模式:全局对象(window) 104 | * 构造函数中:对象的实例 105 | * 对象方法:对象本身 106 | 107 | ### call apply bind 108 | 109 | ## 原型对象是什么? 110 | 111 | * 每个函数都有一个prototype的对象属性 112 | * 每个对象都有一个__proto__属性,该属性指向其父类的prototype对象 113 | 114 | ### 原型对象中的constructor 115 | 116 | 默认指向本身 117 | 118 | ```js 119 | Person.prototype.constructor === Person; 120 | Function.prototype.constructor === Function; 121 | Object.prototype.constructor === Object; 122 | Object,constructor === Function; 123 | ``` 124 | 125 | ```js 126 | function make(num) { 127 | return function() { 128 | return num; 129 | } 130 | } 131 | var arr = [make(0), make(1), make(2)]; 132 | console.log(arr[0]()); 133 | console.log(arr[1]()); 134 | console.log(arr[2]()); 135 | ``` 136 | 137 | ```js 138 | var name = 'global'; 139 | function A(name) { 140 | console.log(name); 141 | this.name = name; 142 | var name = '1'; 143 | } 144 | A.prototype.name = '2'; 145 | var a = new A('3'); 146 | console.log(a.name); 147 | delete a.name; 148 | console.log(a.name); 149 | ``` 150 | 151 | 下面这段代码太有意思了。 152 | 153 | ```js 154 | function fun(n, o) { 155 | console.log(o); 156 | return { 157 | fun: function(m) { 158 | return fun(m, n) 159 | } 160 | } 161 | } 162 | 163 | var a = fun(0); 164 | a.fun(1); 165 | a.fun(2); 166 | var b = fun(0).fun(1).fun(2).fun(3); 167 | var c = fun(0).fun(1); 168 | c.fun(2); 169 | c.fun(3); 170 | ``` 171 | 172 | ```js 173 | window.Glog = function(msg){ 174 | console.log(msg) 175 | } 176 | // this was added before the main closure. 177 | 178 | (function(win){ 179 | //the former closure that contains the main javascript logic; 180 | })(window) 181 | ``` 182 | 183 | ```js 184 | this.m = 100; 185 | var obj = { 186 | m: 1000, 187 | test: function () { 188 | console.log(this.m); 189 | return function () { 190 | console.log(this.m); 191 | } 192 | } 193 | } 194 | (obj.test())(); 195 | ``` 196 | 197 | ```js 198 | var s = { 199 | p: function() { 200 | return function() { 201 | console.log('enen') 202 | } 203 | } 204 | } 205 | (s.p())() 206 | ``` 207 | 208 | The ECMAScript specification has specific rules for automatic semicolon insertion, however in this case a semicolon isn't automatically inserted because the parenthesised expression that begins on the next line can be interpreted as an argument list for a function call. 209 | 210 | This means that without that semicolon, the anonymous window.Glog function was being invoked with a function as the msg parameter, followed by (window) which was subsequently attempting to invoke whatever was returned. 211 | 212 | This is how the code was being interpreted: 213 | 214 | ```js 215 | window.Glog = function(msg) { 216 | console.log(msg); 217 | }; // <--- Add this semicolon 218 | 219 | (function(win) { 220 | // ... 221 | })(window); 222 | 223 | window.Glog = function(msg) { 224 | console.log(msg); 225 | }(function(win) { 226 | // ... 227 | })(window); 228 | ``` 229 | 230 | Everything works fine when I wrote the js logic in a closure as a single js file, as: 231 | 232 | ``` js 233 | (function(win){ 234 | //main logic here 235 | win.expose1 = .... 236 | win.expose2 = .... 237 | })(window) 238 | ``` 239 | 240 | but when I try to insert a logging alternative function before that closure in the same js file, 241 | 242 | ```js 243 | window.Glog = function(msg){ 244 | console.log(msg) 245 | } 246 | // this was added before the main closure. 247 | 248 | (function(win){ 249 | //the former closure that contains the main javascript logic; 250 | })(window) 251 | ``` 252 | 253 | it complains that there is a TypeError: 254 | 255 | ``` console 256 | Uncaught TypeError: (intermediate value)(...) is not a function 257 | ``` 258 | 259 | ```js 260 | window.Glog = function(msg) { 261 | console.log(msg); 262 | }; // <--- Add this semicolon 263 | 264 | (function(win) { 265 | // ... 266 | })(window); 267 | ``` 268 | 269 | The ECMAScript specification has specific rules for automatic semicolon insertion, however in this case a semicolon isn't automatically inserted because the parenthesised expression that begins on the next line can be interpreted as an argument list for a function call. 270 | 271 | This means that without that semicolon, the anonymous window.Glog function was being invoked with a function as the msg parameter, followed by (window) which was subsequently attempting to invoke whatever was returned. 272 | 273 | This is how the code was being interpreted: 274 | 275 | ```js 276 | window.Glog = function(msg) { 277 | console.log(msg); 278 | }(function(win) { 279 | // ... 280 | })(window); 281 | ``` 282 | 283 | ```js 284 | **Error Case:** 285 | var handler = function(parameters) { 286 | console.log(parameters); 287 | } 288 | 289 | (function() { //IIFE 290 | // some code 291 | })(); 292 | ``` 293 | Output: TypeError: (intermediate value)(intermediate value) is not a function *How to Fix IT -> because you are missing semi colan(;) to separate expressions; 294 | 295 | ```js 296 | **Fixed** 297 | var handler = function(parameters) { 298 | console.log(parameters); 299 | }; // <--- Add this semicolon(if you miss that semi colan ... 300 | //error will occurs ) 301 | 302 | (function() { //IIFE 303 | // some code 304 | })(); 305 | ``` -------------------------------------------------------------------------------- /functional-programming/article/2019-1-18.md: -------------------------------------------------------------------------------- 1 | # 函数编程基础之——范畴论 2 | 3 | ## 基本概念 4 | 5 | ### 范畴论 6 | 7 | * 数学构造(Mathematical structure) 8 | 在数学上,在集合上的一个构造是一个附加的数学对象,赋予这个集合某种意义。 9 | 10 | * 范畴论(category theory) 11 | 范畴论的目的是:规范化数学构造。 12 | 方法为:使用带标签的有向图。 13 | 研究内容:各种数学结构之间的关系。 14 | 15 | ### 范畴(category) 16 | 17 | 一个范畴是一个带标签的有向图,其节点为对象(object),带有标签的有向边为箭头(arrow or morphism)。 18 | 19 | 一个范畴C包含3个数学实体: 20 | 21 | * 对象集合:ob(C) 22 | 每个元素都是一个对象,一个对象又可以认为是一个集合。 23 | 24 | * 态射集合: hom(C) 25 | 态射集合的每个元素是一个态射, `f:a→b`,每个态射f有一个源对象 (source object) a和目标对象(target object)b。 26 | `hom(a,b)` 表示从a到b的所有态射。 27 | 28 | * 性质:二元操作:态射结合(composition of morphisms): ∘ 29 | f:a→b,g:b→c 的结合是 g∘f。具有 30 | * 满足结合律(Associativity): h∘(g∘f)=(h∘g)∘f 31 | * 存在恒等态射(identity): 32 | 对于每个对象x,存在一个恒等态射(identity morphism) 1x:x→x, 33 | 其性质为,对于任何态射f:a→b,有1b∘f=f=f∘1a 34 | 恒等态射的含义是:定义了相同关系(equality relation A = B)。 35 | 可以简单的认为是;f(x)=x。 36 | 37 | ### 态射(morphism) 38 | 39 | 态射可以理解为一个函数,在范畴论中,往往表示为一个对象和另一个对象的map关系。 40 | 态射作为函数理解的时候,不用纠结于参数的个数。 41 | 42 | 态射的种类 `(f:a→b)` : 43 | 44 | * 单态射(monomorphism or monic) 45 | 如果f∘g1=f∘g2⟹g1=g2,∀g1,g2:x→a。 46 | 其含义是:不存在两个a中元素 map 到同一个b中的元素。 47 | ∀a1,a2∈A,a1≠a2⟹f(a1)≠f(a2) 48 | 49 | * 满态射(epimorphism or epic) 50 | 如果g1∘f=g2∘f⟹g1=g2,∀g1,g2:b→x。 51 | 其含义是:每一个b中的元素,都在a中有至少一个 source mapper。 52 | 53 | * 双态射(bimorphism) 54 | 即是单态射,有时满态射。 55 | 56 | * 同构(isomorphism) 57 | 如果存在一个同构g:b→a,有f∘g=1b,g∘f=1a。 58 | 其含义是:a,b两个对象的元素存在一对一的 map 关系。 59 | 同构 = 双态射 + 存在逆态射。 60 | g称为逆态射,也是一个同构,g 的逆态射是 f。 61 | 比如:f是加法,g是减法。 62 | 63 | * 自态射(endomorphism) 64 | 表示一个态射源对象和目标对象是同一个, f:a→a。记为:end(a)。 65 | 66 | * 自同构(automorphism) 67 | 如果f既是一种自态射,又是具有同构性。记为:aut(a)。 68 | 69 | * 撤回射(retraction) 70 | 如果存在一个f的右逆,也就是说,如果存在: $g : b \to a, f \circ g = 1_b。 71 | f 是另一个态射g的撤回射,其含义是:g 可以通过f找到 source element。f必定是一个满态射(epimorphism)。 72 | 73 | * 部分射(section) 74 | 如果f的左逆是存在的,也就是说,如果存在: $g : b \to a, g \circ f = 1_a。 75 | f 是另一个态射g的部分射,其含义是:f 确定了g的同构部分。f必定是一个单态射(monomorphism)。 76 | 是不是可以理解为f的g应用的一个条件??? 77 | 78 | * 同态(homomorphism) 79 | 同态(homomorphism)是一个态射,表示一个数学结构\mathcal{A}(C, , e)到另一个数学结构\mathcal{B}(C', ', e')的map关系,并且维持了数学结构上的的每一种操作*。 80 | 同态(homomorphism) f:A→B,有: 81 | f(x∗y)=f(x)∗′f(y) 82 | 83 | 同一种操作在不同的数学结构上定义可以不同。 84 | 比如:指数函数是一个同态。 85 | 86 | f:R→R 87 | f(x)=ex 88 | ex+y=exey→f(x∗y)=f(x)∗f(y) 89 | where 90 | f exponential function is a homomorphism 91 | the source is A=R 92 | the target is B=R 93 | ∗=+ in A 94 | ∗=× in B(1) 95 | 96 | 域(domain)/协域(codomain) 97 | 对于一个态射(morphism) f:S→T。 域(domain)是这个态射的源,协域(codomain)是这个态射目标。 98 | 99 | 范畴的表达 100 | 表达1 101 | C=(Ob(C),HomC(x,y),idx,∘)whereOb(C)∈SetobjectHomC(x,y)∈Setmorphism | x,y∈Ob(C)idx∈HomC(x,x) : identity morphism of x∘:HomC(y,z)×HomC(x,y)→HomC(x,z) : composition formulaIdentity Law:∀x,y∈Ob(C),f:x→yf∘idx=fidy∘f=fAssociative Law:∀w,x,y,z∈Ob(C),h:w→x,g:x→y,f:y→z(h∘g)∘f=h∘(g∘f)∈HomC(w,z)(2) 102 | 103 | ### 函子(functor) 104 | 105 | 函子是范畴之间的map关系。可以理解为范畴之间的态射。 106 | 107 | * 协变(covariant)函子 108 | F:C→D, F包含: 109 | 对于每一个 C 中的对象x,对应一个 D 中的F(x)。 110 | 对于每一个C中的态射f:x→y,对应D的态射F(f):F(x)→F(y)。 111 | 112 | * 逆变(contravariant)函子 113 | 和协变函子类似,只不过态射的方向是从D到C。 114 | 115 | ### 自然转换(Natural transformation) 116 | 117 | 自然转换是两个函子之间的关系。函子描述“自然构造(natural constructions)”,自然转换描述两个这样构造的"自然同态(natural homomorphisms)"。 118 | homomorphism的意思是相同的形状。 119 | 120 | ### 其它 121 | 122 | 本体(olog) 123 | 交换图(commutative diagram) 124 | 通用性质(universal property) 125 | 如果,两个数学结构是同构(isomorphism),那么它们之间就存在通用性质。 126 | 同构意味着两个数学结构X和Y中的元素是存在一一对应。 127 | 那么,Y上的性质(态射)意味着X上存在一个对应的性质(态射)。比如: 128 | X = {A, B, C} 129 | Y = {1, 2, 3} 130 | A --> 1 131 | B --> 2 132 | C --> 3 133 | 如果+(1, 2) = 3,我们也可以认为存在 +(A, B) = C。起点性质。 134 | 如果+(1, 2),我们也可以认为存在 +(C) = (A, B)。起点性质。 135 | 通用性质(universal property)要么是一个起点性质(initial property),要么是一个终点性质(terminal property)。 136 | 137 | 起点性质(initial property) 138 | 唯一存在 139 | U:D→CD{A,Y}C{X,U(A),U(Y),ϕ:X→U(A)}∴initial property:∀f:X→U(Y)∃g,g:A→Y∃U(g),g:U(A)→U(Y)(3) 140 | 终点性质(terminal property) 141 | 唯一存在 142 | U:D→CD{A,Y}C{X,U(A),U(Y),ϕ:U(A)→X}∴terminal property:∀f:U(Y)→X∃g,g:Y→A∃U(g),g:U(Y)→U(A)(4) 143 | 起点性质示例 144 | X=(L:(a,b,c),A:L×L,B:L)Y=(N:(1,2,3),Ay:N×N,By:N,Cy:Ay)ϕ:Cy→Ay=ce.g. (1,2)=(1,2)f:Cy→By=c1+c2e.g. 1+2=3∴∃g:A→Be.g. a+b=c(5) 145 | 终点性质示例 146 | X=(L:(a,b,c),A:L×L,B:L)Y=(N:(1,2,3),Ay:N×N,By:N,Cy:By)ϕ:Ay→Cy=a1+a2e.g. 1+2=3f:By→Cy=be.g. 3=3∴∃g:B→Ae.g. c=a+b(6) 147 | 拉回(pullback) 148 | 推出(pushout) 149 | limit/colimit 150 | 151 | 关系 152 | 二元关系(binary relation) 153 | 一个基于集合X的二元关系,是一个R⊆X×X的子集。 154 | 155 | 等价关系(equivalence relations) 156 | 一个集合X上的等价关系∼,是一个R⊆X×X的子集,具有 157 | 自反性(Reflexivity) 158 | (x,x)∈R 159 | 对称性(Symmetry) 160 | $(x, y) \in R \text{, iff } (y,x)∈R$ 161 | 传递性(Transitivity) 162 | $\text{if } (x, y) \in R, (y, z) \in R \text{, then } (x,z)∈R$ 163 | 幺半群(monoid) 164 | 幺半群可以代表一个序列或者列表。 165 | 166 | 幺半群(monoid) 167 | 一个幺半群是一个序列(sequence)(M,e,⋆)。M是一个集合, 168 | e∈M是一个元素,成为单位元素(identity element)。 169 | ⋆:M×M→M是一个函数,称为乘法公式(multiplication formula)。 170 | 幺半群具有以下属性: 171 | 同一律(identity law) 172 | m⋆e=m 173 | e⋆m=m 174 | 结合律(associativity law) 175 | (m⋆n)⋆p=m⋆(n⋆p) 176 | 比如:字符串就是一个幺半群,e = "", ⋆ = +。 177 | List in set 178 | 集合X,在X上的List是 179 | (n,f)wheren∈N : the length of the listf:n––→Xn––={1,2,⋯,n}(7) 180 | 181 | 记做: 182 | (n,f)=[f(1),f(2),⋯,f(n)](8) 183 | 184 | 列表单体(free monoid generated by X) 185 | M:=(List(X),[],++) 186 | List(X);集合X的元素列表集合。 187 | []是一个空列表。 188 | ++是连接操作(concatenation)。 189 | 190 | 显示幺半群(presented monoid) 191 | 192 | 显示幺半群的作用是提供了替换方法。 193 | 由有限集合G和等价关系产生的显示幺半群(the monoid presented by generators G and relation (mi,m′i)|1≤i≤n) 194 | 195 | M={M,e,⋆}where{(xmiy∼xm′iy)|x,y∈List(G),1≤i≤n} : equivalence relationM=List(G)/∼e=[]⋆ : concatenating operation(9) 196 | 197 | 循环(cyclic)幺半群 198 | 199 | 循环幺半群的作用是提供了一个环形列表的定义方法。 200 | 循环(cyclic)幺半群是只有一个等价关系的显示幺半群。 201 | 202 | 幺半群行动(monoid actions) 203 | 在集合S上的幺半群(M,e,⋆)的行动为函数: 204 | ↪(10) 205 | 206 | 符号 207 | 术语 English Notation 208 | 单态射 monomorphisms ↪ 209 | 满态射 epimorphisms ↠ 210 | 同构 isomorphisms →∼ 211 | 群(group) 212 | group是一个monoid,并且每个元素都有一个倒数(inverse)。 213 | 推论:倒数具有唯一性。 214 | 215 | 图形(graphs) 216 | 图形(graphs)是由多个顶点(vertex)和顶点之间的箭头(arrow)定义而成。 217 | 路径(path) 218 | 219 | 顺序(order) 220 | 预次序关系(preorder) 221 | 预次序关系(preorder)(S,≤)是一个基于集合S的二元关系R⊆S×S, 222 | R的关系:if (s,s′)∈R,then s≤s′ 223 | 并且有 224 | 自反性(Reflexivity): s≤s 225 | 传递性(Transitivity): if s≤s′and s′≤s",then s≤s" 226 | 部分有序(partial order) 227 | 部分有序(partial order)是预次序关系,并且 228 | 反对称性(Antisymmetry) 229 | 如果s <= s', 并且 s' <= s, 则 s = s'。 230 | 线性有序(linear order) 231 | 线性有序(linear order)是部分有序,并且 232 | 可比较性(Comparability) 233 | 要么 s <= s', 要么 s' <= s。 234 | 派系(clique) 235 | 派系(clique)中的每两个点都是毗邻的(adjacent)。 236 | clique≐S′where(S,⩽) is a preorderS′⊆Sa⩽b,∀a,b∈S′(11) 237 | 238 | meet 和 join 239 | (S,⩽)是一个preorder。s,t∈S 240 | s和t的meet(the biggest thing smaller than both)是一个元素w∈S, 241 | 表示为:w≅s∧t 242 | 具有: 243 | w⩽sw⩽tx⩽w | ∀x∈S,x⩽s & x⩽t(12) 244 | 245 | s和t的join(the smallest thing bigger than both)是一个元素w∈S, 246 | 表示为:w≅s∨t 247 | 具有: 248 | s⩽wt⩽ww⩽x | ∀x∈S,s⩽x & t⩽x(13) 249 | 250 | meet 和 join 不一定是唯一的。任何两个meet一定在同一个派系内。 251 | 252 | digraph finite_state_machine { 253 | rankdir=LR; 254 | size="8,5" 255 | 256 | node [shape = doublecircle]; S; 257 | node [shape = point ]; qi 258 | 259 | node [shape = circle]; 260 | qi -> S; 261 | S -> q1 [ label = "a" ]; 262 | S -> S [ label = "a" ]; 263 | q1 -> S [ label = "a" ]; 264 | q1 -> q2 [ label = "ddb" ]; 265 | q2 -> q1 [ label = "b" ]; 266 | q2 -> q2 [ label = "b" ]; 267 | } 268 | 反顺序(Opposite order) 269 | S:=(S,⩽)是一个预次序(preorder),则反顺序Sop:=(s,⩽op), 270 | 有: 271 | s⩽s′⟺s′⩽s 272 | 次序的态射(morphism of orders) 273 | 从S:=(S,⩽)到S′:=(S′,⩽′)次序的态射f,表示为f:S→S′。 274 | if s1⩽s2, then f(s1)⩽f(s2)(14) 275 | 276 | Database vs Graph 277 | 路径等价声明(path equivalence declaration) 278 | 图形G:=(V,A,src,tgt), PathG为G中所有路径的集合。 279 | p≃q|p,g∈PathG为路径等价声明,表示p,g有相同的起点和终点。 280 | 在G上的一个一致(congruence) 是一个在PathG上的 等价关系≃,具有: 281 | ≃是一个等价关系. 282 | 如果 p≃q,则src(p) = src(q). 283 | 如果 p≃q,则tgt(p) = tgt(q). 284 | 假设 p,q:b→c是路径,并且m:a→b。如果 p≃q,则mp≃mq. 285 | 假设 p,q:b→c是路径,并且n:b→c。如果 p≃q,则pn≃qn. 286 | 287 | ≃ 是一个集合,定义了图形上的所有约束。 288 | 289 | 引理:假设psimeqq:a→b,r≃s:b→c,则pr≃qs。 290 | 291 | Database Schema 292 | Database Schema C:=(G,≃),G是一个图形,≃是G上的一致。 293 | 294 | Olog = Database Schema 295 | 296 | 实例(instance) 297 | 一个顶点对应的集合,和出入箭头的所有路径上的节点集合。 298 | (PK,FK):C→Set is an instancewhereC=(G,≃)G=(V,A,src,tgt)PK:V→Set, one set for one vertexFK(a):PK(v)→PK(w) | v=src(a),w=tgt(a),∀a∈A(15) 299 | 300 | 路径法则(Law 1 - Path through a database) 301 | FK(am)∘⋯∘FK(a1)(x)=FK(a′n)∘⋯∘FK(a′1)(x)=PK(w),∀x∈PK(v)wherep=va1a2…am:v→wq=va′1a′2…a′n:v→wp≃q(16) 302 | 303 | 范畴化 304 | 如何将一个monoid/order group/group index通过一个函子转化成一个范畴。 305 | Database Schema present categories -------------------------------------------------------------------------------- /es5-senior/article/this.md: -------------------------------------------------------------------------------- 1 | # JavaScript探究this奥秘之旅 2 | 3 | ## 一、this的理解 4 | 5 | > 我们知道,关于this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。 6 | >
--------《JavaScript高级程序设计》
7 | > **核心提炼:函数中的this总指向调用它的对象。** 8 | 9 | ## 二、错误认识 10 | 11 | **1、指向自身** 12 | 人们很容易把this理解成指向函数自身,其实this的指向在函数定义阶段是无法确定的,只有函数执行时才能确定this到底指向谁,实际上this的最终指向是调用它的那个对象。 13 | 14 | **2、指向函数的作用域** 15 | 对this的第二种误解就是this指向函数的作用域, 16 | window,在chrome console环境里是没有问题的,全局声明的函数放在了window下,foo函数里面的this代指的是window对象,在全局环境中并没有声明变量a,因此在bar函数中的this.a自然没有定义,输出undefined。 17 | 18 | nodejs,在node环境下,声明的function不会放在global全局对象下,因此在foo函数里调用this.bar函数会报 TypeError: this.bar is not a function错误,调用bar函数,要省去前面的this。 19 | 20 | ## 三、this绑定规则 21 | 22 | this有四种绑定规则 23 | * 默认绑定 24 | * 隐式绑定 25 | * 显示绑定 26 | * new绑定 27 | 28 | ### 3.1默认绑定 29 | 30 | 当函数调用属于独立调用(不带函数引用的调用),无法调用其他的绑定规则,我们给它一个称呼“默认绑定”,在非严格模式下绑定到全局对象,在使用了严格模式(use strict)下绑定到undefined。 31 | 当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象 32 | 33 | 默认绑定引发一个很深的疑问!? 34 | 函数的调用必须绑定对象,默认绑定就是绑定到window对象。 35 | 36 | ``` js 37 | function default() { 38 | console.log(this === window) 39 | } 40 | default(); // 输出true 41 | 42 | function default() { 43 | function inner() { 44 | console.log(this === window) 45 | } 46 | inner(); // 独立函数调用 47 | } 48 | default(); // 输出true 49 | 50 | var obj = { 51 | outer: function () { 52 | function inner() { 53 | console.log(this === window) 54 | } 55 | inner(); // 独立函数调用 56 | } 57 | } 58 | obj.outer(); //输出 true 59 | ``` 60 | 61 | 【总结】 凡事函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异 62 | 63 | ### 3.2隐式绑定 64 | 65 | 当函数被一个对象“包含”的时候,我们称函数的this被隐式绑定到这个对象里面了,这时候,通过this可以直接访问所绑定的对象里面的其他属性, 66 | 67 | ``` js 68 | 81 | ``` 82 | 83 | outer函数和inner函数被定义在obj对象的内部和外部而有任何区别,也就是说在上述隐式绑定的两种形式下,outer通过this还是可以访问到obj内的a属性,这告诉我们: 84 | 85 | 1. this是动态绑定的,或者说是在代码运行期绑定而不是在书写期 86 | 2. 函数于对象的独立性, this的传递就存在丢失问题 87 | 88 | > 隐式绑定下,作为对象属性的函数,对于对象来说是独立的。基于this动态绑定的特点,写在对象内部,作为对象属性的函数,对于这个对象来说是独立的。(函数并不被这个外部对象所“完全拥有”) 89 | 90 | 在上文中,函数虽然被定义在对象的内部中,但它和“在对象外部声明函数,然后在对象内部通过属性名称的方式取得函数的引用”,这两种方式在性质上是等价的(而不仅仅是效果上) 91 | 92 | 在一串对象属性链中,this绑定的是最内层的对象 93 | 94 | **隐式绑定的问题!!!** 95 | 当进行隐式绑定时,如果进行一次引用赋值或者传参操作,会造成this的丢失,使this绑定到全局对象中去。 96 | 97 | 1引用赋值丢失 98 | 99 | ``` js 100 | function thisTo() { 101 | console.log(this.a); 102 | } 103 | var data = { 104 | a: 2, 105 | foo: thisTo //通过属性引用this所在函数 106 | }; 107 | var a = 3;//全局属性 108 | 109 | var newData = data.foo; //这里进行了一次引用赋值 110 | newData(); // 3 111 | ``` 112 | 113 | 原理:因为newData实际上引用的是foo函数本身,这就相当于:var newData = thisTo;data对象只是一个中间桥梁,data.foo只起到传递函数的作用,所以newData跟data对象没有任何关系。而newData本身又不带a属性,最后a只能指向window。 114 | 115 | 2传参丢失 116 | 117 | ```js 118 | function thisTo() { 119 | console.log(this.a); 120 | } 121 | var data = { 122 | a: 2, 123 | foo: thisTo //通过属性引用this所在函数 124 | }; 125 | var a = 3;//全局属性 126 | 127 | setTimeout(data.foo, 100);// 3 128 | ``` 129 | 130 | 这就是本文开始的那个题目。所谓传参丢失,就是在将包含this的函数作为参数在函数中传递时,this指向改变。setTimeout函数的本来写法应该是setTimeout(function(){......},100);100ms后执行的函数都在“......”中,可以将要执行函数定义成var fun = function(){......},即:setTimeout(fun,100),100ms后就有:fun();所以此时此刻是data.foo作为一个参数,是这样的:setTimeout(thisTo,100);100ms过后执行thisTo(),实际道理还跟1.1差不多,没有调用thisTo的对象,this只能指向window。 131 | 132 | 3隐式丢失解决方法 133 | 为了解决隐式丢失(隐式丢失专用)的问题,ES5专门提供了bind方法,bind()会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。(这个bind可跟$(selector).bind('click',function(){......})的用法不同) 134 | 135 | ``` js 136 | function thisTo() { 137 | console.log(this.a); 138 | } 139 | var data = { 140 | a: 2 141 | }; 142 | var a = 3; 143 | var bar = thisTo.bind(data); 144 | console.log(bar()); //2 145 | ``` 146 | 147 | 2.间接引用 148 | 间接引用是指一个定义对象的方法引用另一个对象存在的方法,这种情况下会使得this指向window: 149 | 150 | ``` js 151 | function thisTo() { 152 | console.log(this.a); 153 | } 154 | var data = { 155 | a: 2, 156 | foo: thisTo 157 | }; 158 | var newData = { 159 | a: 3 160 | } 161 | var a = 4; 162 | data.foo(); //2 163 | (newData.foo = data.foo)() //4 164 | newData.foo(); //3 165 | ``` 166 | 167 | 这里为什么`(newData.foo=data.foo)()`的结果是4,与newData.foo()的结果不一样呢?按照正常逻辑的思路,应该是先对newData.foo赋值,再对其进行调用,也就是等价于这样的写法:newData.foo=data.foo;newData.foo();然而这两句的输出结果就是3,这说明两者不等价。 168 | 169 | 接着,当我们console.log(newData.foo=data.foo)的时候,发现打印的是thisTo这个函数,函数后立即执行括号将函数执行。这句话中,立即执行括号前的括号中的内容可单独看做一部本,该部分虽然完成了赋值操作,返回值却是一个函数,该函数没有确切的调用者,故而立即执行的时候,其调用对象不是newData,而是window。下一句的newData.foo()是在给newData添加了foo属性后,再对其调用foo(),注意这次的调用对象为newData,即我们上面说的隐式绑定的this,结果就为3。 170 | 171 | 1、为函数调用创建别名 172 | 173 | ``` js 174 | function foo() { 175 | console.log(this.a); 176 | } 177 | var obj = { 178 | a: 2, 179 | foo: foo 180 | } 181 | var bar = obj.foo; 182 | var a = "window"; 183 | bar()//window 184 | ``` 185 | 186 | 虽然bar是obj.foo的一个引用,但是bar引用的是foo函数的本身,此时的bar()其实就是一个不带任何修饰的函数调用,所以应用了默认绑定,this为全局 187 | 188 | 2、传入回调函数 189 | 190 | ``` js 191 | function foo() { 192 | console.log(this.a); 193 | } 194 | function doFoo(fn) { 195 | fn(); 196 | } 197 | var obj = { 198 | a: 2, 199 | foo: foo 200 | } 201 | var a = "window"; 202 | doFoo(obj.foo)//window 203 | ``` 204 | 参数传递其实就是隐式赋值。相当于var fn=obj.foo,与创建别名的结果一样,应用了默认绑定,应该注意的是,return返回一个函数时,也是应用了默认绑定 205 | 206 | 3、传入语言内置的函数 207 | 208 | ``` js 209 | function foo() { 210 | console.log(this.a); 211 | } 212 | var obj = { 213 | a: 2, 214 | foo: foo 215 | } 216 | var a = "window"; 217 | setTimeout(obj.foo, 100)//window 218 | ``` 219 | 220 | 在JavaScript内部,内置函数setTimeout函数实现可以看作 221 | 222 | ``` js 223 | function setTimeout(fn, delay) { 224 | //等待delay毫秒 225 | fn(); 226 | } 227 | ``` 228 | 229 | 最后,还剩下的疑问就是:为什么会出现隐式丢失的问题? 230 | 231 | ### 3.3显式绑定 232 | 233 | 上面我们提到了this的隐式绑定所存在的this绑定丢失的问题,也就是对于 “ fireInGrobal = obj.fire”fireInGrobal调用和obj.fire调用的结果是不同的, 234 | 因为这个函数赋值的过程无法把fire所绑定的this也传递过去。这个时候,call函数就派上用场了 235 | 236 | call的基本使用方式: fn.call(object) 237 | fn是你调用的函数,object参数是你希望函数的this所绑定的对象。 238 | fn.call(object)的作用: 239 | 1.即刻调用这个函数(fn) 240 | 2.调用这个函数的时候函数的this指向object对象 241 | 242 | ``` js 243 | var obj = { 244 | a: 1, // a是定义在对象obj中的属性 245 | fire: function () { 246 | console.log(this.a) 247 | } 248 | } 249 | 250 | var a = 2; // a是定义在全局环境中的变量 251 | var fireInGrobal = obj.fire; 252 | fireInGrobal(); // 输出2 253 | fireInGrobal.call(obj); // 输出1 254 | ``` 255 | 256 | 原本丢失了与obj绑定的this参数的fireInGrobal再次重新把this绑回到了obj 257 | 258 | 但是,我们其实不太喜欢这种每次调用都要依赖call的方式,我们更希望:能够一次性 返回一个this被永久绑定到obj的fireInGrobal函数,这样我们就不必每次调用fireInGrobal都要在尾巴上加上call那么麻烦了。 259 | 260 | 怎么办呢? 聪明的你一定能想到,在fireInGrobal.call(obj)外面包装一个函数不就可以了嘛! 261 | 262 | ``` js 263 | var obj = { 264 | a: 1, // a是定义在对象obj中的属性 265 | fire: function () { 266 | console.log(this.a) 267 | } 268 | } 269 | var a = 2; // a是定义在全局环境中的变量 270 | var fn = obj.fire; 271 | var fireInGrobal = function () { 272 | fn.call(obj) //硬绑定 273 | } 274 | fireInGrobal(); // 输出1 275 | ``` 276 | 277 | 如果使用bind的话会更加简单 278 | var fireInGrobal = function () { 279 | fn.call(obj) //硬绑定 280 | } 281 | 可以简化为: 282 | var fireInGrobal = fn.bind(obj); 283 | call和bind的区别是:在绑定this到对象参数的同时: 284 | 285 | 1.call将立即执行该函数 286 | 2.bind不执行函数,只返回一个可供执行的函数 287 | 288 | 【其他】:至于apply,因为除了使用方法,它和call并没有太大差别,这里不加赘述 289 | 290 | 在这里,我把显式绑定和隐式绑定下,函数和“包含”函数的对象间的关系比作买房和租房的区别。 291 | 292 | 因为this的缘故 293 | 在隐式绑定下:函数和只是暂时住在“包含对象“的旅馆里面,可能过几天就又到另一家旅馆住了 294 | 在显式绑定下:函数将取得在“包含对象“里的永久居住权,一直都会”住在这里“ 295 | 296 | 显示绑定和隐士绑定从字面意思理解,有一个相反的对比,一个表现的更直接,一个表现的更委婉,下面在看下两个规则各自的含义: 297 | 298 | 隐士绑定 在一个对象的内部通过属性间接引用函数,从而把this隐士绑定到对象内部属性所指向的函数(例如上例中的对象parent的child属性引用函数function child(){})。 299 | 300 | 显示绑定 需要引用一个对象时进行强制绑定调用,js有提供call()、apply()方法,ES5中也提供了内置的方法 Function.prototype.bind。 301 | 302 | call、apply这两个函数的第一个参数都是设置this对象,关于两个个函数的区别可以查看 [函数] call和apply的使用与区别? 303 | ``` js 304 | // 水果对象 305 | function fruit() { 306 | console.log(this.name, arguments); 307 | } 308 | var apple = { 309 | name: '苹果' 310 | } 311 | var banana = { 312 | name: '香蕉' 313 | } 314 | fruit.call(banana, banana, apple) // 香蕉 {'0': {name: '香蕉'}, '1': { name: '苹果'}} 315 | fruit.apply(apple, [banana, apple]) // 苹果 {'0': {name: '香蕉'}, '1': { name: '苹果'}} 316 | ``` 317 | 318 | ### 3.4 new绑定 319 | 320 | 执行new操作的时候,将创建一个新的对象,并且将构造函数的this指向所创建的新对象 321 | 322 | ``` js 323 | function foo(a) { 324 | this.a = a; 325 | } 326 | 327 | var a1 = new foo(1); 328 | var a2 = new foo(2); 329 | var a3 = new foo(3); 330 | var a4 = new foo(4); 331 | 332 | console.log(a1.a); // 输出1 333 | console.log(a2.a); // 输出2 334 | console.log(a3.a); // 输出3 335 | console.log(a4.a); // 输出4 336 | ``` 337 | 338 | ## 四、优先级 339 | 340 | new绑定 341 | 显示绑定 342 | 隐式绑定 343 | 默认绑定(严格模式下会绑定到undefined) 344 | 箭头函数 345 | 箭头函数并非使用function关键字进行定义,也不会使用上面所讲解的this四种标准规范,箭头函数会继承自外层函数调用的this绑定。 346 | 347 | 执行 fruit.call(apple)时,箭头函数this已被绑定,无法再次被修改。 348 | 349 | ``` js 350 | function fruit() { 351 | return () => { 352 | console.log(this.name); 353 | } 354 | } 355 | var apple = { 356 | name: '苹果' 357 | } 358 | var banana = { 359 | name: '香蕉' 360 | } 361 | var fruitCall = fruit.call(apple); 362 | fruitCall.call(banana); // 苹果 363 | ``` 364 | 365 | this在项目中使用问题总结,React中使用this注意的问题 366 | 367 | ``` js 368 | class App extends React.Component { 369 | componentDidMount() { 370 | console.log(this); 371 | //注意this在setTimeout函数外代表的是App这个对象 372 | setTimeout(function () { 373 | this.setState({ //这里的this指的是windows对象 374 | initDone: true 375 | }) 376 | }, 1000); 377 | //两种解决方案: 378 | //1. var that = this 379 | setTimeout(() => { 380 | that.setState({ 381 | initDone: true 382 | }) 383 | }, 1000); 384 | //2.使用es6的箭头函数 385 | setTimeout(() => { 386 | this.setState({ 387 | initDone: true 388 | }) 389 | }, 1000); 390 | } 391 | } 392 | ``` -------------------------------------------------------------------------------- /functional-programming/article/2019-1-19.md: -------------------------------------------------------------------------------- 1 | # FP编程进阶之——Point-Free 2 | 3 | ## 引言 4 | 5 | 在学习point-free之前,我先谷歌了一下关于point-free的定义,wiki上是这样说的: 6 | 7 | [wiki Tacit_programming](https://en.wikipedia.org/wiki/Tacit_programming) 8 | 9 | >Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate. Instead the definitions merely compose other functions, among which are combinators that manipulate the arguments. Tacit programming is of theoretical interest, because the strict use of composition results in programs that are well adapted for equational reasoning.[1] It is also the natural style of certain programming languages, including APL and its derivatives,[2] and concatenative languages such as Forth. The lack of argument naming gives point-free style a reputation of being unnecessarily obscure, hence the epithet "pointless style".[1] 10 | > UNIX scripting uses the paradigm with pipes. 11 | 12 | 翻译如下: 13 | 14 | `Tacit programming` 也叫做 `point-free style` ,是一种编程范式,其中函数定义不用定义它们所操作的参数(或“点”)。相反,定义只是组成其他函数,其中包括操作参数的组合器。隐性编程在理论上很有意义,因为严格使用组合会产生适合于等式推理的程序[1]它也是某些编程语言的自然风格,包括APL及其派生语言[2]和连接语言,如FORTH。缺乏参数命名使无点风格享有不必要模糊的声誉,因此绰号“无点风格”。 15 | 16 | ```python 17 | def example(x): 18 | y = foo(x) 19 | z = bar(y) 20 | w = baz(z) 21 | return w 22 | ``` 23 | 24 | ## 一、程序的本质 25 | 26 | 为了理解 Pointfree,请大家先想一想,什么是程序? 27 | ![什么是函数](./img/pointfree1.png) 28 | 上图是一个编程任务,左侧是数据输入(input),中间是一系列的运算步骤,对数据进行加工,右侧是最后的数据输出(output)。一个或多个这样的任务,就组成了程序。 29 | 30 | 输入和输出(统称为 I/O)与键盘、屏幕、文件、数据库等相关,这些跟本文无关。这里的关键是,中间的运算部分不能有 I/O 操作,应该是纯运算,即通过纯粹的数学运算来求值。否则,就应该拆分出另一个任务。 31 | 32 | I/O 操作往往有现成命令,大多数时候,编程主要就是写中间的那部分运算逻辑。现在,主流写法是过程式编程和面向对象编程,但是我觉得,最合适纯运算的是函数式编程。 33 | 34 | ## 二、函数的拆分与合成 35 | 36 | 上面那张图中,运算过程可以用一个函数fn表示。 37 | ![什么是函数](./img/pointfree2.png) 38 | 39 | fn的类型如下。 40 | 41 | ```c++ 42 | fn :: a -> b 43 | ``` 44 | 45 | 上面的式子表示,函数fn的输入是数据a,输出是数据b。 46 | 47 | 如果运算比较复杂,通常需要将fn拆分成多个函数。 48 | ![什么是函数](./img/pointfree3.png) 49 | 50 | f1、f2、f3的类型如下。 51 | 52 | ```c++ 53 | f1 :: a -> m 54 | f2 :: m -> n 55 | f3 :: n -> b 56 | ``` 57 | 58 | 上面的式子中,输入的数据还是a,输出的数据还是b,但是多了两个中间值m和n。 59 | 60 | 我们可以把整个运算过程,想象成一根水管(pipe),数据从这头进去,那头出来。 61 | 62 | ![什么是函数](./img/pointfree4.png) 63 | 64 | 函数的拆分,无非就是将一根水管拆成了三根。 65 | 66 | ![什么是函数](./img/pointfree5.png) 67 | 68 | 进去的数据还是a,出来的数据还是b。fn与f1、f2、f3的关系如下。 69 | 70 | ```c++ 71 | fn = R.pipe(f1, f2, f3); 72 | ``` 73 | 74 | 上面代码中,我用到了 Ramda 函数库的pipe方法,将三个函数合成为一个。Ramda 是一个非常有用的库,后面的例子都会使用它,如果你还不了解,可以先读一下教程。 75 | 76 | ## 三、Pointfree 的概念 77 | 78 | ```c++ 79 | fn = R.pipe(f1, f2, f3); 80 | ``` 81 | 82 | 这个公式说明,如果先定义f1、f2、f3,就可以算出fn。整个过程,根本不需要知道a或b。 83 | 84 | 也就是说,我们完全可以把数据处理的过程,定义成一种与参数无关的合成运算。不需要用到代表数据的那个参数,只要把一些简单的运算步骤合成在一起即可。 85 | 86 | 这就叫做 Pointfree:不使用所要处理的值,只合成运算过程。中文可以译作"无值"风格。 87 | 88 | 请看下面的例子。 89 | 90 | ```js 91 | var addOne = x => x + 1; 92 | var square = x => x * x; 93 | ``` 94 | 95 | 上面是两个简单函数addOne和square。 96 | 97 | 把它们合成一个运算。 98 | 99 | ```js 100 | var addOneThenSquare = R.pipe(addOne, square); 101 | 102 | addOneThenSquare(2) // 9 103 | ``` 104 | 105 | 上面代码中,addOneThenSquare是一个合成函数。定义它的时候,根本不需要提到要处理的值,这就是 Pointfree。 106 | 107 | ## 四、Pointfree 的本质 108 | 109 | Pointfree 的本质就是使用一些通用的函数,组合出各种复杂运算。上层运算不要直接操作数据,而是通过底层函数去处理。这就要求,将一些常用的操作封装成函数。 110 | 111 | 比如,读取对象的role属性,不要直接写成obj.role,而是要把这个操作封装成函数。 112 | 113 | ```js 114 | var prop = (p, obj) => obj[p]; 115 | var propRole = R.curry(prop)('role'); 116 | ``` 117 | 118 | 上面代码中,prop函数封装了读取操作。它需要两个参数p(属性名)和obj(对象)。这时,要把数据obj要放在最后一个参数,这是为了方便柯里化。函数propRole则是指定读取role属性,下面是它的用法(查看完整代码)。 119 | 120 | ```js 121 | var isWorker = s => s === 'worker'; 122 | var getWorkers = R.filter(R.pipe(propRole, isWorker)); 123 | 124 | var data = [ 125 | {name: '张三', role: 'worker'}, 126 | {name: '李四', role: 'worker'}, 127 | {name: '王五', role: 'manager'}, 128 | ]; 129 | getWorkers(data) 130 | // [ 131 | // {"name": "张三", "role": "worker"}, 132 | // {"name": "李四", "role": "worker"} 133 | // ] 134 | ``` 135 | 136 | 上面代码中,data是传入的值,getWorkers是处理这个值的函数。定义getWorkers的时候,完全没有提到data,这就是 Pointfree。 137 | 138 | 简单说,Pointfree 就是运算过程抽象化,处理一个值,但是不提到这个值。这样做有很多好处,它能够让代码更清晰和简练,更符合语义,更容易复用,测试也变得轻而易举。 139 | 140 | ## 五、Pointfree 的示例一 141 | 142 | ```js 143 | var str = 'Lorem ipsum dolor sit amet consectetur adipiscing elit'; 144 | ``` 145 | 146 | 上面是一个字符串,请问其中最长的单词有多少个字符? 147 | 148 | 我们先定义一些基本运算。 149 | 150 | ```js 151 | // 以空格分割单词 152 | var splitBySpace = s => s.split(' '); 153 | // 每个单词的长度 154 | var getLength = w => w.length; 155 | // 词的数组转换成长度的数组 156 | var getLengthArr = arr => R.map(getLength, arr); 157 | // 返回较大的数字 158 | var getBiggerNumber = (a, b) => a > b ? a : b; 159 | // 返回最大的一个数字 160 | var findBiggestNumber = 161 | arr => R.reduce(getBiggerNumber, 0, arr); 162 | ``` 163 | 164 | 然后,把基本运算合成为一个函数(查看完整代码)。 165 | 166 | ```js 167 | var getLongestWordLength = R.pipe( 168 | splitBySpace, 169 | getLengthArr, 170 | findBiggestNumber 171 | ); 172 | 173 | getLongestWordLength(str) // 11 174 | 175 | ``` 176 | 177 | 可以看到,整个运算由三个步骤构成,每个步骤都有语义化的名称,非常的清晰。这就是 Pointfree 风格的优势。 178 | 179 | Ramda 提供了很多现成的方法,可以直接使用这些方法,省得自己定义一些常用函数(查看完整代码)。 180 | 181 | ```js 182 | // 上面代码的另一种写法 183 | var getLongestWordLength = R.pipe( 184 | R.split(' '), 185 | R.map(R.length), 186 | R.reduce(R.max, 0) 187 | ); 188 | ``` 189 | 190 | ## 六、Pointfree 示例二 191 | 192 | 最后,看一个实战的例子,拷贝自 Scott Sauyet 的文章《Favoring Curry》。那篇文章能帮助你深入理解柯里化,强烈推荐阅读。 193 | 194 | 下面是一段服务器返回的 JSON 数据。 195 | 196 | ```js 197 | var data = { 198 | result: "SUCCESS", 199 | interfaceVersion: "1.0.3", 200 | requested: "10/17/2013 15:31:20:", 201 | lastUpdated: "10/16/2013 10:52:39" 202 | tasks: [ 203 | {id: 104, complete: false, priority: "high", 204 | dueDate: "2013-11-29", username: "Scott", 205 | title: "Do something", created: "9/22/2013"}, 206 | {id: 105, complete: false, priority: "medium", 207 | dueDate: "2013-11-22", username: "Lena", 208 | title: "Do something else", created: "9/22/2013"}, 209 | {id: 107, complete: true, priority: "high", 210 | dueDate: "2013-11-29", username: "Mike", 211 | title: "Fix the foo", created: "9/22/2013"}, 212 | {id: 108, complete: false, priority: "low", 213 | dueDate: "2013-11-15", username: "Punam", 214 | title: "Adjust the bar", created: "9/25/2013"}, 215 | {id: 110, complete: false, priority: "medium", 216 | dueDate: "2013-11-15", username: "Scott", 217 | title: "Rename everything", created: "10/2/2013"}, 218 | {id: 112, complete: true, priority: "high", 219 | dueDate: "2013-11-27", username: "Lena", 220 | title: "Alter all quuxes", created: "10/5/2013"}, 221 | ] 222 | } 223 | ``` 224 | 225 | 现在要求是,找到用户 Scott 的所有未完成任务,并按到期日期升序排列。 226 | 227 | ```js 228 | [ 229 | {id: 110, complete: false, priority: "medium", 230 | dueDate: "2013-11-15", username: "Scott", 231 | title: "Rename everything", created: "10/2/2013"}, 232 | {id: 104, complete: false, priority: "high", 233 | dueDate: "2013-11-29", username: "Scott", 234 | title: "Do something", created: "9/22/2013"} 235 | ] 236 | ``` 237 | 238 | ```js 239 | getIncompleteTaskSummaries = function(membername) { 240 | return fetchData() 241 | .then(function(data){ 242 | return data.tasks; 243 | }) 244 | .then(function(tasks){ 245 | var results = []; 246 | for (var i = 0, len = tasks.length; i < len; i++) { 247 | if (tasks[i].username == membername) { 248 | results.push(tasks[i]); 249 | } 250 | } 251 | return results; 252 | }) 253 | .then(function(tasks){ 254 | var results = []; 255 | for (var i = 0, len = tasks.length; i < len; i++) { 256 | if (!tasks[i].complete) { 257 | results.push(tasks[i]); 258 | } 259 | } 260 | return results; 261 | }) 262 | .then(function(tasks){ 263 | var results = [], task; 264 | for (var i = 0, len = tasks.length; i < len; i++) { 265 | task = tasks[i]; 266 | results.push({ 267 | id: task.id, 268 | dueDate: task.dueDate, 269 | title: task.title, 270 | priority: task.priority 271 | }) 272 | } 273 | return results; 274 | }) 275 | .then(function(tasks){ 276 | tasks.sort(function(first, second){ 277 | var a = first.dueDate, b = second.dueDate; 278 | return a < b ? -1 : a > b ? 1: 0; 279 | }) 280 | return tasks; 281 | }) 282 | } 283 | ``` 284 | 285 | 上面代码不易读,出错的可能性很大。 286 | 287 | 现在使用 Pointfree 风格改写(查看完整代码)。 288 | 289 | ```js 290 | var getIncompleteTaskSummaries = function(membername) { 291 | return fetchData() 292 | .then(R.prop('tasks')) 293 | .then(R.filter(R.propEq('username', membername))) 294 | .then(R.reject(R.propEq('complete', true))) 295 | .then(R.map(R.pick(['id', 'dueDate', 'title', 'priority']))) 296 | .then(R.sortBy(R.prop('dueDate'))); 297 | }; 298 | ``` 299 | 300 | 上面代码已经清晰很多了。 301 | 302 | 另一种写法是,把各个then里面的函数合成起来(查看完整代码)。 303 | 304 | ```js 305 | // 提取 tasks 属性 306 | var SelectTasks = R.prop('tasks'); 307 | // 过滤出指定的用户 308 | var filterMember = member => R.filter( 309 | R.propEq('username', member) 310 | ); 311 | // 排除已经完成的任务 312 | var excludeCompletedTasks = R.reject(R.propEq('complete', true)); 313 | // 选取指定属性 314 | var selectFields = R.map( 315 | R.pick(['id', 'dueDate', 'title', 'priority']) 316 | ); 317 | // 按照到期日期排序 318 | var sortByDueDate = R.sortBy(R.prop('dueDate')); 319 | // 合成函数 320 | var getIncompleteTaskSummaries = function(membername) { 321 | return fetchData().then( 322 | R.pipe( 323 | SelectTasks, 324 | filterMember(membername), 325 | excludeCompletedTasks, 326 | selectFields, 327 | sortByDueDate, 328 | ) 329 | ); 330 | }; 331 | ``` 332 | 333 | 上面的代码跟过程式的写法一比较,孰优孰劣一目了然。 -------------------------------------------------------------------------------- /es5-senior/exercises.md: -------------------------------------------------------------------------------- 1 | ## ES5核心技术 2 | 3 | > **主要通过例题的方式讲解一些ES5的核心知识** 4 | 5 | ### 1.第一题 以下代码运算结果 6 | 7 | ``` html 8 | 17 | ``` 18 | 19 | 讲解: 20 | 21 | 该题涉及的知识点有: 22 | 变量、作用域、函数声明、函数表达式、立即执行函数(IIFE) 23 | * 我们看到了一个立即执行函数 24 | 25 | ``` js 26 | (function(){...})() 27 | (function(){...}()) 28 | ``` 29 | 30 | > IIFE的作用 31 | > * 一是不必为函数命名,避免了污染全局变量 32 | > * 二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取 的私有变量。 33 | 34 | javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用IIFE可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。 35 | 36 | 立即执行函数体内的变量 `var a` ,覆盖了全局声明的变量的 `var a` ,另外IIFE内部的 `var a` 因为变量提升的关系,在 `console.log(a)` 访问前就已经声明完毕(此时只是执行`var a`的过程,所以变量处于 **undefined** 状态),在 `var a=30` 时完成赋值的过程。 37 | 38 | 所以最后的执行结果是:`undefined` 39 | 40 | 另外这道题简单做一些修改,使上边所说的特性更加清晰: 41 | 42 | ``` html 43 | 56 | ``` 57 | 58 | ### 2.第二题 以下代码运算结果 59 | 60 | ``` js 61 | this.a = 20; 62 | var test = { 63 | a: 40, 64 | init: ()=> { 65 | console.info(this.a); 66 | function go() { 67 | // this.a = 60; 68 | console.info(this.a); 69 | } 70 | go.prototype.a = 50; 71 | return go; 72 | } 73 | }; 74 | // var p = test.init(); 75 | // p(); 76 | new (test.init())(); 77 | ``` 78 | 79 | 这道题目相当复杂,所以在解析这道题目的时候,我们可以尝试着把复杂的问题解析成小问题,逐个击破。 80 | 81 | ``` js 82 | 94 | ``` 95 | 96 | 这个例子主要就是分析this的指向,关于这个我有详细的文章介绍。请参考。 97 | [this问题详解][1] 98 | 99 | [1]: https://github.com/Martin-Shao/yideng-note/blob/master/es5-senior/article/this.md 100 | 101 | ``` js 102 | 119 | ``` 120 | 121 | 上面的例子中,`this.a` 的值和P变量是完全没有关系的。`(p.test())();`这段代码和下面注释掉的两段代码是一样,这样就能很明白的看出来,`s();` 函数时挂载在window对象的,所以函数中的this就是指向window对象的。 122 | 123 | 第二点就是要注意`this.a = 60;` 和 `this.a = 20;` 中的this都是指向window的,所以前者的 `a` 的值要覆盖后者。 124 | 125 | ``` js 126 | 141 | ``` 142 | 143 | 这段代码和前面的代码得出的结果是一样,另一方面也证明了,在浏览器中,凡是没有指明调用对象的,都会默认绑定window对象。 144 | 145 | ### 3.第二题 以下代码运算结果 146 | 147 | ``` js 148 | 163 | ``` 164 | 165 | 这个例题就是典型的闭包,造成的问题就是N这个变量永驻内存,其实就会造成内存泄露问题。得出的结果是 166 | > 1 2 3 167 | 168 | 解决这个问题的办法是在逻辑结束之后,给变量赋值 `null` ,这是闭包带来不好的问题,另外一方面,闭包还能给JavaScript这么语言带来私有变量。 169 | 170 | ``` js 171 | 186 | ``` 187 | 188 | ### 4. 面向对象与继承 189 | 190 | 第四部分开始,就要涉及到JavaScript面向对象编程的问题了。 191 | 192 | 下面代码简单演示了什么是对象、构造器和以及最简单的原型继承实例对象。 193 | 194 | 1. 面向对象编程范式的概念介绍 195 | 2. JavaScript语言支持面向对象的基石 196 | 3. JavaScript之原型、构造函数、继承解析 197 | 4. JavaScript按值传递和按引用传递在继承中问题 198 | 5. 原型继承、构造继承、组合继承、寄生组合继承优缺点解析 199 | 200 | ``` js 201 | 217 | ``` 218 | 219 | 从这里开始,算是接触到JavaScript面相对象编程的知识了。 220 | 关于这部分知识,会单独做一个专题,请移步。 221 | 222 | ### 5. 变量提升和函数提升 223 | 224 | ``` js 225 | (function() { 226 | var a = 20; 227 | function a() {} 228 | console.info(a); // console => 20 229 | })(); 230 | ``` 231 | 232 | ``` js 233 | (function() { 234 | var a = 20; 235 | var b = c = a; 236 | })(); 237 | console.info(c); // console => 20 238 | ``` 239 | 240 | ``` js 241 | (function() { 242 | function a() { 243 | var a = 20; 244 | var b = c = a; 245 | } 246 | })(); 247 | console.info(c); // Uncaught ReferenceError: c is not defined 248 | ``` 249 | 250 | ``` js 251 | (function() { 252 | var a = 20; 253 | var b, c = a; 254 | })(); 255 | console.info(c); // Uncaught ReferenceError: c is not defined 256 | ``` 257 | 258 | ``` js 259 | function test() { 260 | this.a = 20; 261 | } 262 | test.prototype.a = 30; 263 | var q = new test(); 264 | console.info(q.a); 265 | ``` 266 | 267 | ``` js 268 | var user = { 269 | age: 20, 270 | init: function() { 271 | console.info(this.age); 272 | } 273 | } 274 | var data = {age: 40}; 275 | var u = user.init.bind(data); 276 | u.init(); 277 | ``` 278 | 279 | ``` js 280 | function test(m) { 281 | m.v = 20; 282 | } 283 | var m = {age: 30}; 284 | test(m); 285 | console.info(m); 286 | console.info(m.v); // console => 20 287 | ``` 288 | 289 | ```js 290 | var bo = 10; 291 | function foo() { 292 | console.log(bo); 293 | } 294 | foo(); 295 | 296 | (function() { 297 | var bo = 20; 298 | foo(); 299 | })() 300 | 301 | (function (func) { 302 | var bo = 30; 303 | func(); 304 | })(foo) 305 | ``` 306 | 307 | ## 大总结 308 | 309 | 1. 理解执行函数 310 | 2. 闭包,内部函数可以访问外部函数的变量,把函数返回出去 311 | 闭包可以保护内部的变量,闭包造成内存泄露 == null 312 | 3. 原型链 313 | 3.1. 构造函数里的属性的优先级比原型链的要高 314 | 3.2. 面向对象编程的时候,js没有类的概念,可以用函数替代 315 | 3.3. constructor实际就是对应的那个函数 316 | 3.4. prototype按引用传递的,Object.create原型链的副本 317 | 4. 数值 字符串 布尔类型按值传递 318 | 5. 改变this的方法 call apply bind 319 | 6. 函数提升,变量提升 函数提升的级别要比变量提升高 320 | 7. jquery内部有很多经典的写法 模块化编程的概念 闭包 321 | 322 | ## ES5核心知识提炼 323 | 324 | JavaScript基础之变量类型 325 | JavaScript探究this奥秘之旅 326 | 面向对象编程范式简介 327 | JavaScript面向对象编程基础 328 | JavaScript之理解“一等公民”--函数 329 | Function原型方法call、apply、bind全方位解析 330 | JavaScript深度理解原型链 331 | JavaScript全方位理解闭包 332 | JavaScript继承深度解析 333 | 334 | 问题是这样的:有这样的一个代码: 335 | 336 | ``` js 337 | var s = { 338 | p: function () { 339 | return function () { 340 | console.log('enna') 341 | } 342 | } 343 | } 344 | (s.p())() 345 | ``` 346 | 347 | 志佳老师运行的结果是这样的: 348 | 349 | ![alt text](./article/img/微信图片_20181221105234.jpg "Title") 350 | 但是我的结果是这样的: 351 | ![alt text](./article/img/微信图片_20181221105738.png "Title") 352 | 353 | 报错很清楚呀 {} 对象不是一个函数 直接跟() 就等于执行了 不是函数执行不了 es5可执行的代码只有全局代码 函数 和 eval呀 354 | 355 | 这个问题的报错分为两个部分: 356 | 357 | ``` js 358 | // 1. Cannot read property 'xxx' of undefined 359 | // 2. {(intermediate value)} is not a function 360 | ``` 361 | 362 | ```js 363 | function Car(color, price) { 364 | this.color = color; 365 | this.price = price; 366 | } 367 | Car.prototype.sail = function() { 368 | console.info(this.color + ' car is sailing ' + this.price); 369 | } 370 | 371 | function inherite(subType, SuperType) { 372 | var prototype = Object.create(SuperType.prototype); 373 | prototype.constructor = subType; 374 | subType.prototype = prototype; 375 | } 376 | 377 | function Cruze(color, price, seller) { 378 | Car.call(this, color, price); 379 | this.seller = seller; 380 | } 381 | 382 | inherite(Cruze, Car); 383 | 384 | var cruze = new Cruze('red', 140000); 385 | cruze.sail(); 386 | ``` 387 | 388 | ```js 389 | var s = []; 390 | var arr = s; 391 | var pusher; 392 | var tmp; 393 | for (var i = 0; i < 3; i++) { 394 | pusher = { 395 | value: 'item' + i 396 | }; 397 | if (i !== 2) { 398 | tmp = [] 399 | pusher.children = tmp 400 | } 401 | arr.push(pusher); 402 | arr = tmp; 403 | } 404 | console.log(s[0]); 405 | ``` 406 | 407 | ```js 408 | var Container = function (x) { 409 | this._value = x; 410 | } 411 | Container.of = x => new Container(x); 412 | Container.prototype.map = function (f) { 413 | return Container.of(f(this._value)) 414 | } 415 | Container.of(3) 416 | .map(x => x + 1) 417 | .map(x => 'Result is' + x); 418 | ``` 419 | 420 | ```js 421 | const timeout = ms => 422 | new Promise((resolve, reject) => { 423 | setTimeout(() => { 424 | resolve(); 425 | }, ms); 426 | }); 427 | const ajax1 = () => //#endregion 428 | timeout(2000).then(() => { 429 | console.log('1'); 430 | return 1; 431 | }); 432 | const ajax2 = () => //#endregion 433 | timeout(1000).then(() => { 434 | console.log('2'); 435 | return 2; 436 | }); 437 | const ajax3 = () => //#endregion 438 | timeout(2000).then(() => { 439 | console.log('3'); 440 | return 3; 441 | }); 442 | const mergePromise = (ajaxArray) => { 443 | var data = []; 444 | var sequence = Promise.resolve(); 445 | ajaxArray.forEach(function (item) { 446 | sequence = sequence.then(item).then(function (res) { 447 | data.push(res); 448 | return data; 449 | }); 450 | }) 451 | 452 | return sequence; 453 | } 454 | mergePromise([ajax1, ajax2, ajax3]).then(data => { 455 | console.info('done'); 456 | console.log(data); 457 | }) 458 | ``` -------------------------------------------------------------------------------- /functional-programming/article/2019-1-20.md: -------------------------------------------------------------------------------- 1 | # FP编程进阶之——Functor、Applicative 和 Monad 2 | 3 | 本文转自: [雷纯锋的技术博客-Functor、Applicative 和 Monad](http://blog.leichunfeng.com/blog/2015/11/08/functor-applicative-and-monad/) 4 | 5 | ![fam](./img/fam.png) 6 | 7 | ## 引言 8 | 9 | 首先这篇文章是写的十分好的,对于函数式编程中Functor、Applicative 和 Monad的理解比较深刻,但是阅读此文的同学最好知道一点Haskell语言的知识,将提升不少阅读体验。如果实在不想触碰Haskell,也是能够勉强理解的。最后补充一点:Haskell是一种标准化的、通用纯函数式编程语言,通过学习Haskell,我们将对于函数式编程有着更加深刻的理解。通过降维打击的学习JavaScript的函数式编程,将不再那么困难。 10 | 11 | 我们在学习JavaScript函数式编程的时候,通常所说的Functor函子、AP函子、Monad函子的概念就来源此:Haskell函数式编程中Functor、Applicative 和 Monad。笔者也是受到这篇文章的启发,开始启动Haskell学习计划。这里推荐: [Haskell 趣學指南](https://legacy.gitbook.com/book/mno2/learnyouahaskell-zh/details/zh-cn) 12 | 13 | `Functor` `、Applicative` 和 `Monad` 是函数式编程语言中三个非常重要的概念,尤其是 `Monad` ,难倒了不知道多少英雄好汉。事实上,它们的概念是非常简单的,但是却很少有文章能够将它们描述清楚,往往还适得其反,越描越黑。与其它文章不同的是,本文将从结论出发,层层深入,一步步为你揭开它们的神秘面纱。 14 | 15 | **说明**:本文中的主要代码为 Haskell 语言,它是一门纯函数式的编程语言。其中,具体的语法细节,我们不需要太过关心,因为这并不影响你对本文的理解。 16 | 17 | 结论 18 | 关于 `Functor` `、Applicative` 和 `Monad` 的概念,其实各用一句话就可以概括: 19 | 20 | 1. 一个 `Functor` 就是一种实现了 `Functor typeclass` 的数据类型; 21 | 2. 一个 `Applicative` 就是一种实现了 `Applicative typeclass` 的数据类型; 22 | 3. 一个 `Monad` 就是一种实现了 `Monad typeclass` 的数据类型。 23 | 当然,你可能会问那什么是 `typeclass` 呢?我想当你在看到**实现**二字的时候,就应该已经猜到了: 24 | 25 | > A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages. Well, they’re not. You can think of them kind of as Java interfaces, only better. 26 | 27 | 是的, `typeclass` 就类似于 `Java` 中的接口,或者 `Objective-C` 中的协议。在 `typeclass` 中定义了一些函数,实现一个 `typeclass` 就是要实现这些函数,而所有实现了这个 `typeclass` 的数据类型都会拥有这些共同的行为。 28 | 29 | 那 `Functor` `、Applicative` 和 `Monad` 三者之间有什么联系吗,为什么它们老是结队出现呢?其实, `Applicative` 是增强型的 `Functor` ,一种数据类型要成为 `Applicative` 的前提条件是它必须是 `Functor` ;同样的, `Monad` 是增强型的 `Applicative` ,一种数据类型要成为 `Monad` 的前提条件是它必须是 `Applicative` 。注:这个联系,在我们看到 `Applicative typeclass` 和 `Monad typeclass` 的定义时,自然就会明了。 30 | 31 | ## Maybe 32 | 33 | 在正式开始介绍 `Functor` 、 `Applicative` 和 `Monad` 的定义前,我想先介绍一种非常有意思的数据类型, **`Maybe`** 类型(可类比 `Swift` 中的 `Optional` ): 34 | 35 | > The Maybe type encapsulates an optional value. A value of type Maybe a either contains a value of type a (represented as Just a), or it is empty (represented as Nothing). Using Maybe is a good way to deal with errors or exceptional cases without resorting to drastic measures such as error. 36 | 37 | `Maybe` 类型封装了一个可选值。一个 `Maybe a` 类型的值要么包含一个 `a` 类型的值(用 `Just a` 表示);要么为空(用 `Nothing` 表示)。我们可以把 `Maybe` 看作一个盒子,这个盒子里面可能装着一个 `a` 类型的值,即 `Just a` ;也可能是一个空盒子,即 `Nothing` 。或者,你也可以把它理解成泛型,比如 `Objective-C` 中的 `NSArray` 。不过,最正确的理解应该是把 `Maybe` 看作一个上下文,这个上下文表示某次计算可能成功也可能失败,成功时用 `Just a` 表示,`a` 为计算结果;失败时用 `Nothing` 表示,这就是 `Maybe` 类型存在的意义: 38 | 39 | ![maybe](./img/maybe.png) 40 | 41 | ```haskell 42 | > :i Maybe 43 | data Maybe a = Nothing | Just a -- Defined in ‘GHC.Base’ 44 | ``` 45 | 46 | 下面,我们来直观地感受一下 `Maybe` 类型: 47 | 48 | ```haskell 49 | ghci> Nothing 50 | Nothing 51 | ghci> Just 2 52 | Just 2 53 | ``` 54 | 55 | 我们可以用盒子模型来理解一下,Nothing 就是一个空盒子;而 Just 2 则是一个装着 2 这个值的盒子: 56 | 57 | ![alt](./img/just2.png) 58 | 59 | 提前剧透: `Maybe` 类型实现了 `Functor typeclass`、`Applicative typeclass` 和 `Monad typeclass` ,所以它同时是 `Functor` 、 `Applicative` 和 `Monad` ,具体实现细节将在下面的章节进行介绍。 60 | 61 | ## Functor 62 | 63 | 在正式开始介绍 `Functor` 前,我们先思考一个这样的问题,假如我们有一个值 2 : 64 | 65 | ![normal2](./img/normal2.png) 66 | 67 | 我们如何将函数 `(+3)` 应用到这个值上呢?我想上过小学的朋友应该都知道,这就是一个简单的加法运算: 68 | 69 | ![value_apply](./img/value_apply.png) 70 | 71 | ``` shell 72 | ghci> (+3) 2 73 | 5 74 | ``` 75 | 76 | 分分钟搞定。那么问题来了,如果这个值 2 是在一个上下文中呢?比如 `Maybe` ,此时,这个值 `2` 就变成了 `Just 2` : 77 | 78 | ![alt](./img/just2.png) 79 | 80 | 这个时候,我们就不能直接将函数 `(+3)` 应用到 `Just 2` 了。那么,我们如何将一个函数应用到一个在上下文中的值呢? 81 | 82 | ![fmap](./img/fmap.png) 83 | 84 | 是的,我想你应该已经猜到了, `Functor` 就是干这事的,欲知后事如何,请看下节分解。 85 | 86 | ## Functor typeclass 87 | 88 | 首先,我们来看一下 `Functor typeclass` 的定义: 89 | 90 | ```haskell 91 | class Functor f where 92 | fmap :: (a -> b) -> f a -> f b 93 | ``` 94 | 95 | 在 `Functor typeclass` 中定义了一个函数 `fmap` ,它将一个函数 `(a -> b)` 应用到一个在上下文中的值 `f a` ,并返回另一个在相同上下文中的值 `f b` ,这里的 `f` 是一个类型占位符,表示任意类型的 `Functor` 。 96 | 97 | 注: `fmap` 函数可类比 `Swift` 中的 `map` 方法。 98 | 99 | ## Maybe Functor 100 | 101 | 我们知道 `Maybe` 类型就是一个 `Functor` ,它实现了 `Functor typeclass` 。我们将类型占位符 `f` 用具体类型 `Maybe` 代入可得: 102 | 103 | ```haskell 104 | class Functor Maybe where 105 | fmap :: (a -> b) -> Maybe a -> Maybe b 106 | ``` 107 | 108 | 因此,对于 `Maybe` 类型来说,它要实现的函数 `fmap` 的功能就是将一个函数 `(a -> b)` 应用到一个在 `Maybe` 上下文中的值 `Maybe a` ,并返回另一个在 `Maybe` 上下文中的值 `Maybe b` 。接下来,我们一起来看一下 `Maybe` 类型实现 `Functor typeclass` 的具体细节: 109 | 110 | ```haskell 111 | instance Functor Maybe where 112 | fmap func (Just x) = Just (func x) 113 | fmap func Nothing = Nothing 114 | ``` 115 | 116 | 这里针对 `Maybe` 上下文的两种情况分别进行了处理:如果盒子中有值,即 `Just x` ,那么就将 `x` 从盒子中取出,然后将函数 `func` 应用到 `x` ,最后将结果放入一个相同类型的新盒子中;如果盒子为空,那么直接返回一个新的空盒子。 117 | 118 | 看到这里,我想你应该已经知道如何将一个函数应用到一个在上下文中的值了。比如前面提到的将函数 `(+3)` 应用到 `Just 2` : 119 | 120 | ![fmap_just](./img/fmap_just.png) 121 | 122 | ```haskell 123 | ghci> fmap (+3) (Just 2) 124 | Just 5 125 | ``` 126 | 127 | 另外,值得一提的是,当我们将函数 `(+3)` 应用到一个空盒子,即 `Nothing` 时,我们将会得到一个新的空盒子: 128 | 129 | ![fmap_nothing](./img/fmap_nothing.png) 130 | 131 | ```haskell 132 | ghci> fmap (+3) Nothing 133 | Nothing 134 | ``` 135 | 136 | ## Applicative 137 | 138 | 现在,我们已经知道如何将函数 `(+3)` 应用到 `Just 2` 了。那么问题又来了,如果函数 `(+3)` 也在上下文中呢,比如 `Maybe` ,此时,函数 `(+3)` 就变成了 `Just (+3)` : 139 | 140 | 那么,我们如何将一个在上下文中的函数应用到一个在上下文中的值呢? 141 | 142 | 这就是 `Applicative` 要干的事,详情请看下节内容。 143 | 144 | ## Applicative typeclass 145 | 146 | 同样的,我们先来看一下 `Applicative typeclass` 的定义: 147 | 148 | ```haskell 149 | class Functor f => Applicative f where 150 | pure :: a -> f a 151 | (<*>) :: f (a -> b) -> f a -> f b 152 | ``` 153 | 154 | 我们注意到,与 `Functor typeclass` 的定义不同的是,在 `Applicative typeclass` 的定义中多了一个类约束 `Functor f` ,表示的意思是数据类型 `f` 要实现 `Applicative typeclass` 的前提条件是它必须要实现 `Functor typeclass` ,也就是说它必须是一个 `Functor` 。 155 | 156 | 在 `Applicative typeclass` 中定义了两个函数: 157 | 158 | * `pure` :将一个值 `a` 放入上下文中; 159 | * `(<*>)` :将一个在上下文中的函数 `f (a -> b)` 应用到一个在上下文中的值 `f a` ,并返回另一个在上下文中的值 `f b` 。 160 | 注:`<*>` 函数的发音我也不知道,如果有同学知道的话还请告之,谢谢。 161 | 162 | ## Maybe Applicative 163 | 164 | 同样的,我们将类型占位符 `f` 用具体类型 `Maybe` 代入,可得: 165 | 166 | ```haskell 167 | class Functor Maybe => Applicative Maybe where 168 | pure :: a -> Maybe a 169 | (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b 170 | ``` 171 | 172 | 因此,对于 `Maybe` 类型来说,它要实现的 `pure` 函数的功能就是将一个值 `a` 放入 `Maybe` 上下文中。而 `(<*>)` 函数的功能则是将一个在 `Maybe` 上下文中的函数 `Maybe (a -> b)` 应用到一个在 `Maybe` 上下文中的值 `Maybe a` ,并返回另一个在 `Maybe` 上下文中的值 `Maybe b` 。接下来,我们一起来看一下 `Maybe` 类型实现 `Applicative typeclass` 的具体细节: 173 | 174 | ```haskell 175 | instance Applicative Maybe where 176 | pure = Just 177 | Nothing <*> _ = Nothing 178 | (Just func) <*> something = fmap func something 179 | ``` 180 | 181 | `pure` 函数的实现非常简单,直接等于 `Just` 即可。而对于 `(<*>)` 函数的实现,我们同样需要针对 `Maybe` 上下文的两种情况分别进行处理:当装函数的盒子为空时,直接返回一个新的空盒子;当装函数的盒子不为空时,即 `Just func` ,则取出 `func` ,使用 `fmap` 函数直接将 `func` 应用到那个在上下文中的值,这个正是我们前面说的 `Functor` 的功能。 182 | 183 | 好了,我们接下来看一下将 `Just (+3)` 应用到 `Just 2` 的具体过程: 184 | 185 | ![applicative_just](./img/applicative_just.png) 186 | 187 | ```haskell 188 | ghci> Just (+3) <*> Just 2 189 | Just 5 190 | ``` 191 | 192 | 同样的,当我们将一个空盒子,即 `Nothing` 应用到 `Just 2` 的时候,我们将得到一个新的空盒子: 193 | 194 | ```haskell 195 | ghci> Nothing <*> Just 2 196 | Nothing 197 | ``` 198 | 199 | ## Monad 200 | 201 | 截至目前,我们已经知道了 `Functor` 的作用就是应用一个函数到一个上下文中的值: 202 | ![fmap](./img/fmap.png) 203 | 204 | 而 `Applicative` 的作用则是应用一个上下文中的函数到一个上下文中的值: 205 | ![apply_justadd3_just2](./img/apply_justadd3_just2.png) 206 | 207 | 那么 `Monad` `又会是什么呢?其实,Monad` 的作用跟 `Functor` 类似,也是应用一个函数到一个上下文中的值。不同之处在于, `Functor` 应用的是一个接收一个普通值并且返回一个普通值的函数,而 `Monad` 应用的是一个接收一个普通值但是返回一个在上下文中的值的函数: 208 | 209 | ![bind](./img/bind.png) 210 | 211 | ## Monad typeclass 212 | 213 | 同样的,我们先来看一下 `Monad typeclass` 的定义: 214 | 215 | ```haskell 216 | class Applicative m => Monad m where 217 | return :: a -> m a 218 | 219 | (>>=) :: m a -> (a -> m b) -> m b 220 | 221 | (>>) :: m a -> m b -> m b 222 | x >> y = x >>= \_ -> y 223 | 224 | fail :: String -> m a 225 | fail msg = error msg 226 | ``` 227 | 228 | 哇,这什么鬼,完全看不懂啊,太复杂了。兄台莫急,且听我细说。在 `Monad typeclass` 中定义了四个函数,分别是 `return` 、`(>>=)`、`(>>)` 和 `fail` ,且后两个函数 `(>>)` 和 `fail` 给出了默认实现,而在绝大多数情况下,我们都不需要去重写它们。因此,去掉这两个函数后,`Monad typeclass` 的定义可简化为: 229 | 230 | ```haskell 231 | class Applicative m => Monad m where 232 | return :: a -> m a 233 | (>>=) :: m a -> (a -> m b) -> m b 234 | ``` 235 | 236 | 怎么样?现在看上去就好多了吧。跟 `Applicative typeclass` 的定义一样,在 `Monad typeclass` 的定义中也有一个类约束 `Applicative m` ,表示的意思是一种数据类型 `m` 要成为 `Monad` 的前提条件是它必须是 `Applicative` 。另外,其实 `return` 函数的功能与 `Applicative` 中的 `pure` 函数的功能是一样的,只不过换了一个名字而已,它们的作用都是将一个值 `a` 放入上下文中。而 `(>>=)` 函数的功能则是应用一个(接收一个普通值 `a` 但是返回一个在上下文中的值 `m b` 的)函数 `(a -> m b)` 到一个上下文中的值 `m a` ,并返回另一个在相同上下文中的值 `m b` 。 237 | 238 | 注:`>>=` 函数的发音为 `bind` ,学习 `ReactiveCocoa` 的同学要注意啦。另外,`>>=` 函数可类比 `Swift` 中的 `flatMap` 方法。 239 | 240 | ## Maybe Monad 241 | 242 | 同样的,我们将类型占位符 `m` 用具体类型 `Maybe` 代入,可得: 243 | 244 | ```haskell 245 | class Applicative Maybe => Monad Maybe where 246 | return :: a -> Maybe a 247 | (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b 248 | ``` 249 | 250 | 相信你用盒子模型已经能够轻松地理解上面两个函数了,因此不再赘述。接下来,我们一起来看一下 `Maybe` 类型实现 `Monad typeclass` 的具体细节: 251 | 252 | ```haskell 253 | instance Monad Maybe where 254 | return x = Just x 255 | Nothing >>= func = Nothing 256 | Just x >>= func = func x 257 | ``` 258 | 259 | 正如前面所说, `return` 函数的实现跟 `pure` 函数一样,直接等于 `Just` 函数即可,功能就是将一个值 `x` 放入 `Maybe` 盒子中,变成 `Just x` 。同样的,对于 `(>>=)` 函数的实现,我们需要针对 `Maybe` 上下文的两种情况分别进行处理,当盒子为空时,直接返回一个新的空盒子;当盒子不为空时,即 `Just x` ,则取出 `x` ,直接将 `func` 函数应用到 `x` ,而我们知道 `func x` 的结果就是一个在上下文中的值。 260 | 261 | 下面,我们一起来看一个具体的例子。我们先定义一个 `half` 函数,这个函数接收一个数字 `x` 作为参数,如果 `x` 是偶数,则将 `x` 除以 `2` ,并将结果放入 `Maybe` 盒子中;如果 `x` 不是偶数,则返回一个空盒子: 262 | 263 | ![half](./img/half.png) 264 | 265 | ```haskell 266 | half x = if even x 267 | then Just (x `div` 2) 268 | else Nothing 269 | ``` 270 | 271 | 接下来,我们使用 `(>>=)` 函数将 `half` 函数应用到 `Just 20` ,假设得到结果 `y` ;然后继续使用 `(>>=)` 函数将 `half` 函数应用到上一步的结果 `y` ,以此类推,看看会得到什么样的结果: 272 | 273 | ```shell 274 | ghci> Just 20 >>= half 275 | Just 10 276 | ghci> Just 10 >>= half 277 | Just 5 278 | ghci> Just 5 >>= half 279 | Nothing 280 | ``` 281 | 282 | 看到上面的运算过程,不知道你有没有看出点什么端倪呢?上一步的输出作为下一步的输入,并且只要你愿意的话,这个过程可以无限地进行下去。我想你可能已经想到了,是的,就是链式操作。所有的操作链接起来就像是一条生产线,每一步的操作都是对输入进行加工,然后产生输出,整个操作过程可以看作是对最初的原材料 `Just 20` 进行加工并最终生产出成品 `Nothing` 的过程: 283 | 284 | ![monad_chain](./img/monad_chain.png) 285 | 286 | ```shell 287 | ghci> Just 20 >>= half >>= half >>= half 288 | Nothing 289 | ``` 290 | 291 | 注:链式操作只是 `Monad` 为我们带来的主要好处之一;另一个本文并未涉及到的主要好处是, `Monad` 可以为我们自动处理上下文,而我们只需要关心真正的值就可以了。 292 | 293 | ## 总结 294 | 295 | `Functor` `、Applicative` 和 `Monad` 是什么: 296 | 297 | 一个 `Functor` 就是一种实现了 `Functor typeclass` 的数据类型; 298 | 一个 `Applicative` 就是一种实现了 `Applicative typeclass` 的数据类型; 299 | 一个 `Monad` 就是一种实现了 `Monad typeclass` 的数据类型。 300 | 301 | `Functor` `、Applicative` 和 `Monad` 三者之间的联系: 302 | `Applicative` 是增强型的 `Functor` ,一种数据类型要成为 `Applicative` 的前提条件是它必须是 `Functor` ; 303 | `Monad` 是增强型的 `Applicative` ,一种数据类型要成为 `Monad` 的前提条件是它必须是 `Applicative` 。 304 | 305 | `Functor` `、Applicative` 和 `Monad` 三者之间的区别: 306 | `Functor` :使用 `fmap` 应用一个函数到一个上下文中的值; 307 | `Applicative` :使用 `<*>` 应用一个上下文中的函数到一个上下文中的值; 308 | `Monad` :使用 `>>=` 应用一个接收一个普通值但是返回一个在上下文中的值的函数到一个上下文中的值。 309 | 此外,我们还介绍了一种非常有意思的数据类型 `Maybe` ,它实现了 `Functor typeclass`、`Applicative typeclass` 和 `Monad typeclass` ,所以它同时是 `Functor`、`Applicative` 和 `Monad` 。 310 | 311 | 以上就是本文的全部内容,希望可以对你有所帮助,Good luck ! -------------------------------------------------------------------------------- /es5-senior/article/img/JavaScript-object.svg: -------------------------------------------------------------------------------- 1 | JavaScript之理解对象什么是对象?定义属性属性探测删除属性属性枚举属性类型数据属性访问器属性属性特征通用特征数据属性特征访问器属性特征定义多重属性获取属性特征禁止修改对象禁止扩展对象封印对象冻结 -------------------------------------------------------------------------------- /es5-senior/article/prototype-chain.md: -------------------------------------------------------------------------------- 1 | # JavaScript深度理解原型链 2 | 3 | ## 原型链知识思维导图 4 | 5 | ![alt text](./img/js-prototype.svg "Title") 6 | 7 | ## 序言 8 | 9 | 为什么要有原型和原型链,因为这是JavaScript这门语言实现面向对象编程的基石之一。进一步理解,构造函数、原型和原型链是JavaScript实现继承的基础,而继承是面向对象编程重要特性。原型与原型链是JavaScript这门语言一个重要的特性,深刻理解非常重要。 10 | 11 | ## 什么是原型? 12 | 13 | 我们创建的每个函数都有一个 `prototype` (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。换言之, `prototype` 就是通过调用构造函数而创建的那个对象实例的原型对象。 14 | (每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。 15 | 原型对象、实例原型是一回事,都是不同人的叫法不同) 16 | 17 | ```js 18 | // 通常构造函数默认使用大写开头 19 | function Person(name, age, job) { 20 | this.name = name; 21 | this.age = age; 22 | this.sayName = function () { 23 | console.info(this.name); 24 | } 25 | } 26 | 27 | console.info(Person); 28 | ``` 29 | 30 | 备注:chrome浏览器的控制台不好打印函数属性,Firefox是可以的,下图Firefox打印。 31 | ![alt text](./img/20190101163046.png "constructor function") 32 | 33 | 总结: 34 | 1. JavaScript中,凡是函数都会有 `prototype` 属性,该属性是一个指针,指针指向原型对象。 35 | 2. 原型对象是一个实实在在的对象,原型属性则是一个指针。 36 | 37 | ```js 38 | console.info(typeof Person.prototype) // => object 39 | ``` 40 | 41 | ## 理解原型对象、prototype属性、constructor构造函数 42 | 43 | JavaScript中,**无论什么时候**,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 `prototype` 属性,这个属性指向函数的原型对象。 44 | 在默认情况下,所有原型对象都会自动获得一个 `constructor` (构造函数)属性,这个属性是一个指向 `prototype` 属性所在函数的指针。 45 | 构造函数、原型对象、实例之间关系图 46 | 47 | ![alt text](./img/prototype-img.png "Title") 48 | 49 | 函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是这个例子中的 person1 和 person2 的原型。 50 | 51 | ``` js 52 | function Person(name, age) { 53 | this.name = name; 54 | this.age = age; 55 | 56 | this.selfIntroduction = function () { 57 | console.log(this.name + " is " + this.age + " years old.", 'color:red;'); 58 | }; 59 | } 60 | 61 | var person = new Person("shaogucheng", 18); 62 | console.info(Person.prototype); 63 | ``` 64 | 65 | 控制台可以看到清晰的结构 66 | ![alt text](./img/prototype-console.png "Title") 67 | 68 | 创建了自定义的构造函数之后,其原型对象默认只会取得 `constructor` 属性;至于其他方法,则都是从 `Object` 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。**ECMA-262**第5版中管这个指针叫 `[[Prototype]]` 。虽然在脚本中没有标准的方式访问 `[[Prototype]]` ,但Firefox、Safari和Chrome在每个对象上都支持一个属性 `__proto__` ;而在其他实现中,这个属性对脚本则是完全不可见的。不过,要明确的真正重要的一点就是,**这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间**。 69 | 70 | ### 确定对象之间关系(确立构造函数与实例对象间关系) 71 | 72 | 方法一:简单粗暴图示法 73 | 直接撸出这样一段代码,执行过后: 74 | 75 | ```js 76 | var Lily = new Person('Lily', 18); 77 | console.info(Lily); 78 | ``` 79 | 80 | Firefox浏览器中对于 `[[Prototype]]` 指针的实现是 `` ,这里通过 `[[Prototype]]` 指针就可以找到构造函数原型对象,找到构造函数,那么实例化对象的构造函数就一目了然。 81 | ![alt text](./img/20190101172107.png "实例与构造函数关系") 82 | 83 | chrome浏览器对于 `[[Prototype]]` 指针的实现是 `__proto__` ,这里通过 `[[Prototype]]` 指针就可以找到构造函数原型对象,找到构造函数,那么实例化对象的构造函数就一目了然。 84 | ![alt text](./img/20190101172135.png "实例与构造函数关系") 85 | 86 | 方法二:科学严谨代码法 87 | 一种简单的判断方式是:我们知道实例的 `[[Prototype]]` 指针是指向构造函数原型对象的,所以有这样的代码; 88 | 89 | ```js 90 | var person = new Person("shaogucheng", 18); 91 | console.info(person.__proto__ === Person.prototype); // => true 92 | // 或者更加过分一点 93 | console.info(person.__proto__.constructor === Person); // => true 94 | ``` 95 | 96 | 另外一种方法是利用Object对象函数的API,`isPrototypeOf()` 还有 `getPrototypeOf()` 。 97 | 98 | 很遗憾chrome访问不到Object API 99 | ![chrome Object API](./img/20190101173728.png "chrome Object API") 100 | FireFox提供了API 101 | ![Firefox Object API](./img/20190101173808.png "Firefox Object API") 102 | 103 | 然后撸出这样的代码: 104 | 105 | ```js 106 | console.info(Person.prototype.isPrototypeOf(person)); // => true 107 | console.info(Object.getPrototypeOf(person) === Person.prototype); // => true 108 | ``` 109 | 110 | ------------------ 111 | 112 | ## 原型的用途 113 | 114 | 之前一直介绍原型,各种概念纷繁复杂搞得人头昏脑涨,我一直认为学以致用是一种更加有趣的学习方式,那么接下里就要重点介绍原型的用处了。 115 | > 使用原型最大的好处就是:可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。 116 | 117 | ```js 118 | function Person() { } 119 | 120 | Person.prototype.language = "Chinese" 121 | Person.prototype.gender = "unknown" 122 | Person.prototype.name = "Legend of the Dragon" 123 | Person.prototype.sayName = function () { 124 | console.info(this.name) 125 | } 126 | 127 | var person1 = new Person(); 128 | var person2 = new Person(); 129 | person1.sayName(); // => Legend of the Dragon 130 | person2.sayName(); // => Legend of the Dragon 131 | console.info(person1.sayName === person2.sayName); // => true 132 | ``` 133 | 134 | ------------------ 135 | 136 | ## 分步理解原型链构成 137 | 138 | 在讲解原型链之前,我希望先给大家铺垫一下基础知识。一方面是加深对于JavaScript数据类型的了解,另一方面更有助于对于整个原型链每个环节有个清晰的认识。 139 | 140 | ### 重新认识数据类型 141 | 142 | 首先在以往大家学习JavaScript数据类型的时候,都会有这样的认识: 143 | > JavaScript只有七种数据类型(注意都是首字母小写): 144 | > 基本类型:number、string 、boolean、null、undefined、symbol、 145 | > 引用类型:object 146 | 147 | 这里我想做一个更正,更正以往大家对JavaScript数据类型的认识。 148 | 149 | > JavaScript只有七种数据类型(注意都是首字母小写): 150 | > 基本类型(值类型):number、string 、boolean、undefined、symbol、 151 | > 引用类型:object、function 152 | 153 | 这里之所以把null类型去掉,是因为null只表示数据值,null的实际数据类型是object。 154 | 155 | 然后,object和Object,function和Function表示的意义是不同的,function与object是数据类型,Function与Object是两个函数对象的标识符(简称**函数标识对象**,等价于两个函数对象),Function与Object的数据类型都是function。数据类型为object的数据对象(除了null)外其实例类型是Object,而数据类型为function的实例类型既是Function,也是Object,原因在于 `Function.prototype.__proto__ == Object.prototype` ,所以所有的object和funtion数据类型的数据对象的实例类型都是Object。因为实例类型是按原型链查找的。 156 | 157 | 数据类型为object的数据对象,内置__proto__属性,而数据类型为function的数据对象(函数),内置__proto__,还有scope,prototype,length等属性。关键在于object类型的只有声明创建时,而function类型的除了声明创建时,还有函数运行时。 158 | 159 | 示例代码(typeof 表示数据类型,instanceof表示实例类型---用原型链查找): 160 | 161 | ``` js 162 | var a = function(){}; 163 | var b = {}; 164 | var c = 1; 165 | var d; 166 | var e = null; 167 | var f = false; 168 | var g = ""; 169 | 170 | console.log( typeof a);//function 171 | console.log( a instanceof Function);//true 172 | console.log( a instanceof Object);//true 173 | 174 | console.log( typeof b);//object 175 | console.log( b instanceof Function);//true 176 | console.log( b instanceof Object);//true 177 | 178 | console.log( typeof c);//number 179 | console.log( c instanceof Function);//false 180 | console.log( c instanceof Object);//false 181 | 182 | console.log( typeof d);//undefined 183 | console.log( d instanceof Function);//false 184 | console.log( d instanceof Object);//false 185 | 186 | console.log( typeof e);//object 187 | console.log( e instanceof Function);//false 188 | console.log( e instanceof Object);//false 189 | 190 | console.log( typeof f);//boolean 191 | console.log( f instanceof Function);//false 192 | console.log( f instanceof Object);//false 193 | 194 | console.log( typeof g);//string 195 | console.log( g instanceof Function);//false 196 | console.log( g instanceof Object);//false 197 | 198 | console.log(typeof Object);//function 199 | console.log(typeof Function);//function 200 | 201 | console.log(Object instanceof Object); // true 202 | console.log(Function instanceof Object);// true 203 | console.log(Object instanceof Function);// true 204 | console.log(Function instanceof Function);// true 205 | 206 | var Fn = function(){}; // 这是函数声明时 207 | Fn(); // 这是函数运行时. 208 | ``` 209 | 210 | ### Function和Object 211 | 212 | 常在JavaScript群子里混的同学可能都会听到这样两句话: 213 | > 1. 一切都是对象(万物皆对象) 214 | > 2. 函数是一等公民 215 | 216 | 我们今天抛开JavaScript函数式编程,但从面向对象的角度来理解这两句话。 217 | 218 | ```js 219 | console.info(Object); 220 | console.info(Function) 221 | 222 | console.log(Object instanceof Object); // true 223 | console.log(Function instanceof Object);// true 224 | console.log(Object instanceof Function);// true 225 | console.log(Function instanceof Function);// true 226 | 227 | console.log( Object.__proto__ == Function.prototype);//true 228 | console.log( Function.__proto__ == Function.prototype);//true 229 | console.log( Function.prototype.__proto__ == Object.prototype);//true 230 | // 关键在这一步: Function.prototype.__proto__ == Object.prototype 231 | ``` 232 | 233 | 我们的chrome照例是不支持native code显示的。 234 | ![alt text](./img/20190101233923.png "text") 235 | Firefox展示的就比较清楚了 236 | ![alt text](./img/20190101234057.png "text") 237 | ![alt text](./img/20190101234151.png "text") 238 | 239 | 由此可见,Object继承自己,Funtion继承自己,Object和Function互相是继承对方,也就是说Object和Function都既是函数也是对象。这一点很特别。所有的函数都是对象,可是并不是所有的对象都是函数。证明如下: 240 | 241 | ``` js 242 | function foo(){}; 243 | console.log(foo instanceof Function); // true 244 | console.log(foo instanceof Object); // true 245 | console.log(new foo() instanceof Function); // false 246 | ``` 247 | 248 | 我们看到由new + function的构造器实例化出来的对象不是函数,仅仅是Object的子类。接下来,我们会想所有的对象都有一个Function的构造器存储在原型链的constructor属性中,那么Object的构造器是什麽呢? 证明: 249 | 250 | ``` js 251 | console.log(Object.constructor); // function Function(){ [native code] } 252 | console.log(Function.constructor); // function Function(){ [native code] } 253 | ``` 254 | 255 | 由此我们可以确定Object是由Function这个函数为原型的,而Function是由它自己为原型的。Function函数是由native code构成的,我们不用去深究了。存在function Function(){...}的定义,我们明白了Function其实就是函数指针,也看作函数变量。就相当于function foo(){}中的foo。连Object的构造器都是指向Function的,可以想象Function是一个顶级的函数定义,大大的区别于自定义的如function foo(){}这样的函数定义。 256 | 257 | 看这样一个语句,new Function();以Function为原型来实例化一个对象,按照刚才Object.constructor 为 functon Function(){}来说,new Function()产生的对象应该是一个Object啊,我们来验证一下: 258 | 259 | ``` js 260 | console.log(new Function() instanceof Object); // true 261 | console.log(Object instanceof new Function());// false 262 | console.log(typeof new Function());// function 263 | console.log(typeof Object);// function 264 | ``` 265 | 266 | 其实new Function();将产生一个匿名函数,由于Function是顶级函数所以可以产生自定义函数,我们可以把所有的函数看作Function的子类。但所有的一切包括函数都是Object的子类这点是不变的,也得到了体现。typeof Object的结果说明Object也是一个函数。继续做实验: 267 | 268 | ``` js 269 | alert(new Object().constructor); // function Object(){ [native code] } 270 | ``` 271 | 272 | 一个情理之中的疑惑。可以这么说凡是可以放在new后面的都是一个函数构造器,那么Object确实也像其它函数一样,是一个函数的指针或者是函数变量,但Function是顶级的所以Object要由Function来构造。可以这么理解,Function和Object一个是上帝,一个是撒旦同时诞生于宇宙的最开始,拥相当的力量。但是上帝更为光明,所以高高在上。Object要由Function来构造,Function属于顶级函数。但是撒旦并没有绝对的输给上帝,否则上帝就会消灭撒旦。于是或所有的对象都要继承Object包括Function(Object和Function既是对象又是函数)。就相当于所有的人包括上帝都有邪念一样。 273 | 274 | 拓展一下,由Object我们会想到Array,Number,String等这些内置对象。有理由相信这些都是Object的子类。如下: 275 | 276 | ``` js 277 | console.log(Array instanceof Object) // true 278 | console.log(String instanceof Object) // true 279 | console.log(Number instanceof Object) // true 280 | console.log(Object instanceof Array) // false 281 | console.log(Object instanceof String) // false 282 | console.log(Object instanceof Number) // false 283 | ``` 284 | 285 | 当然他们也都会有Object的特性就像魔鬼和撒旦的关系一样,也是Function的子类,由Function构造。那么有Array,String,Number构造的对像如:new Array();new Number();new String()的构造器是function Array(){...};function String(){...};function Number(){...}; 286 | 287 | ``` js 288 | alert(Array instanceof Function) // true 289 | alert(String instanceof Function) // true 290 | alert(Number instanceof Function) // true 291 | alert(Array.constructor) // function Function(){ [native code] } 292 | alert(String.constructor) // function Function(){ [native code] } 293 | alert(Number.constructor) // function Function(){ [native code] } 294 | ``` 295 | 296 | 总结一下,像内置的函数或说对象把如:Object,String,Array等等和自定义的function关键字定义的函数,都是Function的子类。new Function()相当于function关键字定义。这里可以引出,Function.prototype原型链上的属性所有函数共享,Object.prototype原型链上的属性所有对象共享。 297 | 298 | QAQ:真心想要吐槽一句,如此复杂的数据类型设计,完完全全了解其中复杂的关系,也是颇为费力的一件事情,也正是JavaScript令人吐槽的地方,设计糟糕的一种体现,可同样也是巧妙的让人绝望。 299 | 300 | 说了这么多,做一个简单的总结: 301 | 首先第一个概念:**function与object是数据类型,Function与Object是两个函数对象的标识符(等价于两个函数对象),Function与Object的数据类型都是function。** 302 | 其次第二个概念:**JavaScript中所有的对象都继承自Object原型,而Function又充当了对象(Object)的构造器。** 303 | 然后第三个概念:**一切都是对象** 304 | 305 | 另外附送一张鄙人惊喜绘制的关系图,以供参考理解。 306 | 关系图理解基础(在阅读下图之前,希望牢记下面两点): 307 | 1. 所有函数都会有prototype属性,该属性是个指针,指向prototype实例原型。 308 | 2. 所有对象都会有__proto__属性,该属性是个指针,指向父类prototype原型对象。 309 | 3. prototype是个真真实实,如假包换的对象。 310 | 311 | 这张图可能很好的看到Function和Object的内在联系。 312 | ![alt text](./img/object-function1.jpg "Title") 313 | 314 | * `Object` 是所有对象的爸爸,所有对象都可以通过 `__proto__` 找到它 315 | * `Function` 是所有函数的爸爸,所有函数都可以通过 `__proto__` 找到它 316 | * 函数的 `prototype` 是一个对象 317 | * 对象的 `__proto__` 属性指向原型, `__proto__` 将对象和原型连接起来组成了原型链 318 | 319 | 另外附上Function和Object的API文档以供参考: 320 | 321 | [MDN Object API](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object) 请点击链接访问 322 | [MDN Function API](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function)请点击链接访问 323 | 324 | 这里的Person就是构造函数本尊,它本身是对象,也是函数。当Person这个函数新建之后,就会有一个属性(其实是一个指针),指向原型对象(实例原型),原型对象是客观存在的对象。 325 | 326 | ``` js 327 | function Person(name, age) { 328 | this.name = name; 329 | this.age = age; 330 | 331 | this.selfIntroduction = function () { 332 | console.log(this.name + " is " + this.age + " years old.", 'color:red;'); 333 | }; 334 | } 335 | 336 | var person = new Person("shaogucheng", 18); 337 | console.info(Person.prototype); 338 | console.info(person); 339 | console.info(person.prototype); 340 | ``` 341 | ![alt text](./img/20181218212309.png "Title") 342 | ![alt text](./img/20181218212401.png "Title") 343 | 344 | > 我们创建的每个函数都有一个 `prototype` (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。 ——《JavaScript高级程序设计》 345 | 346 | 正如图中展示的那样,Person函数天生就会有一个属性,prototype,这个属性是一个指针,指向原型对象(实例原型) 347 | ![alt text](./img/stage1.png "Title") 348 | 349 | 在控制台运行 `var person = new Person("shaogucheng", 18);` 根据构造函数new出新的对象,这个其实就是面向对象中,根据模板实例化对象的过程(Person --> person)。 350 | 351 | ![alt text](./img/stage2.png "Title") 352 | 353 | > 这里要稍微介绍一下new的过程: 354 | > 1. 创建一个新对象; 355 | > 2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象); 356 | > 3. 执行构造函数中的代码(为这个新对象添加属性); 357 | > 4. 返回新对象。 358 | 359 | 为了更好的理解上图,我们在控制台输入 `console.info(person);` ,输出相关信息: 360 | ![alt text](./img/20181217233505.png "Title") 361 | 这样,无论使用代码 `console.info(Person.prototype === person.__proto__);` 结果为 `true` 还是看日志中两者的结构,都能得出的结论。实例上的 `__proto__` 属性是指向实例原型,这大概也就是为什么原型对象也叫实例原型的原因吧。 362 | 363 | ![alt text](./img/stage3.png "Title") 364 | 365 | 我们可以在控制台执行 `console.info(Person.prototype);` 根据打印的日志信息,可以直观看到原型对象,并且看到原型对象上的属性。在原型对象上有一个构造属性**constructor**,并且这个构造属性也是一个指针,指向构造函数本身,这样就可以得出上图中实例原型中的constructor属性指向构造函数本身的线。 366 | ![alt text](./img/20181217225601.png "Title") 367 | 368 | 构造函数的原型对象的原型又是谁呢? 369 | ![alt text](./img/stage4.png "Title") 370 | 371 | 喜大普奔的是chrome浏览器支持显示 `__proto__` 属性,所以在控制台输入:`console.info(Person.prototype.__proto__);` 我们就能愉快的观察到Person构造函数的原型对象的原型是谁。 372 | ![alt text](./img/20181217234302.png "Title") 373 | 这样,真相就已经很明显了。 374 | 375 | ![alt text](./img/stage5.png "Title") 376 | 上图就已经很明显的给出了原型链的雏形。 377 | 378 | 最后的最后我们祭出经典原型链图。 379 | ![alt text](./img/stage8.jpg "Title") 380 | 381 | the constructor property of an instance of a function object "specifies the function that creates an object's prototype". This is confusing, so Object.constructor is "the function that creates an object's prototype"? What object is "an object" exactly? 382 | 383 | I'm trying to understand why is Object.constructor's constructor property itself? 384 | 385 | as such: Object.constructor===Object.constructor.constructor // why? 386 | 387 | Edit: i find T.J. Crowder's answer good but the phrasing of his words is pretty vague (making it hard to understand at first read, at least for me). Here's the rephrased answer: 388 | 389 | 1) Object is an instance of Function 390 | 391 | 2) Object does not have a property called constructor so when we call Object.constructor, it actually gives us Object.[[prototype]].constructor (aka Object.__proto__.constructor). 392 | 393 | 3) Object.constructor (aka Object.__proto__.constructor) is an instance of Function. 394 | 395 | 4) Since both Object and Object.constructor (aka Object.__proto__.constructor) are instances of Function therefore they both have a __proto__ property which refer to the same object. In other words Object.__proto__ === Object.constructor.__proto__ (aka Object.__proto__.constructor._proto_) 396 | 397 | 5) The line Object.constructor===Object.constructor.constructor is actually equal to the line Object.__proto__.constructor===Object.constructor.__proto__.constructor 398 | 399 | 6) combining steps 4 and 5 give us Object.constructor===Object.constructor.constructor 400 | 401 | 7) goto step 4) --------------------------------------------------------------------------------