饥人谷

饥人谷 查看完整档案

杭州编辑  |  填写毕业院校杭州饥人谷教育科技有限公司  |  用户运营 编辑 xiedaimala.com 编辑
编辑

最有爱的前端交流社区

个人动态

饥人谷 发布了文章 · 2018-02-24

JS设计模式入门和框架中的实践

本文为饥人谷讲师slashhuang原创文章。

在编写JS代码的过程中,运用一定的设计模式可以让我们的代码更加优雅、灵活。

下面笔者就结合诸如redux的subscribe、ES6的class、vue里面的$dispatch、jquery里面的on/off来给大家简单介绍下设计模式在这些库、语法和框架中的使用。

设计模式解决的问题

设计模式并不是很玄乎的知识,很多同学在编写JS代码的时候已经在不经意间用了不少设计模式了。

笔者认为把设计模式单独抽象出来探讨,就和算法中抽象出来冒泡、排序一样,是为了描述一种常用的JS pattern。

通过研习这类pattern,让模式来指导我们的代码结构及JS算法。

一些常用的设计模式概述

1、observer [观察者模式]

根据状态的变化主动触发观察者队列、hashMap的回调行为

一个简单的观察者模式代码实践

    class StateTracker{
        constructor(){
            this.observers = [];
            this.internalState= 10;
        }
        // 改变内部状态,触发状态的观察者列表
        change(val){
            this.internalState= val;
            this.observers.forEach(observer=>observer(val));
        }// 注册观察者
        registerObserver(ObserverFn){
            this.obserers.push(ObserverFn)
        }
    }

2、publish/subscribe [订阅发布模式]

在代码模块的共享访问空间存储hashMap的topic/callback形式。

添加on/off/trigger等接口实现挂载、移除、触发等动作。

一个简单的订阅发布模式代码实践

    class PubSubHandler{
        constructor(){
            this.eventPool = {};
        }
        //移除
        off(topicName){
            delete this.observers[topicName]
        }
        //发布
        trigger(topicName,...args){
            this.eventPool[topicName] && 
            this.eventPool[topicName].forEach(callback=>callback(...args));
        }
        //订阅
        on(topicName,callback){
            let topic = this.eventPool[topicName] ;
            if(!topic){
                this.eventPool[topicName] =[]
            }
            this.eventPool[topicName].push(callback)
        }
    }

3、singleton[单例模式]

构造函数的实例只有一个,一般是通过闭包存储内部实例,通过接口访问内部实例。

一个简单的单例模式代码实践

    var singleton = ()=>{
        var instance;
        var createInstance = ()=>{
            this.a = 1;
            this.b = 2;
        }// 单例模式方法入口
        return {
            getInstance:()=>{
                if(!instance){
                    instance = createInstance();
                }
                return instance;
            }
        }
    }
    var test = singleton();
    test.getInstance() == test.getInstance() //true

4、decorator装饰者模式

这个模式就是在原有的对象上面装饰更多行为,并且保持变量名不变。

用过ES7的@decorator或者python等语言的,应该对decorator不陌生的。

一个简单的装饰者模式代码实践

    function decorator(sourceObj,decortorFn){
        decortorFn(sourceObj);
        return sourceObj
    }
    var d = {a:1};
    // d变为了{a:1,b:1}
    d = decorator(d,(d)=>{d.b=1});

5、mixin混合模式

这个模式和decorator有点类似,只是它的功能更加垂直。

就是在原有的对象上面增加、覆盖对象的行为。

相比于extends、Object.assign等方法,mixin模式更富有表现力。

mixin模式不能一概而论,可能依据不同的数据类型有不同的mixin策略,比如vue.mixin

一个简单的混合模式代码实践

    class StateTracker{
        constructor(){
            this.raw = {
                a:1,
                b:2
            }
        }// 混合模式方法入口
        mixin(obj){
            Object.assign(this.raw,obj)
        }
    }
笔者暂时先介绍这么多设计模式。

下面就针对常用的框架、语法、库等来说明这些设计模式的应用。

observer模式在redux中的使用示例代码

    var store = createStore(reducer,initialState);
    //注册redux store,存储在 nextListeners数组
    var test = store.subscribe(()=>{console.log('我注册了!')});
    // 取消注册监听
    test.unsubscribe();

publish/subscribe在jquery中的使用示例代码

    $(document).on('hello',()=>{console.log('hello')})
    $(document).trigger('hello');
    $(document).off('hello')

decorator模式在react-redux中的实践

    //装饰器
    @connect(state=>state)
    class Container extends Component{
        render(){
            return JSON.stringify(this.props)   
        }
    }

总结

关于设计模式在前端框架或库的实践,我这边写的是比较简略的, 可能没有写过相关代码的同学不是特别好理解,有相关疑问的同学可以在文末直接提问。

希望本文能够对大家学习和了解设计模式的概念有所了解

本文首发于笔者的github blog

github设计模式教程代码地址

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 0 收藏 3 评论 0

饥人谷 发布了文章 · 2018-02-14

「优秀库推荐」ppt-cli 让你优雅地做 ppt

本文为饥人谷讲师若愚原创文章。

ppt-cli是什么

ppt-cli是一款用于生成在线 slides 的 node 工具,使用它可以通过一个命令把本地甚至线上的 markdown 文件转换成HTML 文件,并拥有各种强大、可配置、神奇的功能。

npm 地址: ppt-cli

github 地址:GitHub - jirengu/ppt-cli: a slides framework using makrdown

为什么要做它

做课件做展示一直找不到合适的工具,虽然像 gitbook、个人博客、课件平台都能使用 markdown 展示内容,但毕竟不像 ppt 那样有良好的展示效果。

