Git Product home page Git Product logo

issue-blog's People

Contributors

zhangsanshi avatar

Watchers

 avatar  avatar  avatar

issue-blog's Issues

once代码重写

重新把 once 的代码重写了一遍,这次写出来的还算满意,其中测试用例帮了很大的忙,写测试过程中发现了不少错误,为了实现100%覆盖,写了不少东西,还是比较累的,不过写到100%的时候又发现了bug,全覆盖还是蛮有用的。

API 书写规范

API 书写规范

如果没什么必要,请不要破坏 约定

ok,从一个例子说起

请求阶段

{
  url: '/api/person/:id',
  describe: '一个清晰的描述同样很重要,用来干嘛的,和哪些页面相关',
  method: 'post',
  params: {
    type: 'a',
    type2: 'b',
  },
  data: {
    sex: 1,
    age: 12,
    ageStr: '12',
    birthday: 1481441364804,
    child: [],
    attr: {},
    attrStr: '{}'
  },
}

解析

url 是链接的主体部分,:id 代表了这个值是动态获取

method 是请求的方法,一般是 getpostputdelete等等

paramsurlsearch 部分,拼接到 url 后, 即:
/api/person/:id?type=a&type2=b

data 是需要发送的参数,这部分是放在请求 body 中的,这里也有需要注意的点:

  1. attr 的值是一个 JSON 对象,如果你需要的是一个序列化的 JSON 对象,请写成 attrStr 对应的形式, 前端的处理方案是 JSON.stringify({}),对于 Array 同理

特别说明

原则上,后端如果从 url 部分读取了 id, 就不应该从 paramsdata 再次读取id,
而事实上我遇到过,遇到的情况是会从 paramid,读不到 id 去拿列表的数据,读到 id 就去干别的事情。
这说明了啥,说明了一个 API 可以干两件不同的事情,所以可不可以这么推断,所有的 API 都可以通过一个链接解决,只需要给不同的 params,这种设计表面上是有了所谓了灵活性,实际上是不能再糟糕的设计了

其他方面

  1. Content-Type

目前是application/x-www-form-urlencoded;charset=utf-8
默认的 form (enctype)表单的值也是这个,

对于 form 有文件的,需要是这个 multipart/form-data

对于越来越多的前端框架来说,默认的是这个 application/json,但是后端目前不能解析,需要手动配置 header, 改成第一种,同时手动拼接 data

还有一种,参见链接

这里多写一点东西

  • application/x-www-form-urlencoded;charset=utf-8 类型,
    发送到服务端的格式是 x=1&b=2&c[]=1&c[]=2 或者 x=1&b=2&c[0]=1&c[1]=2

  • multipart/form-data

  ------WebKitFormBoundaryMb9nnUlXLKUn49G9
  Content-Disposition: form-data; name="a"

  1
  ------WebKitFormBoundaryMb9nnUlXLKUn49G9
  Content-Disposition: form-data; name="b"

  2
  ------WebKitFormBoundaryMb9nnUlXLKUn49G9--

同时 header 部分是

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryMb9nnUlXLKUn49G9
  1. 下载链接

这个也有值得说的地方

有时候下载链接仅仅是一个 a 标签跳转即可,但需要注意的是错误处理方面

有时候是返回数据,让前端拼接下载,这个时候需要在 data 里给足详情

数据返回阶段

目前已有约定

{
    data: [],   // 根据具体业务需求,返回数据
    code: 200, // 返回状态码
    message: ''// 返回文本提示信息
}

code 码

200	正常请求
403	未登录
401	权限不足
406	业务级错误
409	参数错误

解析

  1. code 这个作用很明显,不理解的人仅仅会得知是一个数字,自己人会知道代表的含义,同时 message 作为补充用来提醒用户

  2. message 返回信息

  3. data 返回数据

特别说明

上面三个即可解决绝大部分的问题,但是data 这里未规定,但是有一些东西需要补充的,这里和前端尤为相关,
来一个例子

{
  time1: '1481441364',
  child: ['a', 'b'],
  childObj: {1: 'a', 2: 'b'},
}

childArraychildObj 是对象, 在 PHP 里,貌似都是 Array,但需要分清楚 childchildObj 的区别,尤其是对于 key 是数字的情况

time1unix 时间戳,这个属性的问题有两个:

  1. unix 时间戳 是按秒算的,首先需要明确告知前端 time1unix 时间戳,在交流这个数据的时候,什么时候用 unix 时间戳,什么时候用其他格式的,同时做好约定,不要这个接口我用这种类型的,下个接口就换了,或者过段时间就忘了。。。

  2. 时间戳是 Number 类型的吧,事实上我遇到过 'false''0' 类型的返回值,处理起来很繁琐,本来可以很简单的解决的问题,导致写了一大堆代码,有些项目采用了 typescript,因为有类型约束,导致问题更严重,虽然不知道在 js 中,这些约束报错不。。。

  3. ageNumber 类型的,倾向于写成 Number 类型,而不是 ageStr,在 js

(!!'0') === true
(!!0) === false

其他方面

  1. 希望后端有个通用的处理流程,我感觉很多数据还是手工转的,一不注意,写出的代码就不符合约定了,比如出来个 'false' 之类的。

  2. 接口尽量定义的规范一些。。。虽然有时间不规范很省事啊

  3. 希望能统一交流语言吧,很多人还描述不清自己想要什么数据,写又写不出来。。。不如现在就用此做个规范吧。

讨论

  1. 前端接手 API 的时候,其实已经比较迟了,而且做页面会遇到各种问题,后端可不可先写文档,然后再编程,前端遇到问题,可以及时更改思路?

  2. 接上一个,后端的文档是 JSON 格式,后面可以实现一个 mock服务器,来生成数据,方便前端测试?

  3. API 文档的保存问题,聊天记录里?PM 里?wiki里?还是专门写一个服务管理?

  4. API 文档能否生成的问题,毕竟手写还是有点累的。

梳理时间戳

主要由一个问题引发的

new Date(0);
new Date('0');

执行一下会发现两个并不一样,数字的时间戳,执行出来是正常的结果,但是字符串的并不是想要的结果,很显然,对于字符串和数字,js有不同的处理办法,实际上字符串的先会去date.parse。后端传递的时间戳看起来并不是那么规范,因为按照时间戳的定义就应该是number类型的,而不是string。
同样的还有一个问题,后端对于默认不填的时间给0,后端数据库统一存储时间戳,在这里就更有问题了,0是歧义的,如果一个人选择了1970-01-01,那他原本就是0,而不是默认是0,目前后端已经改成了默认null...
再一点,后端传过来的时间戳都是unix时间戳,每次处理的时候都先*1000
其实前两条都应该是约定好的,现在前端先处理掉

苹果手机测试方案

手里没有苹果手机,公司配有苹果电脑,IOS 的 web 下有一些坑是不常遇到的,有时候会出一些意料不到的情况,于是就思考怎么测试,不能时时去借别人的手机吧

工具

苹果系统

方案

打开 xcode,待其完全打开,右击 dock 上的图标,点击 open develop tool,找到模拟器这一项,打开。
然后打开里面的浏览器,放入测试链接即可。
(注:复制链接要通过 command + c 复制,才能被粘贴进模拟器里)
(注: localhost 链接也完全没问题的,不会产生问题,走的是本机的配置)
(注:webpack 起的服务,调试起来巨爽,有修改就直接刷新了)

fetch easy

util.js

const toString = Object.prototype.toString;

export function isString(str) {
    if (str == null) {
        return false;
    }
    return toString.call(str) === '[object String]';
}

export function isObject(obj) {
    if (obj == null) {
        return false;
    }
    return toString.call(obj) === '[object Object]';
}

import qs from 'qs';
import { isString, isObject } from './util.js';

const hasOwnProperty = Object.prototype.hasOwnProperty;

