1、概述
在Javascript中,function非常灵活且功能强大。我们可以通过new Function(‘’,’’) 、eval()来等动态构建函数,这在别的语言(Java)中很难做到的。但这里不是介绍Function的特性,而是考量Function函数的扩展,比较Mootools1.2,Prototype1.6,Ext2.02中对Function函数的扩展,分析在构建新的类库时,怎样扩展Function函数。
Function.prototype有两个特有用,使用频率极高的两个函数,一是function.apply(thisArg[, argsArray]);,二是fun.call(thisArg[, arg1[, arg2[, ...]]]);。两个函数的作用都是一样的,更改函数作用域并执行函数。使函数的this指向thisArg。也可以它变换成调用执行thisArg.fun.(argsArray或arg1[, arg2[, ...]])。Call与Apply不同的地方就是一个采用数组传入参数,一个采用Arguments的形式。
除了这种更改函数作用域并执行函数的功能,我们在操作函数时,还需要别的什么功能呢?试想一下Java,C++,C#等语言的中函数。我们会想到函数回调,函数委托,函数包装。这对应着模式设计中的观察者模式,代理模式,装饰模式。这样在JavaScript的函数中又如何处理呢?
说到回调函数,在JavaScript中,我们也并不陌生,我们在网页上经常会写如:node.onclick=myFn,为该Node的click事件注册一个处理函数。这个处理函数就是回调函数。它要按一定的格式来写。比如它默认把onclick的事件做为参数传入到myFn中。也就是说myFn函数要这样写:function(e){…};在函数中就可以调用系统传进来e,根据e做相关的处理(IE中不兼容)。但是在Javascript中,对于函数的参数没有其它的语言那样的要求(个数,类型等)。不管你的函数有几个参数,它只传入一个参数。当然啊,对于Javascript的其它的回调就不一定是一个参数了。
这样看来,为某个函数创建回调函数的意义不大,但是在代码采用createCallBack的方式比直接写函数名有更好的可理解和可读性。假如我的一个函数如下:funciton(e,x1,x2,x3){},采用createCallBack的方式,我们可以预先设定x1,x2,x3的值。这样一来,我们就可以做到为多种回调写一个处理函数。只在在注册创建回调时设定值来区分。
函数委托,就是本来是我这个函数的工作,我要委托别的函数来实现。但是调用的却是我这个函数。在JavaScript中,改变了函数的执行作用域,其实就改变函数的归属。本来属于A对象,现在可以就变成了B对象。函数委托或代理其实就只要改变这个执行作用域就可以了。与Apply不同的,改变执行作用域的时候不执行该函数,来推迟到下一次的调用时才执行。那不就是把这个函数代理包装另外一个函数,实际上还是执行这个改变了作用域的函数。
函数包装,就把原来的函数包装成功能更强大的函数。在JavaScript中,要包装这个函数,那首先得把这个函数传到包装的函数中来,然后才进行相关的加强装饰操作。因为函数是有执行作用域的,对于被包装的函数,我们的要求肯定是和包装后的函数的执行作用域是一样的。这样一来,才相当于调用那个被包装的函数,同时又加强了功能。
上面三种都是对函数对于一些改变成为另外一个函数,那么函数是不是还需要有一些操作呢,比如我想在调用这个函数时1s后执行呢,比如我们每隔执行这个函数呢?还有在执行这个函数之前,想执行别的函数,或之后执行别的函数?定时或间隔执行在操作网页的样式渐变有很大的作用。
采用函数名表式:createCallBack, createDelegate, createWrap, delay, periodical, before,after,在扩展Function时,我们大约就只需要这几种操作。那么这几种操作在Mootools1.2,Prototype1.6,Ext2.02中是否全都实现了呢,又是如何实现的呢?
2.分析与比较
在Mootools1.2,Prototype1.6,Ext2.02都实现了callback,degelate。不过感觉起来差强人意。先
2.1说callback吧:
Ext中:
createCallback : function(){
var args = arguments;
var method = this;
return function() {
return method.apply(window, args);
};
}
这个callback,没有处理闭包函数传进来的参数,比如在事件回调中,会传入event进来,而这样却处理不了。唯一能处理的是闭包函数无传进来的参数的回调。而函数的执行作用域竟然是window,理应该是调用该闭包函数的this。
ProtoType中:
bindAsEventListener: function() {
var __method = this, args = $A(arguments), object = args.shift();
return function(event) {
return __method.apply(object, [event || window.event].concat(args));
}
}
这个也是完成callback的功能,不过我它只能完成事件的callback功能,如果要用的callback,就捉襟见肘了。在这里,函数中的执行作用域也只是事先传来的object,觉得object不好,为什么不给一个默认的作用域为闭包函数的调用对象呢?可以改成object||this。[event || window.event]改成$A(arguments)可以了。
Moo中:
bindWithEvent: function(bind, args){
return this.create({bind: bind, event: true, arguments: args});
},
create: function(options){
var self = this;
options = options || {};
return function(event){
var args = options.arguments;
args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
if (options.event) args = [event || window.event].extend(args);
var returns = function(){
return self.apply(options.bind || null, args);
};
if (options.delay) return setTimeout(returns, options.delay);
if (options.periodical) return setInterval(returns, options.periodical);
if (options.attempt) return $try(returns);
return returns();
};
},
发现ProtoType中一模一样的处理。只不过是集中在create()中了。
从上面三者可以看出来,它们都没有真正地实现callback函数。如下实现:
createCallBack : function(obj, args, appendArgs){
var method = this;
return function() {
var callArgs = args || arguments;
if(appendArgs === true){
callArgs = Array.prototype.slice.call(arguments, 0);
callArgs = callArgs.concat(args);
}else if(typeof appendArgs == "number"){
callArgs = Array.prototype.slice.call(arguments, 0); var applyArgs = [appendArgs, 0].concat(args); Array.prototype.splice.apply(callArgs, applyArgs); }
return method.apply(obj ||this|| window, callArgs);
}; },
这样一来,我们在创建Callback时,可以动态指定参数的位置,预设参数。突破了callback仅仅只是事件中的。
2.2 Delegate
Ext中:
createDelegate : function(obj, args, appendArgs){
var method = this;
return function() {
var callArgs = args || arguments;
if(appendArgs === true){
callArgs = Array.prototype.slice.call(arguments, 0);
callArgs = callArgs.concat(args);
}else if(typeof appendArgs == "number"){
callArgs = Array.prototype.slice.call(arguments, 0); var applyArgs = [appendArgs, 0].concat(args); Array.prototype.splice.apply(callArgs, applyArgs); }
return method.apply(obj || window, callArgs);
};
},
感觉到一点不好的地方就是函数的作用域obj || window改成obj ||this|| window使用默认的时候指调用对象的本身。这样没有什么不好。
Prototype中:
bind: function() {
if (arguments.length < 2 && arguments[0] === undefined) return this;
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
比Ext中的差远了。不能设定参数的位置。而且用bind命名,给人误解。myFn.bind(this,xx,yy),给人感觉和myFn.call(this,xx,yy)是一样的,修改执行作用域并执行。那里可以看得出来返回是函数。
Moo中:
bind: function(bind, args){
return this.create({bind: bind, arguments: args});
},
其调用create(),和Prototype中bind()差不多。
2.3包装
只有prototype中有:
wrap: function(wrapper) {
var __method = this;
return function() {
return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
}
},
一不能指定作用域,二不能用函数预设参数。
修改如下:
createWrap:function(wrapper,scope,args, appendArgs){
var _method = this;
return function() {
var allArgs = Array.prototype.slice.call(arguments, 0);
return wrapper.apply(scope||this,
[method.createDelegate(scope||this, args, appendArgs)].concat(allArgs));
}
},
现在可以传入作用域,还可以指该被包装的函数的预设参数。同时能改变参数的位置。
var a = {
b: {
c: function () {
}
},
f: function () {
alert(this==a.b);
}
};
a.b.c = a.f.wrap(function (F) {
F();
});
a.b.c();
最好是采用a.f.wrap(function (F) { F();})这样的形式来书写wrapper;
Delay吧;
Ext中:
defer : function(millis, obj, args, appendArgs){
var fn = this.createDelegate(obj, args, appendArgs);
if(millis){
return setTimeout(fn, millis);
}
fn();
return 0;
},
Protype中:
delay: function() {
var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
},
Moo
delay: function(delay, bind, args){
return this.create({delay: delay, bind: bind, arguments: args})();
},
periodical: function(interval, bind, args){
return this.create({periodical: interval, bind: bind, arguments: args})();
},
periodical : function(millis, obj, args, appendArgs){
var fn = this.createDelegate(obj, args, appendArgs);
if(millis){
return setInterval(fn, millis);
}
fn();
return 0;
},
prk 2008-7-30
分享到:
相关推荐
JavaScript是一种广泛应用于网页和网络应用的脚本语言,它的强大之处在于其灵活性和可扩展性。在实际开发中,我们经常需要对JavaScript进行扩展,以满足特定项目的需求。本话题将深入探讨如何编写JavaScript插件来...
### JavaScript Function对象扩展之延时执行函数 #### 引言 在JavaScript开发中,经常会遇到需要延时执行某个函数的情况。传统的做法是直接使用`window.setTimeout`方法来实现这一功能。然而,在实际项目中,为了...
HTML5的Javascript API扩展在提升Web应用程序的性能和用户体验方面发挥了重要作用。在这篇文章中,我们将聚焦于三个关键的API扩展:应用缓存、服务端消息和桌面通知。 首先,让我们详细探讨**应用缓存**。应用缓存...
随着版本的迭代,JavaScript引入了许多新的特性,其中扩展运算符(Spread Operator)就是ES6引入的一个强大而实用的新功能。 扩展运算符通常用三个点(...)表示,它在JavaScript中的应用广泛且灵活。首先,我们来...
**定义:** 多态性是指子类可以重写或扩展父类的方法,使得不同的子类实例能够以不同的方式响应相同的调用。 **JavaScript中的实现:** JavaScript通过方法覆盖实现了多态性。子类可以覆盖父类的方法,从而根据具体...
【JavaScript源代码】ES6扩展运算符的理解与使用场景 扩展运算符(Spread Operator)是ES6引入的新特性,它以“…”的形式出现在代码中,主要用于数组和对象的展开,从而实现灵活的数据处理。以下是对扩展运算符在...
### JavaScript Prototype 对象扩展 #### 一、简介与背景 在JavaScript中,`prototype`是一个非常重要的概念,尤其是在面向对象编程(OOP)方面。通过利用`prototype`,开发者能够更高效地管理对象间的共享属性和...
这个压缩包"ios-一个扩展简单搞定Native 调用 webView JavaScript.zip"提供了一个解决方案,通过扩展`UIWebView`,使我们能够方便地调用JavaScript函数,并获取回调结果。 首先,`UIWebView+Tools.swift`文件中定义...
由于JavaScript的Array对象已经提供了这些功能,我们可以通过扩展Array原型来创建ArrayList。 ```javascript function ArrayList() { this.array = []; } ArrayList.prototype = Object.create(Array.prototype);...
通过构造函数、原型和实例化,我们可以创建可扩展、模块化的代码,而DOM操作使得我们可以与用户界面进行交互,实现动态网页。结合DOM操作的例子,如TodoList,你可以看到JavaScript OOP如何在实际项目中发挥作用,...
在JavaScript中有两种主要的函数定义方式,即函数声明(Function Declaration)和函数表达式(Function Expression)。这两种方式在大部分场景下表现相同,但在特定条件下会有差异。下面将详细讨论这两种函数定义...
标题中的“MYSQL_FUNCTION.rar_javascript_mysql DB”暗示了这个压缩包可能包含与使用JavaScript操作MySQL数据库相关的资源。在IBM Process Designer中,JavaScript API允许开发者在流程设计中进行动态交互和数据...
JavaScript是一种基于原型的编程语言,它允许开发者通过原型(prototype)的方式来扩展原生对象的功能。在JavaScript中,Array和Date对象是两个常用的内置对象,分别用于处理数组和日期相关操作。本知识点将详细解释...
JavaScript5是Web开发中至关重要的一个版本,它奠定了现代JavaScript的...随着技术的发展,JavaScript5的许多特性在后续版本中得到了进一步的增强和扩展,但它的影响力仍然深远,是现代JavaScript开发者的必备知识。
### 可扩展的JavaScript架构详解 #### 一、引言 在《可扩展的JavaScript架构》这份由雅虎资深工程师分享的PPT中,我们能够深入了解到如何通过一种全新的视角来构建灵活且易于维护的Web应用程序。该PPT虽然篇幅不长...
这通常用于添加或扩展对象的方法。例如: ```javascript function Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log('Hello, ' + this.name); }; var person = ...
- **扩展函数功能**:可以为函数原型添加新的方法来扩展函数的功能。 ```javascript Function.prototype.method1 = function() { alert("aaa"); }; function fun1() {} fun1.method1(); // 调用 method1 ``` ...
9. **设计模式**:如工厂模式、单例模式、观察者模式等,这些设计模式在JavaScript中有着广泛应用,能提高代码的可重用性和可扩展性。 10. **调试与测试**:使用开发者工具进行断点调试,编写单元测试,使用持续...
JavaScript中,函数(function)是创建类或对象的一种方式。传统上,我们可以使用构造函数来创建类,也可以使用现代的ES6语法使用class关键字创建类。然而,在ES6之前,开发者们多采用函数来模拟类的行为。本文将...