Javascript如果遇到SytaxError异常如何获取准确位置

发布于 2024-11-28 18:11:42 字数 14921 浏览 0 评论 0原文

在我压缩代码后,javascript 解释器触发了 SytaxError,但我不知道在我的代码中触发此问题的确切位置。很难找到答案。我尝试了 jsLint aslo,但它也无法指出问题。

(function(){var session,start,end,pool={length:0},result=[],id=0;function poolAdd(procedure,expected,description){pool[id+++""]={"description":description||"No description","procedure":procedure,"expected":expected};pool["length"]++;return id-1;}function poolRun(id){var actual,expect,pass,test=pool[id];if(test){try{actual=typeof test.procedure==="function"?test.procedure():test.procedure;expect=typeof test.expected==="function"?test.expected():test.expected;pass=actual===expect;if(pass){test["pass"]=true}else{test["fail"]={"actual":actual,"expect":expect}}}catch(e){test["fail"]={"message":e.toString()}}finally{delete pool[id];pool.length--;return result.push(test)-1;}}return-1;}function percentage(fraction){return!(fraction>=0&&fraction<=1)?"Not available":(fraction*100+"").substr(0,5)+"%"}function Statics(data,session){if(this instanceof Statics){this.data=(data||result).slice(0);if(session){this.session={id:session.id,end:session.end,start:session.start}}}else{return new Statics(data,session)}}Statics.prototype={statics:function(format){var temp,report,passed=[],failed=[];format=format||"text";for(var i=0,l=this.data.length;i<l;i++){temp=this.data[i];if(typeof temp==="number"){temp=result[temp+""]}temp.pass&&passed.push(temp);temp.fail&&failed.push(temp)}if(format==="text"){report=[];report.push("***"+(this.session?" Session("+this.session.id+")":"")+" Test Report ***");report.push("  Cost: "+(this.session?(this.session.end-this.session.start)/1000:(end-start)/1000)+" seconds");report.push(" Total: "+this.data.length);report.push("Passed: "+passed.length);report.push("Failed: "+failed.length);report.push("Percet: "+percentage(passed.length/this.data.length));if(failed.length>0){report.push("Detail:");for(var i=0,l=failed.length;i<l;i++){temp=failed[i];if(temp.fail.message){report.push(i+1+")."+" >>>EXCEPTION THROWN<<< in test("+temp.description+") "+temp.fail.message)}else{report.push(i+1+")."+" >>>VALUE NOT MATCH<<< in test("+temp.description+") "+" expected "+temp.fail.expect+" actual "+temp.fail.actual)}}}return report.join("\n")}}}function session_start(){session=new Session();session.start=+new Date;}function session_end(){session&&(session.end=+new Date)}function session_destory(){session=null}function Session(){this.id=QQWB.uid();this.idPool=[];}Session.prototype={_addId:function(id){this.idPool.push(id)},add:function(procedure,expected,description){this._addId(poolAdd(procedure,expected,description));return this},run:function(){var temp,statics,results=[];for(var i=0,l=this.idPool.length;i<l;i++){temp=poolRun(this.idPool[i]);temp&&results.push(temp)}session_end();statics=Statics(results,session);session_destory();return statics}};QQWB.extend("test",{add:function(procedure,expected,description){session_start();session._addId(poolAdd(procedure,expected,description));return session},run:function(){session_destory();start=+new Date;for(var id in pool){if(id!=='length'&&pool.hasOwnProperty(id)){poolRun(id)}}end=+new Date;return Statics()},remains:function(){return pool.length}})}());

添加了我的原始代码

