Dappur

Dappur 查看完整档案

绍兴编辑上海科技大学  |  计算机科学与技术 编辑  |  填写所在公司/组织 dappur.cf 编辑
编辑

IT爱好者

个人动态

Dappur 关注了问题 · 2017-09-18

解决关于程序优化的问题,c/c++赋值数组快慢问题?

有个面试题:
步长为1给数组赋值和步长为4给数组赋值的操作。
前者耗时是不是后者的4倍。

for (int i = 0; i < n; i += 1) {

a[i] = 1;

}
for (int i = 0; i < n; i += 4) {

a[i] = 1;

}

应该从内存局部性和缺页命中这方面考虑吧。
我觉得这两种不同的赋值,都直接从内存去取数组,一般数组不大,一次就全部取来了,所以这两种操作的时间差距不应该有4倍那么大。
而具体赋值耗时之类的,我就有点疑惑了。
有没有大佬能说说。

关注 2 回答 1

Dappur 赞了文章 · 2017-08-23

【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇

书接上一篇: 150行代码教你实现一个低配版的MVVM库(1)- 原理篇

写在前面

为了便于分模块,和阅读,我使用了Typescript来进行coding,总行数是正好150行,最早写DEMO的时候用了ES2015,代码行数应该在100行出头,如果你不会搭ts+webpack的编译UMD环境,你也可以把本文中的ts语法人肉转成es6或者es2015,我相信这对你(一个有志于学写mvvm库的青年)来说没有什么难度。

作为作者呢,虽然最后我会放出源码的地址,你可以去github上扫一眼代码,但我还是希望你们可以跟我一起,打开个文本编辑器,一个模块一个模块把代码人肉敲出来,这样的感觉是不一样的,就好比是你可能之前就阅读过angular,vue的源码,但你现在还不是在读我的文章么?

第一步 先把骨架搭好, 血肉晚点再填充

还是再上一遍设计图
图片描述
设计的类不多,一共就5个

//SegmentFault.ts
export let SegmentFault = class SegmentFault {
    private viewModelPool = {};   //用来维护viewModel的别名alias与viewModel之间的关系
    private viewViewModelMap = {};//用来维护viewModel和被绑定的view之间的关系
    public registerViewModel(alias:string, vm:object) {};//在sf正式运作之前我们先要注册一个下viewModel并给他起一个别名
    public init() {};  //sf库开始运作的入口函数
    
    public refresh(alias:string){}; // 暴露一个强制刷新整个viewModel的方法,因为毕竟有你监控不到的角落
}

SegmentFault是对用户暴露的唯一的对象,就像Angular他会暴露一个angular对象给用户使用一样。
最终,用户会这样来操作SF以达到双向绑定的目的
不妨再看看使用效果

<script data-original="dist/sf.js"></script> <!-- 这里引入我们的sf.js库-->
<script>
    var sf = new SegmentFault();   //生成一个sf的实例
    sf.registerViewModel("vm", new ViewModel());  //注册一个viewModel,起一个叫vm的别名
    sf.init();  //调用init方法,开始初始化,sf正式开始一些列工作

    //以下是viewModel的定义
    function ViewModel() {
        this.message = "hello, SegmentFault";
        this.buttonClickHandler = function() {
            this.message = "clicked: " + this.message;
        }
    }
</script>

有没有觉得SF的API干净利落,清新爽洁!

根据设计图的Step 1,先给已注册的viewModel加上监视,这里我们需要一个Watcher类

export class Watcher {
    private sf;
    
    //构造函数里传入一个sf的对象,便于callback调用时的作用域确定。。。这是后话
    constructor(sf) {
        this.sf = sf;
    }
    public observe(viewModel, callback) {} //暗中观察
}

再来看一下Step 2, 另一个主要的类Scanner,Scanner是干什么的呢?作用就一个遍历整个DOM Tree把出现sf-xxxx这个attribute的Elements全部挑出来,然后找sf-xxxx = expression,等号右边这个表达式里如果出现了viewModel的alias,那就说么这个element是跟viewModel搭界了,是绑定在一起了,scanner负责把这对"恋人"关系用一个数据结构维护一下,等全部扫描完了一起返回给SegmentFault去听候发落。
图片描述

