简体   繁体   中英

Unable to add a property to an object using either the dot notation or the indexer notation?

I am trying to add a property named params to a property named url of a class named Page . The property params must be assigned the return value of an anonymous function.

The function runs just fine.

However, after assigning the return value of the function, when I check to see if the property params has been assigned to, I see no such property in the watches window or in the console. When I try to read back the property params from the url property, I get undefined .

Why is that? This happens whether I use the dot notation or the indexer notation to write / assign a value to the property. Below is my code:

var app = 
{
    ...
};

$(document).ready(function() {
    var page = new Page(app);
    page.display();
});

var Page = function(app) {

    this.url = window.location.href;
    this.app = app;

    this.url.params = (function() {

        var p = { };

        if (this.indexOf('?') < 0) return p;

        var query = this.split('?')[1];

        var pairs = query.split('&');

        if (pairs === undefined || pairs.length == 0) return p;

        for(var i = 0; i < pairs.length; i++) {
            var pair = pairs[i].trim();

            if (pair.length == 0) continue;

            if (pair.indexOf('=') < 0) {
                p[pair[0]] = '';
            }
            else {
                var prop = pair.split('=');

                p[prop[0]] = prop[1];
            }
        }

    }.bind(this.url))();

    this.display = function() {

        debugger;

        // over here, when I watch 'url', it does
        // not have a 'params' property
    };
};

Assigning properties to primitive string values does not cause an error. Instead it internally promotes the string type to a String object, performs the assignment and then discards the String object.

The reason for doing this is to enable the use of String.prototype getters and methods on primitive string values. (Similar considerations apply to using Number.prototype methods on primitive number type values)

In your case

this.url = window.location.href;

sets this.url to a primitive string value. Then when you set url.params to a function it is internally and effectively treated as

new String(this.url).params = function () ....

for the purposes of the assignment, but the String object created gets discarded after the statement has been executed. The primitive value in this.url does not gain properties - it is not an object data type and doesn't have properties.

You just forgot to add the return statement of the function. Fix that and your code will work pretty well :D

First, the bind syntax/call is wrong. You actually don't need to use bind function to do something like this. Is there a specific reason you are using bind ? I made this work by just passing the window.location.href to the url.params assignment function; also I've changed the url to initialize as an empty object this.url = {}; .

My full working code:

var app = 
{
};

$(document).ready(function() {
    var page = new Page(app);
    page.display();
});

var Page = function(app) {

    //this.url = window.location.href;
    this.url = {};
    this.app = app;

    this.url.params = (function(url) {

        var p = { };

        if (url.indexOf('?') < 0) return p;

        var query = url.split('?')[1];

        var pairs = query.split('&');

        if (pairs === undefined || pairs.length == 0) return p;

        for(var i = 0; i < pairs.length; i++) {
            var pair = pairs[i].trim();

            if (pair.length == 0) continue;

            if (pair.indexOf('=') < 0) {
                p[pair[0]] = '';
            }
            else {
                var prop = pair.split('=');

                p[prop[0]] = prop[1];
            }
        }

        return p;

    })(window.location.href); //.bind(window.location.href /*this.url*/)();

    this.display = function() {

        debugger;

        console.log('display', this.url);

        // This url (http://zikro.gr/dbg/html/js-bind.html?some=1&url=2&vars=3)
        // Outputs in the console:
        // Object
        //  params: Object
        //    some: "1"
        //    url: "2"
        //    vars: "3"

    };
};

You can check the debugger and the console after you resume the debugger breakpoint (example with ?some=1&url=2&vars=3 url query string):

Object
 params: Object
   some: "1"
   url: "2"
   vars: "3"

And you can check my working example here: http://zikro.gr/dbg/html/js-bind.html?some=1&url=2&vars=3

EDIT

It appears that your code works quite well. You just have to initialize this.url to be an object like this.url = {}; and then just return the p array inside the binded function like this:

var Page = function(app) {

    //this.url = window.location.href;
    this.url = {};
    this.href = window.location.href;
    this.app = app;

    this.url.params = (function(url) {

        var p = { };

        if (this.indexOf('?') < 0) return p;

        var query = this.split('?')[1];

        var pairs = query.split('&');

        if (pairs === undefined || pairs.length == 0) return p;

        for(var i = 0; i < pairs.length; i++) {
            var pair = pairs[i].trim();

            if (pair.length == 0) continue;

            if (pair.indexOf('=') < 0) {
                p[pair[0]] = '';
            }
            else {
                var prop = pair.split('=');

                p[prop[0]] = prop[1];
            }
        }

        return p;

    }.bind(this.href))();

    this.display = function() {
        debugger;
    };
};

The problem, as pointed out by @baao in the comments to the question is -- you may not add custom properties to a string literal but you can, to a String object. There is a difference between the two.

The code snippet below proves this.

 var foo = "foo"; var bar = new String("bar"); foo.foo = "foo"; bar.bar = "bar"; alert(foo.foo); alert(bar.bar); 

The fix, which was obvious, was to change the url to being an object. This has also been demonstrated by @Christos Lytras and suggested by @baao in the comments to the original question.

var app = 
{
    ...
};

$(document).ready(function() {
    var page = new Page(app);
    page.display();
});

var Page = function(app) {

    this.url = { url : window.location.href };
    this.app = app;

    this.url.params = (function() {

        var p = { };

        if (this.indexOf('?') < 0) return p;

        var query = this.split('?')[1];

        var pairs = query.split('&');

        if (pairs === undefined || pairs.length == 0) return p;

        for(var i = 0; i < pairs.length; i++) {
            var pair = pairs[i].trim();

            if (pair.length == 0) continue;

            if (pair.indexOf('=') < 0) {
                p[pair[0]] = '';
            }
            else {
                var prop = pair.split('=');

                p[prop[0]] = prop[1];
            }
        }

        return p;

    }.bind(this.url.url))();

    this.url.getErrorMessage = function() {
        return this.params['error'];
    };

    this.url.getAccesToken = function() {
        return this.params['access_token'];
    };

    this.url.search = function(pattern) {
        return this.url.search(pattern);
    }

    this.display = function() {

        ...
    };
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM