面试知识点

面试知识点汇总

  • 以太网 MAC 地址的长度是 48位
  • 在Unix系统中进程由三部分组成,分别是进程控制块、正文段和数据段。Unix系统中把进程控制块分成proc结构和user结构两部分
    proc存放的是系统经常要查询和修改的信息,需要快速访问,因此常将其装入内存
  • 段页式存储管理, 段内页面 地址是连续的,这种连续地址采用 维地址空间
  • 现代操作系统的基本特征是 程序的并发执行
  • 在Java中 数组是一种对象

评论和共享

Airbnb JavaScript Style Guide() {

用更合理的方式写 JavaScript

目录

  1. 类型
  2. 对象
  3. 数组
  4. 字符串
  5. 函数
  6. 属性
  7. 变量
  8. 提升
  9. 比较运算符 & 等号
  10. 注释
  11. 空白
  12. 逗号
  13. 分号
  14. 类型转化
  15. 命名规则
  16. 存取器
  17. 构造函数
  18. 事件
  19. 模块
  20. jQuery
  21. ECMAScript 5 兼容性
  22. 测试
  23. 性能
  24. 资源
  25. 谁在使用
  26. 翻译
  27. JavaScript 风格指南说明
  28. 与我们讨论 JavaScript
  29. 贡献者
  30. 许可

类型

  • 原始值: 存取直接作用于它自身。

    • string
    • number
    • boolean
    • null
    • undefined
    1
    2
    3
    4
    5
    6
    var foo = 1;
    var bar = foo;

    bar = 9;

    console.log(foo, bar); // => 1, 9
  • 复杂类型: 存取时作用于它自身值的引用。

    • object
    • array
    • function
    1
    2
    3
    4
    5
    6
    var foo = [1, 2];
    var bar = foo;

    bar[0] = 9;

    console.log(foo[0], bar[0]); // => 9, 9

⬆ 回到顶部

对象

  • 使用直接量创建对象。

    1
    2
    3
    4
    5
    // bad
    var item = new Object();

    // good
    var item = {};
  • 不要使用保留字作为键名,它们在 IE8 下不工作。更多信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    var superman = {
    default: { clark: 'kent' },
    private: true
    };

    // good
    var superman = {
    defaults: { clark: 'kent' },
    hidden: true
    };
  • 使用同义词替换需要使用的保留字。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    var superman = {
    class: 'alien'
    };

    // bad
    var superman = {
    klass: 'alien'
    };

    // good
    var superman = {
    type: 'alien'
    };

⬆ 回到顶部

数组

  • 使用直接量创建数组。

    1
    2
    3
    4
    5
    // bad
    var items = new Array();

    // good
    var items = [];
  • 向数组增加元素时使用 Array#push 来替代直接赋值。

    1
    2
    3
    4
    5
    6
    7
    8
    var someStack = [];


    // bad
    someStack[someStack.length] = 'abracadabra';

    // good
    someStack.push('abracadabra');
  • 当你需要拷贝数组时,使用 Array#slice。jsPerf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var len = items.length;
    var itemsCopy = [];
    var i;

    // bad
    for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
    }

    // good
    itemsCopy = items.slice();
  • 使用 Array#slice 将类数组对象转换成数组。

    1
    2
    3
    4
    function trigger() {
    var args = Array.prototype.slice.call(arguments);
    ...
    }

⬆ 回到顶部

字符串

  • 使用单引号 '' 包裹字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    var name = "Bob Parr";

    // good
    var name = 'Bob Parr';

    // bad
    var fullName = "Bob " + this.lastName;

    // good
    var fullName = 'Bob ' + this.lastName;
  • 超过 100 个字符的字符串应该使用连接符写成多行。

  • 注:若过度使用,通过连接符连接的长字符串可能会影响性能。jsPerf & 讨论.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // bad
    var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

    // bad
    var errorMessage = 'This is a super long error that was thrown because \
    of Batman. When you stop to think about how Batman had anything to do \
    with this, you would get nowhere \
    fast.';

    // good
    var errorMessage = 'This is a super long error that was thrown because ' +
    'of Batman. When you stop to think about how Batman had anything to do ' +
    'with this, you would get nowhere fast.';
  • 程序化生成的字符串使用 Array#join 连接而不是使用连接符。尤其是 IE 下:jsPerf.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    var items;
    var messages;
    var length;
    var i;

    messages = [{
    state: 'success',
    message: 'This one worked.'
    }, {
    state: 'success',
    message: 'This one worked as well.'
    }, {
    state: 'error',
    message: 'This one did not work.'
    }];

    length = messages.length;

    // bad
    function inbox(messages) {
    items = '<ul>';

    for (i = 0; i < length; i++) {
    items += '<li>' + messages[i].message + '</li>';
    }

    return items + '</ul>';
    }

    // good
    function inbox(messages) {
    items = [];

    for (i = 0; i < length; i++) {
    // use direct assignment in this case because we're micro-optimizing.
    items[i] = '<li>' + messages[i].message + '</li>';
    }

    return '<ul>' + items.join('') + '</ul>';
    }

⬆ 回到顶部