(function (){

    var 
        session, // test session
        start, // start time of call test.run()
        end, // end time of test.run()
        pool = {length:0}, // pool of all the tests removed from pool when test done
        result = [], // pool of the result of test
        id = 0; // test unique id auto increment

    /**
     * Add a test item to pool
     * 
     * @access private
     * @return {Number} the index in the pool
     */
    function poolAdd(procedure, expected, description) {
        pool[id+++""] = {
              "description": description || "No description"
             ,"procedure": procedure
             ,"expected": expected
        };
        pool["length"]++; // maintain the length property of pool
        return id - 1; // id of current test
    }

    /**
     * Run a test
     *
     * Test item will be marked as pass or fail then move from
     * test's pool to result's pool
     *
     * @access private
     * @param id {Number} the index in the test pool
     * @return {Number} the index in the result pool
     */
    function poolRun(id) {
        var 
            actual, // actual value
            expect, // expected value
            pass, // indicate test whether passed
            test = pool[id]; // the test object

        if (test) {
            try{
                actual = typeof test.procedure === "function" ? test.procedure() : test.procedure; // run test procedure
                expect = typeof test.expected === "function" ? test.expected() : test.expected; // run expect procedure
                pass = actual === expect;
                if (pass) {
                   test["pass"] = true;
               } else {
                   test["fail"] = {
                       "actual": actual
                      ,"expect": expect
                   }
               }
            } catch (e) {
                test["fail"] = {
                    "message": e.toString()
                }
            } finally {
                delete pool[id]; // remove from pool
                pool.length--; // maintain the length property of pool
                return result.push(test) - 1; // the index in the result array for current test item
            }
        }
        return -1; // id not valid
    }

    /**
     * Float to percentage string
     *
     * @access private
     * @param fraction {Number} float number
     * @return {String} percentage string
     */
    function percentage (fraction) {
        return !(fraction >= 0 && fraction <= 1) ? "Not available" : (fraction * 100 + "").substr(0,5) + "%";
    }

    /**
     * Statics constructor
     *
     * Self invoke constructor
     *
     * Note:
     * If data is passed in,it means the statics running in a session
     * Data contains *position* of result pool
     *
     * @access private
     * @param data {Array} partial result keys array or the result
     * @return {Statics} a statics instance
     */
    function Statics(data, session) {
        if (this instanceof Statics) {
            this.data = (data || result).slice(0); // clone array
            if (session) {
                this.session = { 
                    id: session.id
                   ,end: session.end
                   ,start: session.start
                };
            }
        } else {
            return new Statics(data, session);
        }
    }

    Statics.prototype = {
        /**
         * Get statics
         *
         * Currently only support text statics
         *
         * @access public
         * @param format {String} text,xml,json etc.
         * @return {Mixed} result statics
         */
        statics: function (format) {
            var 
                temp, // temp value
                report, // statics report
                passed = [], // passed tests pool
                failed = []; // failed tests pool

            format = format || "text"; // default report type is text

            for (var i=0,l=this.data.length; i<l; i++) {
                temp = this.data[i];
                if (typeof temp === "number") { // convert key to its value
                    temp = result[temp+""];
                }
                temp.pass && passed.push(temp);
                temp.fail && failed.push(temp);
            }

            if (format === "text") {
                report = [];
                report.push("***" + (this.session ? " Session(" + this.session.id +")":"") + " Test Report ***");
                report.push("  Cost: " + (this.session ? (this.session.end - this.session.start)/1000 : (end - start)/1000) + " seconds");
                report.push(" Total: " + this.data.length);
                report.push("Passed: " + passed.length);
                report.push("Failed: " + failed.length);
                report.push("Percet: " + percentage(passed.length/this.data.length));
                if (failed.length > 0) {
                    report.push("Detail:");
                    for (var i=0,l=failed.length; i<l; i++) {
                        temp = failed[i];
                        if (temp.fail.message) {
                            report.push( i + 1 + ")." + " >>>EXCEPTION THROWN<<< in test(" + temp.description + ") " + temp.fail.message);
                        } else {
                            report.push( i + 1 + ")." + " >>>VALUE NOT MATCH<<< in test(" + temp.description + ") " + " expected " + temp.fail.expect + " actual " + temp.fail.actual);
                        }
                    }
                }
                return report.join("\n");
            }
        } 
    }

    /**
     * Start a new test session
     *
     * @access private
     * @return {Void}
     */
    function session_start() {
     // if checked T.test.add(...); T.test.add(...); will be treated as one session till run is called
     // if (!session) {
            session = new Session();
            session.start = + new Date;
     // }
    }

    /**
     * End current session
     *
     * @access private
     * @return {Void}
     */
    function session_end() {
        session && (session.end = + new Date);
    }

    /**
     * Destory current session
     *
     * @access private
     * @return {Void}
     */
    function session_destory() {
        session = null;
    }

    /**
     * Session constructor
     *
     * @access private
     * @return {Session} session instance
     */
    function Session() {
        this.id = QQWB.uid(); // random session id
        this.idPool = []; // contains id of test
    }

    Session.prototype = {
        /**
         * Add test id to session
         *
         * @access private
         * @param id {Number} test id
         * @return {Void}
         */
        _addId: function (id) {
            this.idPool.push(id);
        }

        /**
         * Add test item to session
         *
         * Note:
         * Test item added to global pool
         * Session just store the position of test in the pool
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *
         *
         * @access public
         * @param procedure {Mixed} the procedure to finish this test
         *                          if procedure is a function it will
         *                          be executed automaticlly
         * @param expected {Mixed}  the expected value of this test
         *                          if expected is a function it will
         *                          be executed automaticlly
         * @param description {String} speak more about this test
         * @return {Session} the current session     
         */
       ,add: function (procedure, expected, description) {
            this._addId(poolAdd(procedure, expected, description));
            return this;
        }

        /**
         * Run the tests in current session
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *       .run() // run the test in this session
         *       .statics() // get statics for this session
         *
         * @access public
         * @return {Statics} statics object
         */
       ,run: function () { // run in session
            var temp,
                statics,
                results = [];
            for (var i=0,l=this.idPool.length; i<l; i++) {
                temp = poolRun(this.idPool[i]);
                temp && results.push(temp);
            }
            session_end(); // record the time of this session end
            statics = Statics(results,session);
            session_destory();// destory test session
            return statics;
        }
    };

    QQWB.extend("test",{

        /**
         * Add test item to session
         *
         * Note:
         * Test item added to global pool
         * Current session will store the position of test in the pool
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *
         * @access public
         * @param procedure {Mixed} the procedure to finish this test
         *                          if procedure is a function it will
         *                          be executed automaticlly
         * @param expected {Mixed}  the expected value of this test
         *                          if expected is a function it will
         *                          be executed automaticlly
         * @param description {String} speak more about this test
         * @return {Session} the current session     
         */
        add: function (procedure, expected, description) {
            session_start(); // spawn a new session object
            session._addId(poolAdd(procedure, expected, description));
            return session;
        }

        /**
         * Run all the tests except the test already done
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *
         * T.test
         *       .run() // run all the tests except already done
         *       .statics() //get full statics of all the tests
         *
         * @access public
         * @return {Statics} statics object
         */
       ,run: function () {
           session_destory(); // destory current session
           start = + new Date; // start time of global test
           for (var id in pool) {
               if (id !== 'length' && pool.hasOwnProperty(id)) {
                   poolRun(id);
               }
           }
           end = + new Date; // end time of global test
           return Statics();
       }
       /**
        * Get count of tests left in the pool
        * 
        * @access public
        * @return {Number} count of tests unfinished yet
        */
       ,remains: function () {
           return pool.length;
       }

    });

}());

