是否有等效于 __noSuchMethod__ 属性的特性,或者在 JS 中实现它的方法?

IT技术 javascript
2021-01-17 06:27:12

在某些 javascript 实现(Rhino、SpiderMonkey)中有一个noSuchMethod特性

proxy = {
    __noSuchMethod__: function(methodName, args){
        return "The " + methodName + " method isn't implemented yet. HINT: I accept cash and beer bribes" ;
    },

    realMethod: function(){
     return "implemented" ;   
    }
}

js> proxy.realMethod()
implemented
js> proxy.newIPod()
The newIPod method isn't implemented yet. HINT: I accept cash and beer bribes
js>

我想知道,有没有办法对属性做类似的事情?我想编写可以在属性和方法上调度的代理类。

6个回答

更新: ECMAScript 6 代理现在得到广泛支持基本上,如果你不需要支持IE11,你可以使用它们。

代理对象允许您为基本操作定义自定义行为,例如属性查找、赋值、枚举、函数调用等。

使用 ES6 代理模拟 __noSuchMethod__

通过对属性访问实施陷阱,您可以模拟非标准__noSuchMethod__陷阱的行为

function enableNoSuchMethod(obj) {
  return new Proxy(obj, {
    get(target, p) {
      if (p in target) {
        return target[p];
      } else if (typeof target.__noSuchMethod__ == "function") {
        return function(...args) {
          return target.__noSuchMethod__.call(target, p, args);
        };
      }
    }
  });
}

// Example usage:

function Dummy() {
  this.ownProp1 = "value1";
  return enableNoSuchMethod(this);
}

Dummy.prototype.test = function() {
  console.log("Test called");
};

Dummy.prototype.__noSuchMethod__ = function(name, args) {
  console.log(`No such method ${name} called with ${args}`);
  return;
};

var instance = new Dummy();
console.log(instance.ownProp1);
instance.test();
instance.someName(1, 2);
instance.xyz(3, 4);
instance.doesNotExist("a", "b");

2010 年的原始答案

目前只有一件事可以真正做你想做的事,但不幸的是没有被广泛实施:

只有两个工作,此时可用的实现,在最新的火狐4测试版(因为FF3.7前释放它已经存在了)和节点代理服务器端JavaScript -Safari浏览器目前正在IT- .

这是一个早期的提案ECMAScript中的下一个版本,它是一个API,可让您实现虚拟化的对象(代理),您可分配各种陷阱-callbacks-了在不同情况下执行的,你得到完全控制关于此时 - 在 ECMAScript 3/5 中 - 只有宿主对象可以做的事情。

要构建代理对象,您必须使用该Proxy.create方法,因为您对setandget陷阱感兴趣,我给您留下一个非常简单的示例:

var p = Proxy.create({
  get: function(proxy, name) {        // intercepts property access
    return 'Hello, '+ name;
  },
  set: function(proxy, name, value) { // intercepts property assignments
    alert(name +'='+ value);
    return true;
  }
});

alert(p.world); // alerts 'Hello, world'
p.foo = 'bar';  // alerts foo=bar

在这里尝试一下

编辑:代理 API 演变,该Proxy.create方法被删除以支持使用Proxy构造函数,请参阅上面的代码更新到 ES6:

代理 API 非常新,甚至在 Mozilla 开发人员中心都没有记录,但正如我所说,自 Firefox 3.7 预发行版以来,已经包含了一个有效的实现。

Proxy对象在全局范围内可用,并且该create方法可以采用两个参数,一个handler对象,它只是一个包含命名为要实现的陷阱的属性的对象,以及一个可选proto参数,它使您能够指定一个对象您的代理继承自。

可用的陷阱有:

// TrapName(args)                          Triggered by
// Fundamental traps
getOwnPropertyDescriptor(name):           // Object.getOwnPropertyDescriptor(proxy, name)
getPropertyDescriptor(name):              // Object.getPropertyDescriptor(proxy, name) [currently inexistent in ES5]
defineProperty(name, propertyDescriptor): // Object.defineProperty(proxy,name,pd)
getOwnPropertyNames():                    // Object.getOwnPropertyNames(proxy) 
getPropertyNames():                       // Object.getPropertyNames(proxy) 
delete(name):                             // delete proxy.name
enumerate():                              // for (name in proxy)
fix():                                    // Object.{freeze|seal|preventExtensions}(proxy)

// Derived traps
has(name):                                // name in proxy
hasOwn(name):                             // ({}).hasOwnProperty.call(proxy, name)
get(receiver, name):                      // receiver.name
set(receiver, name, val):                 // receiver.name = val
keys():                                   // Object.keys(proxy)

除了提案本身之外,我看到的唯一资源是以下教程:

编辑:更多信息即将发布,Brendan Eich 最近在JSConf.eu会议上发表了演讲,您可以在此处找到他的幻灯片:

虽然范围很广,但这个答案完全忽略了一个事实,即问题的重点是调用方法而不是访问属性!不是一回事。
2021-03-15 06:27:12
@ValeriuPaloş:我添加了一个示例,说明如何__noSuchMethod__使用 ES6 代理模拟非标准 陷阱。
2021-03-26 06:27:12
Chrome 也有代理。
2021-03-30 06:27:12
JSConf.eu 网站上似乎不再提供 Brendan Eich 视频。幸运的是,它现在在 YouTube 上
2021-03-31 06:27:12

以下是如何获得类似于 __noSuchMethod__ 的行为

首先,这是一个带有一个方法的简单对象:

var myObject = {
    existingMethod: function (param) {
        console.log('existing method was called', param);
    }
}

现在创建一个代理,它将捕获对属性/方法的访问,并将您现有的对象添加为第一个参数。

var myObjectProxy = new Proxy(myObject, {
   get: function (func, name) {
       // if property or method exists, return it
       if( name in myObject ) {
           return myObject[name];
       }
       // if it doesn't exists handle non-existing name however you choose
       return function (args) {
           console.log(name, args);
       }
    }
});

现在试试:

myObjectProxy.existingMethod('was called here');
myObjectProxy.nonExistingMethod('with a parameter');

适用于 Chrome/Firefox/Opera。在 IE 中不起作用(但已经在 Edge 中工作)。也在移动 Chrome 上进行了测试。

代理的创建可以自动化并且不可见,即如果您使用工厂模式来构建您的对象。我这样做是为了创建可以直接从主线程调用内部函数的工作线程。由于这个名为 Proxy 的酷炫新功能,现在使用 worker 变得如此简单。有史以来最简单的 worker 实现:

var testWorker = createWorker('pathTo/testWorker.js');
testWorker.aFunctionInsideWorker(params, function (result) {
    console.log('results from worker: ', result);
});

我不相信这种类型的元编程在 javascript 中(还)是可能的。相反,尝试使用该__noSuchMethod__功能来实现属性 getter 的效果。不是跨浏览器,因为它是Mozilla 扩展

var proxy = {
    __noSuchMethod__: function(methodName, args) {
       if(methodName.substr(0,3)=="get") {
          var property = methodName.substr(3).toLowerCase();                             
          if (property in this) {
              return this[property];
          }
       }
    }, color: "red"
 };
 alert(proxy.getColor());           

您可以使用Proxy类。

var myObj = {
    someAttr: 'foo'
};

var p = new Proxy(myObj, {
    get: function (target, propName) {
        // target is the first argument passed into new Proxy,
        // in this case target === myObj
        return 'myObj with someAttr:"' + target.someAttr 
               + '" had "' + propName 
               + '" called on it.';
    }
});
console.log(p.nonExsistantProperty);
// outputs: 
// myObj with someAttr:"foo" had "nonExsistantProperty" called on it

__defineGetter____defineSetter____lookupGetter__以及__lookupSetter__除了__noSuchMethod__在SpiderMonkey的。