函数

  • 函数表达式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 匿名函数表达式
    var anonymous = function() {
    return true;
    };

    // 命名函数表达式
    var named = function named() {
    return true;
    };

    // 立即调用的函数表达式(IIFE)
    (function () {
    console.log('Welcome to the Internet. Please follow me.');
    }());
  • 永远不要在一个非函数代码块(if、while 等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。

  • 注: ECMA-262 把 定义为一组语句。函数声明不是语句。阅读对 ECMA-262 这个问题的说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    if (currentUser) {
    function test() {
    console.log('Nope.');
    }
    }

    // good
    var test;
    if (currentUser) {
    test = function test() {
    console.log('Yup.');
    };
    }
  • 永远不要把参数命名为 arguments。这将取代函数作用域内的 arguments 对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function nope(name, options, arguments) {
    // ...stuff...
    }

    // good
    function yup(name, options, args) {
    // ...stuff...
    }

⬆ 回到顶部

属性

  • 使用 . 来访问对象的属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var luke = {
    jedi: true,
    age: 28
    };

    // bad
    var isJedi = luke['jedi'];

    // good
    var isJedi = luke.jedi;
  • 当通过变量访问属性时使用中括号 []

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var luke = {
    jedi: true,
    age: 28
    };

    function getProp(prop) {
    return luke[prop];
    }

    var isJedi = getProp('jedi');

⬆ 回到顶部

变量

  • 总是使用 var 来声明变量。不这么做将导致产生全局变量。我们要避免污染全局命名空间。

    1
    2
    3
    4
    5
    // bad
    superPower = new SuperPower();

    // good
    var superPower = new SuperPower();
  • 使用 var 声明每一个变量。
    这样做的好处是增加新变量将变的更加容易,而且你永远不用再担心调换错 ;,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // bad
    var items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

    // bad
    // (跟上面的代码比较一下,看看哪里错了)
    var items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

    // good
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
  • 最后再声明未赋值的变量。当你需要引用前面的变量赋值时这将变的很有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // bad
    var i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

    // bad
    var i;
    var items = getItems();
    var dragonball;
    var goSportsTeam = true;
    var len;

    // good
    var items = getItems();
    var goSportsTeam = true;
    var dragonball;
    var length;
    var i;
  • 在作用域顶部声明变量。这将帮你避免变量声明提升相关的问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    // bad
    function () {
    test();
    console.log('doing stuff..');

    //..other stuff..

    var name = getName();

    if (name === 'test') {
    return false;
    }

    return name;
    }

    // good
    function () {
    var name = getName();

    test();
    console.log('doing stuff..');

    //..other stuff..

    if (name === 'test') {
    return false;
    }

    return name;
    }

    // bad - 不必要的函数调用
    function () {
    var name = getName();

    if (!arguments.length) {
    return false;
    }

    this.setFirstName(name);

    return true;
    }

    // good
    function () {
    var name;

    if (!arguments.length) {
    return false;
    }

    name = getName();
    this.setFirstName(name);

    return true;
    }

⬆ 回到顶部

提升

  • 变量声明会提升至作用域顶部,但赋值不会。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 我们知道这样不能正常工作(假设这里没有名为 notDefined 的全局变量)
    function example() {
    console.log(notDefined); // => throws a ReferenceError
    }

    // 但由于变量声明提升的原因,在一个变量引用后再创建它的变量声明将可以正常工作。
    // 注:变量赋值为 `true` 不会提升。
    function example() {
    console.log(declaredButNotAssigned); // => undefined
    var declaredButNotAssigned = true;
    }

    // 解释器会把变量声明提升到作用域顶部,意味着我们的例子将被重写成:
    function example() {
    var declaredButNotAssigned;
    console.log(declaredButNotAssigned); // => undefined
    declaredButNotAssigned = true;
    }
  • 匿名函数表达式会提升它们的变量名,但不会提升函数的赋值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function example() {
    console.log(anonymous); // => undefined

    anonymous(); // => TypeError anonymous is not a function

    var anonymous = function () {
    console.log('anonymous function expression');
    };
    }
  • 命名函数表达式会提升变量名,但不会提升函数名或函数体。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    superPower(); // => ReferenceError superPower is not defined

    var named = function superPower() {
    console.log('Flying');
    };
    }

    // 当函数名跟变量名一样时,表现也是如此。
    function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    var named = function named() {
    console.log('named');
    }
    }
  • 函数声明提升它们的名字和函数体。

    1
    2
    3
    4
    5
    6
    7
    function example() {
    superPower(); // => Flying

    function superPower() {
    console.log('Flying');
    }
    }
  • 了解更多信息在 JavaScript Scoping & Hoisting by Ben Cherry.

⬆ 回到顶部

比较运算符 & 等号

  • 优先使用 ===!== 而不是 ==!=.
  • 条件表达式例如 if 语句通过抽象方法 ToBoolean 强制计算它们的表达式并且总是遵守下面的规则:

    • 对象 被计算为 true
    • Undefined 被计算为 false
    • Null 被计算为 false
    • 布尔值 被计算为 布尔的值
    • 数字 如果是 +0、-0 或 NaN 被计算为 false,否则为 true
    • 字符串 如果是空字符串 '' 被计算为 false,否则为 true
    1
    2
    3
    4
    if ([0]) {
    // true
    // 一个数组就是一个对象,对象被计算为 true
    }
  • 使用快捷方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if (name !== '') {
    // ...stuff...
    }

    // good
    if (name) {
    // ...stuff...
    }

    // bad
    if (collection.length > 0) {
    // ...stuff...
    }

    // good
    if (collection.length) {
    // ...stuff...
    }
  • 了解更多信息在 Truth Equality and JavaScript by Angus Croll.

⬆ 回到顶部

  • 使用大括号包裹所有的多行代码块。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if (test)
    return false;

    // good
    if (test) return false;

    // good
    if (test) {
    return false;
    }

    // bad
    function () { return false; }

    // good
    function () {
    return false;
    }
  • 如果通过 ifelse 使用多行代码块,把 else 放在 if 代码块关闭括号的同一行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // bad
    if (test) {
    thing1();
    thing2();
    }
    else {
    thing3();
    }

    // good
    if (test) {
    thing1();
    thing2();
    } else {
    thing3();
    }

⬆ 回到顶部

注释

  • 使用 /** ... */ 作为多行注释。包含描述、指定所有参数和返回值的类型和值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {

    // ...stuff...

    return element;
    }

    // good
    /**
    * make() returns a new element
    * based on the passed in tag name
    *
    * @param {String} tag
    * @return {Element} element
    */
    function make(tag) {

    // ...stuff...

    return element;
    }
  • 使用 // 作为单行注释。在评论对象上面另起一行使用单行注释。在注释前插入空行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // bad
    var active = true; // is current tab

    // good
    // is current tab
    var active = true;

    // bad
    function getType() {
    console.log('fetching type...');
    // set the default type to 'no type'
    var type = this.type || 'no type';

    return type;
    }

    // good
    function getType() {
    console.log('fetching type...');

    // set the default type to 'no type'
    var type = this.type || 'no type';

    return type;
    }
  • 给注释增加 FIXMETODO 的前缀可以帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。这将有别于常见的注释,因为它们是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

  • 使用 // FIXME: 标注问题。

    1
    2
    3
    4
    5
    6
    7
    function Calculator() {

    // FIXME: shouldn't use a global here
    total = 0;

    return this;
    }
  • 使用 // TODO: 标注问题的解决方式。

    1
    2
    3
    4
    5
    6
    7
    function Calculator() {

    // TODO: total should be configurable by an options param
    this.total = 0;

    return this;
    }