使用国内的在线 ppt 网站,一堆繁复的功能配置和广告看的心累;使用演说在线定制化太差且不能分享;使用http://slides.com 无奈太卡;使用reveal.js 写 HTML太复杂;用 keynote 写起来麻烦分享起来也不方便。

没有合适的轮子只能自己造了。于是花了一个下午写了工具的原型,晚上熬个夜做了功能的细化。这个轮子要体现几个特点:

  1. 书写 ppt 一定要极致方便,最好直接用 markdown 写,不需要任何特殊的语法
  2. 使用起来极其弱智,一个命令实现目的
  3. 细细挖掘会发现功能简约而不简单。需要实现绝大多数所需的个性化功能,比如转场效果、排列位置、代码亮亮 、 可嵌入 HTML CSS JS、自定义样式、自定义风格、自定义JS、背景图片、背景视频、转场对应页面执行当页JS、添加属性、支持 markdown 的一二三级标题分页和特殊字符 --- 分页
  4. 新轮子最好基于老轮子,可大大降低开发成本

如何使用

  1. 安装 node
  2. 打开命令窗口,执行

    npm install -g ppt-cli
  3. 创建 markdown 文件
  4. 执行

    ppt sample.md
    ppt sample.md --align=center --theme=black --transition=zoom
  5. 或者可以直接使用线上的 markdown 文件
ppt https://raw.githubusercontent.com/jirengu/server-mock/master/README.md

注意事项

在书写 markdown 的时候要注意,每个一级标题、二级标题、三级标题都对应 slides 里的一个页面,所以这些标题下对应的内容不要太长,否则一个页面展示不下。

本文和工具均为作者若愚原创,点个赞又不会怀孕,也让我熬夜到2点破碎的心有个慰藉

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 0 收藏 0 评论 0

饥人谷 发布了文章 · 2018-02-10

JS 中 __proto__ 和 prototype 存在的意义是什么?

本文为饥人谷讲师方方原创文章。

  1. 你的 JS 代码还没运行的时候,JS 环境里已经有一个 window 对象了
  2. window 对象有一个 Object 属性,window.Object 是一个函数对象
  3. window.Object 这个函数对象有一个重要属性是 prototype,干什么用的等会说
  4. window.Object.prototype 里面有这么几个属性 toString(函数)、valueOf(函数)

好,目前先知道这些就够了。
然后我们写一句代码

var obj = {}
obj.toString()

这句代码做了啥?为什么 obj 有 toString() 属性?

这句话大概是让 obj 变量指向一个空对象,这个空对象有个 proto 属性指向 window.Object.prototype。

这样你在调用 obj.toString() 的时候,obj 本身没有 toString,就去 obj.__proro__ 上面去找 toString。

所以你调用 obj.toString 的时候,实际上调用的是 window.Object.prototype.toString

那么 window.Object.prototype.toString 是怎么获取 obj 的内容的呢?

那是因为 obj.toString() 等价于 obj.toString.call(obj)

同时 obj.toString.call(obj) 等价于 window.Object.prototype.toString.call(obj)

这句话把 obj 传给 toString 了。

再看复杂一点的

回到第一幅图


我们写一句代码

var arr = []
arr.push(1) // [1]

请问这两句话做了什么?

看红色部分,var arr = [] 大概会让 arr 指向一个空对象,然后 arr.__proto__ 指向 window.Array.prototype。(其实 arr 有一个 length:0,不过这里就忽略吧)

这样你在调用 arr.push 的时候,arr 自身没有 push 属性,就去 arr.__proto__ 上找 push

因此 arr.push 实际上是 window.Array.prototype.push

arr.push(1) 等价与 arr.push.call(arr,1)

arr.push.call(arr,1) 等价于 window.Array.prototype.push.call(arr, 1)

再再复杂一点

arr.valueOf() 做了什么?

arr 自身没有 valueOf,于是去 arr.__proto__ 上找

arr.__proto__ 只有 pop、push 也没有 valueOf,于是去 arr.__proto__.__proto__ 上找

arr.__proto__.__proto__ 就是 window.Object.prototype

所以 arr.valueOf 其实就是 window.Object.prototype.valueOf

arr.valueOf() 等价于 arr.valueOf.call(arr)

arr.valueOf.call(arr) 等价于 window.Object.prototype.valueOf.call(arr)

看,JavaScript 其实很优美很简单。

只是你想复杂了而已:

prototype 指向一块内存,这个内存里面有共用属性

proto 指向同一块内存

prototype 和 proto 的不同点在于

prototype 是构造函数的属性,而 proto 是对象的属性

难点在于……构造函数也是对象!

如果没有 prototype,那么共用属性就没有立足之地

如果没有 __proto__,那么一个对象就不知道自己的共用属性有哪些。

反证法

假设我们把 proto 去掉,那么

var obj = {}
obj.toString() // 报错,没有 toString 方法

所以你只能这样声明一个对象咯:

var obj = {
  toString: window.Object.prototype.toString,
  valueOf: window.Object.ptototype.valueOf
}
obj.toString() // '[object Object]'

知道 proto 帮你省多少代码了吗?

假设我们删掉 prototype,包括 window.Object.prototype 和 window.Array.prototype。

那么 window.Object.prototype.toString 也一并被删除了。

然后我们基本就没法写代码了……

var obj = {}
obj.toString() // toString 不存在,因为 toString 没有定义过啊

prototype 的意义就是把共有属性预先定义好,给之后的对象用。

自己想想吧~

新人搞不懂原型大抵是因为

  1. 不懂内存、引用
  2. 不懂链表、树等数据结构
  3. 不知道函数是一种对象
  4. 被 Java 的 class 关键字毒害了
  5. 还有一种可能是因为没遇到我方应杭:
「每日一题」什么是 JS 原型链?
JS 的 new 到底是干什么的?
this 的值到底是什么?一次说清楚

以上是「方三篇」新人一定要看哦。

想看视频版本可以购买我的网课(收费):
JS 深入浅出 - 写代码啦!

这幅图我还可以继续讲,把 JS 所有基础知识都能串起来。

比如很多人不懂什么是伪数组,很简单:

  1. 如果一个数组的 proto 直接或间接指向 Array.prototye(用到了数组的共用属性),那么就是真数组
  2. 如果一个数组的 proto 没有直接或间接指向 Array.prototye,那么就是伪数组
var realArr = {0: 'a', 1:'b', length: 2}
realArr.__proto__ = Array.prototye
// 这就是真数组
// 等价于 realArr = ['a', 'b']
realArr.push !== undefined // true

var fakeArr = {0: 'a', 1:'b', length: 2}
// 这就是伪数组
realArr.push === undefined // true

完。

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 3 收藏 3 评论 0

饥人谷 发布了文章 · 2018-02-08

免费直播 | 2018,你最需要的前端学习指南&求职指南!

小A(信誓旦旦脸.jpg):小谷呀,春节过后,我在前端坑里学习那么久,等年后一定能顺利找到一份前端工作!(内心:嘤嘤嘤(╥╯^╰╥)~到底该怎么准备简历和面试?投出去的简历怎么都没有回应?面试是应该怎么应对面试官的刁难……)

小B:听我七大姑家的八大姨家的大表哥小A说,最近前端很火,而且很有发展前景,我也想要试试进前端的坑了……可素,坑的入口在哪呢?

小谷:别担心,小谷头老师开课啦!重点是免费哟!

饥人谷免费公开课!

主讲大咖导师

若愚老师
若愚,北航计算机硕士,曾任职于百度LBS搜索研发部、阿里巴巴B2B技术部,开发过百度地图、阿里巴巴中文站等产品,擅长CSS3、HTML5、Node.JS。

方方老师
资深前端开发、饥人谷资深前端讲师,饥人谷课程及技术CTO。华中科技大学毕业,毕业后先后在腾讯、阿里巴巴和彩程任职。

直播时间

2018年2月10日(本周六)20:00

课程安排

若愚老师:《求职篇》
针对年后招聘季的前端求职者,内容涉及简历和笔试面试等。

  • 简历怎么写才有吸引力?
  • 如何准备笔试和面试?
  • 面试时如果遇到不会的问题怎么办?

方方老师:《学习篇》
针对18年准备入坑和已经在坑里的准前端们,内容涉及学习方法和误区等。

  • 前端知识体系是怎样的?
  • 能看到老师的代码,但是自己写不出来怎么办?
  • 懒癌犯了怎么办?
  • 没有相关工作经历怎么办?
  • 自学该看哪些书?
  • 觉得自己的智商不足以学会编程怎么办?
  • 前端和前端配合,前端和设计配合

如何参加本期公开课?

添加任意一位班主任微信,备注:SF,邀请你进公开课群,等待开课呦~

  • 墨凌:hungervalley
  • 青青:xiedaimala01
  • 天天:astak10

春节将至,小谷提前祝大家新春快乐,年后求职旗开得胜!学习愉快!ღ( ´・ᴗ・` )比心

查看原文

赞 0 收藏 0 评论 0

饥人谷 发布了文章 · 2018-02-08

免费直播 | 2018,你最需要的前端学习指南&求职指南!

小A(信誓旦旦脸.jpg):小谷呀,春节过后,我在前端坑里学习那么久,等年后一定能顺利找到一份前端工作!(内心:嘤嘤嘤(╥╯^╰╥)~到底该怎么准备简历和面试?投出去的简历怎么都没有回应?面试是应该怎么应对面试官的刁难……)

小B:听我七大姑家的八大姨家的大表哥小A说,最近前端很火,而且很有发展前景,我也想要试试进前端的坑了……可素,坑的入口在哪呢?

小谷:别担心,小谷头老师开课啦!重点是免费哟!

饥人谷免费公开课!

主讲大咖导师

若愚老师
若愚,北航计算机硕士,曾任职于百度LBS搜索研发部、阿里巴巴B2B技术部,开发过百度地图、阿里巴巴中文站等产品,擅长CSS3、HTML5、Node.JS。

方方老师
资深前端开发、饥人谷资深前端讲师,饥人谷课程及技术CTO。华中科技大学毕业,毕业后先后在腾讯、阿里巴巴和彩程任职。

直播时间

2018年2月10日(本周六)20:00

课程安排

若愚老师:《求职篇》
针对年后招聘季的前端求职者,内容涉及简历和笔试面试等。

  • 简历怎么写才有吸引力?
  • 如何准备笔试和面试?
  • 面试时如果遇到不会的问题怎么办?

方方老师:《学习篇》
针对18年准备入坑和已经在坑里的准前端们,内容涉及学习方法和误区等。

  • 前端知识体系是怎样的?
  • 能看到老师的代码,但是自己写不出来怎么办?
  • 懒癌犯了怎么办?
  • 没有相关工作经历怎么办?
  • 自学该看哪些书?
  • 觉得自己的智商不足以学会编程怎么办?
  • 前端和前端配合,前端和设计配合

如何参加本期公开课?

添加任意一位班主任微信,备注:掘金,邀请你进公开课群,等待开课呦~

  • 墨凌:hungervalley
  • 青青:xiedaimala01
  • 天天:astak10

春节将至,小谷提前祝大家新春快乐,年后求职旗开得胜!学习愉快!ღ( ´・ᴗ・` )比心

