• Javascript高级程序设计


    1.高级函数

    ①var isArray = value instanceof Array;

    上述代码要返回true,value必须是一个数组,而且还必须与Array构造函数在同个全局作用域中。(别忘了,Array是window的属性)。如果value是在另个frame里定义的数组,那么以上代码就会返回false

    在任何值上调用Object原生的toString()方法,都会返回[object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属性,这个属性就指定了上述字符串的构造函数名。

    alert(Object.prototype.toString.call(value));     //"[object Array]"

    由于原生数组的构造函数名与全局作用域无关,所以使用toString()都能保证返回一致的值。因此验证是否是一个数组:

    1. function isArray(value){
    2. return Object.prototyoe.toString.call(value) == [object Array];
    3. }

    ②作用域安全的构造函数

    1. function Person(name, age, job){
    2. if (this instanceof Person){
    3. this.name = name;
    4. this.age = age;
    5. this.job = job;
    6. } else {
    7. return new Person(name, age, job);
    8. }
    9. }
    10.  
    11. var person1 = Person("Nicholas", 29, "Software Engineer");
    12. alert(window.name); //""
    13. alert(person1.name); //"Nicholas"
    14.  
    15. var person2 = new Person("Shelby", 34, "Ergonomist");
    16. alert(person2.name); //"Shelby"


    想一想,如果没验证this是否为Person,person1中的this就会解析成window对象。由于window.name属性是用于识别链接目标和frame的,所以这里对该属性的偶然覆盖可能会导致该页面上出现错误。window.name的作用是什么?详情戳:window.name实现的跨域

    ③惰性载入函数

    1. function createXHR(){
    2. if (typeof XMLHttpRequest != "undefined"){
    3. return new XMLHttpRequest();
    4. } else if (typeof ActiveXObject != "undefined"){
    5. if (typeof arguments.callee.activeXString != "string"){
    6. var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
    7. "MSXML2.XMLHttp"],
    8. i, len;
    9.  
    10. for (i=0,len=versions.length; i < len; i++){
    11. try {
    12. new ActiveXObject(versions[i]);
    13. arguments.callee.activeXString = versions[i];
    14. break;
    15. } catch (ex){
    16. //skip
    17. }
    18. }
    19. }
    20.  
    21. return new ActiveXObject(arguments.callee.activeXString);
    22. } else {
    23. throw new Error("No XHR object available.");
    24. }
    25. }

    像上面那样,每次调用createXHR(),它都要对浏览器所支持的能力仔细检查,所以如果if语言不必每次都执行,那么代码可以运行地更快一些,解决方案就是称之为惰性载入的技巧

    惰性函数表示函数执行的分支仅会发生一次。有两种实现惰性载入的方式。

    第一种就是在函数被调用时再处理函数。在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支。

    1. function createXHR(){
    2. if (typeof XMLHttpRequest != "undefined"){
    3. createXHR = function(){
    4. return new XMLHttpRequest();
    5. };
    6. } else if (typeof ActiveXObject != "undefined"){
    7. createXHR = function(){
    8. if (typeof arguments.callee.activeXString != "string"){
    9. var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
    10. "MSXML2.XMLHttp"],
    11. i, len;
    12.  
    13. for (i=0,len=versions.length; i < len; i++){
    14. try {
    15. new ActiveXObject(versions[i]);
    16. arguments.callee.activeXString = versions[i];
    17. } catch (ex){
    18. //skip
    19. }
    20. }
    21. }
    22.  
    23. return new ActiveXObject(arguments.callee.activeXString);
    24. };
    25. } else {
    26. createXHR = function(){
    27. throw new Error("No XHR object available.");
    28. };
    29. }
    30.  
    31. return createXHR();
    32. }
    33.  
    34. var xhr1 = createXHR();
    35. var xhr2 = createXHR();

    这个堕入载入的createXHR()钟,if语句的每一个分支都会为creatXHR()变量赋值,有效覆盖了原有的函数。最后一步便是调用新赋的函数。下次调用createXHR()的时候,就会直接调用被分配的函数,这样就不用再次执行if语句了。

    第二种实现惰性载入的方式是在声明函数时就指定适当的函数。这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一点性能。

    1. var createXHR = (function(){
    2. if (typeof XMLHttpRequest != "undefined"){
    3. return function(){
    4. return new XMLHttpRequest();
    5. };
    6. } else if (typeof ActiveXObject != "undefined"){
    7. return function(){
    8. if (typeof arguments.callee.activeXString != "string"){
    9. var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
    10. "MSXML2.XMLHttp"],
    11. i, len;
    12.  
    13. for (i=0,len=versions.length; i < len; i++){
    14. try {
    15. new ActiveXObject(versions[i]);
    16. arguments.callee.activeXString = versions[i];
    17. break;
    18. } catch (ex){
    19. //skip
    20. }
    21. }
    22. }
    23.  
    24. return new ActiveXObject(arguments.callee.activeXString);
    25. };
    26. } else {
    27. return function(){
    28. throw new Error("No XHR object available.");
    29. };
    30. }
    31. })();
    32.  
    33. var xhr1 = createXHR();
    34. var xhr2 = createXHR();

    总结:惰性载入函数的优点是只在执行分支代码时牺牲一点而性能。至于两种方式哪种更适合,就要看你的具体需求而定。不过这两种方式都能避免执行不必要的代码。

    4.函数绑定

    该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境

    函数绑定的bind方法是:fun.bind(binedObject),binedObject是为fun函数绑定的对象,但这原生的bind()方法的兼容性为:IE9+、Firefox、Chrome,得自己写一个bind()函数

    1. var handler = {
    2. message: "Event handled",
    3.  
    4. handleClick: function(event){
    5. alert(this.message + ":" + event.type);
    6. }
    7. };
    8.  
    9. var btn = document.getElementById("my-btn");
    10. EventUtil.addHandler(btn, "click", handler.handleClick));

    这个this对象指向了DOM按钮而非handler(在IE中,this指向window),所以可以使用闭包来修改这个问题。

    1. function bind(fn, context){
    2. return function(){
    3. return fn.apply(context, arguments);
    4. };
    5. }
    6.  
    7. var handler = {
    8. message: "Event handled",
    9.  
    10. handleClick: function(event){
    11. alert(this.message + ":" + event.type);
    12. }
    13. };
    14.  
    15. var btn = document.getElementById("my-btn");
    16. EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));

    不过不懂的是为什么要在bind()函数的闭包函数也return,明明不写return也能正常执行的,而且返回的还是undefined

    这个函数看似简单,但其功能是非常强大的。注意这里使用的arguments对象是内部函数的,而非bind()的。

    函数绑定主要用于事件处理程序以及setTimeout()和setInterval()。然而,被绑定函数与普通函数相比有更多的开销,它们需要更多内存(闭包),同时也因为多重函数调用稍微慢一些,所以最好只在必要时使用。

    ⑤函数柯里化(function currying)(但说实话,我还是没搞懂要怎么用,好扯淡。。)

    它用于创建已经设置好了一个或多个参数的函数。基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的函数

    柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。其简单概念如下:

    1. function add(num1, num2){
    2. return num1 + num2;
    3. }
    4.  
    5. function curriedAdd(num2){
    6. return add(5, num2);
    7. ]
    8.  
    9. alert(add(2, 3)); //5
    10. alert(curriedAdd(3)); //8


    下面是创建柯里化函数的通用方式:

    1. function curry(fn){
    2. var args = Array.prototype.slice.call(arguments, 1); //args存放外部函数参数,除了fn这个参数
    3. return function(){
    4. var innerArgs = Array.prototype.slice.call(arguments), //存放内部函数参数
    5. finalArgs = args.concat(innerArgs);
    6. return fn.apply(null, finalArgs);
    7. };
    8. }
    9.  
    10. function add(num1, num2){
    11. return num1 + num2;
    12. }
    13.  
    14. var curriedAdd = curry(add, 5);
    15. alert(curriedAdd(3)); //8
    16.  
    17. var curriedAdd2 = curry(add, 5, 12);
    18. alert(curriedAdd2()); //17


    函数柯里化还常常作为函数绑定的一部分包含在其中

    1. function bind(fn, context){
    2. var args = Array.prototype.slice.call(arguments, 2); //注意这里要传除了函数参数fn和对象参数context
    3. return function(){
    4. var innerArgs = Array.prototype.slice.call(arguments),
    5. finalArgs = args.concat(innerArgs);
    6. return fn.apply(context, finalArgs); //注意这里要传入context参数
    7. };
    8. }
    9.  
    10. var handler = {
    11. message: "Event handled",
    12.  
    13. handleClick: function(name, event){
    14. alert(this.message + ":" + name + ":" + event.type);
    15. }
    16. };
    17.  
    18. var btn = document.getElementById("my-btn");
    19. EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));

    JavaScript中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是curry()要根据是否需要object对象响应来决定。它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外开销。

    2.防篡改对象

    注意的是:一旦把对象定义为防篡改,就无法撤销了。ECMAScript增加了几个方法,通过它们可以指定对象的行为

    ①不可扩展对象:第一保护级别

    使用Object.preventExtensions(object)使你不能再给对象添加属性和方法,object为你要改变的对象。在非严格模式下,尝试,给对象添加新成员会导致静默失败,因此object.property将是undefined。而在严格模式下,尝试给不可扩展的对象添加新成员会导致错误。但不会对已有的对象成员造成应该。

    另外可使用Object.isExtensible()方法确定对象是否可扩展

    ②密封的对象:第二保护级别

    密封对象不可扩展,而且已有成员的[[Configurable]]特性将被设置为false,这将意味着不能删除属性和方法。但已有属性值是可修改的。在严格模式和非严格模式下的影响和不可扩展对象一样,导致错误或被忽略。

    另外可使用Object.isSealed()可确定对象是否被密封了。

    ③冻结的对象:第三保护级别

    最严格的防篡改级别。冻结的对象既不可扩展,又是密封的,而且对象数据属性的[ [ Writable ]]特性会被设置为false。如果设置[ [ Set] ]函数,访问器属性仍然是可写的。在严格模式和非严格模式下的影响和不可扩展对象一样,导致错误或被忽略。

    可使用Object.isFrozen()方法确定是否检测冻结对象

    对JavaScript库的作者而言,冻结对象是很很有用的。因为JavaScript库最怕有人意外(或有意)地修改了库中的核心对象。冻结(或密封)主要的库对象能够防止这些问题的发生。

    3.高级定时器

    虽然人们对JavaScript的定时器存在普遍的误解,认为它们是线程,其实JavaScript是运行在单线程的环境中的,而定时器仅仅只是计划在未来的某个时间执行。执行实际是不能保证的,因为在页面的声明周期内,不同时间可能有其他代码在控制进程。(如设定一个150ms的定时器不代表到了150ms代码就立刻执行,它表示会在150ms后会被加入到执行队列中)在页面下载完后的代码运行、时间处理程序、Ajax回调函数都必须使用同样的线程来执行。实际上,浏览器负责进行排序,指派某段代码在某个时间点运行的优先级。

    ①重复的定时器:setInterval()

    重复的定时器问题在于,定时器代码可能在代码再次被添加到队列之前还没完全执行,结果导致定时器代码连续执行好几次,而之间没有停顿。幸好,JavaScript引擎够聪明,能避免这个问题。当使用setInterval()时,仅当没有该定时器代码实例时,才将定时器代码代码添加到队列中。这确保定时器代码加入到队列中的最小时间间隔为指定间隔。

    这个规则有两个问题:(1)某些间隔会被跳过;(2)多个定时器的代码执行之间的间隔可能会被预期的小。

    为了避免setInterval()的重复定时器的这两个缺点,你可以用如下模式使用链式setTimeout()调用。

    1. setTimeout(function(){
    2. //处理中
    3.  
    4. setTimeout(argumrnts.callee, interval);
    5.  
    6. }, interval);

    这么做的好处是,在前一个定时器代码执行完之前,不会向队列中插入新的定时器代码,确保不会有任何缺失的间隔。而且它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。应用如下:

    1. setTimeout(function(){
    2.  
    3. var div = document.getElementById("myDiv"),
    4. left = parseInt(div.style.left) + 5;
    5. div.style.left = left + "px";
    6.  
    7. if (left < 200){
    8. setTimeout(arguments.callee, 50);
    9. }
    10.  
    11. }, 50);

    注意:每个浏览器窗口、标签页、或者frame都有各自的代码执行队列。这意味着,进行跨frame或者跨串口的定时调用,当代码同时执行的时候可能会导致竞争条件。无论何时需要使用这种通信类型,最好是在接受frame或者窗口中创建一个定时器来执行代码。

    3.Yielding Processes(让出进程)

    背景:运行在浏览器中的JavaScript都被分配了一个确定数量的资源。不同于桌面应该往往能够随意控制他们要的内存大小和处理器时间,JavaScript被严格限制了,以防止恶意的Web程序员把用户的计算机搞挂了。其中一个限制是长时间运行脚本的限制,如果代码运行超过特定的时间或特定语句数量就不让它继续执行。如果代码达到了这个限制,会弹出一个浏览器错误的对话框,告诉用户某个脚本会用过长的时间执行,询问是否其继续执行还是停止它。所有JavaScript开发人员的目标就是,确保用户永远不会在浏览器中看到这个令人费解的对话框。定时器是绕开此限制的方法之一。

    脚本长时间运行的问题通常是由两个原因之一造成的:①过长的、过深嵌套的函数调用②进行大量处理的循环

      长时间运行的循环通常遵循以下模式:

    1. for(var i =0, len=data.length; i<len; i++){
    2. process(data[i]);
    3. }

    如果process()要处理很久,那意味着这段时间内用户无法与页面交互的时间会很久

    在展开该循环之前,你需要回答以下两个问题

    【】该处理是否必须同步完成?

    【】数据是否必须按顺序完成?

    因此,当你发现某个循环占用了大量时间,同时对于上述两个问题,你的回答都是否,那么你就可以使用定时器分隔这个循环。这是一种叫做数据分组(array chunking)的技术,小块小块地处理数组,通常每次处理一块。

    1. var data = [12,123,1234,453,436,23,23,5,4123,45,346,5634,2234,345,342];
    2.  
    3. function chunk(array, process, context){
    4. setTimeout(function(){
    5. var item = array.shift();
    6. process.call(context, item);
    7.  
    8. if (array.length > 0){
    9. setTimeout(arguments.callee, 100);
    10. }
    11. }, 100);
    12. }
    13.  
    14. function printValue(item){
    15. var div = document.getElementById("myDiv");
    16. div.innerHTML += item + "<br>";
    17. }
    18.  
    19. chunk(data, printValue); //如果想保持原数组,chunck(data.concat(), printValue);



    函数节流

    背景:浏览器中某些计算和处理比其他的昂贵很多,例如DOM操作,消耗更多的内存和CPU时间

    基本思想:某些代码不可以在没有间断的情况连续重复执行

    实现:定时器

    应用:只要代码是周期性执行,都应该使用节流,但是你不能控制请求执行的频率。

    1. function throttle(method, scope) {
    2. clearTimeout(method.tId);
    3. method.tId= setTimeout(function(){
    4. method.call(scope); //没有传入scope时,那么就在全局作用域内执行该方法
    5. }, 100);
    6. }


    节流在resize事件中是最常见的。

    1. function resizeDiv(){
    2. var div = document.getElementById("myDiv");
    3. div.style.height = div.offsetWidth + "px";
    4. }
    5.  
    6. window.onresize = function(){
    7. throttle(resizeDiv);
    8. };

    但要注意这函数节流在onscroll事件并不合适,因为onscroll事件要求滚动条每变化1像素就要求有所反应,要是有些滚动条变化没被响应,效果就没那么好了。

    自定义事件

    背景:事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术。对象可以发布事件,用来表示在该对象生命周期中某个有趣的时刻到了。然后其他对象可以观察该对象,等待这些有趣的时刻到来并通过运行代码来响应

    观察者模式:由两类对象组成,主体和观察者。主题负责发布事件,同时观察者通过订阅这些事件来观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。从另一方面来说,观察者知道主体并能注册事件的回调函数(事件处理程序)。涉及DOM上时,DOM元素便是主体,你的事件处理代码便是观察者。

    事件是与DOM交互的最常见的方式,但它们也可以用于非DOM代码中-----通过实现自定义事件。自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。实现此功能的基本模式可以如下定义:

    1. function EventTarget(){
    2. this.handlers = {};
    3. }
    4.  
    5. EventTarget.prototype = {
    6. constructor: EventTarget,
    7.  
    8. addHandler: function(type, handler){
    9. if (typeof this.handlers[type] == "undefined"){
    10. this.handlers[type] = [];
    11. }
    12.  
    13. this.handlers[type].push(handler);
    14. },
    15.  
    16. fire: function(event){
    17. if (!event.target){
    18. event.target = this;
    19. }
    20. if (this.handlers[event.type] instanceof Array){
    21. var handlers = this.handlers[event.type];
    22. for (var i=0, len=handlers.length; i < len; i++){
    23. handlers[i](event);
    24. }
    25. }
    26. },
    27.  
    28. removeHandler: function(type, handler){
    29. if (this.handlers[type] instanceof Array){
    30. var handlers = this.handlers[type];
    31. for (var i=0, len=handlers.length; i < len; i++){
    32. if (handlers[i] === handler){
    33. break;
    34. }
    35. }
    36.  
    37. handlers.splice(i, 1);
    38. }
    39. }
    40. };


    简单的使用如下:

    1. function handleMessage(event){
    2. alert("Message received: " + event.message);
    3. }
    4.  
    5. var target = new EventTarget();
    6.  
    7. target.addHandler("message", handleMessage);
    8.  
    9. target.fire({ type: "message", message: "Hello world!"});
    10.  
    11. target.removeHandler("message", handleMessage);
    12.  
    13. target.fire({ type: "message", message: "Hello world!"});

    应用:当代码中存在多个部分在特定时刻相互交互的情况,自定义事件就非常有用了。这时,如果每个对象都有对其他所有对象引用。那么整个代码就会紧密耦合,同时维护也变得很困难,因此对某个对象的修改也会影响其他对象。使用自定义事件有助于解耦相关对象,保护功能的隔绝。在很多情况中,触发事件的代码和监听事件代码是完全分离的。

    拖放

    因为没什么好说的,就直接贴代码了,下面是一个单例对象,并使用了模块模式来隐藏某些实现细节

    1. var DragDrop = function(){
    2.  
    3. var dragging = null,
    4. diffX = 0,
    5. diffY = 0;
    6.  
    7. function handleEvent(event){
    8.  
    9. //get event and target
    10. event = EventUtil.getEvent(event);
    11. var target = EventUtil.getTarget(event);
    12.  
    13. //determine the type of event
    14. switch(event.type){
    15. case "mousedown":
    16. if (target.className.indexOf("draggable") > -1){
    17. dragging = target;
    18. diffX = event.clientX - target.offsetLeft; //注意这里的event和target
    19. diffY = event.clientY - target.offsetTop;
    20. }
    21. break;
    22.  
    23. case "mousemove":
    24. if (dragging !== null){
    25.  
    26. //assign location
    27. dragging.style.left = (event.clientX - diffX) + "px";
    28. dragging.style.top = (event.clientY - diffY) + "px";
    29. }
    30. break;
    31.  
    32. case "mouseup":
    33. dragging = null;
    34. break;
    35. }
    36. };
    37.  
    38. //public interface
    39. return {
    40. enable: function(){
    41. EventUtil.addHandler(document, "mousedown", handleEvent);
    42. EventUtil.addHandler(document, "mousemove", handleEvent);
    43. EventUtil.addHandler(document, "mouseup", handleEvent);
    44. },
    45.  
    46. disable: function(){
    47. EventUtil.removeHandler(document, "mousedown", handleEvent);
    48. EventUtil.removeHandler(document, "mousemove", handleEvent);
    49. EventUtil.removeHandler(document, "mouseup", handleEvent);
    50. }
    51. }
    52. }();
    53.  
    54. DragDrop.enable();


    添加自定义事件----拖放

    拖放功能还不能真正应用起来,除非能知道什么时候拖动开始了。从这点看,前面的代码没有提供任何方法表示拖动开始、正在拖动或者已经结束。这是,可以使用自定义事件来指示这几个事件的发生,让应用的其他部分与拖动功能进行交互。

    1. var DragDrop = function(){
    2.  
    3. var dragdrop = new EventTarget(),
    4. dragging = null,
    5. diffX = 0,
    6. diffY = 0;
    7.  
    8. function handleEvent(event){
    9.  
    10. //get event and target
    11. event = EventUtil.getEvent(event);
    12. var target = EventUtil.getTarget(event);
    13.  
    14. //determine the type of event
    15. switch(event.type){
    16. case "mousedown":
    17. if (target.className.indexOf("draggable") > -1){
    18. dragging = target;
    19. diffX = event.clientX - target.offsetLeft;
    20. diffY = event.clientY - target.offsetTop;
    21. dragdrop.fire({type:"dragstart", target: dragging, x: event.clientX, y: event.clientY});
    22. }
    23. break;
    24.  
    25. case "mousemove":
    26. if (dragging !== null){
    27.  
    28. //assign location
    29. dragging.style.left = (event.clientX - diffX) + "px";
    30. dragging.style.top = (event.clientY - diffY) + "px";
    31.  
    32. //fire custom event
    33. dragdrop.fire({type:"drag", target: dragging, x: event.clientX, y: event.clientY});
    34. }
    35. break;
    36.  
    37. case "mouseup":
    38. dragdrop.fire({type:"dragend", target: dragging, x: event.clientX, y: event.clientY});
    39. dragging = null;
    40. break;
    41. }
    42. };
    43.  
    44. //public interface
    45. dragdrop.enable = function(){
    46. EventUtil.addHandler(document, "mousedown", handleEvent);
    47. EventUtil.addHandler(document, "mousemove", handleEvent);
    48. EventUtil.addHandler(document, "mouseup", handleEvent);
    49. };
    50.  
    51. dragdrop.disable = function(){
    52. EventUtil.removeHandler(document, "mousedown", handleEvent);
    53. EventUtil.removeHandler(document, "mousemove", handleEvent);
    54. EventUtil.removeHandler(document, "mouseup", handleEvent);
    55. };
    56.  
    57. return dragdrop;
    58. }();
    59.  
    60. DragDrop.enable();
    61.  
    62. DragDrop.addHandler("dragstart", function(event){
    63. var status = document.getElementById("status");
    64. status.innerHTML = "Started dragging " + event.target.id;
    65. });
    66.  
    67. DragDrop.addHandler("drag", function(event){
    68. var status = document.getElementById("status");
    69. status.innerHTML += "<br>Dragged " + event.target.id + " to (" + event.x + "," + event.y + ")";
    70. });
    71.  
    72. DragDrop.addHandler("dragend", function(event){
    73. var status = document.getElementById("status");
    74. status.innerHTML += "<br>Dropped " + event.target.id + " at (" + event.x + "," + event.y + ")";
    75. });


    这段代码定义了三个事件:dragstart、drag和dragend。和上面不支持事件的代码的区别还是很大的。为DragDrop添加自定义事件可以使这个对象更加健壮,它将可以在网络应用中处理复杂的拖放功能。

    总结:①可以创建作用域安全的构造函数,确保在缺少new操作符时调用构造函数不会改变错误的环境对象

    ②可以使用惰性载入函数,将任何代码分支推迟到第一次调用函数的时候

    ③函数绑定可以让你创建始终在制定环境中运行的函数,同时函数函数柯里化可以让你创建已经填了某些参数的函数

    ④将绑定和函数柯里化结合起来,就能够给你一种在任意环境中以任意参数执行任意函数的方法

    ⑤定时器原理可以让你实现数组分块和函数节流的技术

    ⑥使用自定义事件有助于将不同部分的代码相互之间解耦,让维护更加容易,并减少引入错误的机会。

    ⑦将拖放行为和自定义事件结合起来可以创建一个可重复使用的框架,它能应用于各种不同的情况下。

    这一章真的能学到很多,而且并不是空洞的知识,本人亲测,这些知识都能应用在实际中,棒棒哒。PS:看不懂就先放下,这本书第一次看不懂很正常,等有了一些项目实践后再看,会更加清晰,而且必须看很多遍,等书上的例子都应用到实际中用烂了,这本书你才算真正的看懂了。

  • 相关阅读:
    [转]C# 动态调用 WebService
    [转]走进 LINQ 的世界
    [转]mybatis-generator 代码自动生成工具(maven方式)
    [转]Netty入门(最简单的Netty客户端/服务器程序)
    FastJson 常见问题
    初识 ElasticSearch
    Maven Gradle 区别
    IDEA 自动生成serialVersionUID
    restful 架构详解
    初识shell expect
  • 原文地址:https://www.cnblogs.com/airen123/p/9596686.html
Copyright © 2020-2023  润新知