⬆ 回到顶部

空白

  • 使用 2 个空格作为缩进。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    function () {
    ∙∙∙∙var name;
    }

    // bad
    function () {
    var name;
    }

    // good
    function () {
    ∙∙var name;
    }
  • 在大括号前放一个空格。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // bad
    function test(){
    console.log('test');
    }

    // good
    function test() {
    console.log('test');
    }

    // bad
    dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog'
    });

    // good
    dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog'
    });
  • 在控制语句(ifwhile 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // bad
    if(isJedi) {
    fight ();
    }

    // good
    if (isJedi) {
    fight();
    }

    // bad
    function fight () {
    console.log ('Swooosh!');
    }

    // good
    function fight() {
    console.log('Swooosh!');
    }
  • 使用空格把运算符隔开。

    1
    2
    3
    4
    5
    // bad
    var x=y+5;

    // good
    var x = y + 5;
  • 在文件末尾插入一个空行。

    1
    2
    3
    4
    // bad
    (function (global) {
    // ...stuff...
    })(this);
    1
    2
    3
    4
    5
    // bad
    (function (global) {
    // ...stuff...
    })(this);↵

    1
    2
    3
    4
    // good
    (function (global) {
    // ...stuff...
    })(this);↵
  • 在使用长方法链时进行缩进。使用前面的点 . 强调这是方法调用而不是新语句。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();

    // bad
    $('#items').
    find('.selected').
    highlight().
    end().
    find('.open').
    updateCount();

    // good
    $('#items')
    .find('.selected')
    .highlight()
    .end()
    .find('.open')
    .updateCount();

    // bad
    var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

    // good
    var leds = stage.selectAll('.led')
    .data(data)
    .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
    .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);
  • 在块末和新语句前插入空行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // bad
    if (foo) {
    return bar;
    }
    return baz;

    // good
    if (foo) {
    return bar;
    }

    return baz;

    // bad
    var obj = {
    foo: function () {
    },
    bar: function () {
    }
    };
    return obj;

    // good
    var obj = {
    foo: function () {
    },

    bar: function () {
    }
    };

    return obj;

⬆ 回到顶部

逗号

  • 行首逗号: 不需要

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // bad
    var story = [
    once
    , upon
    , aTime
    ];

    // good
    var story = [
    once,
    upon,
    aTime
    ];

    // bad
    var hero = {
    firstName: 'Bob'
    , lastName: 'Parr'
    , heroName: 'Mr. Incredible'
    , superPower: 'strength'
    };

    // good
    var hero = {
    firstName: 'Bob',
    lastName: 'Parr',
    heroName: 'Mr. Incredible',
    superPower: 'strength'
    };
  • 额外的行末逗号:不需要。这样做会在 IE6/7 和 IE9 怪异模式下引起问题。同样,多余的逗号在某些 ES3 的实现里会增加数组的长度。在 ES5 中已经澄清了 (source):

    Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // bad
    var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn',
    };

    var heroes = [
    'Batman',
    'Superman',
    ];

    // good
    var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn'
    };

    var heroes = [
    'Batman',
    'Superman'
    ];

⬆ 回到顶部

分号

  • 使用分号。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad
    (function () {
    var name = 'Skywalker'
    return name
    })()

    // good
    (function () {
    var name = 'Skywalker';
    return name;
    })();

    // good (防止函数在两个 IIFE 合并时被当成一个参数
    ;(function () {
    var name = 'Skywalker';
    return name;
    })();

    了解更多.

⬆ 回到顶部

类型转换

  • 在语句开始时执行类型转换。
  • 字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //  => this.reviewScore = 9;

    // bad
    var totalScore = this.reviewScore + '';

    // good
    var totalScore = '' + this.reviewScore;

    // bad
    var totalScore = '' + this.reviewScore + ' total score';

    // good
    var totalScore = this.reviewScore + ' total score';
  • 使用 parseInt 转换数字时总是带上类型转换的基数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var inputValue = '4';

    // bad
    var val = new Number(inputValue);

    // bad
    var val = +inputValue;

    // bad
    var val = inputValue >> 0;

    // bad
    var val = parseInt(inputValue);

    // good
    var val = Number(inputValue);

    // good
    var val = parseInt(inputValue, 10);
  • 如果因为某些原因 parseInt 成为你所做的事的瓶颈而需要使用位操作解决性能问题时,留个注释说清楚原因和你的目的。

    1
    2
    3
    4
    5
    6
    7
    // good
    /**
    * parseInt was the reason my code was slow.
    * Bitshifting the String to coerce it to a
    * Number made it a lot faster.
    */
    var val = inputValue >> 0;
  • 注: 小心使用位操作运算符。数字会被当成 64 位值,但是位操作运算符总是返回 32 位的整数(source)。位操作处理大于 32 位的整数值时还会导致意料之外的行为。讨论。最大的 32 位整数是 2,147,483,647:

    1
    2
    3
    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
  • 布尔:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var age = 0;

    // bad
    var hasAge = new Boolean(age);

    // good
    var hasAge = Boolean(age);

    // good
    var hasAge = !!age;

⬆ 回到顶部

命名规则

  • 避免单字母命名。命名应具备描述性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    function q() {
    // ...stuff...
    }

    // good
    function query() {
    // ..stuff..
    }
  • 使用驼峰式命名对象、函数和实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    var o = {};
    function c() {}

    // good
    var thisIsMyObject = {};
    function thisIsMyFunction() {}
  • 使用帕斯卡式命名构造函数或类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // bad
    function user(options) {
    this.name = options.name;
    }

    var bad = new user({
    name: 'nope'
    });

    // good
    function User(options) {
    this.name = options.name;
    }

    var good = new User({
    name: 'yup'
    });
  • 不要使用下划线前/后缀。

    为什么?JavaScript 并没有私有属性或私有方法的概念。虽然使用下划线是表示「私有」的一种共识,但实际上这些属性是完全公开的,它本身就是你公共接口的一部分。这种习惯或许会导致开发者错误的认为改动它不会造成破坏或者不需要去测试。长话短说:如果你想要某处为「私有」,它必须不能是显式提出的。

    1
    2
    3
    4
    5
    6
    7
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';

    // good
    this.firstName = 'Panda';
  • 不要保存 this 的引用。使用 Function#bind。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    // bad
    function () {
    var self = this;
    return function () {
    console.log(self);
    };
    }

    // bad
    function () {
    var that = this;
    return function () {
    console.log(that);
    };
    }

    // bad
    function () {
    var _this = this;
    return function () {
    console.log(_this);
    };
    }

    // good
    function () {
    return function () {
    console.log(this);
    }.bind(this);
    }
  • 给函数命名。这在做堆栈轨迹时很有帮助。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    var log = function (msg) {
    console.log(msg);
    };

    // good
    var log = function log(msg) {
    console.log(msg);
    };
  • 注: IE8 及以下版本对命名函数表达式的处理有些怪异。了解更多信息到 http://kangax.github.io/nfe/

  • 如果你的文件导出一个类,你的文件名应该与类名完全相同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // file contents
    class CheckBox {
    // ...
    }
    module.exports = CheckBox;

    // in some other file
    // bad
    var CheckBox = require('./checkBox');

    // bad
    var CheckBox = require('./check_box');

    // good
    var CheckBox = require('./CheckBox');

