秋雨

秋雨 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织 github.com/leinov 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

秋雨 赞了文章 · 2020-09-30

五个大型项目实践总结,解密React Hooks最佳实践

赞 97 收藏 73 评论 2

秋雨 关注了用户 · 2020-08-05

乌柏木 @wubomu

曾在阿里巴巴跑龙套,现在自己公司打酱油。

关注 940

秋雨 发布了文章 · 2019-08-08

使用Vue CLI创建typescript项目

使用最新的Vue CLI @vue/cli创建typescript项目,使用vue -V查看当前的vue cli版本

安装命令

npm install -g @vue-cli

创建项目

vue create my-vue-typescript

1

上下键选择,空格键确定
2

接下来是一些常规选项

3

下面是询问要不要记录这次配置以便后面直接使用,我们选择y

4

当确定配置后会在C:\Users\Administrator\.vuerc下生成一个刚选好的配置记录

{
  "useTaobaoRegistry": true,
  "presets": {
    "my-vue-typescript": {
      "useConfigFiles": true,
      "plugins": {
        "@vue/cli-plugin-babel": {},
        "@vue/cli-plugin-typescript": {
          "classComponent": true,
          "tsLint": true,
          "lintOn": [
            "save"
          ],
          "useTsWithBabel": true
        }
      },
      "router": true,
      "routerHistoryMode": true,
      "vuex": true
    }
  }
}

然后再回车就开始创建项目,但是这里却报了一个错,大概意思是源有问题。

ERROR  command failed: yarn --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist

于是搜了下原因,原来是创建时候的默认源配置导致,vue cli 安装报错问题解决
5

所以修改下刚才生成的.vuerc下的useTaobaoRegistry属性值为false就可以了

{
  "useTaobaoRegistry": false,
   ...
}

再次安装成功

原文

查看原文

赞 0 收藏 0 评论 0

秋雨 评论了文章 · 2019-04-12

理解nodejs的module

module

在 Node.js 模块系统中,每个文件都视为独立的模块,node在运行某个模块儿时会生成一个module对象

Module {
  id: '.',
  exports: 2,
  parent: null,
  filename: '/Users/leinov/github/node-api/module/module.js',
  loaded: false,
  children:
   [ Module {
       id: '/Users/leinov/github/node-api/module/circle.js',
       exports: [Object],
       parent: [Circular],
       filename: '/Users/leinov/github/node-api/module/circle.js',
       loaded: true,
       children: [],
       paths: [Array] } ],
  paths:
   [ '/Users/leinov/github/node-api/module/node_modules',
     '/Users/leinov/github/node-api/node_modules',
     '/Users/leinov/github/node_modules',
     '/Users/leinov/node_modules',
     '/Users/node_modules',
     '/node_modules' ] }
  • id为当前文件
  • exports为当前node文件模块儿导出的值
  • parent为父级调用,如果为null则该文件没有被调用
  • filename为当前文件名
  • loaded是否被加载
  • children 引入模块数组,数组项格式同module
  • paths为node模块儿 node_modules 模块儿查找路径,一直查到根目录

module.exports & exports

Node里面的模块系统遵循是CommonJs规范,CommonJs定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require),在模块儿运行的时候都会生成一个module对象和一个exports对象,module对象下也有一个exports对象,默认情况下这两个对象都是空对象。如果有引用其他模块儿或定义模块儿 即module.exports或者exports被赋值时,则该模块儿就是一个有效的带有返回值的模块儿。

一个模块儿真正导出的是module.exports的值,exports只是module.exports的一个引用
可以简单理解为下面这种对象引用和赋值的区别

let obj1 = {a=1};
let obj2 = obj1;

console.log(obj1,obj2); // {a:1} {a:1}

obj2.a = 2 
console.log(obj1,obj2); // {a:2} {a:2}

obj2 = {b:3} 

console.log(obj1,obj2); // {a:2} {b:3}

obj2只是obj1的一个引用。当 obj2.a 改变时其实改变的是 obj1obj2 都指向的同一个堆里的数据。但 obj2 ={b:3} 则重新在堆里开辟了另一个内存块儿来存储。已经跟 obj1 脱离没有关系了

所以经常会看到node模块儿里会像下面这样来导出模块儿。

exports = module.exports = ()=>{
    do something
}

这是为了让exports引用指向module.exports同一块内存,确保数据的一致性。

