本文共 2964 字,大约阅读时间需要 9 分钟。
本节书摘来华章计算机出版社《JavaScript应用程序设计》一书中的第2章,第2.11节,作者:Eric Elliott 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
在计算机学科中,多态性意味一件事物的行为取决于它当前所处的上下文环境,就像单词一样,在不同的句子中的含义也不尽相同,如下例中“东西”一词。
· 迈尔斯是韩国东西大学专门研究北韩官方宣传与传播的教授。· 据谣传这所房间每天夜里都有什么东西吼叫。· 这是个交通枢纽,铁路由此向东西南北伸展出去。同理,多态函数意味着函数在执行期间的行为由传入的具体参数决定,在JavaScript中,这些参数存储在一个被称为arguments的类数组对象中,说它是类数组是因为它本身不具备Array类的实例方法。使用Array.prototype.slice()可以对数或类数组对象进行浅拷贝操作。你可以从Array类的原型对象上“借用”slice()方法,这种方法借用机制,我们称之为“方法代理”,调用方式如下:var args = Array.prototype.slice.call(arguments, 0);这将返回一个真正意义上的数据,不过这种语法看起来有一些冗余,我们换一种写法:var args = [].slice.call(arguments, 0);
我们使用方括号语法创建了一个用来被slice()方法代理的空数组对象,这听上去感觉会对性能造成一定损耗,不过对JavaScript引擎来说创建空数组是一件极为迅速的操作,根据反复性能测试的数据结果来看,这项操作对内存与性能的影响是微乎其微的。
你可以使用这种方法创建一个对入参进行排序的函数:function sort() { var args = [].slice.call(arguments, 0); return args.sort();}test('Sort', function () { var result = sort('b', 'a', 'c'); ok(result, ['a', 'b', 'c'], 'Sort works!');});
因为arguments不是一个真正意义上的数组,所以它没有sort()方法。不过你可以通过slice()方法将arguments对象转变为一个真数组,之后再调用数组方法sort()返回一个经过排序的数组。
多态函数需要判断第一个参数的类型来决定后续的执行流程。现在args已经是数组,可以使用shift()方法获取第一个参数:var first = args.shift(); 若第一个参数类型是String,则进入分支流程:function morph(options) { var args = [].slice.call(arguments, 0), animals = 'turtles'; // Set a default if (typeof options === 'string') { animals = options; args.shift(); } return('The pet store has ' + args + ' ' + animals + '.');}test('Polymorphic branching.', function () { var test1 = morph('cats', 3), test2 = morph('dogs', 4), test3 = morph(2); equal(test1, 'The pet store has 3 cats.', '3 Cats.'); equal(test2, 'The pet store has 4 dogs.', '4 Dogs.'); equal(test3, 'The pet store has 2 turtles.', 'The pet store has 2 turtles.');});
方法调度
方法调度是指对象在接收到外界的消息时再决定其具体行为。在JavaScript中,接受消息的对象先检查自己是否有方法,如果没有,则查找原型对象,如果还没有,则查找父级原型对象,如此往复,直到找到匹配的方法随后将参数传入并调用,这在JavaScript这种基于原型的语言中被称为行为代理。在程序运行期间根据入参的类型选择合适的函数执行,这种多态性我们称为动态调度。某些语言对动态调度有特殊语法支持,而在 JavaScript中的一般实现是,在一个方法中获取入参,随后根据入参决定另一个方法的调用。var methods = { init: function (args) { return 'initializing...'; }, hello: function (args) { return 'Hello, ' + args; }, goodbye: function (args) { return 'Goodbye, cruel ' + args; } }, greet = function greet(options) { var args = [].slice.call(arguments, 0), initialized = false, action = 'init'; // init will run by default if (typeof options === 'string' && typeof methods[options] === 'function') { action = options; args.shift(); } return methods[action](args); };test('Dynamic dispatch', function () { var test1 = greet(), test2 = greet('hello', 'world!'), test3 = greet('goodbye', 'world!'); equal(test2, 'Hello, world!', 'Dispatched to hello method.'); equal(test3, 'Goodbye, cruel world!', 'Dispatched to goodbye method.');});
动态调度机制在jQuery的插件中较为常见,解决了开发人员为了增加插件中的方法而去污染jQuery原型链的问题。利用动态调度,你可以先在jQuery原型链中注册一个插件名称,随后就可以在此名称上任意添加你所需要的方法,插件使用者则通过下列方式调用你的方法。
$(selection).yourPlugin('methodName', params);转载地址:http://xcato.baihongyu.com/