⬆ 回到顶部

存取器

  • 属性的存取函数不是必须的。
  • 如果你需要存取函数时使用 getVal()setVal('hello')

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // bad
    dragon.age();

    // good
    dragon.getAge();

    // bad
    dragon.age(25);

    // good
    dragon.setAge(25);
  • 如果属性是布尔值,使用 isVal()hasVal()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // bad
    if (!dragon.age()) {
    return false;
    }

    // good
    if (!dragon.hasAge()) {
    return false;
    }
  • 创建 get() 和 set() 函数是可以的,但要保持一致。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function Jedi(options) {
    options || (options = {});
    var lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
    }

    Jedi.prototype.set = function set(key, val) {
    this[key] = val;
    };

    Jedi.prototype.get = function get(key) {
    return this[key];
    };

⬆ 回到顶部

构造函数

  • 给对象原型分配方法,而不是使用一个新对象覆盖原型。覆盖原型将导致继承出现问题:重设原型将覆盖原有原型!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function Jedi() {
    console.log('new jedi');
    }

    // bad
    Jedi.prototype = {
    fight: function fight() {
    console.log('fighting');
    },

    block: function block() {
    console.log('blocking');
    }
    };

    // good
    Jedi.prototype.fight = function fight() {
    console.log('fighting');
    };

    Jedi.prototype.block = function block() {
    console.log('blocking');
    };
  • 方法可以返回 this 来实现方法链式使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // bad
    Jedi.prototype.jump = function jump() {
    this.jumping = true;
    return true;
    };

    Jedi.prototype.setHeight = function setHeight(height) {
    this.height = height;
    };

    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20); // => undefined

    // good
    Jedi.prototype.jump = function jump() {
    this.jumping = true;
    return this;
    };

    Jedi.prototype.setHeight = function setHeight(height) {
    this.height = height;
    return this;
    };

    var luke = new Jedi();

    luke.jump()
    .setHeight(20);
  • 写一个自定义的 toString() 方法是可以的,但是确保它可以正常工作且不会产生副作用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Jedi(options) {
    options || (options = {});
    this.name = options.name || 'no name';
    }

    Jedi.prototype.getName = function getName() {
    return this.name;
    };

    Jedi.prototype.toString = function toString() {
    return 'Jedi - ' + this.getName();
    };

⬆ 回到顶部

事件

  • 当给事件附加数据时(无论是 DOM 事件还是私有事件),传入一个哈希而不是原始值。这样可以让后面的贡献者增加更多数据到事件数据而无需找出并更新事件的每一个处理器。例如,不好的写法:

    1
    2
    3
    4
    5
    6
    7
    8
    // bad
    $(this).trigger('listingUpdated', listing.id);

    ...

    $(this).on('listingUpdated', function (e, listingId) {
    // do something with listingId
    });

    更好的写法:

    1
    2
    3
    4
    5
    6
    7
    8
    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });

    ...

    $(this).on('listingUpdated', function (e, data) {
    // do something with data.listingId
    });

    ⬆ 回到顶部

模块

  • 模块应该以 ! 开始。这样确保了当一个不好的模块忘记包含最后的分号时,在合并代码到生产环境后不会产生错误。详细说明
  • 文件应该以驼峰式命名,并放在同名的文件夹里,且与导出的名字一致。
  • 增加一个名为 noConflict() 的方法来设置导出的模块为前一个版本并返回它。
  • 永远在模块顶部声明 'use strict';

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // fancyInput/fancyInput.js

    !function (global) {
    'use strict';

    var previousFancyInput = global.FancyInput;

    function FancyInput(options) {
    this.options = options || {};
    }

    FancyInput.noConflict = function noConflict() {
    global.FancyInput = previousFancyInput;
    return FancyInput;
    };

    global.FancyInput = FancyInput;
    }(this);

⬆ 回到顶部

jQuery

  • 使用 $ 作为存储 jQuery 对象的变量名前缀。

    1
    2
    3
    4
    5
    // bad
    var sidebar = $('.sidebar');

    // good
    var $sidebar = $('.sidebar');
  • 缓存 jQuery 查询。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // bad
    function setSidebar() {
    $('.sidebar').hide();

    // ...stuff...

    $('.sidebar').css({
    'background-color': 'pink'
    });
    }

    // good
    function setSidebar() {
    var $sidebar = $('.sidebar');
    $sidebar.hide();

    // ...stuff...

    $sidebar.css({
    'background-color': 'pink'
    });
    }
  • 对 DOM 查询使用层叠 $('.sidebar ul') 或 父元素 > 子元素 $('.sidebar > ul')jsPerf

  • 对有作用域的 jQuery 对象查询使用 find

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad
    $('ul', '.sidebar').hide();

    // bad
    $('.sidebar').find('ul').hide();

    // good
    $('.sidebar ul').hide();

    // good
    $('.sidebar > ul').hide();

    // good
    $sidebar.find('ul').hide();

⬆ 回到顶部

ECMAScript 5 兼容性

⬆ 回到顶部

测试

  • Yup.

    1
    2
    3
    function () {
    return true;
    }

⬆ 回到顶部

性能

⬆ 回到顶部

资源

推荐阅读

工具

其它风格指南

其它风格

进一步阅读

书籍

博客

播客

⬆ 回到顶部

谁在使用

这是一个使用本风格指南的组织列表。给我们发 pull request 或开一个 issue 让我们将你增加到列表上。

