ReferenceError: Cannot access 'Math' before initialization

843 views Asked by At

today I met a bug I have never seen before, also, there is none of relevant SO post about this.

ReferenceError: Cannot access 'Math' before initialization

I have tried to add IIFE but still not working. In my previous work on web dev, I never needed to include math.js or import it, I always thought it was provided. Before my last attempt, it always works fine, in my other scripts, I can call Math.abs, Math.floor, etcetera.

Sorry I could not provide any minimum working example, here is the full error reporting.

Uncaught ReferenceError: Cannot access 'Math' before initialization


This is the script I am trying to run:
// round to 2 decimal places
const rit = val => Math.round(val * 100) / 100;

function render_element(styles, el) {
  for (const [kk, vv] of Object.entries(styles)) {
    el.style[kk] = vv;
  }
}

class Clock{
    // unit: min
    static day(){
        return 1440;
    }

    constructor(str){
        var [h, min] = str.split(':');
        this.h = parseInt(h);
        this.min = parseInt(min);
    }

    concat(){
        return new Clock(this.parse());
    }

    // convert clock h, min to min
    minutes(){
        return this.h * 60 + this.min
    }

    // pass in a clock, return gap in minute
    gap(clock){
        return Math.abs(this.minutes() - clock.minutes());
    }

    gaplength(clock, h, min, length){
        return this.gap(clock) / (h * 60 + min) * length;
    }

    parse(){
        var h = this.h.toString();
        var min = this.min.toString();
        min = min.length == 1 ? min + '0' : min;
        return h + ':' + min;
    }

    add_h(h){
        if(this.h + h >= 24){
            this.h = this.h + h - 24;
        }else{
            this.h += h;
        }
    }

    add_min(min){
        if(this.min + min >= 60){
            this.add_h(1);
            this.min = this.min + min - 60;
        }else{
            this.min = this.min + min;
        }
    }
}

class Period{
    // pass in two clock object
    constructor(from_, to_){
        this.from_ = from_.concat();
        this.to_ = to_.concat();
    }

    concat(){
        return new Period(this.from_, this.to_);
    }

    gap(){
        return this.from_.gap(this.to_);
    }

    gaplength(h, min, length){
        return this.from_.gaplength(this.to_, h, min, length);
    }

    shift_min(min){
        this.from_.add_min(min);
        this.to_.add_min(min);
    }

    shift_h(h){
        this.from_.add_h(h);
        this.to_.add_h(h);
    }
}