总结

  • 在执行 node 文件时同事创建了 module.exportsexports 对象
  • exports 是指向 module.exports 的一个引用
  • require("xxx") 其实引用的是xxx中的 module.exports 而非 exports

github nodejs

查看原文

秋雨 赞了文章 · 2018-12-12

提高 JavaScript 开发效率的高级 VSCode 扩展!

阿里云最近在做活动,低至2折,有兴趣可以看看:
https://promotion.aliyun.com/...

为了保证的可读性,本文采用意译而非直译。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

Quokka.js

Quokka.js 是一个用于 JavaScript 和 TypeScript 的实时运行代码平台。这意味着它会实时运行你输入后的代码,并在编辑器中显示各种执行结果,建议亲自尝试一下。

图片描述

安装此扩展后,可以按Ctrl / Cmd(⌘)+ Shift + P显示编辑器的命令选项板,然后键入 Quokka 以查看可用命令的列表。选择并运行 “New JavaScript File”命令。你也可以按(⌘+ K + J)直接打开文件。在此文件中输入的任何内容都会立即执行。

图片描述

Quokka.js类似的扩展 –

  • Code Runner – 支持多种语言,如C,C ++,Java,JavaScript,PHP,Python,Perl,Perl 6等。
  • Runner

括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)

花括号和圆括号是许多编程语言不可分割的部分,在 JavaScript 等语言中,在一屏代码中花括号和园括号可能有多层嵌套,有些括号不太容易识别哪个对应哪个,然而却没有简单的方法来识别这些括号前后的对应关系。

括号配对着色(Bracket Pair Colorizer)彩虹缩进(Indent Rainbow)。这是两个不同的扩展。然而,他们就像是一对情侣,可以完美的配合使用。这些扩展将为你的编辑器添加一系列颜色,并使代码块易于辨别,一旦你习惯了它们,如果 VSCode 没有它们就会让人觉得很平淡。

不使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)

不使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)

使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)后

使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)后

snippets(代码片段)

代码片段是编辑器中的短代码。因此,可以输入 imr 并按Tab 来展开该代码片段,而不是'import React from '。类似地,clg 变成了 console.log。

各种各样的框架和类库都有很多代码片段:Javascript,React,Redux,Angular,Vue,Jest。 我个人认为 Javascript 代码片段非常有用,因为我主要使用 JS 。

一些很好的代码片段扩展 –

TODO高亮

通常在进行编码时,你认为可能有更好的方法来执行相同的操作。这时你留下注释// TODO: 需要重构 或其他相关的东西。但是你很容易忘记了这个注释,并将你的代码推送到主版本库(master) 或者生产环境(production)。 但是你如果使用 Todo Highlighter(高亮),它会高亮的显示并让你容易看到这个注释。

它以明亮的颜色突出代码中的 “TODO/FIXME” 或代码任何其他注释,以便始终清晰可见。另外还有一个很好的功能是 List Highlighted annotations ,它会在控制台中列出了所有 TODO。

图片描述

使用 Todo Highlighter(高亮)类似的扩展 –

  • Todo+ —  更强大的 Todo 高亮扩展,具有更多功能。
  • Todo Parser

Import Cost

扩展允许您查看导入模块的大小,它对 Webpack 中的 bundlers 有很大帮助,你可以查看是导入整个库还是只导入特定的实用程序。

图片描述

REST Client

作为 web 开发人员,我们经常需要使用 REST api。为了检查url和检查响应,使用了 Postman 之类的工具。但是,既然编辑器可以轻松地完成相同的任务,为什么还要使用不同的应用程序呢? REST Client 它允许你发送 HTTP 请求并直接在 Visual Studio 代码中查看响应。

图片描述

自动闭合标记(Auto Close Tag)和自动重命名标记(Auto Rename Tag)

自从React的出现以及它在过去几年获得的吸引力以来,以 JSX 形式出现的类似 html 的语法现在非常流行。我们还必须使用 JavaScript 标签进行编码。任何web开发人员都会告诉你,输入标签是一件痛苦的事情。在大多数情况下,我们需要一个能够快速、轻松地生成标签及其子标签的工具。Emmet 是 VSCode 中一个很好的例子,然而,有时候,你只是想要一些简单明了的东西。例如自动更新标签,它在你输入开始标签时自动生成结束标签。当你更改相同的标签时,关闭标记会自动更改,这两个扩展就是这样做的。

