控制“类”库的作用域
上文提到的proxy 函数是一个非常有用的模式,我们应当将其添加至我们的“类”库中。我们在类和实例中都添加proxy 函数,这样就可以在事件处理程序之外处理函数的时候和下面这段代码所示的场景中保持类的作用域:
- var Class = function(parent){
- var klass = function(){
- this.init.apply(this, arguments);
- };
- klass.prototype.init = function(){};
- klassklass.fn = klass.prototype;
- // 添加一个proxy 函数
- klass.proxy = function(func){
- var self = this;
- return(function(){
- return func.apply(self, arguments);
- });
- }
- // 在实例中也添加这个函数
- klassklass.fn.proxy = klass.proxy;
- return klass;
- };
广州网站建设,广州网站设计,广州网站制作,网站建设,网站设计,广州网站建设公司,广州网站设计公司
现在我们可以使用proxy() 函数来包装函数,以确保它们在正确的作用域中被调用:- var Button = new Class;
- Button.include({
- init: function(element){
- this.element = jQuery(element);
- // 代理了这个click 函数
- this.element.click(this.proxy(this.click));
- },
- click: function(){ /* ... */ }
- });
如果我们没有使用proxy 将click() 的回调包装起来,它就会基于上下文this.element来调用,而不是Button,这会造成各种问题。在新版本的JavaScript——ECMAScript 5(ES5)中同样加入了bind() 函数用以控制调用的作用域。bind() 是基于函数进行调用的,用来确保函数是在指定的this 值所在的上下文中调用的。例如:
- Button.include({
- init: function(element){
- this.element = jQuery(element);
- // 绑定这个click 函数
- this.element.click(this.click.bind(this));
- },
- click: function(){ /* ... */ }
- });
这个例子和我们的proxy() 函数是等价的,它能确保click() 函数基于正确的上下文进行调用。但老版本的浏览器不支持bind(),幸运的是,如果需要则可以手动实现它。对于老版本的浏览器来说,手动实现的bind() 兼容性也不错,可直接扩展相关对象的原型,这样就可以像今天在ES5 中使用bind() 那样在任意浏览器中调用它。例如,下面就是一段实现了bind() 函数的代码:
- if (!Function.prototype.bind) {
- Function.prototype.bind = function (obj) {
- var slice = [].slice,
- args = slice.call(arguments, 1),
- self = this,
- nop = function () {},
- bound = function () {
- return self.apply( this instanceof nop ? this : (obj || {}),
- args.concat(slice.call(arguments)));
- };
- nop.prototype = self.prototype;
- bound.prototype = new nop();
- return bound;
- };
- }
广州网站建设,广州网站设计,广州网站制作,网站建设,网站设计,广州网站建设公司,广州网站设计公司
如果浏览器原生不支持bind(),我们仅须重写Function 的原型。现代浏览器则可以继续使用内置的实现。对于数组来说这种“打补丁”译注2 式的做法非常有用,因为在新版本的JavaScript 中,数组增加了很多新的特性。我个人推荐使用es5-shim(https://github.com/kriskowal/es5-shim)项目,因为它涵盖了ES5 中新增的尽可能多的特性。