翻译

这份风格指南也提供了其它语言的版本:

JavaScript 风格指南说明

⬆ 回到顶部

};

转自 sivan/javascript-style-guide

评论和共享

项目指引

项目指引

1. Git

1.1 Some Git Rules

有一套要牢记的规则:

  • feature branch 进行工作.

    why:

    因为这样,所有的工作都是在一个专门的分支而不是主分支上隔离完成的。 它允许您提交多个拉请求而不会混淆。 您可以迭代,而不会污染具有潜在不稳定的未完成代码的主分支。 read more…

  • develop 拓展分支

    why:

    这样,您可以确保master中的代码几乎总是无问题地构建,并且可以直接用于发行版(对于某些项目而言,这可能是过度的)。

  • 不要developmaster上push分支。 做一个拉请求。

    why:

    它通知团队成员已完成功能。 它还能够轻松地对代码进行同行评审,并专门讨论论坛讨论所提出的功能

  • 在 push 你的 feature 并且提交一个 Pull Request 的时候,你应该先更新你本地 develop 分支,并且做一次交互式 rebase

    why:

    Rebasing将在请求的分支(masterdevelop)中合并,并将您本地进行的提交应用于历史的顶端,而不创建合并提交(假设没有冲突)。 产生一个漂亮和干净的历史。read more …

  • Pull Request 前,解决潜在冲突,并且 rebasing

  • 合并后删除本地和远程功能分支。

    why:

    它会使您的分支列表中的死枝混乱,确保您只将合并到(masterdevelop)一次。 功能部门只能在工作进行中存在。

  • Pull Request 前,确保你的 feature 分支可以构建成功并且通过所有测试(包括代码风格)

  • 使用 .gitignore.

  • 保护你的 developmaster 分支 .

1.2 书写良好的 commit messages

  • 将主体与身体之间用换行符分开
  • 用一个空行隔开标题和正文
  • 限制标题字数在 50 个字符内
  • 标题行的首字母大写
  • 不要用句号结束标题行
  • 在标题行使用祈使语气
  • 正文在 72 个字符处换行
  • 使用正文解释是什么和为什么,而不是如何做

3. 环境

3.1 一致的开发环境:

  • package.jsonengines 中配置你所用的 node 版本

    1
    { "engines" : { "node" : ">=0.10.3 <0.12" } }

    why:

    让他人知道你所用的 node.js 版本 read more…

  • 另外,使用 nvm 并在您的项目根目录中创建一个 .nvmrc。 不要忘了在文档中提及它

    why:

    所有使用 nvm 的人都可以简单的使用 nvm use 去选择合适的 node 版本 read more…

  • 您还可以使用 preinstall 脚本来检查节点和npm版本

    why:

    当使用新的版本node时,有些项目可能会失败

  • 使用 Docker images,只要它不会使事情更复杂

    why:

    它可以在整个工作流程中为您提供一致的环境。 没有太多的需要解决libs,依赖或配置。read more…

  • 使用本地模块,而不是使用全局安装的模块

    why:

    让你与你的同事分享你的工具,而不是期望他们在他们的系统上。

4. 依赖

在使用包之前,请检查它的GitHub。 查找开放问题的数量,每日下载次数和贡献者数量以及上次更新软件包的日期。

  • 如果不太了解依赖关系,请在使用之前与团队进行讨论。
  • 跟踪您当前可用的包: e.g: npm ls --depth=0. read more…
  • 看看您的任何包裹是否已被使用或不相关: depcheck. read more…
  • 检查下载统计信息,查看依赖关系是否被社区大量使用: npm-stat. read more…
  • 检查依赖关系是否具有良好的成熟版本发布频率与大量的维护者: e.g., npm view async. read more…
  • 始终确保您的应用程序适用于最新版本的依赖关系而不会中断: npm outdated. read more…
  • 检查包是否有已知安全漏洞, e.g, Snyk.

4.1 一致的依赖:

  • npm@5 或者更高版本上使用 package-lock.json
  • 对于旧版本的 npm, 当安装新的依赖时使用 --save --save-exact 并且发布前创建一个 npm-shrinkwrap.json
  • 或者你可以使用 Yarn 并且确保在 README.md 中提及. 你的 lock 文件和 package.json 在每次依赖关系更新后应该具有相同的版本。
  • 多读读这里: package-locks | npm Documentation

5. 测试

  • 如果需要,请使用测试模式环境。
  • 使用 *.test.js*.spec.js 命名约定将测试文件放在测试模块旁边
  • 将其他测试文件放入一个单独的测试文件夹以避免混淆。
  • 写可测试代码,避免副作用,提取副作用,写纯函数
  • 不要写太多的测试来检查类型,而是使用静态类型检查器
  • 做任何 对 developpull requests 前进行测试
  • 文档化您的测试,并附上说明。

6. 结构和命名

  • 围绕产品功能/页面/组件组织您的文件,而不是角色

Bad

1
2
3
4
5
6
7
.
├── controllers
| ├── product.js
| └── user.js
├── models
| ├── product.js
| └── user.js

Good

1
2
3
4
5
6
7
8
9
.
├── product
| ├── index.js
| ├── product.js
| └── product.test.js
├── user
| ├── index.js
| ├── user.js
| └── user.test.js
  • 使用 ./config 文件夹。 配置文件中使用的值由环境变量提供。
  • 将脚本放在 ./scripts 文件夹中。 这包括用于数据库同步,构建和捆绑等的 bashnode 脚本。
  • 将构建输出放在 ./build 文件夹中。 将 build/ 添加到 .gitignore
  • 使用 PascalCase(帕斯卡拼写法) 和 camelCase(驼式命名法) 作为文件名和目录名。 使用 PascalCase 仅用于组件名。
  • CheckBox/index.js 应该有 CheckBox 组件, 但 不是 CheckBox/CheckBox.js 或者 checkbox/CheckBox.js,这些是冗余的。
  • 理想情况下,目录名称应与 index.js 默认导出的名称相匹配。

7. 代码风格

  • 为新项目使用 stage-1 和更高版本的JavaScript(现代)语法。 对于旧项目,与现有语法保持一致,除非您打算使项目现代化。
  • 在构建过程之前包括代码样式检查
  • 使用 ESLint - Pluggable JavaScript linter 来强制执行代码样式。
  • 使用 Airbnb JavaScript Style Guide 的JavaScript样式指南,阅读更多。使用项目或您的团队所需的JavaScript风格指南。
  • 使用 Flow type style check rules for ESLint. ,当使用 FlowType
  • 使用 .eslintignore 从代码样式检查中排除文件或文件夹。
  • 删除任何 eslint 禁用注释,然后再执行 Pull Request
  • 始终使用 //TODO: 评论来提醒自己和他人关于未完成的工作。
  • 始终评论并保持与代码更改相关。
  • 尽可能删除注释的代码块。
  • 避免生产中的js警报。
  • 避免不相关或有趣的评论,日志或命名(源代码可能会交给另一家公司/客户,他们可能不会分享同样的笑声)。
  • 编写可测试代码,避免副作用,提取副作用,写纯函数。
  • 使您的名字可以搜索到有意义的区别,避免缩短名称。 对于函数使用长的描述性名称。 功能名称应该是一个动词或动词短语,需要传达其意图。
  • 按照降档规则在文件中组织您的功能。 较高级别的职能应在上下级以上。 这样读取源代码就更为自然了。

8. 日志

  • 避免生产中的客户端控制台日志
  • 生产可读的生产日志。 理想地使用在生产模式下使用的日志记录库(winston
    node-bunyan

9. API 设计

遵循资源导向的设计。 这有三个主要因素:resources, collection, 和 URLs.

  • 资源具有数据,与其他资源的关系以及对其进行操作的方法
  • 一组资源称为集合
  • URL标识资源的在线位置

9.1 API Naming

9.1.1 Naming URLs

  • /users 一组用户(复数名词)。
  • /users/id 具有特定用户信息的资源。
  • 资源始终应该是URL中的复数。 将动词从资源网址中删除。
  • 使用动词非资源。 在这种情况下,您的API不会返回任何资源。 而是执行一个操作并将结果返回给客户机。 因此,您应该在URL中使用动词而不是名词来清楚地区分与资源相关的响应中的非资源响应。

GET /translate?text=Hallo

9.1.2 Naming fields

  • 请求体或响应类型是JSON,请遵循 camelCase 来保持一致性。
  • 公开资源,而不是数据库模式详细信息。 您不必使用您的 table_name 作为资源名称。 与资源属性相同,它们不应与您的列名称相同。
  • 仅在您的网址命名中使用名词,不要尝试解释其功能,只能解释资源(优雅)

9.2 Operating on resources

9.2.1 Use HTTP methods

Only use nouns in your resource URLs, avoid endpoints like /addNewUser or /updateUser . Also avoid sending resource operations as a parameter. Instead explain the functionalities using HTTP methods:

  • GET Used to retrieve a representation of a resource.
  • POST Used to create new resources and sub-resources
  • PUT Used to update existing resources
  • PATCH Used to update existing resources. PATCH only updates the fields that were supplied, leaving the others alone
  • DELETE Used to delete existing resources

9.3 Use sub-resources

Sub resources are used to link one resource with another, so use sub resources to represent the relation.
An API is supposed to be an interface for developers and this is a natural way to make resources explorable.
If there is a relation between resources like employee to a company, use id in the URL:

  • GET /schools/2/students Should get the list of all students from school 2
  • GET /schools/2/students/31 Should get the details of student 31, which belongs to school 2
  • DELETE /schools/2/students/31 Should delete student 31, which belongs to school 2
  • PUT /schools/2/students/31 Should update info of student 31, Use PUT on resource-URL only, not collection
  • POST /schools Should create a new school and return the details of the new school created. Use POST on collection-URLs

9.4 API Versioning

When your APIs are public for other third parties, upgrading the APIs with some breaking change would also lead to breaking the existing products or services using your APIs. Using versions in your URL can prevent that from happening:
http://api.domain.com/v1/schools/3/students

9.5 Send feedbacks

9.5.1 Errors

Response messages must be self descriptive. A good error message response might look something like this:

1
2
3
4
5
{
"code": 1234,
"message" : "Something bad happened",
"description" : "More details"
}

or for validation errors:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"code" : 2314,
"message" : "Validation Failed",
"errors" : [
{
"code" : 1233,
"field" : "email",
"message" : "Invalid email"
},
{
"code" : 1234,
"field" : "password",
"message" : "No password provided"
}
]
}

Note: Keep security exception messages as generic as possible. For instance, Instead of saying ‘incorrect password’, you can reply back saying ‘invalid username or password’ so that we don’t unknowingly inform user that username was indeed correct and only password was incorrect.

9.5.2 Align your feedback with HTTP codes.

The client and API worked (success – 2xx response code)
  • 200 OK This HTTP response represents success for GET, PUT or POST requests.
  • 201 Created This status code should be returned whenever a new instance is created. E.g on creating a new instance, using POST method, should always return 201 status code.
  • 204 No Content represents the request was successfully processed, but has not returned any content. DELETE can be a good example of this. If there is any error, then the response code would be not be of 2xx Success Category but around 4xx Client Error category.
The client application behaved incorrectly (client error – 4xx response code)
  • 400 Bad Request indicates that the request by the client was not processed, as the server could not understand what the client is asking for.
  • 401 Unauthorized indicates that the request lacks valid credentials needed to access the needed resources, and the client should re-request with the required credentials.
  • 403 Forbidden indicates that the request is valid and the client is authenticated, but the client is not allowed access the page or resource for any reason.
  • 404 Not Found indicates that the requested resource was not found.
  • 406 Not Acceptable A response matching the list of acceptable values defined in Accept-Charset and Accept-Language headers cannot be served.
  • 410 Gone indicates that the requested resource is no longer available and has been intentionally and permanently moved.
The API behaved incorrectly (server error – 5xx response code)
  • 500 Internal Server Error indicates that the request is valid, but the server could not fulfill it due to some unexpected condition.
  • 503 Service Unavailable indicates that the server is down or unavailable to receive and process the request. Mostly if the server is undergoing maintenance or facing a temporary overload.

9.6 Resource parameters and metadata

  • Provide total numbers of resources in your response
  • The amount of data the resource exposes should also be taken into account. The API consumer doesn’t always need the full representation of a resource.Use a fields query parameter that takes a comma separated list of fields to include:

    1
    GET /student?fields=id,name,age,class
  • Pagination, filtering and sorting don’t need to be supported by default for all resources. Document those resources that offer filtering and sorting.