它还适用于JSX和许多其他语言,如XML,PHP,Vue,JavaScript,TypeScript,TSX。

在这里获取这两个扩展 – 自动闭合标记(Auto Close Tag)自动重命名标记(Auto Rename Tag)

Auto Rename Tag

Auto Close Tag

类似的扩展 –

GitLens

正如其作者所说,GitLens 增强了 Visual Studio Code 中内置的 Git 功能,它包含了许多强大的功能,例如通过跟踪代码显示的代码作者,提交搜索,历史记录和GitLens资源管理器。你可以在此处阅读这些功能的完整说明。

图片描述

类似的扩展 –

Git项目管理器(Git Project Manager,GPM)

Git项目管理器(Git Project Manager,GPM)允许你直接从 VSCode 窗口打开一个针对Git存储库的新窗口。 基本上,你可以打开另一个存储库而无需离开VSCode。

安装此扩展后,您必须将 gitProjectManager.baseProjectsFolders 设置为包含 repos 的URL列表。例如:

{
    "gitProjectManager.baseProjectsFolders": [
        "/home/user/nodeProjects",
        "/home/user/personal/pocs"
    ]
} 

图片描述

类似的扩展 –

Project Manager – 我没有亲自使用它,但它有百万+安装。所以建议你一定要看一下。

Indenticator(缩进指示器)

在视觉上突出显示当前的缩进个数,因此,你可以轻松区分在不同级别缩进的各种代码块。

图片描述

VSCode Icons

使您的编辑更具吸引力的图标!

图片描述

类似的扩展 –

Dracula (Theme)

Dracula 是我最喜欢的主题。

图片描述

我们可以使用快捷键来快速的选择更换主题;

首先:按下 Ctrl + k

然后再按下:Ctrl + t

其它推荐

  • Fira Code — 带编程连体字的等宽字体。 愚人码头注:clone 项目后,找到 ttf 文件夹,然后安装该文件夹中的字体文件。重新启动 VSCode ,选择TOOLS -> Options -> Fonts and Colors ,选择 Fira Code 即可。
  • Live Server — 一个具有静态和动态页面的实时重新加载功能的本地开发服务器。
  • EditorConfig for VS Code – 此插件尝试使用.editorconfig文件中的设置覆盖用户/工作区设置,不需要其他或特定于 vscode 的文件。与任何EditorConfig插件一样,如果未指定root = true,EditorConfig将继续在项目外部查找.editorconfig文件。
  • Prettier for VSCode — 一个代码格式化工具。
  • Bookmarks – 它可以帮助您在代码中导航,轻松快速地在重要位置之间移动。不再需要搜索代码,它还支持一组选择命令,允许您选择书签线和书签线之间的区域,它对日志文件分析非常有用。
  • Path Intellisense — Visual Studio Code插件,可自动填充文件名。
  • Version Lens — 在Visual Studio代码编辑器中显示npm,jspm,bower,dub和dotnet核心的软件包版本信息。

编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具Fundebug

原文:https://codeburst.io/top-java...

你的点赞是我持续分享好东西的动力,欢迎点赞!

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 153 收藏 117 评论 7

秋雨 赞了文章 · 2018-12-12

浅谈JavaScript的面向对象和它的封装、继承、多态

写在前面

既然是浅谈,就不会从原理上深度分析,只是帮助我们更好地理解...

面向对象与面向过程

面向对象和面向过程是两种不同的编程思想,刚开始接触编程的时候,我们大都是从面向过程起步的,毕竟像我一样,大家接触的第一门计算机语言大概率都是C语言,C语言就是一门典型的面向过程的计算机语言。
面向过程主要是以动词为主,解决问题的方式是按照顺序一步一步调用不同的函数。
面向对象是以名词为主,将问题抽象出具体的对象,而这个对象有自己的属性和方法,在解决问题的时候,是将不同的对象组合在一起使用。

//面向过程装大象
1.开(冰箱)
2.(大象)装进(冰箱)
3.关(冰箱)
//面向对象装大象
1. 冰箱.开门()
2. 冰箱.装进(大象)
3. 冰箱.关门()