查看原文

赞 0 收藏 0 评论 0

饥人谷 发布了文章 · 2018-02-05

webpack技术讲解及入门

本文为饥人谷讲师slashhuang原创文章,首发于 前端学习指南

webpack技术入门

webpack基本已经成为前端项目的标配构建工具了。

然而,在一个前端团队里面,除了架构师之外,其他开发者很难有机会在工作中完整的实现整个配置流程。

本篇文章是我在公司里面分享webpack及babel配置和插件开发的一个细节版本,

希望能让大家通过阅读本文,比较好的梳理webpack工具。

webpack的出现解决了什么问题

JavaScript自面世之后,就成为浏览器的标准脚本语言。

然而JS本身并没有提供python和java的package包、子模块的import等语法。

同时,前端代码还需要处理类似CSS、png、webfonts等非JS的文件。

在前端工程化大潮下,一个既能处理JS又能处理别的资源文件的加载器(bundler)亟待出现。

webpack就是这类解决方案中的杰出代表。

下面,我将按照如下的目录结构对webpack使用进行讲解。

1、webpack概述
2、一个简单而通用的webpack配置文件
3、webpack的配置文件解读
4、webpack常用的plugins及loader

1.webpack概述

webpack = module building system。

在webpack看来,所有的资源文件都是模块(module),只是处理的方式不同。

上面两句话就把webpack从top-level的角度讲清楚了。

关于webpack的使用,其实和我们平时开发业务产品是一个道理。

产品需求 ===> 代码设计 ===> 提供API给开发者使用。

webpack解决的需求点就是如何更好的加载前端模块

这里我用了模块二字,是因为webpack从JS出发,将所有的文件看做它要处理的模块。

webpack本身并不关心这个模块是什么,它只是调度配置文件中对模块处理的方式来完成这一切,而不必纠结文件类型。

比如我们会在项目中使用ES6/7的语法来编写JS代码,

于是我们只需要配置babel-loader即可处理这种JS。

比如我们需要加载html文件获取html字符串,

于是我们只需要配置raw-loader即可拿到对应文件的字符串。

比如我们需要将sass/less文件预编译成css,

于是我们只需要配置sass-loader/less-loader即可处理。

webpack提供了一套API接口,开发者只需要按照它提供的规范照着做就行了。

对于开发者来说,除了需要阅读英语文档能力和nodeJS之外,webpack的学习门槛真的不高。

2.一个简单而通用的webpack配置文件

我们以如下的 webpack.config文件来进行分析
var webpack = require("webpack");
var DefinePlugin = require('webpack/lib/DefinePlugin');
module.exports =  { 
        context:process.cwd(),
        watch: true,
        entry: './index.js',
        devtool: 'source-map',
        output: {
            path: path.resolve(process.cwd(),'dist/'),
            filename: '[name].js'
        },
        resolve: {
            alias:{ jquery: process.cwd()+'/src/lib/jquery.js', }
        },
        plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                _: 'underscore',
                React: 'react'
            }),
            new DefinePlugin({
              'process.env': {
                'NODE_ENV': JSON.stringify('development')
              }
            })
        ],
        module: {
            loaders: [{
                test: /\.js[x]?$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },  {
                test: /\.less$/,
                loaders:['style-loader', 'css-loader','less-loader']
            }, {
                test: /\.(png|jpg|gif|woff|woff2|ttf|eot|svg|swf)$/,
                loader: "file-loader?name=[name]_[sha512:hash:base64:7].[ext]"
            }, {
                test: /\.html/,
                loader: "html-loader?" + JSON.stringify({minimize: false })
            } ]
        }
    };

这个简单的webpack配置文件,基本可以处理大多数的前端业务场景了。

通过配置plugins、module.loaders、entry、output基本可以构建一个兼容本地开发和生产环境的富应用web工程,下面针对以上的配置文件进行分析。

3. webpack的配置文件解读

* module.loaders数组
    [{
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
    }]
比如有个文件require('index.jsx'), webpack会根据文件名是否满足test字段的正则来决定是否使用babel-loader来处理文件。 exclude则是告诉webpack不需要对node_modules目录进行处理
  • resolve对象
  resolve: {
            alias:{ jquery: path.resolve(process.cwd(),'src/lib/jquery.js')},
            extensions:['.js','.json']
        }
resolve对象是在webpack预编译时,就加载进整个webpack编译的配置中的。

比如alias会建立文件标识符映射表

require('jquery')==> require('/Users/**/src/lib/jquery.js')

  • plugins数组
    /**
    比如有个文件代码中存在process.env对象,则process.env将会被替换成上面的{
        'NODE_ENV': JSON.stringify('development')
    }    console.log(process.env)==>console.log(({"NODE_ENV":'development'}))
    */

    plugins: [new DefinePlugin({
        'process.env': {
            'NODE_ENV': JSON.stringify('development')
            }
        })]
关于这个配置文件,读者如果有疑问,可以直接在评论区留言,我会尽快回复,这里就不赘述了。

更进一步的webpack配置含义可以参考我的github博客webpack编译流程漫谈

4. webpack常用的loaders和plugins

关于loader这块呢,webpack官方给出了非常详尽的解决方案,基本不用开发者再去开源社区上寻找。前端资源loaders列表

关于plugins这块,除了webpack官方推荐的几款plugins外,社区上也有非常多不错的插件。webpack内置的plugins列表

经过多个前端项目搭建实践下,笔者认为如下几款plugins是非常不错的。

1、代码优化之:

  • CommonsChunkPlugin - 抽取公共代码
  • UglifyJsPlugin - 压缩混淆代码