//Scanner.ts
export class Scanner {

    private prefix = "sf-"; //库的前缀
    private viewModelPool;
    
    constructor(viewModelPool) {
        this.viewModelPool = viewModelPool; //Scanner肯定是为SegmentFault服务的,所以初始化的时候SegmentFault会把之前注册过的viewModel信息传给Scanner,便于它去扫描。
    }
    
    public scanBindDOM():object {} //找出attribute里带sf-,且等号右边表达式里含有viewModel的alias的Element,并返回一个view与viewModel的map

}

接下去,SegmentFault会获得Scanner.scanBindDOM()所返回的view_viewModel Map,来看看这个Map的具体数据结构

//template
{
    "vm_alias":[
        {
            "viewModel":viewModel,
            "element":element,
            "expression":expression,
            "attributeName":attributeName
        }
    ]
}
//如果实际中的DOM Tree是这样的,
<body>
    <p sf-text="userVM.username"></p>
    <input type="text" sf-value="userVM.username">
</body>
//那么,Scanner扫描到的结果应该是
{
    "userVM":[
        {
            "viewModel": userViewModel,
            "element": <p/>,
            "expression": "vm.username",
            "attributeName": "sf-text"
        },
        {
            "viewModel": userViewModel,
            "element": <input>,
            "expression": "vm.username",
            "attributeName": "sf-value"
        }
    ]
}

我的实现中特地定一个了一个BoundItem类来描述 {"viewModel":viewModel,"element":element,"expression":expression,"attributeName":attributeName}

//BoundItem.ts
export class BoundItem {
    public viewModel: object;
    public element: Element;
    public expression: string;
    public attributeName: string;
 
    constructor(viewModel: object, element: Element, expression: string, attributeName: string) {
        this.viewModel = viewModel;
        this.element = element;
        this.expression = expression;
        this.attributeName = attributeName;
    } 
}

拿到view_viewModel map后,SegmentFault会调用Renderer去挨个渲染每一个BoundItem。
图片描述

export class Renderer{
    public render(boundItem:BoundItem) {};
}

好至此,几个主要的类都一一登场了,接下去我们完善下SegmentFault类,让ta和其它几个类联动起来

import {Scanner} from "./Scanner";
import {Watcher} from "./Watcher";
import {Renderer} from "./Renderer";
export let SegmentFault = class SegmentFault {
    private viewModelPool = {};
    private viewViewModelMap = {};
    private renderer = new Renderer();
    public init() {
        let scanner = new Scanner(this.viewModelPool);
        let watcher = new Watcher(this);
        //step 1, 暗中观察各个viewModel
        for (let key in this.viewModelPool) {
            watcher.observe(this.viewModelPool[key],this.viewModelChangedHandler);
        }
        /step 2 3, 扫描DOM Tree并返回Map 
        this.viewViewModelMap = scanner.scanBindDOM();
        //step 4, 渲染DOM
        Object.keys(this.viewViewModelMap).forEach(alias=>{
            this.refresh(alias);
        });   
    };
    public registerViewModel(alias:string, viewModel:object) {
        viewModel["_alias"] = alias;
        window[alias] = this.viewModelPool[alias] = viewModel;
    };
    public refresh(alias:string){
        let boundItems = this.viewViewModelMap[alias];
        boundItems.forEach(boundItem => {
            this.renderer.render(boundItem);
        });
    }
    private viewModelChangedHandler(viewModel,prop) {
        this.refresh(viewModel._alias);
    }
}

好,写到这里,骨架全部构建完成,你有没有兴趣自己花点时间去填充血肉呢?
我希望你能做到

这里贴出其它几个类的具体实现,仅供参考,你一定可以写得比我更好。

也放出github地址,上面有完整工程
https://github.com/momoko8443...

以及在线演示地址
https://momoko8443.github.io/...