从这个例子可以看出,面向对象是以主谓为主,将主谓堪称一个一个的对象,然后对象有自己的属性和方法。
面向对象是以功能来划分问题的,而不是步骤。功能上的统一保证了面向对象设计的可扩展性,解决了代码重用性的问题。
这也是在漫长的程序设计的发展过程中得到的验证结果,面向对象的编程思想较之于面向过程较好一点

封装

面向对象有封装、继承和多态三大特性。
封装:就是把事物封装成,隐藏事物的属性和方法的实现细节,仅对外公开接口。

在ES5中,并没有class的概念,但是由于js的函数级作用域(函数内部的变量函数外访问不到)。所以我们可以模拟class。在es5中,类其实就是保存了一个函数的变量,这个函数有自己的属性和方法。将属性和方法组成一个类的过程就是封装。

1.通过构造函数添加

JavaScript提供了一个构造函数(Constructor)模式,用来在创建对象时初始化对象。构造函数其实就是普通的函数,只不过有以下的特点

①首字母大写(建议构造函数首字母大写,即使用大驼峰命名,非构造函数首字母小写)
②内部使用this
③使用new生成实例

通过构造函数添加属性和方法实际上也就是通过this添加的属性和方法。因为this总是指向当前对象的,所以通过this添加的属性和方法只在当前对象上添加,是该对象自身拥有的。所以我们实例化一个新对象的时候,this指向的属性和方法都会得到相应的创建,也就是会在内存中复制一份,这样就造成了内存的浪费。

function Cat(name,color){
    this.name = name;
    this.color = color;
    this.eat = (() => {
        console.log("fish!")
    })
}

//生成实例
var cat1 = new Cat("tom", "gray")

通过this定义的属性和方法,我们实例化对象的时候斗湖重新复制一份

2.通过原型prototype封装

在类上通过this的方式添加属性和方法会导致内存浪费的现象,有什么办法可以让实例化的类所使用的属性和方法 直接使用指针 指向同一个属性和方法。

这就是原型的方法

JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
也就是说,对于那些不变的属性和方法,我们可以直接将其添加在类的prototype对象上。
function Cat(name,color){
            this.name = name;
            this.color = color;
        }
        Cat.prototype.type = "英短";
        Cat.prototype.eat = ( () => {
            alert("fish!")
        } )
          
        //生成实例
        var cat1 = new Cat('Tom', 'gray');
        var cat2 = new Cat('Kobe', 'purple');
        console.log(cat1.type); //英短
        cat2.eat(); //fish!

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
但是这样做也有弊端,因为实例化的对象的原型都是指向同一内存地址,改动其中一个对象的属性可能会影响到其他的对象

es6中的类和封装

es6声明一个类
①构造器:构造器内创建自有属性
②方法:声明类实例具有的方法

class Cat {
    //等价于Cat构造器
    constructor(name) {
        this.name = name;
    }
    //更加简单的声明类的内部函数
    //等价于 Cat.prototype.eat
    eat() {
        console.log("fish!");
    }
}

//生成实例
var cat1 = new Cat("tom");
cat1.eat(); //fish!
console.log(cat1 instanceof Cat); //true
console.log(cat1 instanceof Object); //true
console.log(typeof Cat); //function
console.log(typeof Cat.prototype.eat); //function

从上面class声明的Cat为例:Cat类是一个具有构造函数行为的函数,其中内部方法eat实际上就是Cat.prototype.eat()
所以说es6的class封装类,本质上是es5实现方式的语法糖
最主要的区别在于,class类的属性是不可重新赋值和不可枚举的,Cat.prototype就是一个只读属性

class和自定义类型的区别
(1)class的声明不会提升,与let类似
(2)class的声明自动运行于严格模式之下
(3)class声明的方法不可枚举
(4)class的内部方法没有 constructor 属性,无法new
(5)调用class的构造函数必须new
(6)class内部方法不能同名

class类的使用
class作为js中的一级公民,可以被当作值来直接使用

//1.类名作为参数传入函数
function createObj (ClassName) {
    return new ClassName()
}