// 检查是否需要格式化
function isFormatBody(body) {
    const isFormData = body instanceof FormData;
    const isBlob = body instanceof Blob;
    const isURLSearchParams = body instanceof URLSearchParams;
    return isFormData || isBlob || isURLSearchParams || isString(body);
}
// 检查 request 的 content type
function isDefault(contentType = '') {
    return contentType.indexOf('application/x-www-form-urlencoded') !== -1;
}
function isFormData(contentType = '') {
    return contentType.indexOf('multipart/form-data') !== -1;
}
// 生成 form data
function getFormData(obj) {
    const fd = new FormData();
    for (let key in obj) {
        if (hasOwnProperty.call(obj, key)) {
            fd.append(key, obj[key]);
        }
    }
    return fd;
}
// 检查返回的状态
function checkStatus(response) {
    if (response.status >= 200 && response.status < 300) {
        return response;
    } else {
        const error = new Error(response.statusText);
        error.response = response;
        throw error;
    }
}
// 默认把返回值 json 化
function parseJSON(response) {
    const respContentType = response.headers.get('Content-Type');
    if (respContentType.indexOf('/json') !== -1) {
        return response.json();
    }
    const error = new Error('返回值出错');
    error.response = response;
    throw error;
}
function FetchEasy(url, data= {}, ops = {}) {
    const defaultOps = {
        mode: 'same-origin',
        credentials: 'same-origin',
    };
    const init = Object.assign({}, defaultOps, ops);
    // 替换 url 中的变量
    if (url.indexOf(':') !== -1) {
        url = url.replace(/:(.*?)(?=\/|\.|$)/g, (a, b) => {
            return encodeURIComponent(data[b] || '');
        });
    }
    const method = init.method = init.method || 'get';
    const body = init.body;
    // 设置 request header
    let headers = init.headers = Object.assign({
        'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }, init.headers);
    // 设置 request body
    if (method !== 'get' && method !== 'head') {
        if (body && isObject(body) && !isFormatBody(body)) {
            const contentType = headers['Content-type'];
            if (isDefault(contentType)) {
                init.body = qs.stringify(body);
            } else if (isFormData(contentType)) {
                init.body = getFormData(body);
            } else {
                init.body = JSON.stringify(body);
            }
        }
    } else if (body) {
        delete init.body;
        const query = qs.stringify(body);
        url += url.indexOf('?') === -1 ? '?' + query : '&' + query;
    }

    return fetch(url, init).then(checkStatus).then(parseJSON);
}
export default FetchEasy;

模板引擎

简单实现了一个,目前存在的一个很大的问题,无法对变量进行添加调用者操作

function parse(code) {
  // todo
    return code;
}
var helper = {
    escape: function (content) {
        content = content || '';
        var escapeMap = {
            "<": "&#60;",
            ">": "&#62;",
            '"': "&#34;",
            "'": "&#39;"
        };
        return content.toString().replace(/[<>"']/g, function (s) {
            return escapeMap[s];
        });
    }
};
function compile(template) {
    var str = 'var s = "";';
    var splitTmpl = template.split(/<%(.*?)%>/);
    splitTmpl.forEach((item, index) => {
        if (index % 2) {
            if (item[0] === '=') {
                if (item[1] === '#') {
                    str += `s += (${parse(item.substring(2))});`;
                } else {
                    str += `s += h.escape(${parse(item.substring(1))});`;
                }
            } else if (/[}{]{1}\s*$/.test(item)) {
               str += item; 
            } else {
               str += `(${item});`;
           }
        } else {
            str += `s += "${item}";`.replace(/[\r\n]/g, '\\n');
        }
    });
    // return str;
    return new Function('o', 'h', `${str} return s;`);
}

function render(template, data) {
    var compileFn = compile(template);
    console.log(compileFn(data, helper));
}

测试代码,由于未完成,涉及到变量必须写成o.a的形式,其中o是定死的

console.log(render(`<div>
<%=o.a+1%></div>
<%

a%>
<%=1+2%>
<%=#'<'%>
<%='<'%>
<div><%1+1%></div>
`, {
    a: 1
}));

Angular2.x跳到Angular4.x说几句

这是一个老新闻,也是一个老话题,一般人看到标题的文字,都会认为Angular开始丧心病狂了,我也是这么认为的,没有去考虑原由,后面看到一篇官方博客,才知道为什么会从2-4,不过在国内貌似没有看到有人提原因。。。
好吧,原因是 Angular route 的版本现在是3.x,其他Angular相关都是2.x,为了保证整个的版本一致,只能从2-4,就是这么简单的原因把3跳过了,合情合理。

一个前端去学嵌入式

简单的对话过程中,感受了不少东西。
react是他教了全公司每个人三四遍,全都教会了,webpack新手也学会了,他感觉到追逐这些技术没什么用处,公司的人都希望靠着他去教,还在考虑要不要切成ng2。
如果诚如他所言,那这样的地方呆起来确实很累,分分钟跑路的节奏。
有一个点是技术推动者,可能对于这样的公司,仅仅技术推动还不够,还需要培养技术氛围,这项活本身就是痛苦的,很容易让人绝望的。
另一个点,对于框架和技术的追逐,到底有什么大用呢?解决业务需求是一方面,他也提到了seajs/requirejs的学习很快变得没用了,被 import from 取代了,那之前学的还有什么用。确实这样来说很容易让人感到迷茫,新的东西来的这么快,拼命的学,也不可能每个都学的好。
不过,我对这个问题有一些不同的看法。一些工具的兴起,有一定的历史原因,你从历史的角度去看,很多事情就有了合理的解释,不可能刚会走就要造飞机吧。现在这个时代是百花齐放,很开心看到前端的发展,火花都是碰撞出来的,希望能产生更多更好的技术吧

2进制8进制16进制转换另一种思路

可能一般的方法是转成10进制,再进行转换
今天想到一个新的办法,
举一个例子

var map = {
    0: 0,
    1: 1,
    10: 2,
    11: 3,
    100: 4,
    101: 5,
    110: 6,
    111: 7
};
function $2to8 (num) {
    var result = '';
    var tmp = '';
    for (var i = num.length; i >= 0; i = i - 3) {
        tmp = num.substring(i - 3 , i);
        if (tmp) {
            result = map[tmp - 0] + result;
        }

    }
    return result;
}

想法很简单,2进制转8进制,把2进制字符串分成3个一组,换算成8进制,同理16进制
8进制转2进制,则把每一位拆成3位,不足补0即可
随手测试了下,速度还是可以看的:)

js函数里面的参数根据类型再分配的问题

js函数里面的参数根据类型再分配的问题,
有人会写一大堆的if else 去判断类型,我感觉这实在没有必要,因为实现起来会很混乱,大量的if else 的类型判断,看起来很头疼,而且很容易写成源码不可读的代码。当然jQuery里就另说,jQuery实现还不错的。
我的建议是不如放在options里,但是都放在options里,也不好,因为有些字段确实需要独立出来,用来明确这个参数的重要性,这个就需要具体问题进行拿捏。
喜欢jQuery里面通过 arguments.length 去判断的做法

PHP1

说明

练习配置后端环境

环境

os: vagrant + vbox 搭建的 ubuntu 14
shell: zsh

设置 apt-get 源

默认情况下,替换 hostnamemirrors.aliyun.com 即可

sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak #备份
sudo nano /etc/apt/sources.list #修改
sudo apt-get update #改完需要更新列表一次

apt-get

软件更新

apt-get update && apt-get upgrade

数据库

sudo apt-get install mysql-server mysql-client

管理员账号密码 root root

自建用户: someone 123456

create user 'someone'@'%' IDENTIFIED BY '123456';
GRANT ALL ON *.* TO 'someone'@'%';

说明: @'%'不限制IP,测试就不限制IP了

---遇到问题,需要在虚拟机里对mysql进行配置,开启远程访问

sudo vim /etc/mysql/my.cnf
//找到如下内容,并注释
bind-address = 127.0.0.1

退出,重启

sudo restart mysql

安装PHP7.0

添加仓库地址

sudo add-apt-repository ppa:ondrej/php

安装

sudo apt-get install php7.0-fpm php7.0-mysql php7.0-mcrypt php7.0-zip php7.0-xml

说明:

  • php7.0-fpm : php 解释器 以及 和服务器交互
  • php7.0-mysql : 连接 mysql(php7.0-mysqli被替代了)
  • php7.0-mcrypt : 加解密工具
  • php7.0-xml : composer 生成项目的时候依赖

PHP 概念说明

大致懂得了 php7.0-fpm 是啥,php7.0-mysql 是啥, PDO, PHP 扩展是啥
https://segmentfault.com/q/1010000004113822
https://segmentfault.com/q/1010000000256516
http://holyrain1314.blog.163.com/blog/static/10011413520122414811921/
http://php-fpm.org/

安装 composer

  1. 按网页说明下载 https://getcomposer.org/download/
  2. sudo mv composer.phar /usr/local/bin/composer
  3. 测试 composer

安装 Laravel

composer global require "laravel/installer"

确保 ~/.composer/vendor/bin 被加入到PATH
使用zsh的 请加到 ~/.zshrc 下添加完成后,执行 source ~/.zshrc
使用bash的类似

执行上面的可能会有错误,则需要执行

sudo chown -R $USER $HOME/.composer

下载很慢,用国内的镜像,会对全局包指定镜像,方法如下

cd ~/.composer

查看 composer.json 文件

添加镜像,镜像随意更换

composer config repo.packagist composer https://packagist.phpcomposer.com

再次查看 composer.json 文件

重新安装

composer global require "laravel/installer"

生成项目

composer create-project laravel/laravel blog

安装 nginx

apt-get install nginx

配合 php7.0-fpm 遇到的问题
如果在nginx配置里 fastcgi_pass 127.0.0.1:9000; 这样配置,需要在 /etc/php/7.0/fpm/pool.d/www.conf 里替换
listen = /run/php/php7.0-fpm.socklisten= 127.0.0.1:9000, 如果nginx配置 unix:/run/php/php7.0-fpm.sock则相反,
注意listen 的值 地址不要写错, unix:/run/php/php7.0-fpm.sockphp7.0-fpm的默认值

配置项目

更改数据库 mysql 配置 config/database.php

运行

打开浏览器输入http://blog.me/

报错 这时候在 虚拟机外部 执行

sudo chmod -R 777 ~/Documents/work/php/blog-base

如果报错

RuntimeException in EncryptionServiceProvider.php line 45:
No supported encrypter found. The cipher and / or key length are invalid.

则执行

php artisan key:generate

over!!!

拖拽元素到页面任意位置

拖拽元素到页面任意位置

由于项目不需要兼容问题,拖拽元素到任意位置就打算自己用 drag 写了,不过中间还是有遇到一些坑的,在写 demo 的时候,结果又遇到了坑,稍后再说

demo的第一个坑就是在源码模式下根本无法测试,在drag方法最后一次触发的时候,鼠标位置肯定会获得错误的结果,换了几个在线编辑器后,一度怀疑人生,所幸找到一个带预览模式的,猜测和 iframe 有关,没时间研究,这个问题暂且记下

版本1

源码
效果

在这个版本下,有一个很明显的问题,就是总是拖拽不对地方,没有完全按照拖拽的位置进行定位,而且看似没有规律。
后来终于找到了原因(直接移动到左上角,后面处理方式相同),没有记录 初始的时候的鼠标位置,更改代码后,有了版本2

版本2

源码
效果

在这个版本下,问题解决了一部分,这次拖拽位置,总是差 固定的距离,然后注意比对版本1和版本2的 css,会发现删掉了

body {
  margin: 0;
}

没错,固定的部分就是 margin的值(这个问题还是写 demo 的时候发现的,没有重置样式,发现正确的代码不正确了),同时思考 padding 会不会造成这个问题,找到问题,修改代码就有了版本3

版本3

源码
效果

css里,样式进行了改变

/*body {
  margin: 0;
}*/
#main {
  padding: 10px;
}