9.7 API security

9.7.1 TLS

To secure your web API authentication, all authentications should use SSL. OAuth2 requires the authorization server and access token credentials to use TLS.
Switching between HTTP and HTTPS introduces security weaknesses and best practice is to use TLS by default for all communication. Throw an error for non-secure access to API URLs.

9.7.2 Rate limiting

If your API is public or have high number of users, any client may be able to call your API thousands of times per hour. You should consider implementing rate limit early on.

9.7.3 Input Validation

It’s difficult to perform most attacks if the allowed values are limited.

  • Validate required fields, field types (e.g. string, integer, boolean, etc), and format requirements. Return 400 Bad Request with details about any errors from bad or missing data.

  • Escape parameters that will become part of the SQL statement to protect from SQL injection attacks

  • As also mentioned before, don’t expose your database scheme when naming your resources and defining your responses

9.7.4 URL validations

Attackers can tamper with any part of an HTTP request, including the URL, query string,

9.7.5 Validate incoming content-types.

The server should never assume the Content-Type. A lack of Content-Type header or an unexpected Content-Type header should result in the server rejecting the content with a 406 Not Acceptable response.

9.7.6 JSON encoding

A key concern with JSON encoders is preventing arbitrary JavaScript remote code execution within the browser or node.js, on the server. Use a JSON serialiser to entered data to prevent the execution of user input on the browser/server.

9.8 API documentation

  • Fill the API Reference section in README.md template for API.
  • Describe API authentication methods with a code sample
  • Explaining The URL Structure (path only, no root URL) including The request type (Method)

For each endpoint explain:

  • URL Params If URL Params exist, specify them in accordance with name mentioned in URL section

    1
    2
    Required: id=[integer]
    Optional: photo_id=[alphanumeric]
  • If the request type is POST, provide a working examples. URL Params rules apply here too. Separate the section into Optional and Required.

  • Success Response, What should be the status code and is there any return data? This is useful when people need to know what their callbacks should expect!

    1
    2
    Code: 200
    Content: { id : 12 }
  • Error Response, Most endpoints have many ways to fail. From unauthorised access, to wrongful parameters etc. All of those should be listed here. It might seem repetitive, but it helps prevent assumptions from being made. For example

    1
    2
    3
    4
    5
    {
    "code": 403,
    "message" : "Authentication failed",
    "description" : "Invalid username or password"
    }

9.8.1 API design tools

There are lots of open source tools for good documentation such as API Blueprint and Swagger.

10. Licensing

Make sure you use resources that you have the rights to use. If you use libraries, remember to look for MIT, Apache or BSD but if you modify them, then take a look into licence details. Copyrighted images and videos may cause legal problems.


Sources:
RisingStack Engineering,
Mozilla Developer Network,
Heroku Dev Center,
Airbnb/javascript
Atlassian Git tutorials

评论和共享

git rebase

rebase

假设你现在基于远程分支”origin”,创建一个叫”mywork”的分支。

1
$ git checkout -b mywork origin

现在我们在这个分支做一些修改,然后生成两个提交(commit).

1
2
3
4
5
$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit
...

但是与此同时,有些人也在”origin”分支上做了一些修改并且做了提交了. 这就意味着”origin”和”mywork”这两个分支各自”前进”了,它们之间”分叉”了。

rebase1

在这里,你可以用”pull”命令把”origin”分支上的修改拉下来并且和你的修改合并; 结果看起来就像一个新的”合并的提交”(merge commit):

rebase2

但是,如果你想让”mywork”分支历史看起来像没有经过任何合并一样,你也许可以用 git rebase:

1
2
$ git checkout mywork
$ git rebase origin

这些命令会把你的”mywork”分支里的每个提交(commit)取消掉,并且把它们临时 保存为补丁(patch)(这些补丁放到”.git/rebase”目录中),然后把”mywork”分支更新 到最新的”origin”分支,最后把保存的这些补丁应用到”mywork”分支上。

rebase3

当’mywork’分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。 如果运行垃圾收集命令(pruning garbage collection), 这些被丢弃的提交就会删除. (请查看 git gc)

rebase4

现在我们可以看一下用合并(merge)和用rebase所产生的历史的区别:

rebase5

在rebase的过程中,也许会出现冲突(conflict). 在这种情况,Git会停止rebase并会让你去解决 冲突;在解决完冲突后,用”git-add”命令去更新这些内容的索引(index), 然后,你无需执行 git-commit,只要执行:

1
$ git rebase --continue

这样git会继续应用(apply)余下的补丁。

在任何时候,你可以用–abort参数来终止rebase的行动,并且”mywork” 分支会回到rebase开始前的状态。

1
$ git rebase --abort

评论和共享

客户端JavaScript时间线

  1. 浏览器创建 Document 对象,并开始解析Web页面,解析 HTML 元素和它们的文本内容后添加 Element 对象和 Text 节点套文档中。这个阶段 document.readyState 属性的值是 "loading"
  2. HTML 解释器遇到了没有 asyncdefer 属性的 <script> 元素时,他把这些元素添加到文档中,并且同步执行。在脚本下载(如果需要)和执行时解释器会暂停。这样脚本就可以用 document.write() 来把文本插入到输入流中。
  3. 当遇到 async 属性(如果 <script> 标签同时有 asyncdefer 属性,会遵从 async 并忽略 defer)的 <script> 元素时,它开始下载脚本文本,并继续解析文档。脚本会在它下载完成后尽快执行。禁止使用 document.write() 方法。
  4. 当文档完成解析,document.readyState 属性变成 "interactive"
  5. 所有带有 defer 属性的脚本,会按它们在文档里出现的顺序执行。异步脚本可能也会在这个时间执行。延迟脚本能访问完整的文档树,禁止使用 document.write() 方法。
  6. 浏览器在 Document 对象上触发 DOMContentLoaded 事件。这标志着 程序执行从同步脚本执行阶段转换到了异步事件驱动阶段 但是,这时可能还有异步脚本没有执行完成。
  7. 这时,文档已经完成解析完成,但是浏览器可能还在等待其他内容载入,如图片。当所有这些内容完全载入时,并且所有异步脚本完全载入和执行,document.readyState 属性改变为 complete ,Web浏览器触发 window 对象上的 load 事件。
  8. 从此刻起,会调用异步事件,以异步响应用户输入事件,网络事件,计时器过期等。