//Watcher.ts
export class Watcher {
    private sf;
    constructor(sf) {
        this.sf = sf;
    }
    public observe(viewModel, callback) {
        let host = this.sf;
        for (var key in viewModel) {
            var defaultValue = viewModel[key];
            (function (k, dv) {
                if (k !== "_alias") {
                    Object.defineProperty(viewModel, k, {
                        get: function () {
                            return dv;
                        },
                        set: function (value) {
                            dv = value;
                            console.log("do something after set a new value");
                            callback.call(host, viewModel, k);
                        }
                    });
                }
            })(key, defaultValue);
        }
    }
}
//Scanner.ts
import { BoundItem } from "./BoundItem";
export class Scanner {
    private prefix = "sf-";
    private viewModelPool;

    constructor(viewModelPool) {
        this.viewModelPool = viewModelPool;
    }
    public scanBindDOM() :object{
        let boundMap = {};
        
        let boundElements = this.getAllBoundElements(this.prefix);
        boundElements.forEach(element => {
           for (let i = 0; i < element.attributes.length; i++) {
                let attr = element.attributes[i];
                if (attr.nodeName.search(this.prefix) > -1) {
                    let attributeName = attr.nodeName;
                    let expression = element.getAttribute(attributeName);
                    for (let alias in this.viewModelPool) {
                        if (expression.search(alias + ".") != -1) {
                            let boundItem = new BoundItem(this.viewModelPool[alias], element, expression,attributeName);
                            if (!boundMap[alias]) {
                                boundMap[alias] = [boundItem];
                            } else {
                                boundMap[alias].push(boundItem);
                            }
                        }
                    }
                }
            }
        });  
        return boundMap;
    }

    private fuzzyFind(element:HTMLElement,text:string):HTMLElement {
        if (element && element.attributes) {
            for (let i = 0; i < element.attributes.length; i++) {
                let attr = element.attributes[i];
                if (attr.nodeName.search(text) > -1) {
                    return element;
                }
            }
        }
        return null;
    }
     private getAllBoundElements(prefix): Array<HTMLElement> {
        let elements = [];
        let allChildren = document.querySelectorAll("*");
        for (let i = 0; i < allChildren.length; i++) {
            let child: HTMLElement = allChildren[i] as HTMLElement;
            let matchElement = this.fuzzyFind(child, prefix);
            if (matchElement) {
                elements.push(matchElement);
            }
        }
        return elements;
    }
}
//BoundItem.ts
export class BoundItem {
    public viewModel: object;
    public element: Element;
    public expression: string;
    public attributeName: string;
    private interactiveDomConfig = {
        "INPUT":{
            "text":"input",
            "password":"input",
            "email":"input",
            "url":"input",
            "tel":"input",
            "radio":"change",
            "checkbox":"change",
            "color":"change",
            "date":"change",
            "datetime":"change",
            "datetime-local":"change",
            "month":"change",
            "number":"change",
            "range":"change",
            "search":"change",
            "time":"change",
            "week":"change",
            "button":"N/A",
            "submit":"N/A"
        },
        "SELECT":"change",
        "TEXTAREA":"change"
    }
    constructor(viewModel: object, element: Element, expression: string, attributeName: string) {
        this.viewModel = viewModel;
        this.element = element;
        this.expression = expression;
        this.attributeName = attributeName;
        this.addListener(this.element,this.expression);
    }

    private addListener(element,expression){
        let tagName = element.tagName;
        let eventName = this.interactiveDomConfig[tagName];
        if(!eventName){
            return;
        }
        if(typeof eventName === "object"){
            let type = element.getAttribute("type");
            eventName = eventName[type];
        }
        element.addEventListener(eventName, (e)=> {
            let newValue = (element as HTMLInputElement).value;
            let cmd = expression + "= \"" + newValue + "\"";
            try{
                eval(cmd);
            }catch(e){
                console.error(e);
            }
        });
    }
}
//Renderer.ts
import {BoundItem} from "./BoundItem";
export class Renderer{
    public render(boundItem:BoundItem) {
        var value = this.getValue(boundItem.viewModel, boundItem.expression);
        var attribute = boundItem.attributeName.split('-')[1];

        if (attribute.toLowerCase() === "innertext") {
            attribute = "innerText";
        }
        boundItem.element[attribute] = value;
    };
    private getValue(viewModel, expression) {
        return (function () {
            var alias = viewModel._alias;
            var tempScope = {};
            tempScope[alias] = viewModel;
            try {
                var pattern = new RegExp("\\b" + alias + "\\b", "gm");
                expression = expression.replace(pattern, "tempScope." + alias);
                var result = eval(expression);
                tempScope = null;
                return result;
            } catch (e) {
                throw e;
            }
        })();
    }
}

相关阅读

【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理篇
【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇
【教学向】再加150行代码教你实现一个低配版的web component库(1) —设计篇
【教学向】再加150行代码教你实现一个低配版的web component库(2) —原理篇

查看原文

赞 52 收藏 153 评论 18

Dappur 赞了文章 · 2017-08-23

FPB 2.0:免费的计算机编程类中文书籍 2.0

之前我在 github 上整理了来一份:free-programming-books-zh_CN(免费的计算机编程类中文书籍)

截至目前为止,已经有 90 多人发了 300 多个 Pull Requests 和 200 多个 issues,从最初的几十本书到现在的近 500 本书。

欢迎大家提 PR: https://github.com/justjavac/...

语言无关类

操作系统

智能系统

分布式系统

编译原理

函数式概念

计算机图形学

WEB服务器

版本控制

编辑器

NoSQL

PostgreSQL

MySQL

管理和监控

项目相关

设计模式

Web

大数据

编程艺术

其它

语言相关类

Android

APP

AWK

C/C++

C#

Clojure

CSS/HTML

Dart

Elixir

Erlang

Fortran

Go

Groovy

Haskell

iOS

Java

JavaScript

LaTeX

LISP

Lua

OCaml

Perl

PHP

Prolog

Python

R

Ruby

Rust

Scala

Shell

Swift

读书笔记及其它

测试相关

查看原文

赞 524 收藏 2250 评论 48

Dappur 赞了文章 · 2017-08-16

送给大家一个好看的简历神器

很多人看到里边有好看的东西就习惯性的点进来看看,还一边点一边想 —— 好看的简历我见多了,你这个又能好看到哪里去。我想差不多可以:

哪里

因为最近有在准备简历,就习惯性的找一找有没有现成的简历模板。结果全是付费的,丑的收5块,稍微讲究一点的就差不多要10块钱了,这让一个普通家庭出身的年轻人怎么负担得起。
于是就产生了写一个简历模板的想法,后来就有了这个轻量的简历神器。

vue-resume

地址:https://luosijie.github.io/vue-resume/#/

源码:https://github.com/luosijie/vue-resume

使用方法

简历模板的操作可以说很简单,基本上把握住这么几个原则

  1. 想改哪里点哪里
  2. 右键删除
  3. 加号增加

应用场景

简历作为图片下载后,可以

  1. 直接作为招聘网站的附件简历
  2. 打印出来:实际效果稍微有点模糊,不过在可接受范围

不足之处

写这个的初衷是做一个便捷好用的简历模板,但我觉得距离实现这个目标还有一段距离。目前还有以下缺陷

  1. 不能选择模板样式
  2. 不支持多页简历
  3. 不支持转换为PDF

功能实现

这是一个基于Vue的项目,从最后实现来看,本身没什么技术难点。不过也要考虑实际用户需求和应用场景,然后将这些和 自己的技术水平愿意投入的时间成本 做一个平衡。

Logo设计

Logo设计无关技术,只不过项目无论大小,我感觉有一个Logo才完整。不过关于Logo设计,大多数人是不太懂的, 我自己总结了一条规律:只要你设计的Logo让人一眼没看懂,基本上就成功了一半了。 就好像你看出下面的Logo是字母 “V”“R” 的结合体吗。

Logo

下面是Logo的设计过程

Logo_process

功能分析

这个项目最基本的单元是 图片文字 然后组成各个 list组件 ,包括 About me, Skill, Education, Working Experience等。

图片文字组件

图片和文字只要实现可编辑功能就可以了。

文字

<p contenteditable="true">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris blandit metus in libero rutrum congue aliquam eu libero. Donec tristique est pharetra fringilla sollicitudin. Etiam eu ipsum vitae nulla tincidunt scelerisque semper id arcu. Phasellus quam tellus, laoreet id felis a, dignissim facilisis orci. Mauris feugiat vulputate quam quis tincidunt. In eleifend augue eu tristique bibendum. Donec gravida, eros sed iaculis iaculis, magna est finibus tortor, ultricies accumsan diam lorem non neque.
</p>

