hook顾名思义,钩子,挂钩,即有钩子就挂在上面,否则就无需理会。钩子是编程惯用的一种手法,用来解决一种或多种特殊情况的处理。
设计模式中的模板模式中也有个钩子函数,它的含义是:父类提供一系列钩子,子类实现时可以自行选择是否挂钩。
在jquery中特殊样式(兼容性不好的样式)都是用hook解决的。
首先举了小例子,说说hook的好处吧,也看看hook到底是个何方神圣。
现在考公务员,要么靠实力,要么靠关系,但领导肯定也不会弄的那么明显,一般都是暗箱操作,这个场景用钩子实现再合理不过了。
// 如果不用钩子的情况 // 考生分数以及父亲名 function examinee(name, score, fatherName) { return { name: name, score: score, fatherName: fatherName }; } // 审阅考生们 function judge(examinees) { var result = {}; for (var i in examinees) { var curExaminee = examinees[i]; var ret = curExaminee.score; // 判断是否有后门关系 if (curExaminee.fatherName === 'xijingping') { ret += 1000; } else if (curExaminee.fatherName === 'ligang') { ret += 100; } else if (curExaminee.fatherName === 'pengdehuai') { ret += 50; } result[curExaminee.name] = ret; } return result; } var lihao = examinee("lihao", 10, 'ligang'); var xida = examinee('xida', 8, 'xijinping'); var peng = examinee('peng', 60, 'pengdehuai'); var liaoxiaofeng = examinee('liaoxiaofeng', 100, 'liaodaniu'); var result = judge([lihao, xida, peng, liaoxiaofeng]); // 根据分数选取前三名 for (var name in result) { console.log("name:" + name); console.log("score:" + score); } // 这样的话如果我们增加有后台的考生,成本会比较大,需要改动两个地方 // 并且代码块会越来越大,不适合阅读
使用钩子则如下:
// 结果根据取决于最终积分 var relationHook = { "ligang": 100, "xijinping": 1000, "pengdehuai": 50 } // 考生分数以及父亲名 function examinee(name, score, fatherName) { return { name: name, score: score, fatherName: fatherName }; } // 审阅考生们 function judge(examinees) { var result = {}; for (var i in examinees) { var curExaminee = examinees[i]; var ret = curExaminee.score; if (relationHook[curExaminee.fatherName] ) { ret += relationHook[curExaminee.fatherName] ; } result[curExaminee.name] = ret; } return result; } var lihao = examinee("lihao", 10, 'ligang'); var xida = examinee('xida', 8, 'xijinping'); var peng = examinee('peng', 60, 'pengdehuai'); var liaoxiaofeng = examinee('liaoxiaofeng', 100, 'liaodaniu'); var result = judge([lihao, xida, peng, liaoxiaofeng]); // 根据分数选取前三名 for (var name in result) { console.log("name:" + name); console.log("score:" + score); }
可能大家会觉得钩子的实现方式好熟悉,这不就是表驱动嘛。是的,他的实现方式的确是用的表驱动,但是hook是一种抽象的概念,在不同的场景可以用不同的方式实现。
通过上述例子,大家也看到了hook的方便性。扩展性,自我表达性都特别好。还很符合人的思维逻辑。(把有后台和没后台分开处理)
再让我们看看jquery中hooks的实现吧。由于浏览器兼容性差,jquery内部是有很多不同的hooks的,在这里就不一一列举了,就说说jquery.attr的实现:
jQuery.extend({ attr: function( elem, name, value, pass ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { return jQuery( elem )[ name ]( value ); } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { return jQuery.prop( elem, name, value ); } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); // All attributes are lowercase // Grab necessary hook if one is defined if ( notxml ) { name = name.toLowerCase(); // 获取相应的hook, 这里主要是获取 attrHooks, hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); } // 如果value存在,则设置对应属性值为value if ( value !== undefined ) { // value 为null,则删除该属性 if ( value === null ) { jQuery.removeAttr( elem, name ); return; // 如果hooks存在, 且hooks中有set属性,且不为xml,则执行该set方法, // 如果有返回值,则返回该返回值 } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; // 处理普通情况的属性赋值 } else { elem.setAttribute( name, value + "" ); return value; } // 如果value存在,则取出该属性对应的值 // 与上述钩子一样,处理特殊情况 } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { return ret; // 处理普通情况 } else { ret = elem.getAttribute( name ); // Non-existent attributes return null, we normalize to undefined return ret === null ? undefined : ret; } } // 再来看看钩子的实现 attrHooks: { // 这个钩子只支持type和value type: { // type 是只有set的 set: function( elem, value ) { // We can't allow the type property to be changed (since it causes problems in IE) if ( rtype.test( elem.nodeName ) && elem.parentNode ) { jQuery.error( "type property can't be changed" ); } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to it's default in case type is set after value // This is for element creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } }, // Use the value property for back compat // Use the nodeHook for button elements in IE6/7 (#1954) value: { // value get: function( elem, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.get( elem, name ); } return name in elem ? elem.value : null; }, set: function( elem, value, name ) { // 内部实现也有嵌套使用nodeHook钩子 if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.set( elem, value, name ); } // Does not return so that setAttribute is also used elem.value = value; } } }, });
jQuery为了兼容各大浏览器,用了大量hook,如果没有这些hook,真不敢想象。。。