2、 依赖注入之:

  • DefinePlugin - 自由变量注入
  • ProvidePlugin - 模块变量标示符注入

3、 文件抽取之:

  • file-loader - 传送font等文件
  • ExtractTextPlugin - 抽取css文件

4、 开发体验优化之:

  • WebpackNotifierPlugin - 编译完成动态通知
  • HtmlWebpackPlugin - 采用模板引擎形式注入到html文件,让开发更加easy

5、 目录/文件拷贝之:

  • CopyWebpackPlugin - 目录及文件拷贝

5.webpack总结

本篇文章对webpack的讲解主要集中在API层面。

另外,关于更加深入的webpack的编译流程, 我几个月前写过一篇博客webpack编译流程漫谈,可以供大家参考学习。

最后 欢迎大家关注我的知乎前端学习专栏

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 3 收藏 3 评论 0

饥人谷 发布了文章 · 2018-02-01

每个程序员都应该读《Unix编程艺术》

本文为饥人谷讲师方方原创文章,首发于 前端学习指南

我已经向饥人谷的学员不止十次地推荐过《Unix编程艺术》这本没有代码的编程书。
如果你想写出优雅的代码,这本书必读。

而且,知乎上各种编程大神说话时,经常会提到什么「自底向上」、「模块化」,该书都有很详细的解释。该书用来提升bigger效果十分显著。

看不完不要紧,能看多少是多少。

以下是该书对「模块化」的阐述:

  • 软件设计有两种方式:一种是设计得极为简洁,明显没有缺陷;另一种是设计得极为复杂,没有明显的缺陷。第一种设计方式要难得多。——《皇帝的旧衣》
  • 模块化原则就是:要编写复杂软件又不至于一塌糊涂的唯一方法,就是用定义清晰的接口把若干简单模块组合起来,这样一来,多数问题只在出现在局部,那么我们还有希望对局部进行改写或优化,不会牵一发而动全身。
  • 模块化代码的首要特质就是封装。封装良好的模块不会过多向外部披露自身的细节,不会直接调用其他模块的实现代码,也不是胡乱共享全局数据。模块之间通过 API ——一组严密的、定义良好的程序调用和数据结构来通行。
  • 正交性是有助于使复杂设计也能紧凑的最重要的特性之一。在纯粹的正交设计中,任何操作均无副作用;每一个动作只改变一件事情,不会影响其它。无论你控制的是什么系统,改变每个属性的方法有且只有一个。
  • 重复的代码会导致前后矛盾,产生质量差的代码。原因是当你修改这些代码的时候,往往只修改了一部分而不是全部。通常,这也意味着你对代码的组织没有想清楚。
  • 软件是多层的。一般来说,设计函数或对象的层次结构可以选择两个方向——自顶向下和自底向上。
  • 一个方向是自底向上,就是从具体到抽象——先从要解决的问题中,确定要进行的具体操作,然后向上进行抽象。另一个方向是自顶向下,就是从抽象到具体——从最高层面描述整个项目的功能和逻辑,层层向下,直到各个具体的操作。
  • 实际代码往往是自顶向下和自底向上的综合产物,同一个项目中经常同时兼有自顶向下的代码和自底向上的代码,这就导致了「胶合层」的出现。
  • 胶合层是个挺讨厌的东西,必须尽可能的薄,这一点极为重要。胶合层用来将东西粘在一起,但不应该用来隐藏各层的裂缝和不平整。
  • 面向对象理念的价值最初在图形系统、图形用户界面和某些仿真程序中被认可。令大家惊讶并逐渐失望的是,我们很难发现面向对象在其他领域有多少显著的优点。
  • 面向对象语言让抽象变得很容易——有点太容易了。面向对象语言鼓励「厚重的胶合和复杂的层次」。所有的面向对象语言都显示出某种使程序员陷入过度分层陷阱的倾向。

Unix编程艺术,教你如何优雅地编程。

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 1 收藏 1 评论 0

饥人谷 发布了文章 · 2018-01-31

我们来画一只皮卡丘吧

本文为饥人谷讲师方方原创文章,首发于 前端学习指南

之前我写过一个会动的简历,还写过一个皮卡丘,今天上课时突发奇想,想把两者结合起来了,于是就有了「画只皮卡丘给你看」这个项目。

我已经把制作的全过程录屏,放在了任务班第42课的视频里(限时免费),如果你也想动手做一只皮卡丘,点击画只皮卡丘给你看即可。

完整的代码在:https://github.com/FrankFang/...
在线预览页面:https://frankfang.github.io/m...

目前免费,很快就不免费了,快看吧。

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

【新班】又双叒改版啦!2018最系统的前端学习课程,同时新班将采用最新改版的学习模式前50名免费升级价值1000元的教练模式

过来人经验!入职今日头条的谷里学员分享前端攻城狮的自我规划 | 饥人谷免费直播

开年学习福利大放送,200元学习基金等你来领取!饥人谷

查看原文

赞 2 收藏 1 评论 0

饥人谷 发布了文章 · 2018-01-24

重构 - 代码优化技巧

本文为饥人谷讲师方方原创文章,首发于 前端学习指南

以下是我《JS深入浅出》第6课的讲义,如果对你有帮助点个赞即可

这次课讲的是「如何提高代码的可读性」,跟前端关系不大,是写代码的普遍技巧。

注意我们讲得不是「如何提高代码的性能」。

代码优化基本原则

  1. 易读性优先
  2. 如果不是性能瓶颈,就不要为了性能而改写代码
  3. 复杂性守恒原则:无论你怎么写代码,复杂性都是不会消失的