图片

edit-image.vue

基本上就是将本地图片上传并转为base64格式

<template>
  <div class="edit-image" :style="{ width: width + 'px', height: height + 'px'}">
    <img :data-original="imgSrc" alt="image" :class="{ circle: isCircle }">
    <input type="file" accept="image/gif,image/jpeg,image/jpg,image/png" @change="changeImage">
  </div>
</template>
<script>
  export default {
    name: 'EditImage',
    props: {
     ...
    },
    data: function () {
      return {
        imgSrc: this.src
      }
    },
    methods: {
      changeImage: function (evt) {
        let _this = this
        let reader = new FileReader()
        let file = evt.target.files[0]
        reader.readAsDataURL(file)
        reader.onload = function (evt) {
          _this.imgSrc = evt.target.result
        }
      }
    }
  }
</script>

List组件

context-list.vue

List-item组件

List组件要实现 About me, Skill, Education, Working Experience 这些组件的通用功能。也就是:

  1. 标题
  2. 内容
  3. 增加条目
<template>
  <div class="context-list" :class="{ 'icon-margin-bottom': icon }">
    <div class="list-heading" :class="{ 'icon-class': icon }">
      <div class="title">
        <EditImage v-if="icon" :data-original="icon" height="36" width="36" class="img"></EditImage>
        <h2 class="title" contenteditable="true">{{title}}</h2>
      </div>
      <button class="add" @click="add" :class="{ 'icon-margin-right': icon }">+</button>
    </div>
    // 实现内容功能
    <ul id="luo">
      <ListItemAbout v-if="title == 'About me'" v-for="item in arry" :key="item.id"></ListItemAbout>
      <ListItemSkill v-if="title == 'Skill'" v-for="item in arry" :key="item.id"></ListItemSkill>
      <ListItemEducation v-if="title == 'Education'" v-for="item in arry" :key="item.id"></ListItemEducation>
      <ListItemExperience v-if="title == 'Working Experience'" v-for="item in arry" :key="item.id"></ListItemExperience>
      <ListItemInfo v-if="icon" v-for="item in arry" :key="item.id"></ListItemInfo>
      <slot name="listItem"></slot>
    </ul>
  </div>
</template>
<script>
  ...
  export default {
    name: 'ContextList',
    components: {
      EditImage,
      ListItemAbout,
      ListItemSkill,
      ListItemEducation,
      ListItemExperience,
      ListItemInfo
    },
    props: {
      // 实现标题功能
      title: {
        type: String,
        default: 'Title'
      },
      icon: {
        type: String,
        default: ''
      }
    },
    data: function () {
      return {
        arry: []
      }
    },
    methods: {
      showAdd: function () {
        this.add = true
      },
      // 实现条目增加功能
      add: function () {
        this.arry.push(1)
      }
    }
  }
</script>
<style>
  ...
</style>

List-item组件

list-item.vue

List-item组件

List-item组件 主要实现每个条目的删除功能, 然后不同类型的条目在 list-Item组件 的基础上定义各自的内容

<template>
  <li class="list-item" @contextmenu.prevent="showControl" v-if="listItem">
    <slot></slot>
    <div v-if="listControl" class="list-control">
      <span @click="deleteControl">delete</span>
      <span @click="cancelControl">cancel</span>
    </div>
  </li>
</template>
<script>
  export default {
    name: 'ListItem',
    data: function () {
      return {
        listControl: false,
        listItem: true
      }
    },
    methods: {
      // 显示控件
      showControl: function () {
        this.listControl = true
      },
      // 取消操作
      cancelControl: function () {
        this.listControl = false
      },
      // 删除控件
      deleteControl: function () {
        this.listItem = false
      }
    }
  }
</script>

先这样了 欢迎star

查看原文

赞 79 收藏 487 评论 32

Dappur 赞了文章 · 2017-07-28

解决input[type=file]打开时慢、卡顿问题

昨天临下班测试给我问我为什么图片上传插件打开文件夹的速度这么慢,让我想办法优化一下
然后我就努力的搞了起来_(:з」∠)_