引入了padding,是为了验证版本2的猜想,然后再次测试,一切正常

总结

元素的任意拖拽的定位,和 鼠标在元素上的位置有关,同时受到父元素的 offsetLeftoffsetTop 的影响

new Date(0) 和 new Date('0') 问题

好早之前遇到的问题,今天又想起来,并且有了验证的办法
new Date(0)
这个无疑问返回的是1970年那一个
new Date('0')
一开始是在chrome下试验的,返回的是
Sat Jan 01 2000 00:00:00 GMT+0800 (CST)
百思不解
今天有了新的验证

  • chrome 下
new Date('1')
Mon Jan 01 2001 00:00:00 GMT+0800 (CST)
new Date('12')
Sat Dec 01 2001 00:00:00 GMT+0800 (CST)
new Date('13')
Invalid Date
new Date('111')
Thu Jan 01  111 00:00:00 GMT+0800 (CST)
new Date('1111')
Sun Jan 01 1111 08:00:00 GMT+0800 (CST)
new Date('99')
Fri Jan 01 1999 00:00:00 GMT+0800 (CST)
new Date('100')
Fri Jan 01  100 00:00:00 GMT+0800 (CST)
new Date('32')
Thu Jan 01 2032 00:00:00 GMT+0800 (CST)
new Date('31')
Invalid Date
new Date('999')
Tue Jan 01  999 00:00:00 GMT+0800 (CST)
new Date('9999')
Fri Jan 01 9999 08:00:00 GMT+0800 (CST)
new Date('1000')
Wed Jan 01 1000 08:00:00 GMT+0800 (CST)

可见对于chrome对于字符串计算的规则有点不同.

  • 然后再看safari的
> new Date('0')
< Sat Jan 01 0000 08:00:00 GMT+0800 (CST)
> new Date('1')
< Mon Jan 01 0001 08:00:00 GMT+0800 (CST)
> new Date('11')
< Sat Jan 01 0011 08:00:00 GMT+0800 (CST)
> new Date('111')
< Thu Jan 01 0111 08:00:00 GMT+0800 (CST)
> new Date('1111')
< Sun Jan 01 1111 08:00:00 GMT+0800 (CST)
> new Date('99')
< Thu Jan 01 0099 08:00:00 GMT+0800 (CST)
> new Date('13')
< Tue Jan 01 0013 08:00:00 GMT+0800 (CST)

看起来严格按照 new Date(yyyy) 实现的

  • firefox的
new Date('0')
Invalid Date
new Date('1')
Invalid Date
new Date('11')
Invalid Date
new Date('111')
Invalid Date
new Date('1000')
Date 1000-01-01T00:00:00.000Z
new Date('999')
Invalid Date
new Date('10000')
Invalid Date
new Date('9999')
Date 9999-01-01T00:00:00.000Z
new Date('0229')
Date 0229-01-01T00:00:00.000Z

看起来只接受四位数

结论

老老实实的用 DateAPI

HTTP2

HTTP2

本文希望探讨一下 HTTP2 做出的一些修改以及新增功能以及原因,很多东西翻译而来的,原则上建议是阅读规范本身。

基本概念

HTTP

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是互联网上应用最为广泛的一种网络协议。
HTTP的URL由 "http://" 起始且默认使用端口80

HTTPS

超文本传输安全协议(英语:Hypertext Transfer Protocol Secure,缩写:HTTPS,也被称为HTTP over TLS,HTTP over SSL 或 HTTP Secure)
URL由“https://”起始且默认使用端口443

版本

1. HTTP 0.9

已过时。只接受GET一种请求方法,没有在通讯中指定版本号,且不支持请求头。由于该版本不支持POST方法,因此客户端无法向服务器传递太多信息。

2. HTTP/1.0

这是第一个在通讯中指定版本号的HTTP协议版本,至今仍被广泛采用,特别是在代理服务器中。

3. HTTP/1.1

持久连接被默认采用,并能很好地配合代理服务器工作。还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。

4. HTTP/2