//2.立即执行,实现单例模式
let cat1 = new class{
    constructor (name) {
        this.name = name
    }
    eat() {
        console.log("fish!")
    }
}("tom”)
cat1.eat() //fish!

继承

继承就是子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程。

1.类式继承

所谓的类式继承就是使用的原型的方式,将方法添加在父类的原型上,然后子类的原型是父类的一个实例化对象。

//声明父类
var SuperClass = function(){
    let id = 1;
    this.name = ['java'];
    this.superValue = function() {
        console.log('this is superValue!')
    }
}

//为父类添加共有方法
SuperClass.prototype.getSuperValue = function () {
    return this.superValue();
};

//声明子类
var SubClass = function() {
    this.subValue = (() => {
        console.log('this is subValue!')
    })
}

//继承父类
SubClass.prototype = new SuperClass();

//为子类添加共有方法
SubClass.prototype.getSubValue = function() {
    return this.subValue()
}


//生成实例
var sub1 = new SubClass();
var sub2 = new SubClass();

sub1.getSuperValue(); //this is superValue!
sub1.getSubValue(); //this is subValue!

console.log(sub1.id); //undefined
console.log(sub1.name); //["java"]

sub1.name.push("php"); 
console.log(sub1.name); //["java", "php"]
console.log(sub2.name); //["java", "php"]

其中最核心的是SubClass.prototype = new SuperClass();
类的原型对象prototype对象的作用就是为类的原型添加共有的方法的,但是类不能直接访问这些方法,只有将类实例化之后,新创建的对象复制了父类构造函数的属性和方法,并将原型 proto 指向了父类的原型对象。这样子类就可以访问父类的属性和方法,同时,父类中定义的属性和方法不会被子类继承。

but使用类继承的方法,如果父类的构造函数中有引用数据类型,就会在子类中被所有实例共用,因此一个子类的实例如果更改了这个引用数据类型,就会影响到其他子类的实例。

构造函数继承

为了克服类继承的缺点,才有了构造函数继承,构造函数继承的核心思想就是SuperClass.call(this, id),直接改变this的指向,使通过this创建的属性和方法在子类中复制一份,因为是单独复制的,所以各个实例化的子类互不影响。but会造成内存浪费的问题

//构造函数继承
//声明父类

var SuperClass = function(id){
    var name = 'java'
    this.languages = ['java', 'php', 'ruby'];
    this.id = id 
}

//声明子类
var SubClass = function(id){
    SuperClass.call(this, id)
}

//生成实例
var sub1 = new SubClass(1);
var sub2 = new SubClass(2);


console.log(sub2.id); // 2
console.log(sub1.name); //undefined

sub1.languages.push("python");
console.log(sub1.languages); // ['java', 'php', 'ruby', 'python']
console.log(sub2.languages); // ['java', 'php', 'ruby']
组合式继承

组合式继承是汲取了两者的优点,既避免了内存浪费,又使得每个实例化的子类互不影响。

//组合式继承
//声明父类

var SuperClass = function(name){
    this.languages = ['java', 'php', 'ruby'];
    this.name = name;
}

 //声明父类原型方法
SuperClass.prototype.showLangs = function () {
    console.log(this.languages);
}

//声明子类
var SubClass = function(name){
    SuperClass.call(this, name)
}

//子类继承父类(链式继承)
SubClass.prototype = new SuperClass();

//生成实例
var sub1 = new SubClass('python');
var sub2 = new SubClass('go');

sub2.showLangs(); //['java', 'php', 'ruby']
sub1.languages.push(sub1.name);
console.log(sub1.languages);//["java", "php", "ruby", "python"]
console.log(sub2.languages);//['java', 'php', 'ruby']

but警告:组合式继承方法固然好,但是会导致一个问题,父类的构造函数会被创建两次(call()的时候一遍,new的时候又一遍)

寄生组合继承

组合式继承的缺点的关键是 父类的构造函数在类继承和构造函数继承的组合形式被创建了两边,但是在类继承中我们并不需要创建父类的构造函数,我们只要子类继承父类的原型即可。
所以我们先给父类的原型创建一个副本,然后修改子类的 constructor 属性,最后在设置子类的原型就可以了

//原型式继承
//原型式继承其实就是类式继承的封装,实现的功能返回一个实例,该实例的原型继承了传入的o对象
function inheritObject(o) {
    //声明一个过渡函数
    function F() {}

    //过渡对象的原型链继承父对象
    F.prototype = o;

    //返回一个过渡对象的实例,该实例的原型继承了父对象
    return new F();
}

//寄生式继承
//寄生式继承就是对原型继承的第二次封装,使得子类的原型等于父类的原型。并且在第二次封装的过程中对继承的对象进行了扩展

function inheritPrototype(subClass, superClass){
    //复制一份父类的原型保存在变量中,使得p的原型等于父类的原型
    var p = inheritObject(superClass.prototype);
    //修正因为重写子类原型导致子类constructor属性被修改
    p.constructor = subClass;
    //设置子类的原型
    subClass.prototype = p;
}

//定义父类
var SuperClass = function(name) {
    this.name = name;
    this.languages = ["java", "php", "python"]
}
//定义父类原型方法
SuperClass.prototype.showLangs = function() {
    console.log(this.languages);
}

//定义子类
var SubClass = function(name) {
    SuperClass.call(this,name)
}

inheritPrototype(SubClass, SuperClass);

var sub1 = new SubClass('go');
es6中的继承
class SuperClass {
    constructor(name) {
        this.name = name
        this.languages = ['java', 'php', 'go']; 
    }
    showLangs() {
        console.log(this.languages);
    }
}

class SubClass extends SuperClass {
    constructor(name) {
        super(name)
    }

    //重写父类中的方法
    showLangs() {
        this.languages.push(this.name)
        console.log(this.languages);
    }
}

//生成实例
var sub = new SubClass('韩二虎');

console.log(sub.name); //韩二虎

sub.showLangs(); //["java", "php", "go", "韩二虎"]

多态

多态实际上是不同对象作用与同一操作产生不同的效果。多态的思想实际上是把 “想做什么” 和 “谁去做” 分开。
多态的好处在于,你不必再向对象询问“你是什么类型”后根据得到的答案再去调用对象的某个行为。你尽管去调用这个行为就是了,其他的一切可以由多态来负责。规范来说,多态最根本的作用就是通过吧过程化的条件语句转化为对象的多态性,从而消除这些条件分支语句。
由于JavaScript中提到的关于多态的详细介绍并不多,这里简单的通过一个例子来介绍就好

//非多态     
var hobby = function(animal){
    if(animal == 'cat'){
        cat.eat()
    }else if(animal == 'dog'){
        dog.eat()
    }
}

var cat = {
    eat: function() {
        alert("fish!")
    }
}

var dog = {
    eat: function() {
        alert("meat!")
    }
}

console.log(123);
hobby('cat'); //fish!
hobby('dog'); //meat!

从上面的例子能看到,虽然 hobby 函数目前保持了一定的弹性,但这种弹性很脆弱的,一旦需要替换或者增加成其他的animal,必须改动hobby函数,继续往里面堆砌条件分支语句。我们把程序中相同的部分抽象出来,那就是吃某个东西。然后再重新编程。

//多态     
var hobby = function(animal){
    if(animal.eat instanceof Function){
        animal.eat();
    }
}

var cat = {
    eat: function() {
        alert("fish!")
    }
}

var dog = {
    eat: function() {
        alert("meat!")
    }
}

现在来看这段代码中的多态性。当我们向两种 animal 发出 eat 的消息时,会分别调用他们的 eat 方法,就会产生不同的执行结果。对象的多态性提示我们,“做什么”“怎么去做”是可以分开的,这样代码的弹性就增强了很多。即使以后增加了其他的animal,hobby函数仍旧不会做任何改变。

//多态     
var hobby = function(animal){
    if(animal.eat instanceof Function){
        animal.eat();
    }
}

var cat = {
    eat: function() {
        alert("fish!")
    }
}

var dog = {
    eat: function() {
        alert("meat!")
    }
}

var aoteman = {
    eat: function(){
        alert("lil-monster!")
    }
}

hobby(cat); //fish!
hobby(dog); //meat!
hobby(aoteman); //lil-monster!

快乐面向对象😁

查看原文

赞 62 收藏 49 评论 1

秋雨 分享了头条 · 2018-12-12

React-multi-page-app 是一个基于react和webpack以及nodejs的多页面架构架手架,全局安装,开箱即用

赞 0 收藏 0 评论 0

秋雨 发布了文章 · 2018-12-12

理解nodejs的module

module

在 Node.js 模块系统中,每个文件都视为独立的模块,node在运行某个模块儿时会生成一个module对象

Module {
  id: '.',
  exports: 2,
  parent: null,
  filename: '/Users/leinov/github/node-api/module/module.js',
  loaded: false,
  children:
   [ Module {
       id: '/Users/leinov/github/node-api/module/circle.js',
       exports: [Object],
       parent: [Circular],
       filename: '/Users/leinov/github/node-api/module/circle.js',
       loaded: true,
       children: [],
       paths: [Array] } ],
  paths:
   [ '/Users/leinov/github/node-api/module/node_modules',
     '/Users/leinov/github/node-api/node_modules',
     '/Users/leinov/github/node_modules',
     '/Users/leinov/node_modules',
     '/Users/node_modules',
     '/node_modules' ] }
  • id为当前文件
  • exports为当前node文件模块儿导出的值
  • parent为父级调用,如果为null则该文件没有被调用
  • filename为当前文件名
  • loaded是否被加载
  • children 引入模块数组,数组项格式同module
  • paths为node模块儿 node_modules 模块儿查找路径,一直查到根目录

module.exports & exports

Node里面的模块系统遵循是CommonJs规范,CommonJs定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require),在模块儿运行的时候都会生成一个module对象和一个exports对象,module对象下也有一个exports对象,默认情况下这两个对象都是空对象。如果有引用其他模块儿或定义模块儿 即module.exports或者exports被赋值时,则该模块儿就是一个有效的带有返回值的模块儿。