文档加载事件

事件名称 描述
readystatechange 文档还在加载:loading, 文档解析完成:interactive, 文档完全加载完成:complete
DOMContentLoaded 程序执行从同步脚本执行阶段转换到了异步事件驱动阶段
load 所有内容完全载入,所有异步脚本完全载入和执行
1
2
3
4
5
6
7
8
9
10
11
document.addEventListener('DOMContentLoaded', function(){
console.log('DOMContentLoaded')
}, false)

window.addEventListener('load', function(){
console.log('load')
}, false)

document.onreadystatechange = function(){
console.log(document.readyState)
}

运行结果:

1
2
3
4
interactive
DOMContentLoaded
complete
load

评论和共享

Canvas 线条长拖尾效果

效果图

效果图

分析

其实这个效果不是线条本身就是这个形状的,而是下面这样的

point

之所以会出现这个效果,是因为 Canvas 在画下一帧的时候并没有将上一帧的画面全部擦除,而是用一个和背景一样的透明度非常低的颜色填充了绘图区域,导致上一帧的点比下一帧明度低,所以多次叠加就变成了上面的效果。

关键代码

1
2
ctx.fillStyle = 'rgba(0,0,0,.1)';
ctx.fillRect(0,0,cw,ch);

评论和共享

设计模式:门面模式(Facade Pattern)

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,它相比生成子类更为灵活

类型

结构类模式

类图

Facade Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class ClassA {
public void doSomethingA(){
//业务逻辑
}
}

public class ClassB {
public void doSomethingB() {
//业务逻辑
}
}

public class ClassC {
public void doSomethingC() {
//业务逻辑
}
}

public class Facade {
private ClassA a = new ClassA();
private ClassB b = new ClassB();
private ClassC c = new ClassC();

public void methodA() {
this.a.doSomethingA();
}
public void methodB() {
this.b.doSomethingB();
}
public void methodC() {
this.c.doSomethingC();
}
}

优点

  • 减少系统的互相依赖
  • 提高了灵活性
  • 提高安全性

缺点

不符合开闭原则

使用场景

  • 为一个复杂的模块或子系统提供一个供外界访问的接口
  • 子系统相对独立——外界对子系统的访问只要黑箱操作即可
  • 预防低水平人员带来的风险扩散

评论和共享

设计模式:享元模式模式(Flyweight Pattern)

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,它相比生成子类更为灵活

类型

结构类模式

类图

Flyweight Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public abstract class Flyweight{
//内部状态
private String intrinsic;
//外部状态
protected final String Extrinsic;
//享元角色必须接受外部状态
public Flyweight(String _Extrinsic){
this.Extrinsic = _Extrinsic;
}

public abstract void operate();

public String getIntrinsic() {
return intrinsic;
}

public void setIntrinsic(String intrinsic){
this.intrinsic = intrinsic;
}
}

public class ConcreteFlyweight1 extends Flyweight{
public ConcreteFlyweight1(String _Extrinsic){
super(_Extrinsic);
}
public void operate(){
//业务逻辑
}
}

public class ConcreteFlyweight2 extends Flyweight{
public ConcreteFlyweight2(String _Extrinsic){
super(_Extrinsic);
}
public void operate(){
//业务逻辑
}
}

public class FlyweightFactory{
private static HashMap<String, Flyweight> pool = new HashMap<String, Flyweight>();

//享元工厂
public static Flyweight getFlyweight(String Extrinsic){
Flyweight flyweight = null;

if(pool.containsKey(Extrinsic)){
flyweight = pool.get(Extrinsic);
}else{
flyweight = new ConcreteFlyweight1(Extrinsic);
pool.put(Extrinsic, flyweight);
}
return flyweight;
}
}

优点

大大减少应用程序创建的对象,减低程序内的占用,增强程序的性能

## 缺点
提高了系统的复杂性,需要分离出外部状态和内部状态。

使用场景

  • 系统中存在大量的相似对象
  • 细粒度的对象都具备较结晶的外部状态
  • 需要缓冲池的场景

评论和共享

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,它相比生成子类更为灵活

类型

结构类模式

类图

Decorator Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public abstract class Component {
public abstract void operate();
}

public class ConcreteComponent extends Component {
@Override
public void operate() {
System.out.println("do Something");
}
}

public abstract class Decorator extends Component {
private Component component = null;

public Decorator(Component _component){
this.component = _component;
}

@Override
public void operate() {
this.component.operate();
}
}

public class ConcreteDecorator1 extends Decorator {
public ConcreteDecorator1(Component _component){
super(_component);
}
//定义自己的修饰方法
private void operate(){
System.out.println("method1 修饰");
}

public void operate() {
this.method1();
super.operate();
}
}

public class Client{
public static void main(String[] args){
Component component = new ConcreteComponent();
component = new ConcreteDecorator1(component);
component = new ConcreteDecorator2(component);
component.operate();
}
}

优点

  • 装饰类和被装饰类可以独立发展
  • 装饰模式是继承关系的代替方案
  • 装饰模式可以动态地扩展一个实现类的功能

## 缺点
多层的装饰是比较复杂的,尽量减少装饰类的数量,以便降低系统的复杂度。

评论和共享

定义

将对象组合成树型结构,以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

类型

结构类模式

类图

Composite Pattern

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public abstract class Component {
public void doSomething(){
//业务逻辑
}
}

public class Composite extends Component {
private ArrayList<Component> componentArrayList = new ArrayList<Component>();

public void add(Component component){
this.componentArrayList.add(component);
}

public void remove(Component component){
this.componentArrayList.remove(component);
}

public ArrayList<Component> getChildren() {
return this.componentArrayList;
}
}

public class Leaf extends Component {
public void doSomething() {

}
}

public class Client {
public static void main(String[] args){
Composite root = new Composite();
root.doSomething();

Composite branch = new Composite();
Leaf leaf = new Leaf();
root add(leaf);
}
}

优点

  • 高层模块调用简单
  • 节点自由增加

缺点

树叶和树枝使用时直接使用了实现类,在面向接口编程上是很不恰当的,与依赖倒置原则冲突。

## 使用场景

  • 维护和展示部分——整体关系的场景,比如树形菜单,文件和文件夹管理
  • 从一个整体中能够独立出部分模块或功能的场景

评论和共享

作者的图片

Archie Shi

Nothing to say


Front-End Development Engineer