这是脚本打包器 http://dean.edwards.name/packer/

编辑: 最后我自己解决了这个问题,我把代码一块一块地粘贴到加壳器中。最后发现Statics.prototype的定义不是以分号结尾,导致SytaxError。

The javascript interpreter triggerred a SytaxError after i compressed my code, but i don't know the exact position triggered this problem at my code. It's very hard to find out. I tried jsLint aslo, but it can't point out problem aslo.

(function(){var session,start,end,pool={length:0},result=[],id=0;function poolAdd(procedure,expected,description){pool[id+++""]={"description":description||"No description","procedure":procedure,"expected":expected};pool["length"]++;return id-1;}function poolRun(id){var actual,expect,pass,test=pool[id];if(test){try{actual=typeof test.procedure==="function"?test.procedure():test.procedure;expect=typeof test.expected==="function"?test.expected():test.expected;pass=actual===expect;if(pass){test["pass"]=true}else{test["fail"]={"actual":actual,"expect":expect}}}catch(e){test["fail"]={"message":e.toString()}}finally{delete pool[id];pool.length--;return result.push(test)-1;}}return-1;}function percentage(fraction){return!(fraction>=0&&fraction<=1)?"Not available":(fraction*100+"").substr(0,5)+"%"}function Statics(data,session){if(this instanceof Statics){this.data=(data||result).slice(0);if(session){this.session={id:session.id,end:session.end,start:session.start}}}else{return new Statics(data,session)}}Statics.prototype={statics:function(format){var temp,report,passed=[],failed=[];format=format||"text";for(var i=0,l=this.data.length;i<l;i++){temp=this.data[i];if(typeof temp==="number"){temp=result[temp+""]}temp.pass&&passed.push(temp);temp.fail&&failed.push(temp)}if(format==="text"){report=[];report.push("***"+(this.session?" Session("+this.session.id+")":"")+" Test Report ***");report.push("  Cost: "+(this.session?(this.session.end-this.session.start)/1000:(end-start)/1000)+" seconds");report.push(" Total: "+this.data.length);report.push("Passed: "+passed.length);report.push("Failed: "+failed.length);report.push("Percet: "+percentage(passed.length/this.data.length));if(failed.length>0){report.push("Detail:");for(var i=0,l=failed.length;i<l;i++){temp=failed[i];if(temp.fail.message){report.push(i+1+")."+" >>>EXCEPTION THROWN<<< in test("+temp.description+") "+temp.fail.message)}else{report.push(i+1+")."+" >>>VALUE NOT MATCH<<< in test("+temp.description+") "+" expected "+temp.fail.expect+" actual "+temp.fail.actual)}}}return report.join("\n")}}}function session_start(){session=new Session();session.start=+new Date;}function session_end(){session&&(session.end=+new Date)}function session_destory(){session=null}function Session(){this.id=QQWB.uid();this.idPool=[];}Session.prototype={_addId:function(id){this.idPool.push(id)},add:function(procedure,expected,description){this._addId(poolAdd(procedure,expected,description));return this},run:function(){var temp,statics,results=[];for(var i=0,l=this.idPool.length;i<l;i++){temp=poolRun(this.idPool[i]);temp&&results.push(temp)}session_end();statics=Statics(results,session);session_destory();return statics}};QQWB.extend("test",{add:function(procedure,expected,description){session_start();session._addId(poolAdd(procedure,expected,description));return session},run:function(){session_destory();start=+new Date;for(var id in pool){if(id!=='length'&&pool.hasOwnProperty(id)){poolRun(id)}}end=+new Date;return Statics()},remains:function(){return pool.length}})}());