由于我们内部系统不兼容ie,所以我就没有管ie,在浏览器里面玩了起来

经过测试发现,在mac里面safari、Firefox、Chrome(opera不知道为啥老闪退)都没有卡顿问题

在windows里面,Firefox不卡顿,只有Chrome卡顿。

然而,这个插件是从另一个项目里面借用过来,再加上了限定图片类型的功能而已。
原组件并没有这个卡顿问题,那么问题只可能是在限定图片类型这点上了。

先贴上我的代码

<input
    accpet="image/*"
    style={inputStyle}
    ref={c=> this._imgFile = c}
    onChange={this.handleChange.bind(this)}
    type="file" name="image" disabled={disabled}
/>

于是我决定先去掉accpet试试……
果然就没有了卡顿的问题。
那么本包在试试accpet="image/jpg"果然也不卡卡的了!!
看来问题的所在就是"image/*"

但是写accpet的原意是要想要筛选出所有图片_(:з」∠)_
那么为了实现这个需求,同时提高用户体验,只能采取枚举了

修改后的代码

<input
    style={inputStyle}
    ref={c=> this._imgFile = c}
    onChange={this.handleChange.bind(this)}
    type="file" name="image" disabled={disabled}
    accpet="image/gif,image/png,image/jpeg,image/jpg,image/bmp"
/>

再试试,果然妥妥的了!

但是到底是为什么会这么卡呢??我查了查万能的Stack Overflow→_→

原来是因为Chrome的SafeBrowsing功能会在上传或保存时检查文件,
如果网络连接到google的速度比较快呢,就没有什么问题。
但是如果连接比较慢,或者干脆跪掉了,那SafeBrowsing就会让Chrome挂起一段时间,直到文件检查结束或者超时

使用accept="image/png, image/jpeg, image/gif"就可以解决这个问题,因为这些MIME类型在SafeBrowsing的白名单里面,不需要检查。
但是如果用像是accept="image/*"这样的呢,就不行了,就有可能变得卡卡的。

查看原文

赞 64 收藏 208 评论 16

Dappur 回答了问题 · 2017-06-22

解决在Windows上打开QQ, 会为他分配多大虚拟空间?

是的,分配2G/8T。
注意,分配的是虚拟地址空间,不是虚拟空间;
为什么分配这么大?这是windows给出的承诺,向应用程序承诺,在物理内存足够大的情况下,我能够提供给你这么大的地址空间(注意这里的用词是地址空间,意思是有允许其内存寻址最多到8T这个数字)
当然,虚拟内存空间地址是一回事,实际空间是另一回事。
之前说了,虚拟空间地址只是承诺能够寻址,并非真的分配了空间;当应用程序真的申请了地址空间内能访问的内存时,windows才会开始把被访问的内存页映射到物理内存中。(没错,人家就是打了个空头支票,实际情况是你用一页我分配一页)
所以总结如下:
所谓的8T(0x200000000)有什么用:允许应用程序访问0x0-0x200000000的任意地址,当然访问0x200000001就会报错,因为超出地址空间了(所以虚拟地址空间越大越好,这样编译器布局内存自由度更大)
64位系统为例,物理内存16G,应用程序用了10G,会发生什么:也就是说我允许应用程序用8T内存,但他只用了10G,那没问题,因为可以映射到16G物理内存中;
64位系统为例,物理内存16G,应用程序用了17G,会发生什么:唔,物理内存有点不够用,但是可以把一些内存页存到硬盘上(虚拟内存),所以问题也不大,但是进程切换可能引起内存页在硬盘上的读写,所以电脑会有点卡
64位系统为例,物理内存16G,应用程序申请了8T,会发生什么:没错,应用程序的确被允许使用8T内存,但是此时系统提供不了这么多,空头支票暴露,你的系统奔溃了

关注 3 回答 1

Dappur 赞了回答 · 2017-06-04

解决一个抽奖的效果(如图)?

http://skill-map.stuq.org/参考这个图,应该加上部分js代码可以满足需求。

关注 5 回答 4

认证与成就

  • 获得 67 次点赞
  • 获得 30 枚徽章 获得 1 枚金徽章, 获得 8 枚银徽章, 获得 21 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-01-18
个人主页被 1.8k 人浏览