一个模块儿真正导出的是module.exports的值,exports只是module.exports的一个引用
可以简单理解为下面这种对象引用和赋值的区别

let obj1 = {a=1};
let obj2 = obj1;

console.log(obj1,obj2); // {a:1} {a:1}

obj2.a = 2 
console.log(obj1,obj2); // {a:2} {a:2}

obj2 = {b:3} 

console.log(obj1,obj2); // {a:2} {b:3}

obj2只是obj1的一个引用。当 obj2.a 改变时其实改变的是 obj1obj2 都指向的同一个堆里的数据。但 obj2 ={b:3} 则重新在堆里开辟了另一个内存块儿来存储。已经跟 obj1 脱离没有关系了

所以经常会看到node模块儿里会像下面这样来导出模块儿。

exports = module.exports = ()=>{
    do something
}

这是为了让exports引用指向module.exports同一块内存,确保数据的一致性。

总结

  • 在执行 node 文件时同事创建了 module.exportsexports 对象
  • exports 是指向 module.exports 的一个引用
  • require("xxx") 其实引用的是xxx中的 module.exports 而非 exports

github nodejs

查看原文

赞 1 收藏 0 评论 2

秋雨 发布了文章 · 2018-12-10

React多页面应用脚手架-v1.3.0

react-multi-page-app是一个基于react和webpack的多页面应用架构,通过编译生成对应目录结构清晰的静态页面,实现多页面便捷开发维护。1.3.0 版本对项目整体做了一个全面的升级,添加修改的以下几个方面:

  • 增加项目创建命令rppx-cli
  • 创建Demo展示页面
  • 完善修改REAMDME(中文/英文)
  • 增加了redux的使用
  • 优化目录结构
  • 优化整体代码

