JavaScript Good Parts学习笔记-函数篇


 1 解决this指针指向全局的一种方法。(用that指针保存this)
     // Augment myObject with a double method.
     myObject.double = function() {
        var that = this; // Workaround.
        var helper = function() {
            that.value = add(that.value, that.value);
        };
        helper(); // Invoke helper as a function.
     };
     // Invoke double as a method.
     myObject.double();
     document.writeln(myObject.value);


 2 不推荐使用构造器调用模式(The Constructor Invocation Pattern)
   即:函数名大写,然后使用new构造新对象

    // Create a constructor function called Quo.
    // It makes an object with a status property.
    var Quo = function(string) {
        this.status = string;
    };
    // Give all instances of Quo a public method
    // called get_status.
    Quo.prototype.get_status = function() {
        return this.status;
    };
    // Make an instance of Quo.
    var myQuo = new Quo("confused");
    document.writeln(myQuo.get_status()); // confused


3 Apply调用模式(The Apply Invocation Pattern)

The apply method lets us construct an array of arguments to use to invoke a function.
It also lets us choose the value of this. The apply method takes two parameters.
The first is the value that should be bound to this. The second is an array of
parameters.


    // Make an array of 2 numbers and add them.
    var array = [3, 4];
    var sum = add.apply(null, array); // sum is 7
    // Make an object with a status member.
    var statusObject = {
        status: 'A-OK'
    };
    // statusObject does not inherit from Quo.prototype,
    // but we can invoke the get_status method on
    // statusObject even though statusObject does not have
    // a get_status method.
    var status = Quo.prototype.get_status.apply(statusObject);
    // status is 'A-OK'


4 参数(Arguments)
  A bonus parameter that is available to functions when they are invoked is the
arguments array.

Because of a design error, arguments is not really an array. It is an array-like object.
arguments has a length property, but it lacks all of the array methods.


5 扩充类型的功能(Augmenting Types)
我们可以通过给Function.prototype增加方法使得该方法对所有函数可用
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};

可以通过给Number.prototype增加一个integer方法来取整。
Number.method('integer', function ( ) {
return Math[this < 0 ? 'ceil' : 'floor'](this);
});
document.writeln((-10 / 3).integer( )); // -3
还可以增加一个字符串的trim方法。

String.method('trim', function ( ) {
return this.replace(/^\s+|\s+$/g, '');
});
document.writeln('"' + " neat ".trim( ) + '"');


基本类型的原型是公用结构,所以在类库混用是务必小心,
一个保险的做法是只有确定在没有该方法的时候才添加
// Add a method conditionally.
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
return this;
}
};


6 递归(Recursion)
可以非常高效的操作树形结构,比如浏览器的文档对象类型DOM
    // Define a walk_the_DOM function that visits every
    // node of the tree in HTML source order, starting
    // from some given node. It invokes a function,
    // passing it each node in turn. walk_the_DOM calls
    // itself to process each of the child nodes.
    var walk_the_DOM = function walk(node, func) {
        func(node);
        node = node.firstChild;
        while (node) {
            walk(node, func);
            node = node.nextSibling;
        }
    };
    // Define a getElementsByAttribute function. It
    // takes an attribute name string and an optional
    // matching value. It calls walk_the_DOM, passing it a
    // function that looks for an attribute name in the
    // node. The matching nodes are accumulated in a
    // results array.
    var getElementsByAttribute = function(att, value) {
        var results = [];
        walk_the_DOM(document.body, function(node) {
            var actual = node.nodeType === 1 && node.getAttribute(att);
            if (typeof actual === 'string' &&
                (actual === value || typeof value !== 'string')) {
                results.push(node);
            }
        });
        return results;
    };


7 作用域(Scope)
 缺少块级作用域,所以最好在顶部定义所有的变量。
注意:有函数作用域


8闭包(Closure)
函数可以访问他被创建时的上下文环境,这被称为闭包。

内部函数可以拥有比它的外部函数更长的生命周期,见下面例子。
var myObject = (function() {
    var value = 0;
    return {
        increment: function(inc) {
            value += typeof inc === 'number' ? inc : 1;
        },
        getValue: function() {
            return value;
        }
    };
}());