class Subject{
    constructor(
        bg_url, 
        name, 
        course, 
        teacher, 
        start, // string, e.g. 12:45
        finish,
        color, 
    ){
        this.bg_url = 'img/' + bg_url;
        this.name = name;
        this.course = course;
        this.teacher = teacher;
        this.start = new Clock(start);
        this.finish = new Clock(finish);

        this.color = color;

        this.duration = new Period(this.start, this.finish);
        // percents
        this.left = rit(this.start.gaplength(new Clock('11:00'), 7, 0, 100));
        this.width = rit(this.duration.gaplength(7, 0, 100));

        this.card_style = {
            position: 'relative',
            width: '20%',
            height: '20%',
            borderRadius: '20px',
        };

        this.card_content_style = {
            position: 'absolute',
            width: '20%',
            height: '20%',
            borderRadius: '20px',
        };

        this.h1_style = {
            width: '100%',
            height: '5%',
            fontSize: '1.2em',
            padding: '0 5px 5px 5px',
        };

        this.course_style = {
            width: '100%',
            height: '5%',
            textAlign: 'center',
            fontSize: '1.2em',
            fontWeight: 'bold',
        };

        this.teacher_style = {
            width: '100%',
            height: '5%',
            fontSize: '1.2em',
            fontStyle: 'italic',
            textAlign: 'right',
        };

        this.btn_wrapper_style = {
            position: 'absolute',
            top: '0',
            right: '0',
            width: '40%',
            height: '35%',
            borderRadius: '20px',
            zIndex: '100',
            display: 'flex',
            flexDirection: 'row',
            justifyContent: 'space-between',
            alignItems: 'center',
        };

        this.btn_style = {
            width: '45%',
            height: '45%',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            borderRadius: '10px',
            fontWeight: 'bold',
            cursor: 'pointer',
            overflow: 'hidden',
        };

        this.modify_mouseenter_style = {
            background: 'gold',
            color: 'black',
        };

        this.modify_mouseleave_style = {
            background: 'orange',
            color: 'white',
        };

        this.remove_mouseenter_style = {
            background: 'pink',
            color: 'black',
        };

        this.remove_mouseleave_style = {
            background: 'orchid',
            color: 'white',
        };

        this.card_el = document.createElement('div');
        render_element(this.card_style, this.card_el);
        this.img_el = document.createElement('img');
        this.img_el.setAttribute('src', this.bg_url);
        this.img_el.style.opacity = 0.3;
        this.card_content_el = document.createElement('div');
        render_element(this.card_content_style, this.card_content_el)
        this.h1_el = document.createElement('h1');
        this.h1_el.innerHTML = this.name;
        render_element(this.h1_style, this.h1_el);
        this.hr_el = document.createElement('hr');
        this.hr_el.style.borderColor = this.color;
        this.hr_el.style.opacity = '0.5';
        this.course_el = document.createElement('p');
        this.course_el.innerHTML = this.course;
        render_element(this.course_style, this.course_el);
        this.teacher_el = document.createElement('p');
        this.teacher_el.innerHTML = this.teacher;
        render_element(this.teacher_style, this.teacher_el);
        this.btn_wrapper_el = document.createElement('div');
        render_element(this.btn_wrapper_style, this.btn_wrapper_el);
        this.btn_modify_el = document.createElement('div');
        this.btn_modify_el.innerHTML = 'modify';
        render_element(this.btn_style, this.btn_modify_el);
        this.btn_remove_el = document.createElement('div');
        this.btn_remove_el.innerHTML = 'remove';
        render_element(this.btn_style, this.btn_remove_el);
        
        this.card_el.appendChild(this.img_el);
        this.card_el.appendChild(this.card_content_el);
        this.card_content_el.appendChild(this.h1_el);
        this.card_content_el.appendChild(this.hr_el);
        this.card_content_el.appendChild(this.course_el);
        this.card_content_el.appendChild(this.teacher_el);
        this.card_el.appendChild(this.btn_wrapper_el);
        this.btn_wrapper_el.appendChild(this.btn_modify_el);
        this.btn_wrapper_el.appendChild(this.btn_remove_el);

        this.becomeChildOf = this.becomeChildOf.bind(this);
        this.modifyMouseEnter = this.modifyMouseEnter.bind(this);
        this.modifyMouseLeave = this.modifyMouseLeave.bind(this);
        this.removeMouseEnter = this.removeMouseEnter.bind(this);
        this.removeMouseLeave = this.removeMouseLeave.bind(this);
    
        this.btn_modify_el.addEventListener('mouseenter', this.modifyMouseEnter);
        this.btn_modify_el.addEventListener('mouseleave', this.modifyMouseLeave);
        this.btn_remove_el.addEventListener('mouseenter', this.removeMouseEnter);
        this.btn_remove_el.addEventListener('mouseleave', this.removeMouseLeave);
    }

    becomeChildOf(parent_el){
        this.parent_el = parent_el;
        this.parent_el.appendChild(this.card);
    }

    modifyMouseEnter(){
        render_element(this.modify_mouseenter_style, this.btn_modify_el);
    }

    modifyMouseLeave(){
        render_element(this.modify_mouseleave_style, this.btn_modify_el);
    }

    removeMouseEnter(){
        render_element(this.remove_mouseenter_style, this.btn_remove_el);
    }

    removeMouseLeave(){
        render_element(this.remove_mouseleave_style, this.btn_remove_el);
    }
}

class VisualArts extends Subject{
    constructor(
        course,
        teacher, 
        start,
        finish,
    ){
        super(
            'visual-arts.jpg',
            course, 
            teacher,
            start, 
            finish,
            'red',
        )
    }
}

new VisualArts(
    'Visual Arts',
    'James Collin',
    '11:00',
    '11:45',
)

error occurs at:

    // pass in a clock, return gap in minute
    gap(clock){
        return Math.abs(this.minutes() - clock.minutes());
    }

Update

To run an entire script, I can do:

console.log(Math.abs(-1));

But in the script above, I cannot do:

    gap(clock){
        var min = this.minutes() - clock.minutes();
        console.log(Math.abs(-1));
        // ReferenceError: Cannot access 'Math' before initialization
        return Math.abs(min);
    }

I have literally no idea why this happens

In addition, for testing the script, you don't need html or css.

I do appreciate your suggestion.

1

There are 1 answers

0
Lakshya Thakur On BEST ANSWER

I think this might be the initiator of the issue :-

class VisualArts extends Subject{
    constructor(
        course,
        teacher, 
        start,
        finish,
    ){
        super(
            'visual-arts.jpg',
            course, 
            teacher,
            start, 
            finish,
            'red',
        )
    }
}

You're super expects 7 arguments since Subject constructor takes 7. When you're passing them 6, you will be messing the order of values assigned to start and finish which are responsible for constructing your Clock objects. They are not getting 11.00 and 11:45 as you wanted.