HTTP/2是HTTP协议自1999年HTTP 1.1发布后的首个更新,主要基于SPDY协议。它由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis(httpbis)工作小组进行开发。该组织于2014年12月将 HTTP/2 标准提议递交至IESG进行讨论,于2015年2月17日被批准。HTTP/2 标准于2015年5月以 RFC 7540 正式发表。

什么是 HTTP/2

HTTP/2 没有重写协议,HTTP 方法、状态码、语义等,可以像使用 HTTP/1.x 一样使用它。协议本身关注的重点在性能,终端用户的等待时间,网络和服务器资源的使用。致力于数据从客户端到服务器传输使用一个连接。
协议基于 SPDY 开发的,同时在开发的过程中吸收接纳了社区的很多建议和改善。

规范

HTTP/2 包含了下面两个规范:

  • Hypertext Transfer Protocol version 2 - RFC7540
  • HPACK - Header Compression for HTTP/2 - RFC7541

正文

首先来看一点是,HTTP/2 基于 SPDY 协议开发的,所以我们需要先了解一下 SPDY。

SPDY

SPDY 是一个在万维网用于低延迟传输内容的协议。SPDY 有两个协议层。较低层是通用成帧层,其可以在用于多个并发流的多路复用,优先化和压缩数据通信的可靠传输(可能的TCP)之上使用。该协议的上层提供了一个类似 HTTP 的语义,与现有的 HTTP 应用服务器兼容。

HTTP 实现的瓶颈之一是 HTTP 依赖多个连接用于并发。这会导致一些问题,包括建立连接产生的额外的往返,慢启动延迟,客户端的连接限制等。这就是为什么需要减少连接到单个服务器。HTTP pipelining 有一些帮助的,但只实现了部分复用。此外,pipelining 已被证明在现有浏览器由于中间人干扰的原因而不可部署(链接,参考4也有答案)。

SPDY 添加了一个帧层用于多路复用,在一个单独的 TCP 连接(或者其他可靠的传输流)里合并所有的流。这个帧层对于类似 HTTP 请求响应流可以说是最优的,因为它使得 web应用 作者可以不做任何事情或者只做很小的改动就可以让运行在 HTTP 上面的应用,同时运行在 SPDY 上面。

SPDY 提供了对 HTTP 的四个改进:

  1. 多路复用请求:单个 SPDY 连接同时发出 N 个请求,不会对请求的数量做限制
  2. 优先请求:客户端可以优先请求某些资源。这消除了高优先级的非关键资源被 pending 的情况下会造成网络拥堵的问题
  3. 压缩头: 客户端在 HTTP headers 里发送大量冗余数据。因为单个网页可能会请求 50 或 100 个子请求,所以这个数据就意义重大了
  4. 服务端推送流: 服务端推送允许推送内容到客户端,而不需要客户端发出一个请求,当然客户端可以拒绝,比如说已经缓存该资源

HTTP2 基于 SPDY 做的改进

HTTP2 使用了 HPACK 规范,它是一种高效的表示 HTTP 报头字段的压缩格式。
其他未知,具体参考协议。。。

HPACK

在HTTP / 1.1(参见RFC7230)中,头字段未压缩。随着网页发展到需要几十到几百个请求,这些请求中的冗余报头字段不必要地消耗带宽,等待时间也越来越长。
SPDY 最初通过使用 DEFLATE 格式压缩标题字段来解决这种冗余,这在有效地表示冗余报头字段方面非常有效。然而,该方法暴露了CRIME(压缩率使信息很容易泄露)攻击(参见CRIME)所示的安全风险。
该规范定义了 HPACK,它是一种新的压缩器,它消除了冗余报头字段,限制了已知安全攻击的脆弱性,并且具有在受限环境中使用的有限内存要求。当然其还有一些潜在安全问题。
HPACK 格式是有意简单和不灵活的。这两个特性降低了由于实现错误导致的互操作性或安全性问题的风险。没有定义可扩展性机制;只有通过定义完全替换才能更改格式。
这个规范就是上面 HTTP2 包含的规范之一。

思考

  1. HTTP2 利用同一个 TCP 的情况下,会节省哪些时间
  2. 目前优化手段,将资源分布在多个服务器、合并css、合并js、背景图等,在启用 HTTP2 的情况下有什么影响
  3. HTTP1.1 队首阻塞,在启用 HTTP2 的情况下,情况会有所缓解吗
  4. 客户端严格设置请求优先级,会导致什么问题,比如说在请求高优先级慢请求的情况下
  5. 对于服务端可以主动推送资源,有没有什么好的玩法

参考

  1. https://http2.github.io
  2. https://tools.ietf.org/html/draft-mbelshe-httpbis-spdy-00
  3. https://en.wikipedia.org/wiki/HTTP_pipelining
  4. http://dev.chromium.org/spdy/spdy-whitepaper
  5. http://httpwg.org/specs/rfc7541.html
  6. RFC7540
  7. RFC7541
  8. https://datatracker.ietf.org/wg/httpbis/charter/
  9. https://imququ.com/post/protocol-negotiation-in-http2.html

结后语

写这个花了好久,中间有事又给放弃了一段时间,想着自己都快把读的内容都给忘了,最终还是润润色,就拿出来了,其实看再多的文章,不如对着规范读,再实践,规范我只是阅读部分,想写的更多也写不出来了,实践也没来得及,希望后续能补。

git分支开发

git分支开发的两种方式

一点不成熟的想法,感觉最近流程上出了点问题

1.方法一

分支结构:

-- master
-- release1
    -- feature1
    -- feature2
-- dev

步骤:

a. feature 均从 release1 检出分支,任意合并到 dev,feature 产生的 bug 均归属于该 feature,保证 feature 和 dev 之间形成闭环,不允许在 dev 上修改属于 feature 的功能以及 bug

b. 如果 master 出 bug,检出分支,修复后合并到 dev、release1、master 分支,并确保产生 release2,后续 feature 从 release2 产生

c. UAT 时,若 feature1、feature2 确认开发完成并发布,从 release2 开出新分支,合并相应的 feature1、feature2 到 release3,即使此时 dev 可能有 feature1、feature2、feature3 ...

d. UAT 出现 bug,确认出现问题的 feature,修复该 feature,合并至 dev、release3

问题:

a. 在 feature1 和 feature2 出现功能冲突的时候,同时发到 dev,会出现功能覆盖的问题

b. dev 代码很杂乱,产生问题需要进行排查

2.方法二

分支结构:

-- master
-- release1
-- dev
    -- feature1
    -- feature2

步骤:

a. feature 均从 dev 检出分支获得

b. 需要测试 feature1/2,则把分支切成 feature1/2,待测试完毕后,合并到 dev,同时 bug 均属于该 feature

c. master 出现 bug,检出新分支,修复后合并到 dev master,可能需要合并到各个 feature

d. UAT时,检出 dev 到 release2 即可

问题:

a. 在 feature1、feature2 ... 合并到 dev 后,又出现 feature1 的功能不发布的情况,需要较大改动 dev 分支,但是可以通过检出另一个干净的分支解决

b. 无法及时测试各个 feature 代码之间产生的影响,如果需要动 dev 的代码,需要各个分支同时合并 dev

less helper

LESS-HELPER

一些有帮助的 less 方法

@r

这是一个变量,如果图片是二倍图,可以将这个值设为2。同时图片的宽高,背景图的大小均受影响。

bg(@imgName)

.bg {
  .bg('./images/demo.png');
}
=>
.bg {
  width: 22px;
  height: 22px;
  background-image: url('images/demo.png');
  background-repeat: no-repeat;
  background-size: 22px 22px;
}

.icon(@ICON, @imgName)

.icon(demo, './images/demo.png');
=>
.icon-demo {
  width: 22px;
  height: 22px;
  background-image: url('images/demo.png');
  background-repeat: no-repeat;
  background-size: 22px 22px;
}

.iconActive(@ICON, @imgName, @imgNameActive)

