开发者

How can I return a built-in function as a object property?

开发者 https://www.devze.com 2022-12-18 18:36 出处:网络
This code works, in some cases. If it returns console.log then the call to p.out functions just fine.

This code works, in some cases. If it returns console.log then the call to p.out functions just fine.

function Notice(mode) {
    this.debug = mode;

    this.out = (function() {
            if(mode) {
                if(window.console) {
                    return console.log;
                } else {

                    return alert;
                }
            } else {
                return Notice.doNothing;
            }
    })(mode);
}

var p = new Notice('1');
p.out('Kool-aid, OH YEAH!');

However, when it returns alert (or window.alert) I get an error:

Error: uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object"  nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)"  location: "JS frame :: 开发者_StackOverflow社区http:// .. :: <TOP_LEVEL> :: line 22"  data: no]

And as a simple test, this works:

out = (function() { return alert; })();
out('hello dolly');

How can I get obj.out to function properly when its being set to alert?


The problem is that in some implementations, alert is actually a proper method of the window object. That is, alert expects this to be the window object. This illustrates the problem in the simplest form:

var foo = {alert : alert};

/* this fails because the value of 'this' is foo:
 */
foo.alert('hello'); 

/* but this works because we re-point 'this to the
 * correct object, that is the window object:
 */
foo.alert.call(window,'hello');

So, a solution to your problem is to either return a function that directly call alert or bind window to alert in a closure. Either something like this:

function Notice (mode) {
    this.out = function(message) {
        if(mode) {
            if(window.console) {
                console.log(message);
            } else {
                alert(message);
            }
        }
    };
}

or this:

function Notice (mode) {
    this.out = (function() {
        if(mode) {
            if(window.console) {
                return console.log;
            } else {
                return function (message) {alert(message)};
            }
        }
    })();        
}


Why not return an anonymous function that in return calls the build-in function?

Something like

return function(msg) { alert(msg); }


I suspect the problem you're having isn't because of how you're setting it, but because of the functions involved. console.log calls the log function with the context (the this value) set to console. If you do

var f = console.log;
f("Hi there");

...note that log is called with a different context (in this case, the global object, which is window in browsers), this is no longer the console object.

If you do this:

var obj = {};
obj.f = console.log;
obj.f();

...then log will be called with obj as this.

Some functions will care, because some functions use this in their implementation. Some functions won't care because they don't. It may be that alert didn't care because it just so happens that it's expecting this to be window -- and that's exactly what it is when you call the function raw (e.g., not via dotted notation or in the other ways that explicitly set this), because (again) window is the global object in browsers.

As a separate note: In most implementations, the built-in functions you see are proper JavaScript function objects and have all of their features. In some implementations, though (Internet Explorer is one), they aren't proper JavaScript functions at all and they lack some of the features of proper functions, such as the apply and call properties. When in doubt, wrap.

0

精彩评论

暂无评论...
验证码 换一张
取 消