安装及使用

全局安装rppx-cli命令并创建自己的react多页项目

安装rppx-cli

$ npm install rppx-cli -g

创建react多页项目

$ rppx init my-react

安装依赖

$ npm install 

开发环境

$ npm run dev

编译打包

$ npm run build

运行

$ npm start

自动打开浏览器浏览页面 开发 http://localhost:3100 生产 http://localhost:3118

创建新页面

    1. 在src下创建一个文件目录
    1. 在新创建的文件目录下添加index.js(必须,因为是webpack打包入口文件)
    1. 在新创建文件夹下添加pageinfo.json(非必须) 供html插件使用
    1. npm run dev开发
|-- src
    |-- index/
    |-- page2/
        |-- index.js
        |-- pageinfo.json

目录结构调整

v1.3.0 对整个文件结构做了优化

  • 将将页面文件放在src下的pages里统一管理
  • 增加了store文件夹放redux相关文件。
  • 对整体文件命名以及结构做了大幅度调整

Demo页面

http://www.h5cool.com/react-m...

  • home页为一个简单预览页
  • todo页是一个基于react-redux的todolist页面
  • 整页排版使用bulma.css

完整代码参考项目react-multi-page-app

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 35 次点赞
  • 获得 98 枚徽章 获得 6 枚金徽章, 获得 34 枚银徽章, 获得 58 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

注册于 2014-07-12
个人主页被 1.8k 人浏览