.iconActive(demo, './images/demo.png', './images/demo-active.png');
=>
.icon-demo {
  width: 22px;
  height: 22px;
  background-image: url('images/demo.png');
  background-repeat: no-repeat;
  background-size: 22px 22px;
}
.active .icon-demo {
  width: 22px;
  height: 22px;
  background-image: url('images/demo-active.png');
  background-repeat: no-repeat;
  background-size: 22px 22px;
}

.size(@w,@h)

.xx {
  .size(10px, 10px);
}
=>
.xx {
  width: 10px;
  height: 10px;
}

.size(@long)

.size(@long) => .size(@long, @long)

.text-overflow

.text-ellipsis {
  .text-ellipsis;
}
=>
.text-ellipsis {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

.clearfix

.clearfix {
  .clearfix;
}
=>
.clearfix:before,
.clearfix:after {
  content: " ";
  display: table;
}
.clearfix:after {
  clear: both;
}

webpack 多 spa 页面打包的问题

假设有 A、B、C、D 四个页面,A、B页面是按照路由进行按需加载,C、D无按需加载,同时A、B、C、D都引用了一些相同的代码。现在需要让A、B、C页面共享其公有代码(打包成 core.js),D页面独立打包。

脚本实现redmine提交有感

最近用node实现了redmine的命令行提交issue,最后都实现了,但是redmine的key是每个人都不一样的,需要手动存储这个key,然后还需要存储域名,总不能把域名写死吧,好吧,到最后我没实现去储存域名和key的操作,时间不够,仅仅在用法里写了

将项目下载下来,执行npm link,然后修改config.json文件

但是通过对这个的实现,也理解了为什么git要先全局配置,以前仅仅知道要配置,其实有点抵制,而且没有意识使用这种工具,需要先配置自己的账号,自己实现了一遍,才理解了为什么这么做。也有可能是命令行没有使用界面的意识吧。

webpak 打包 svn提交遇到了文件 line ending 不一致的问题

之前打包没问题的,放到新文件夹出了问题,当然在此期间配置了svn的 svn:eol-style 值
后面发现是打包的一个库出了问题,这个库其他文件都是 lf 的, 唯独最主要的文件是 cr lf,
不知道作者为什么要做这样的考虑,顺便翻了下下载的其他库,文件都是 lf 的,也是醉了,
更改后,顺利提交

API 工程师

API 工程师

问题

感觉现在所有的东西都是知道跟不知道的区别,弄东西了就是框架套套,文档翻翻,可是,有什么是可以跟别人不一样的?
https://ke.qq.com/course/63971#term_id=100087883
http://quanzhan.ucai.cn/
http://oldboy.blog.51cto.com/2561410/1749122
http://web.wuxianedu.com/web/
只学4个月,就能会这么多了,出来比我屌多了

我的理解

首先很羡慕啊

  1. 知道和不知道的区别

    建立知道和不知道的这种想法的本身,就需要时间经验积累,就需要一个学习的过程,就需要对其有一个系统的认知。
    不是天生就会懂得前端,你认为你目前认知的东西可以简单区分知道和不知道的,
    本身代表了有一定的积累了,对你目前所处的环境产生的一个系统的认知。
    之所以产生这种想法,可能遇到了瓶颈(1),可能是眼高手低(2)。

  2. 弄东西就是框架套套,文档翻翻

    你要相信很多人不愿意去学框架,比如框架学习曲线陡峭;
    愿意学框架的人还有不去看 API 文档,就喜欢问人;
    愿意看 API 文档的,他只会照葫芦画瓢,活干完就行。
    要相信框架很多都是易学难精,框架蕴含了作者的**理念。
    除了套套框架,我们能做的还有很多,比如:
    项目文件组织结构,
    优化用户体验,
    优化性能,
    弄懂实现原理,才能解决深入使用框架所产生的问题,
    框架设定的概念名词,
    等等

  3. 第三个问题,大概就是那些链接了

    话说真的信 4个月学成归来?我是抱着怀疑的态度,不认为绝大多数人能比一步步走来的厉害。
    一次吃多了也会撑啊。
    看链接有些是 node,简单点 HTTP 、HTTPS 协议相关,文件组织方式,代码书写方式,要学的太多了啊。
    有些是全栈,这个怎么说呢,是骗子吧,是骗子对吧,是骗子。
    下次找链接找点高质量的,就像上次关注了一个微信号,一篇文章才100多点击,特么的他都敢开学习群,每人收费1024,蜜汁自信。
    再者,万一真的能4个月量产合格的学员,那大家都可以去看啊,干嘛要感叹呢?临渊羡鱼不如退而结网。

  4. 额外的一点,我的标题,有工程师三个字

    技术是为了解决问题的,但问题的解决,又不完全是技术,作为工程师,很多时候,需要综合考虑,
    给出的是合理的解决方案,而不简简单单的拼凑技术。

释疑

  1. 瓶颈
    感觉自己无从提高,接下来最好的做法是离开舒适区,让自己重新处于学习的阶段。
  2. 眼高手低
    很多时候,问题并没有你想象的那么简单,深入到了细节,就会变得复杂而棘手。

一个有趣的问题,关于 `Carriage return` `\n` `\r`

一个有趣的问题,关于 Carriage return \n \r

起因是通过 js 组装下载 csv 文件时,部分文件下载后,有些字段的换行出现了问题,总是会错开两行,一开始简单处理 .replace(/[\r\n]/, ''),试了下发现不行,于是在控制台打印,发现出问题的字符是
,接着.replace(/[↵\r\n]/, ''),再次试验,问题解决了。当然换行符还是损失了,不过这个问题是别人问我的,要讨论的问题也不在这里。

为什么 会造成换行

先看一个描述,看不出什么,

Unicode Decimal Code &#8629;
↵
Symbol Name:	Downwards Arrow With Corner Leftwards
Html Entity:	&crarr;
Hex Code:	&#x21b5;
Decimal Code:	&#8629;
Unicode Group:	Arrows

接着搜索 &crarr;,发现Carriage return这个关键词,然后再去搜索,刚好 wiki 有相关解释https://en.wikipedia.org/wiki/Carriage_return
阅读该 wiki,疑问就基本解开了:)

git使用总结(一)

问题来自于一个刚毕业入职的php新手,整个过程如下:

他在自己的feature/A开发了功能,然后dev分支,经历了N次从线上拉取代码的过程,期间也有向dev分支推代码的时候。但在最后一次中,出现了冲突,但是他并没有真正合并,直接选择一个版本提交了。
经历一段时间的排查,给他确定了方案,用 git cheery-pick,合并了半天,终于好了(话说好逗,白天写的代码,晚上就不知道谁是谁了)。但是在dev测试的时候,又出现问题了,有个结果始终不对,但在feature/A是对的。后来进入调试才发现,feature/A引用的代码被别人修改了,但是这部分修改没有合并到feature/A,导致同样的代码在dev是错的,feature/A是对的。

上面问题说完了,这里就要引入正题了,如何处理feature/A引用的代码B在dev分支被人修改了,如果feature/A分支是一个老分支的话,是很有必要每隔一段时间从dev分支拉取一次代码,不然长久不获取代码,肯定会导致自己写一些在老代码上面的代码,导致大量修改,但是如果不测试,不仔细看提交记录,也不会发现这个问题,谁会想有人动代码B,这时候又必须引入代码测试。好吧,还没试过代码测试。

spa 和 seo

解决方案不是我提供的,是听来的,在这里做个记录。

spa页面的seo的处理比较麻烦,一直的想法都是服务端直出,这就要涉及到node做服务器,去搭建服务版本,当然这里可以做缓存相关的内容。这里的问题就是需要折腾nodejs在服务器端的使用以及直出方案


听来的方案

  1. 当网站碰到爬虫的时候,返回网站的seo版本。
  2. 可以事先用 phantomjs 去爬取页面,做成静态页。这里有一个优化的点,当发生了访问,而缓存没有,才去爬去页面。