推论:如果逻辑很复杂,那么代码看起来就应该是复杂的。如果逻辑很简单,代码看起来就应该是简单的。

命名

程序员三大难题

  1. 变量命名
  2. 缓存失效
  3. 循环边界

可见变量命名的重要性。

网上有很多命名规范,大家可以参考。本节课只讲基本原则。

一、注意词性

  • 普通变量/属性用「名词」
var person = {
      name: 'Frank'
  }
  var student = {
      grade: 3,
      class: 2
  }
  • bool变量/属性用「形容词」或者「be动词」或者「情态动词」或者「hasX」
var person = {
      dead: false, // 如果是形容词,前面就没必要加 is,比如isDead 就很废话
      canSpeak: true, //情态动词有 can、should、will、need 等,情态动词后面接
动词
      isVip: true, // be 动词有 is、was 等,后面一般接名词
      hasChildren: true, // has 加名词
  }
  • 普通函数/方法用「动词」开头
var person = {
      run(){}, // 不及物动词
      drinkWater(){}, // 及物动词
      eat(foo){}, // 及物动词加参数(参数是名词)
  }
  • 回调、钩子函数用「介词」开头,或用「动词的现在完成时态」
var person = {
      beforeDie(){},
      afterDie(){},
      // 或者
      willDie(){}
      dead(){} // 这里跟 bool 冲突,你只要不同时暴露 bool dead 和函数 dead 就行,怕冲突就用上面的 afterDie
  }
  button.addEventListener('click', onButtonClick)
  var component = {
      beforeCreate(){},
      created(){},
      beforeMount(){},
      mounted(){},
      beforeUpdate(){},
      updated(){},
      activated(){},
      deactivated(){},
      beforeDestroy(){},
      destroyed(){},
      errorCaptured(){}
  }
  • 容易混淆的地方加前缀
div1.classList.add('active') // DOM 对象
  div2.addClass('active') // jQuery 对象
  不如改成
  domDiv1 或 elDiv1.classList.add('active')
  $div2.addClass('active')

属性访问器函数可以用名词

$div.text() // 其实是 $div.getText()
  $div.text('hi') // 其实是 $div.setText('hi')

二、注意一致性

  • 介词一致性

如果你使用了 before + after,那么就在代码的所有地方都坚持使用
如果你使用了 before + 完成时,那么就坚持使用
如果你改来改去,就「不一致」了,不一致将导致「不可预测」

  • 顺序一致性

比如 updateContainerWidth 和 updateHeightOfContainer 的顺序就令人很别扭,同样会引发「不可预测」

  • 表里一致性

函数名必须完美体现函数的功能,既不能多也不能少。
比如

function getSongs(){
      return $.get('/songs).then((response){
          div.innerText = response.songs
      })
  }

就违背了表里一致性,getSongs 表示获取歌曲,并没有暗示这个函数会更新页面,但是实际上函数更新了 div,这就是表里不一,正确的写法是

  • 要么纠正函数名
function getSongsAndUpdateDiv(){
      return $.get('/songs).then((response){
          div.innerText = response.songs
      })
  }
  • 要么写成两个函数
function getSongs(){
      return $.get('/songs)
  }
  function updateDiv(songs){
      div.innerText = response.songs
  }
  getSongs().then((response)=>{
      updateDiv(response.songs)
  })
  • 时间一致性

有可能随着代码的变迁,一个变量的含义已经不同于它一开始的含义了,这个时候你需要及时改掉这个变量的名字。
这一条是最难做到的,因为写代码容易,改代码难。如果这个代码组织得不好,很可能会出现牵一发而动全身的情况(如全局变量就很难改)

改代码

如果你的代码有单元测试,那么改起来就很放心。如果没有单元测试,就需要用「小步快跑」的策略来修改。

小步快跑的意思是说,每次只修改一点点,测试通过后,再修改一点点,再测试,再修改一点点……如此反复。

那么如何修改一点点呢?《重构》这本书介绍了很多方法,但是讲得有点抽象的,如果你有时间可以看看。

我这里只说两个经久不衰的方法。

一、使用函数来改代码

步骤:

  1. 将一坨代码放到一个函数里
  2. 将代码依赖的外部变量作为参数
  3. 将代码的输出作为函数的返回值
  4. 给函数取一个合适的名字
  5. 调用这个函数并传入参数
  6. 这个函数里的代码如果超过 5 行,则依然有优化的空间,请回到第 1 步

二、使用对象来改代码

如果使用了函数改造法改造后,发现有太多的小函数,则可以使用对象将这些函数串起来。

记得我们讲过「this 是函数和对象的桥梁」吗,我们会用 this 来串联这个对象和所有函数。

最终代码:http://js.jirengu.com/mimazab...

一些固定的套路

  1. 表驱动编程(《代码大全》里说的)

所有一一对应的关系都可以用表来做

  1. 自说明代码(以 API 参数为例)

把别人关心的东西放在显眼的位置

bad smell(坏味道)

有些代码可以用,但是很「臭」。

哪些代码是有坏味道的

  1. 表里不一的代码
  2. 过时的注释
  3. 逻辑很简单,但是看起来很复杂的代码
  4. 重复的代码
  5. 相似的代码
  6. 总是一起出现的代码

破窗效应

此理论认为环境中的不良现象如果被放任存在,会诱使人们仿效,甚至变本加厉。一幢有少许破窗的建筑为例,如果那些窗不被修理好,可能将会有破坏者破坏更多的窗户。最终他们甚至会闯入建筑内,如果发现无人居住,也许就在那里定居或者纵火。一面墙,如果出现一些涂鸦没有被清洗掉,很快的,墙上就布满了乱七八糟、不堪入目的东西;一条人行道有些许纸屑,不久后就会有更多垃圾,最终人们会视若理所当然地将垃圾顺手丢弃在地上。这个现象,就是犯罪心理学中的破窗效应。

程序员要做到:只要是经过你手的代码,都会比之前好一点。

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 10 收藏 24 评论 0

饥人谷 发布了文章 · 2018-01-22

CSS Grid 系列(下)-使用Grid布局构建网站首页

by Chris House
译者:若愚老师
想要更好的阅读体验可在饥人谷技术博客 查看原文
要看懂这篇文章,推荐先简单过一遍姊妹篇 CSS Grid 系列(上)-Grid布局完整指南

当我开始一个项目,并开始计划如何布局主页时,我的大脑复现出浮动和定位。有些人可能会使用 Bootstrap 或其他框架。 那是因为这是2016年,我们一直在用这些方法来做布局。 但假设我们乘坐时光机来到2018年,所有主流浏览器都支持CSS Grid 布局模块。此时我们的页面布局模式已经完全改变,CSS的功能最终强大到能轻松实现我们的设计目标,这是一个web开发人员最美好的时代。现在,让我们使用超赞的工具——Grid布局来创建一个主页。

设计

下面是我们将要实现的页面

在我们开始编码之前,我们需要进入网格的思维模式。 第一步是观察我们的设计稿,并将其划分为主要的网格组件。 以下是我为此设计做的划分:

你会发现整个页面分为7个顶级网格区域。 我之所以说“顶级”是因为我们可以在其内部继续嵌套网格,这正是我们将要对hero部分所做的事:

HTML

这是HTML的基本结构。 稍后我会显示整个完成的文件,但现在我已经省去了大部分的细节。 这里要注意的重要部分是作为 body 的直接后代的7个元素:top-barmain-headerheroblog-postsnewsside-bar 以及 main-footerbody将成为我们的网格容器(grid container),它的孩子将成为网格项(grid items)。

正如刚刚提到的,我们也将设置 hero 作为网格容器。 它有两个孩子,将作为网格项:messageaward

<body>

  <header class="top-bar">
    <!-- social links and contact info -->
  </header>

  <header class="main-header">
    <!-- logo and main navigation -->
  </header>

  <section class="hero">
    <div class="message">
      <!-- circular element -->
    </div>
    <div class="award">    
      <!-- award image and quote -->      
    </div>
  </section>

  <section class="blog-posts">
    <!-- blog posts and excerpts -->
  </section>

  <section class="news">
    <!-- news headlines and excerpts -->
  </section>

  <aside class="side-bar">
    <!-- critter of the month info -->
  </aside>

  <footer class="main-footer">
    <!-- footer menu and copyright -->
  </footer>

</body>

CSS

Okey,我们按照这种方式讲解,教程中我们不会展示所有使用到的CSS,在文章的最后我会展示最终完整的文件。现在我们只关注吸引我们的网格部分以及任何与它直接相关的样式即可。

我们首先在body上定义主网格容器:

body{
  display: grid;
  grid-template-columns: 12% auto 400px 12%;
  grid-template-rows: auto auto 950px auto auto auto;
}

我们刚刚创建了一个4列6行的网格,第一列和最后一列将作为主内容两侧的填充。 我把第三列设置为400px,因为这是我们将要放置side-bar元素的地方,我们希望这是一个固定的宽度。 hero 元素(第三行)的固定高度为950px。

现在我们使用grid-template-areas来定义某个网格区域会跑到哪里。 这是非常有趣的部分:

body{
  display: grid;
  grid-template-columns: 12% auto 400px 12%;
  grid-template-rows: auto auto 950px auto auto auto;
  grid-template-areas: "top-bar     top-bar     top-bar     top-bar"
                       "main-header main-header main-header main-header"
                       "hero        hero        hero        hero"
                       ".           blog-posts  side-bar    ."
                       ".           news        side-bar    ."
                       "main-footer main-footer main-footer main-footer";  
}

grid-template-areas让我们能把元素放在任何想要放置的地方,并且对于元素的布局该属性给我们提供一个不错的可视化。 值得注意的是,这里使用的值(top-barmain-headerhero等)不是指那些元素的类名,而是指我们用grid-area属性给它们起的名字,下一步我们将对它们命名。

当网格区域名称重复时,该元素将跨越这些列/行。 例如,top-bar 横跨四列,side-bar横跨四行和五行。 .号代表空单元格。如果你回头看看上面的完整设计,您会看到这个定义如何与我们的网格模式相匹配。

假设我们已经应用了我们所有的样式,但还没有为网格项分配网格区域名称,到目前为止我们的页面看起来还不太好:

在将网格区域名称分配给网格项之前,网格将根据它们的源顺序自动将我们的元素放置在网格中。 显然这不是我们想要的。 为了使我们的布局按预期工作,我们需要定义我们的网格区域。所以我们继续往下走:

.top-bar{
  grid-area: top-bar;
} 
.main-header{
  grid-area: main-header;
} 
.hero{
  grid-area: hero;
}
.blog-posts{
  grid-area: blog-posts;
}
.news{
  grid-area: news;
}
.side-bar{
  grid-area: side-bar;
}
.main-footer{
  grid-area: main-footer;
}

需要注意的是这些名称可以随意设置。 为了方便,我选择了让它们与类名相匹配。

现在,我们已经为网格项分配了网格区域名称,它们将在被放置在网格中合适的位置。 这一步带来的变化很大:

除了 hero 部分中的网格项外,所有内容都完全按照需要正确放置,我们差不多要完成了。

但是在我们修复 hero 部分之前,我想解释一下一些难以理解的地方:主要内容两边的填充区域的设置。 作为提醒,我们再次把刚刚的设置搬过来,用如下方式调整列:

body{
  grid-template-columns: 12% auto 400px 12%;
}

设置为12%的两列用于填充主要内容两边的空白,但是它们仅用于第四行和第五行。 回想一下,我们告诉我们的top-barmain-headerheromain-footer元素跨越所有列,包括这两个“填充”列。 我们为什么这样做? 因为我们希望这些元素的背景色横跨越整个视窗宽度,且任何一侧都没有空白。 我们只想在 blog-post/newssidebar元素周围留出空白(第四行和第五行)。

为了让元素水平覆盖整个宽度,同时让元素里面的内容保存一定的padding,我们需要显示地在这些元素上设置padding:

.top-bar{
  padding: 4px 12%; 
}
.main-header{
  padding: 12px 12%;
}
.hero{  
  padding: 55px 12% 0 12%;
}
.main-footer{
  padding: 25px 12%;
}

我们给元素设置左右 padding 为12%,这和grid-template-areas定义中的第一列和最后一列的宽度是一样的。 现在,需要填充整个宽度的元素最终呈现的结果是,背景横跨水平宽度,但其内容在两侧都预留出12%的空白。 很赞!

好了,让我们来修复 hero 部分。 这也将是一个网格容器,因此我们把它定义为一个网格,就像刚刚做过的那样:

.hero{
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto auto;
  grid-template-areas: ".       .  award"
                       "message .  .    "
                       ".       .  .    "; 
 }

这是一个3×3的网格,除了中间的列,其它都设置为 auto。 我们给中间一列大小设为1fr,因为我们希望在第一列和最后一列用东西填充后,剩下的空间完全需要完全填满。

hero中只有两个元素:messageaward。 我们要message占据第二行的第一列,我们要award占据第一行的第三列。所以我们的完整网格定义应该如下所示:

.hero{
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto auto;
  grid-template-areas: ".       .  award"
                       "message .  .    "
                       ".       .  .    ";  
 }

下面我们所要做的就是命名我们的元素:

.message{
  grid-area: message;
}
.award{
  grid-area: award;
}

就这样,messageaward卡入到位,我们的页面完成:

引入响应式

CSS Grid 使用媒体查询让重新排列整个布局变得非常简单。你所做的就是重新放置你的网格项。现在回到我们的设计,简单起见,我们只对两个宽度临界值做响应式处理,1600px 和 1050px。我们需要对一些元素(padding、margin等)进行一些小的样式调整,但是我不会把所有的样式调整都全部展示在这里。后面我会放出完整的代码,现在我们只需要关注关注网格相关的东西即可。

1600px 这个临界点的处理比较简单,当浏览器宽度到底1600px时我们将减少网站外部填充的地方。 之所以选择1600px,是到了这个宽度后12%填充看起来不太合适。为了解决这个问题,我们需要做的是在body上改变grid-template-columns的值,将第一列和最后一列减少到2%。 我们还需要调整其他元素的填充以匹配:

@media (max-width: 1600px) {
  body{
    grid-template-columns: 2% auto 400px 2%;
  }
  .top-bar{
    padding: 4px 2%;
  }
  .main-header{
    padding: 12px 2%;
  }
  .hero{
    padding: 55px 2% 0 2%;
  }
  .main-footer{
    padding: 25px 2%;
  }
}

对于下一个临界值,我们对网格项重新排列,使它们排列在一个列中。 再次回头看看我们原来的代码是如何对body进行设置的:

body{
  display: grid;
  grid-template-columns: 12% auto 400px 12%;
  grid-template-rows: auto auto 950px auto auto auto;
  grid-template-areas: "top-bar     top-bar     top-bar     top-bar"
                       "main-header main-header main-header main-header"
                       "hero        hero        hero        hero"
                       ".           blog-posts  side-bar    ."
                       ".           news        side-bar    ."
                       "main-footer main-footer main-footer main-footer";  
}

下面是重新设置的媒体查询:

@media (max-width: 1050px) {
  body{
    grid-template-columns: 3% auto 3%;
    grid-template-rows: auto auto auto auto auto auto auto;
    grid-template-areas: "top-bar     top-bar     top-bar"
                         "main-header main-header main-header"
                         "hero        hero        hero"
                         ".           blog-posts  ."
                         ".           news        ."
                         ".           side-bar    ."
                         "main-footer main-footer main-footer";
  }
}

我们在这里做了一些重要的改变:将列数从四个减少到三个,将第一列和最后一列的值改为3%(3%在较窄的宽度上优于2%),添加了 附加行,将所有行的长度改为auto,并将side-bar移动到自己的行。 现在我们的页面元素很适合在较窄的宽度下展示:

The Live Code

下面是我们的主页,以及完整的HTML和CSS文件。 你需要一个支持grid的浏览器来查看预览。 我建议启用Experimental Web Platform Features标志的Chrome 49+(地址栏输入 chrome:// flags ,并向下滚动到“Experimental Web Platform Features”)。

下面的嵌入式页面默认会以移动视图展示,可以点击“Edit on Codepen”在页面全宽下展示不同的效果:

在 CodePen 查看效果 Building a Home Page with Grid by Chris House (@chrishouse) .

补充:基本布局代码

加微信号: astak10或者长按识别下方二维码进入前端技术交流群 ,暗号:写代码啦

每日一题,每周资源推荐,精彩博客推荐,工作、笔试、面试经验交流解答,免费直播课,群友轻分享... ,数不尽的福利免费送

查看原文

赞 14 收藏 27 评论 2

认证与成就

  • 获得 249 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-12-12
个人主页被 1.6k 人浏览