Let’ s look at a more useful example:
    // Define a function that sets a DOM node's color
    // to yellow and then fades it to white.
    var fade = function(node) {
        var level = 1;
        var step = function() {
            var hex = level.toString(16);
            node.style.backgroundColor = '#FFFF' + hex + hex;
            if (level < 15) {
                level += 1;
                setTimeout(step, 100);
            }
        };
        setTimeout(step, 100);
    };
fade(document.body);

为了避免下面的问题,理解内部函数能访问外部函数的实际变量而无需复制是很重要的
//糟糕的例子
// Make a function that assigns event handler functions to an array of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal of the node.
// But it always displays the number of nodes instead.
var add_the_handlers = function(nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function(e) {
            alert(i);
        };
    }
};


//好的例子
// Make a function that assigns event handler functions to an array of nodes.
// When you click on a node, an alert box will display the ordinal of the node.
var add_the_handlers = function(nodes) {
    var helper = function(i) {
        return function(e) {
            alert(i);
        };
    };
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = helper(i);
    }
};

9 回调(Callbacks)

10 模块(Module)
模块是一个提供接口却隐藏状态与实现的函数或对象。
通过使用函数产生模块,我们几乎可以完全摒弃全局变量的使用。

下面的例子把html的字符实体转化为他们对应的字符 即:&quot; -> " &lt; -> < &gt; -> >
String.method('deentityify', function() {
    // The entity table. It maps entity names to
    // characters.
    var entity = {
        quot: '"',
        lt: '<',
        gt: '>'
    };
    // Return the deentityify method.
    return function() {
        // This is the deentityify method. It calls the string
        // replace method, looking for substrings that start
        // with '&' and end with ';'. If the characters in
        // between are in the entity table, then replace the
        // entity with the character from the table. It uses
        // a regular expression (Chapter 7).
        return this.replace(/&([^&;]+);/g,
            function(a, b) {
                var r = entity[b];
                return typeof r === 'string' ? r : a;
            }
        );
    };
}());

11 级联(Cascade)
有一些函数没有返回值,如果让这些函数返回this而不是undefined,就可以启用级联。
下面是一个ajax的例子
getElement('myBoxDiv')
    .move(350, 150)
    .width(100)
    .height(100)
    .color('red')
    .border('10px outset')
    .padding('4px')
    .appendText("Please stand by")
    .on('mousedown', function(m) {
        this.startDrag(m, this.getNinth(m));
    })
    .on('mousemove', 'drag')
    .on('mouseup', 'stopDrag')
    .later(2000, function() {
        this
            .color('yellow')
            .setHTML("What hath God wraught?")
            .slide(400, 40, 200, 200);
    })
tip('This box is resizeable');
注: 一个接口没必要一次干太多事情

12 柯里化(Curry)
函数也是值,从而我们可以用有趣的方式去操作函数值。柯里化允许我们把函数与传递个它的参数相结合,产生一个新的函数。
var add1 = add.curry(1);
document.writeln(add1(6)); // 7

Function.method('curry', function() {
    var slice = Array.prototype.slice,
        args = slice.apply(arguments),
        that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
});


13 记忆(Memoization)
函数可以将之前的结果存放在某个对象中,从而避免无谓的重复操作,这种优化称为记忆
例如,兔子数列 可以按下面方式实现(在一个memo数组中存放结果)

var fibonacci = (function() {
    var memo = [0, 1];
    var fib = function(n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fib(n - 1) + fib(n - 2);
            memo[n] = result;
        }
        return result;
    };
    return fib;
}());


我们可以把这种技术推而广之,编写一个函数来帮我们构造带记忆功能的函数。
memoizer函数取得一个初始的memo数组和formula函数,返回一个管理memo存储和在需要调用formula函数的recur函数。
我们把这个recur函数和它的参数传给formula函数

var memoizer = function(memo, formula) {
    var recur = function(n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = formula(recur, n);
            memo[n] = result;
        }
        return result;
    };
    return recur;
};

接下来,兔子数列可以这样实现

var fibonacci = memoizer([0, 1], function(recur, n) {
    return recur(n - 1) + recur(n - 2);
});

posted on 2017-02-14 09:35  sdfczyx  阅读(399)  评论(0编辑  收藏  举报