方案1,后端需要处理的部分较多,由于不是给用户看到的,前端需要处理的内容不多
方案2,需要简单写一个爬虫,需要做好缓存过期以及静态化。

一个项目从0开始到目前为止的总结

  1. 项目最初是去年建立的,是个后台项目,用的是 angular grunt lessAdminLTE(实际上我根本不知道为啥要用这玩意,也问了,也没说出个啥,后来发现另一个项目用,才明白)
  2. 在项目初期,就有两个目标,一个是链接后面带参数,这样便于用户分享合作,一个是一直会以为有实习生进入,初期写代码,写的比较繁琐,一步步的都看得出来,后来一直没实习生,略坑
  3. 项目初期比较浪,主要也不紧张,经常是手上N个项目加它一起开发,很多时候,都是晚上抽出时间开发的,到了前几个月,主要工作才换成开发这个项目
  4. 一开始权限设置的是,增删改查都有权限,字段也有权限,好吧一个个硬编码ng-if写入的,这里取巧了auth.check('10'||'2'),只需要把权限类似这样传进去即可,内部实现用到了eval。这样产生新的问题了,硬编码不好,而且难记忆,于是后来变成了 auth.check(person.edit)
  5. 自己一个人开发一直用的是 svn,前几个月前了个实习生,没办法切到 git, 开始疯狂用 git,写了个hook自动推svn代码。平台不同,git 和 svn 换行符设置切换了下。后端也用git,我比较活跃,讨论过git的使用,git使用还在探索,sourcetree好用的工具
  6. 后面写增删改查累了,就开始用artTemplate做模板,用配置生成代码,略爽
  7. 因为后台项目,其实样式不太在意,初期写好就没管过了
  8. 后面发展了,又做了微信端,有个坑就是,页面回退,form表单有些状态样式会丢
  9. 有时候会喷后端API命名。。。居然告诉我resutful的接口不好写,要写一个post需要另建文件,不如直接get,导致修改数据也是get
  10. 有统一的ajax进出处理真好,帮忙解决了大问题!!!到现在稍微大一点的项目,我都有一个API处理控制中心
  11. artTemplate解决了很多问题
  12. iframe里生成pdfhtml的预览,隔离代码很好用
  13. 项目比较弱,没有产品,有一个测试算半个需求,一开始项目和测试一起帮我们想实现,喷了好几次,不仅出需求,还出实现...也出现过项目和测试需求冲突的情况,真感觉坑,有时候他们不合理的需求,只能和后端商量做优化,改动较大,再反馈回去。

需要改进的

  1. 部分代码太繁琐了,如果简化,还能简化蛮多的,就不确定别人在不深入理解的情况下,能否看懂的问题了
  2. grunt现在来看,太慢了,准备这段时间忙好,切到gulp
  3. 项目没代码测试,这个一直是遗憾
  4. hook最好可以关另一个系统的 issue

CSS 实现时钟效果

链接
主要逻辑

@keyframes clock {
        0 {
            transform: rotate(0deg) translateZ(0);
        }
        100% {
            transform: rotate(360deg) translateZ(0);
        }
}
.clock-s {
        /* steps 60 */
        animation: clock 60s steps(60, end) infinite;
}

我的第一个库应该会推广开了

这个库做的事情很简单,由于在微信端总要自定义分享,我就做了一层薄薄的代理。
写了一个base方法,其余的可配置参数也统统做了暴露,并且用base写了一个share方法暴露了出来,以前写自定义总要先添加sdk,获取签名那些东西,现在都会在base里面做掉,以至于现在的写法很简单

weChat.api.push('xx');
weChat.base(function () {
   wx['xx']();
});

现在分享也是一段代码

weChat.shae(function () {
   //接着是调用微信分享的API
});

他们不需要考虑不同的域名调用的签名接口不一样,不需要考虑sdk添加的问题,他们只需要用微信api,然后实现逻辑即可,目前还都是好评,因为用的最多的分享,你只需要配置一下分享信息即可,好评自然就多了:)

实现了正则表达式 /:id/x/:name/:xx.html

发现很多库的url都是这样的类型的/:id/x/:name/:xx.html,我自己定义都是/{a}/{b},虽然定义的复杂,但是正则简单/{(.*?)}/g,这样就解决了,今天实现了一下,通用的正则

str.replace(/:(.*?)(?=\/|\.|$)/g, function (a, b) {
        console.log(arguments);
});

前端处理元数据的问题

比如一个输入源是csv类型的,可能数据不符合需求,对其改造后才满足需求,这个整理工作可以通过脚本满足,现在就有这么一个问题,如何将这一步整理到工具流中。。。简单做法是有的,现在就想整合进去,比如说webpack,但是还没啥好想法

输入网页发生了什么

输入一个 URL 后,返回一个内容的过程中发生了什么?

访问一个从未访问过的网站

  1. 首先要得到具体的 IP 和端口。网站可以通过 IP 访问,也通过域名访问。域名访问就需要域名解析成具体的 IP了。
这里有几个问题:

  1. 为什么网站不通过 IP 直接访问,而是用域名代替 ?
    一点是域名很好记忆,另一点, HTTP的默认端口是80,如果在一个 IP 上绑定了多个网站,那必然其他网站无法绑定在 80 端口上,就需要在 IP 后面带上端口号。
  域名在这时候就发挥作用了,服务器会监听 80 端口,会读取请求头部的 host(值为域名),如果满足匹配规则,则会把请求转发到相应的服务上,服务不需要在 80 端口上工作。
  同时,在域名解析的过程中,可以把域名指向多个 IP,访问的时候返回距离用户最近,响应最快的那个。利用这个可以实现CDN、负载均衡等等。

  2. 域名解析 IP 的过程?DNS
    1. 浏览器缓存
    2. 操作系统缓存
    3. 路由器缓存
    4. 服务提供商查询
    5. 递归查询
    每一步都需要花费时间,所以页面中含有过多域名不是太好,同时页面可以设置 `dns-prefetch` 去处理这种情况
  1. 得到具体的 IP 和 端口,开始发送请求。
    这里客户端开始向服务端建立 TCP 连接,发送请求。

    这里有几个问题:

    1. HTTP
      默认端口是 80,和 TCP 的连接会经历 3 次握手,四次挥手。
    2. HTTPS
      默认端口是 443,和 TCP 的连接会经历 4 次握手,多出来一步 ssl 过程。发送请求时会验证证书。证书是由一个组织颁发的,所谓的安全证书就是这个组织是受到大家信任的,或者是用户手动信任的。而且服务器证书如果被窃取,中间人就可以窃听通信内容。
    3. HTTP1.1
      HTTP1.1 默认长连接,HTTP1.0是短链接,即握手后发送数据完成后,客户端和服务器就断开了。但是握手和断开本身是消耗资源的,如果客户端和服务器端能保存这个连接,下次就可以节省一些时间。Connection:keep-alive 为开启长连接
    4. HTTP/2
      HTTP/2 的多路复用特性,使得可以在一个连接上同时打开多个流,双向传输数据。头压缩,Server Push
  2. 服务器端获得请求
    服务器根据 host 找到具体的服务提供者,把请求交由给它进行处理,等待请求处理完成,返回资源。

  3. 客户端接收资源
    如果服务端头部设置了 Transfer-Encoding: chunked,则内容是一部分一部分返回的。但是同样浏览器可以解析。
    返回整个资源。然后轮到浏览器去解析该资源了!!!

  4. 浏览器解析
    页面中会含有静态资源,浏览器遇到这些资源会进行下载,这些静态资源,同域名下最多可以建立的连接,不同的浏览器是有不同的限制的,就意味着同一时刻发出的请求是一定的,就会造成阻塞,所以就会有把不同的资源放到不同的域名下,同时对资源的加载顺序进行排序,最重要的会放在最前面。
    这里需要注意一点,浏览器可以同时下载 N 个资源,但是按顺序执行,即使还在执行 A.js,但浏览器可能已经完成下载 Z.js 。