added my original code

(function (){

    var 
        session, // test session
        start, // start time of call test.run()
        end, // end time of test.run()
        pool = {length:0}, // pool of all the tests removed from pool when test done
        result = [], // pool of the result of test
        id = 0; // test unique id auto increment

    /**
     * Add a test item to pool
     * 
     * @access private
     * @return {Number} the index in the pool
     */
    function poolAdd(procedure, expected, description) {
        pool[id+++""] = {
              "description": description || "No description"
             ,"procedure": procedure
             ,"expected": expected
        };
        pool["length"]++; // maintain the length property of pool
        return id - 1; // id of current test
    }

    /**
     * Run a test
     *
     * Test item will be marked as pass or fail then move from
     * test's pool to result's pool
     *
     * @access private
     * @param id {Number} the index in the test pool
     * @return {Number} the index in the result pool
     */
    function poolRun(id) {
        var 
            actual, // actual value
            expect, // expected value
            pass, // indicate test whether passed
            test = pool[id]; // the test object

        if (test) {
            try{
                actual = typeof test.procedure === "function" ? test.procedure() : test.procedure; // run test procedure
                expect = typeof test.expected === "function" ? test.expected() : test.expected; // run expect procedure
                pass = actual === expect;
                if (pass) {
                   test["pass"] = true;
               } else {
                   test["fail"] = {
                       "actual": actual
                      ,"expect": expect
                   }
               }
            } catch (e) {
                test["fail"] = {
                    "message": e.toString()
                }
            } finally {
                delete pool[id]; // remove from pool
                pool.length--; // maintain the length property of pool
                return result.push(test) - 1; // the index in the result array for current test item
            }
        }
        return -1; // id not valid
    }

    /**
     * Float to percentage string
     *
     * @access private
     * @param fraction {Number} float number
     * @return {String} percentage string
     */
    function percentage (fraction) {
        return !(fraction >= 0 && fraction <= 1) ? "Not available" : (fraction * 100 + "").substr(0,5) + "%";
    }

    /**
     * Statics constructor
     *
     * Self invoke constructor
     *
     * Note:
     * If data is passed in,it means the statics running in a session
     * Data contains *position* of result pool
     *
     * @access private
     * @param data {Array} partial result keys array or the result
     * @return {Statics} a statics instance
     */
    function Statics(data, session) {
        if (this instanceof Statics) {
            this.data = (data || result).slice(0); // clone array
            if (session) {
                this.session = { 
                    id: session.id
                   ,end: session.end
                   ,start: session.start
                };
            }
        } else {
            return new Statics(data, session);
        }
    }

    Statics.prototype = {
        /**
         * Get statics
         *
         * Currently only support text statics
         *
         * @access public
         * @param format {String} text,xml,json etc.
         * @return {Mixed} result statics
         */
        statics: function (format) {
            var 
                temp, // temp value
                report, // statics report
                passed = [], // passed tests pool
                failed = []; // failed tests pool

            format = format || "text"; // default report type is text

            for (var i=0,l=this.data.length; i<l; i++) {
                temp = this.data[i];
                if (typeof temp === "number") { // convert key to its value
                    temp = result[temp+""];
                }
                temp.pass && passed.push(temp);
                temp.fail && failed.push(temp);
            }

            if (format === "text") {
                report = [];
                report.push("***" + (this.session ? " Session(" + this.session.id +")":"") + " Test Report ***");
                report.push("  Cost: " + (this.session ? (this.session.end - this.session.start)/1000 : (end - start)/1000) + " seconds");
                report.push(" Total: " + this.data.length);
                report.push("Passed: " + passed.length);
                report.push("Failed: " + failed.length);
                report.push("Percet: " + percentage(passed.length/this.data.length));
                if (failed.length > 0) {
                    report.push("Detail:");
                    for (var i=0,l=failed.length; i<l; i++) {
                        temp = failed[i];
                        if (temp.fail.message) {
                            report.push( i + 1 + ")." + " >>>EXCEPTION THROWN<<< in test(" + temp.description + ") " + temp.fail.message);
                        } else {
                            report.push( i + 1 + ")." + " >>>VALUE NOT MATCH<<< in test(" + temp.description + ") " + " expected " + temp.fail.expect + " actual " + temp.fail.actual);
                        }
                    }
                }
                return report.join("\n");
            }
        } 
    }

    /**
     * Start a new test session
     *
     * @access private
     * @return {Void}
     */
    function session_start() {
     // if checked T.test.add(...); T.test.add(...); will be treated as one session till run is called
     // if (!session) {
            session = new Session();
            session.start = + new Date;
     // }
    }

    /**
     * End current session
     *
     * @access private
     * @return {Void}
     */
    function session_end() {
        session && (session.end = + new Date);
    }

    /**
     * Destory current session
     *
     * @access private
     * @return {Void}
     */
    function session_destory() {
        session = null;
    }

    /**
     * Session constructor
     *
     * @access private
     * @return {Session} session instance
     */
    function Session() {
        this.id = QQWB.uid(); // random session id
        this.idPool = []; // contains id of test
    }

    Session.prototype = {
        /**
         * Add test id to session
         *
         * @access private
         * @param id {Number} test id
         * @return {Void}
         */
        _addId: function (id) {
            this.idPool.push(id);
        }

        /**
         * Add test item to session
         *
         * Note:
         * Test item added to global pool
         * Session just store the position of test in the pool
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *
         *
         * @access public
         * @param procedure {Mixed} the procedure to finish this test
         *                          if procedure is a function it will
         *                          be executed automaticlly
         * @param expected {Mixed}  the expected value of this test
         *                          if expected is a function it will
         *                          be executed automaticlly
         * @param description {String} speak more about this test
         * @return {Session} the current session     
         */
       ,add: function (procedure, expected, description) {
            this._addId(poolAdd(procedure, expected, description));
            return this;
        }

        /**
         * Run the tests in current session
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *       .run() // run the test in this session
         *       .statics() // get statics for this session
         *
         * @access public
         * @return {Statics} statics object
         */
       ,run: function () { // run in session
            var temp,
                statics,
                results = [];
            for (var i=0,l=this.idPool.length; i<l; i++) {
                temp = poolRun(this.idPool[i]);
                temp && results.push(temp);
            }
            session_end(); // record the time of this session end
            statics = Statics(results,session);
            session_destory();// destory test session
            return statics;
        }
    };

    QQWB.extend("test",{

        /**
         * Add test item to session
         *
         * Note:
         * Test item added to global pool
         * Current session will store the position of test in the pool
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *
         * @access public
         * @param procedure {Mixed} the procedure to finish this test
         *                          if procedure is a function it will
         *                          be executed automaticlly
         * @param expected {Mixed}  the expected value of this test
         *                          if expected is a function it will
         *                          be executed automaticlly
         * @param description {String} speak more about this test
         * @return {Session} the current session     
         */
        add: function (procedure, expected, description) {
            session_start(); // spawn a new session object
            session._addId(poolAdd(procedure, expected, description));
            return session;
        }

        /**
         * Run all the tests except the test already done
         *
         * Example usage:
         * T.test
         *       .add(...) //add a test to session(and pool)
         *       .add(...) //add a test to session(and pool)
         *       ...
         *
         * T.test
         *       .run() // run all the tests except already done
         *       .statics() //get full statics of all the tests
         *
         * @access public
         * @return {Statics} statics object
         */
       ,run: function () {
           session_destory(); // destory current session
           start = + new Date; // start time of global test
           for (var id in pool) {
               if (id !== 'length' && pool.hasOwnProperty(id)) {
                   poolRun(id);
               }
           }
           end = + new Date; // end time of global test
           return Statics();
       }
       /**
        * Get count of tests left in the pool
        * 
        * @access public
        * @return {Number} count of tests unfinished yet
        */
       ,remains: function () {
           return pool.length;
       }

    });

}());

This is the script packer
http://dean.edwards.name/packer/

Edited:
I solved this problem by myself finally,I paste the code piece by piece to the packer. finally find out that the defination of Statics.prototype isn't end with a semicolon which caused SytaxError.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

魄砕の薆 2024-12-05 18:11:42

我尝试将 javascript 复制到 http://jsfiddle.net/ 然后单击 JSLint - 给了我各种错误。

您使用什么工具进行压缩?

我的猜测是缺少分号。

例如:

test["fail"] = {
  "message": e.toString()
}

右大括号后面应该有一个分号。

I tried copying the javascript to http://jsfiddle.net/ then clicking JSLint - gave me all sorts of errors.

What tool are you using for the compression?

My guess is missing semicolons.

For example:

test["fail"] = {
  "message": e.toString()
}

Should have a semicolon after the closing brace.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文