再次访问的网站

  1. 网页本身设置了缓存,这时候浏览器就会先去查看缓存的设置。如果是强缓存,便不会向服务器验证,如果是弱缓存,会向服务器发起验证,如果没有修改,则会返回304,有修改返回200和请求体
  2. 静态资源同1
  3. 发送的请求会带有 cookie

参考

http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/
http://blog.csdn.net/cywosp/article/details/38014581
http://blog.csdn.net/cywosp/article/details/38026809
http://blog.csdn.net/cywosp/article/details/38017027
https://www.zhihu.com/question/50646354/answer/122035678
https://imququ.com/post/optimize-tls-handshake.html
https://zh.wikipedia.org/zh-hans/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81
https://www.zhihu.com/question/20474326

ios 下 浏览器 触发 click 规则貌似严格了

很早以前就遇到一次对 a 标签,没有加 href,导致事件的 click 无法触发,当时解决了,也没在意,最近做项目,和同事发现越来越多的出现 “失灵的情况”,事件代理 和 元素事件绑定都会有问题,而且不是必现吧,搞不清楚触发逻辑。不改成 button 或者 对 a href 始终不触发, 虽说规范点没坏处,我担心以前的项目会出一些问题。

有关 prototype 问题的一些理解

一切从这个图开始
image

基础

1.什么是 __proto__

function F() {}; 
var a = new F();
a.__proto__ === a.constructor.prototype; //true

所以我们可以这么说 一般情况下,对象的 __proto__ 是其构造函数的 prototype(下面的题目 1 就出现特殊情况 )

2. 什么是 prototype

F.prototype //  Object {}
a.prototype //  undefined

函数内置的一个属性,默认是 {}(Object 实例)

问题

1. 为何 Object.prototype.__proto__ === null

按照上面的理解来说 Object.prototype{}
{}.__proto__ 应该是其构造函数的 prototypeObject.prototype 所以可以得出 Object.prototype.__proto__ === Object.prototype ,但实际结果是 null ???

使用假设法

假设 Object.prototype.__proto__ === Object.prototype

那对于一个 Object 实例 o1= {}
我们去查找它的属性 xxx(即 o1.xxx),o1 没有 所以 o1 去它的构造函数 Objectprototype 中找(即 o1.__proto__ /Object.prototype)
显而易见默认是没有这个属性的,找不到,继续查询原型链
Object.prototype 的上一级原型链 是 Object.prototype.__proto__ ,这当然也找不到,继续上级原型链, 记得我们一开始假设 Object.prototype.__proto__ === Object.prototype,这时候你会发现进入一个循环中,走不出来,属性 xxx 不存在的时候,原型查找就出问题了
所以 Object.prototype.__proto__===null,直接切断了原型链的查找

2. Function instanceof Object === true

Function 为什么是 Object 的实例,首先看一下 Function 的原型链中存不存在 Object

Function.__proto__ === Object.prototype  // false

不存在,但是为什么会有实例关系 ???
接着发现, Functionprototype__proto__Objectprototype

Function.prototype   // function () {}
Function.prototype.__proto__ === Object.prototype // true

发现 Function.prototype (记为 empty)是 Function 的实例,empty.__proto__ 按理说应该是 Function.prototype 但这里 却指向了 Object.prototype (这个问题暂时不说)
这里要说明一点 FunctionFunction 的构造函数
简单点 Function.__proto__ === Function.prototype
Function.constructor.prototype === Function.prototype
所以有 Function.constructor=== Function
接下来就有了
Function.protype.__proto__ === Function.__proto__.__proto__ = Object.prototype
所以当判断实例的时候,查找 Function 的原型链 就找到了 Object 的原型,于是就有了实例关系

3. Object instanceof Function === true

ObjectFunction 的实例这个比较简单
Object 的原型链中存在 Functionprototype

Object.__proto__ === Function.prototype //true

4. 问题2的疑惑 Function.prototype.__proto__ === Object.prototype // true

**假设 Function.prototype.__proto__ != Object.prototype,且表现为正常的理解,即Function.prototype.__proto__ === Function.prototype**因为 Function 的构造函数是自身

function F() {}; 

接着查找F.notFoundF 没有,会查找到 F.__proto__,即 Function.prototype,接下来很显而易见的会死循环,所以 Function.prototype.__proto__ != Object.prototype

那能不能像 Object 一样设为 null 呢?如果为 null

a instanceof Object  //false
a instanceof Function //true 

这样也就不符合理解,从另一方面来说,a 应该是 Object 的实例
a instanceof Object // true
所以把

Function.prototype.__proto__ === Object.prototype

这样原型最终都会到 null 结束

chrome 插件

chrome 插件

可以自由的页面里运行js

地址

https://chrome.google.com/webstore/detail/run-script/gddnebgjhlhediiojefadmljmnjfomak

使用

点击图标,再次点击管理,使用中添加一项

https://github.com/
 function  changeCSS() {
  var header = document.getElementsByClassName('header-dark')[0];
  if (header) {
   header.className = header.className.replace(/\sheader-dark\s/g, ' ');
  }
}
document.onreadystatechange = function() {
if (this.readyState == "interactive") {                
  changeCSS();
 }
};

这样 github 网站的头部样式就变回来了

对待新框架

最近发现一直都犯了错,面试的时候会有人问道,都用什么框架,答:

技术选择比较随意,一般能解决问题的话,就jquery。
angular1 vue react 可用于项目里,并不做强制。

话虽然的很讨巧,但这是建立在组内有会这些东西人的基础上,但现在有些事情已经显露这些话的风险了。实际上,以前有段时间我也苦不堪言,询问 angular1 的问题,一度严重打扰我的工作,而我当时并没有往这个方向去思考,实际上思考的话,可能事情就好办一些。

  1. 组里的同学直接用了 angular2 在项目里,本身没有对其做调研,遇到了很多坑了,问我,我并不能很好很快的解决,因为我还没有足够的时间去了解,结果我们都会花费很多的时间在坑上。同时,同事并没用官方组件,组件使用出现了问题,也会花很多的时间去理解,想办法去绕过去实现。

  2. 组里有用 vue 的,简单的问题,可以凭借经验进行回答,但对于复杂的问题,就显得很棘手了,我同样不可能一次学两个框架,有问到组件的问题的,一个100多 star 的开源组件,但总感觉组件有些问题,折腾了好半天,不得不劝他换一个试试。vue 目前还没官方组件,只有饿了么的组件库,如果想要更快更好的完成工作,而自己的实力又不足以做好封装,出现 bug,只能不停的踩坑。

这两次事情,让我感觉对对待技术不得不保守,因为是要用到项目里,我更希望使用新技术的时候,最好使用者能对它做个分享,进行全面的评价,如果使用者不能驾驭它,或许就没有使用的必要,因为早晚有坑,如果要使用,就必须提前自己做好调研,以此来说服别人。对待新技术,还是不能太热血,毕竟工作完不成加班的是自己,技术可以回去自己学,一天不会可以第二天。
工作追求和技术追求还是要分开看的。

git使用总结(二)

本来想写一篇文章的,文章内有提及svn,但是经过详细了解,我所认为的svn并不是真正的svn,我在很浅显的用svn,一如不知道git flow的我,所以我只说说使用git感受。
git本地操作的能力,大大简化了平时版本控制的问题,你在本地可以任意操作,而不用担心影响到别人,我认为这是一个极大的优势。同时简便的分支管理,鼓励大家使用分支,同时大大增强了对代码版本的管控。后来演变而来的git flow,更是我喜欢的**,目前边结合问题边探索如何具体实施git flow

  1. fix/feature/就是很好的设计,因为我见过有人在服务器推了N多dev_adev_b的分支,令人头大。。。
  2. 不在dev开发,而采取新分支,一方面是方便代码切换功能,另一方面新功能未完成,不至于推到线上。

树莓派(一)

最近在折腾树莓派,好吧算是折腾系统,喜欢 linux 系统了,但是吧,在若干次的折腾中,我貌似把内置wifi搞挂了。。。实践到能让树莓派说出自己的ip了:)明天再进一步确认是否把wifi弄挂了

记录几个有意思的问题

1.

<button id="a">xxxx</button>

<script>
    a.onclick = function () {
        console.log(1);
    };
    function b() {
        console.log(2);
        var delay = new Date() - 0 + 10000;
        while (new Date()< delay) {

        }
    }
    b();
</script>

页面加载完成后,接着点击 button a N次,会打印什么?答案

2
1(N次)
  1. 数组去重
var a = [3,3,4,5,4];

ES6 答案

var s = new Set(a);
var out = [...s];

ie8下object不居中的问题

问题描述:

页面存在一个div,放着视频信息,然后js读取指定的元素,渲染该视频,并用js对该div设置text-align center(简称 tac),结果发现视频居然不居中,然后反馈问题说,ie8下的bug。

好吧,视频渲染js是我去对接现成云视频的sdk,出事了总要研究下原因。
过程:

一开始js把object的 align 属性设置成center,奇迹般的好了,sdk默认给的是middle,但是吧,查询规范根本没有center这个值,middle倒是有的,但真的有效果!!!以为找到了解决方案。

但我还想再继续研究,然后诡异的情况出现了,div 在 ie8下的最新dom可以看到tac,但是我通过js获取的是 text-align left,这时候用js设置tac,这次居中了。。。于是我试着设置 text-align right,但在dom里看到的还是tac,但页面的object确实right了,真够诡异的,到最后我只能归结于手法问题,跟变魔术似的。

ie8下看到最新dom办法,在dom页面有个刷新按钮的,仔细看是能找到的

jquery Deferred 实现及相关

jquery Deferred 实现及相关

较早的文章了,文章就是很枯燥,很枯燥

$.Callbacks

基础说明

$.Callbacks方法调用,可以传递参数ops,可选值有once memory unique stopOnFalse,这里传递空格隔开的字符串和对象均可。
调用后会返回一个 cb 对象
cb暴露出来的方法有
add remove has empty disable disabled lock locked fireWith fire fired

  1. 在默认情况下即不传opsadd添加回调函数,fire执行回调函数,每次fire均会执行所有add的函数,remove可以移除某些函数

  2. 传递了once,则只会触发一次,再次添加、触发均不会执行,但在同时传递memory的情况下又有所不同,则会执行add添加的函数,不会响应fire

  3. 传递了unique,不会添加重复的回调

  4. 传递stopOnFalse, 在回调失败后,不会执行后续的回调,在有没有memory的情况下,都会清空该memory

  5. 传递了memory,在触发后会记录下最后的请求参数,在下次add的时候,会直接执行掉 add 进来的回调函数,

has方法查看回调是否在内

empty清空回调

disable销毁整个对象内部回调和参数,导致不会响应各种方法

disabled是否被disable

lock 如果不是 memory 模式,执行disable,如果是,仅仅做锁定操作

memory 模式,则不会再被 fire,清空待执行的回调函数

locked 是否被 lock

fireWith 可以决定函数执行的上下文

fire 函数执行的上下文即 cb 本身

fired 判断是否被触发过

$.Deferred

jQuery,通过 $.Callbacks 实现了 Deferred

Deferred 的三种状态,在 jQuery 里分别用

reslove 对应 jQuery.Callbacks( "once memory" )

reject 对应 jQuery.Callbacks( "once memory" )

progress 对应 jQuery.Callbacks( "memory" )

表示

reslovereject 仅被执行一次,但参数可以传递给下一个函数使用,刚好对应起来jQuery.Callbacks( "once memory" )

progress 可以被调用多次,每次传不同的值,所以不能是once,但需要记录下每次触发后的参数,对应起来 jQuery.Callbacks( "memory" )

此方法最终返回的是 deferred 对象和 promise 对象的合集,以 deferred 对象为主,同时会执行传入的函数即$.Deferred(func)的执行效果,相当于var dfd = $.Deferred();func(dfd);return dfd;


下面方法挂载在一个叫 deferred 的对象上面,仅仅是 deferred 拥有

reslove 本质是cb.fireWith,但该方法对应的cb首先就被add进去了一连串的回调,其中包括:状态值的改变,reject回调的disable方法(reslove执行后,reject就不可能被执行了),以及progresslock(在memory下的lock,会自动执行add添加的方法)。reject 同理


下面方法挂载在一个叫 promise 的对象上面,仅仅是 promise 拥有

done,fail,progress本质是对应cbadd方法

always方法就是同时调用了donefail 方法

promise方法就是返回一个 promise 对象,无 reslovereject

then 方法是主动调用$.Deferred,并传入一个函数func,返回Deferred对象的promise方法,即无 reslovereject

这个函数`func`在`$.Deferred`里面会被传入新的一个`Deferred`对象(在这里叫 `d1`,原来的叫 `d`即调用`then`的对象)(在链式调用的时候,如:.then().done()方法,会在自身的上面积累很多回调函数,直至下一个.then()方法)执行掉,这个`func`主要做了以下事情:
首先调用`d`对象的相应的方法即`done | fail | progress`,在方法内部,判断then相应的方法执行结果的返回值,
如果是含有`promise`方法的对象(即d2),会执行`promise`方法(即p2,无`reject` 和 `resolve`),并把 d1的 `notify resolve reject` 给p2对应的方法 `progress done fail`,即当`d2`完成的时候,会执行`d1`上积累的方法,注意这里把状态转移到了`d2`上面,而不是依赖原`d1`对象的完成与否
如果不是`promise`对象,会调用 `d1` 上面的触发回调函数调用的方法 即`xxWith`,在这里多出来一步,用来明确上下文以及传参的过程。
传参的过程很容易确定,如果`done`方法被传递了,则会传递`done`方法的返回值,没有的话,则会传递以前的值。
明确上下文的过程,如果 this 对象 指向的是 `d` 的 `p`,则会返回为 `d1` 对象的 `p1`,即 `then` 方法的返回值

$.when

该方法可以传入N个变量,不传参数会返回resolved Promise
传参的情况下,
1个参数并且含有promise方法的情况下,dfd = 该参数
其他情况下 dfd = jQuery.Deferred()
接着会判断参数是否是有promise方法,如果有,执行promise方法,并且添加相应的回调函数(回调函数本身会调用dfdresolvenotify 方法,同时每次调用的时候会将上下文和参数保留下来,每次参数调用progress都会执行dfd.notify,每次参数调用done都会将未完成的计数减1,在计数减到0的时候,则会触发 dfd.done),但添加失败的回调,则会执行dfd.reject方法,即失败后,不再关注其他参数成功的情况。
如果不含有 promise方法,返回resolved Promise

$.fn.ready

该方法的内部实现

jQuery.fn.ready = function( fn ) {

    // Add the callback
    jQuery.ready.promise().done( fn );

    return this;
};

$.fn.ready,仅仅充当 done 方法,主体在 $.ready.promise 内部,这个方法在初始化的时候就已经被执行了。内部同样是 $.Deferred 实现(称为 d ),d 作为一个全局变量而存在的。
$.ready 方法作为 $.ready.promise 的触发方法。

视频移动端自适应

网页代码PC、移动用的是同一套。其中在PC端,视频库在元素上写死了宽高,导致在移动端高度显示不正常。想到之前的利用padding做指定比例的容器框。就有了如下解决方案

[id^=id_video_container] {
  @media (--mobile) {
    position: relative;
    height: 0!important;
    padding-top: 56.25%;
    margin: 15px 0;
  }
}
[id^=trump_main_unique] {
  @media (--mobile) {
    position: absolute!important;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
  }
}

重点是 56.25 这段。目前视频比例是16:9。所以有 100/(16/9) = 56.25。而 padding margin 百分比是基于宽度计算的,所以就实现了比例。当然要和运营约定一下视频比例固定为 16:9。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.