最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Uncaught TypeError: Function is not a function - Stack Overflow

programmeradmin1浏览0评论

I am getting Fib.inputValidate is not a function

I am wanting to run the inputValidate method so that on keyup the input validates both as an integer and as a Fibonacci number:

HTML looks like this:

<form id="fibonacci-form" action="" method="post">
  <input id="fibonacci" type="text" name="fibonacci"/>
</form>

Javascript ES6:

class Fibonacci {

  constructor() {
    const isPerfectSquare = '';
    const isFibonacci = '';
    const isInt = '';
    const inputValidate = '';
    this.isPerfectSquare = isPerfectSquare;
    this.isFibonacci = isFibonacci;
    this.isInt = isInt;
    this.inputValidate = inputValidate;
  } // constructor

  inputValidate(valueParsed, isInt) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    field.addEventListener("keyup", function(e) {
      if (this.isInt(valueParsed) === false && field.value !== '') { 
        alert('Please enter a valid integer.'); 
      } 

      if(this.isFibonacci(valueParsed)) {
        alert(valueParsed + ' is a Fibonacci Number.');  
      } else {
        alert(valueParsed + ' is not a Fibonacci Number.'); 
      }
    });
  }

  isInt(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
  }

  isPerfectSquare(valueParsed) { 
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
    if (field.value !== '') { 
      return (squaredValue * squaredValue == valueParsed); 
    }
  } 

  isFibonacci(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0)); 
      return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4); 
  } 
} // class

let Fib = new Fibonacci();
console.log(Fib.inputValidate());

I am getting Fib.inputValidate is not a function

I am wanting to run the inputValidate method so that on keyup the input validates both as an integer and as a Fibonacci number:

HTML looks like this:

<form id="fibonacci-form" action="" method="post">
  <input id="fibonacci" type="text" name="fibonacci"/>
</form>

Javascript ES6:

class Fibonacci {

  constructor() {
    const isPerfectSquare = '';
    const isFibonacci = '';
    const isInt = '';
    const inputValidate = '';
    this.isPerfectSquare = isPerfectSquare;
    this.isFibonacci = isFibonacci;
    this.isInt = isInt;
    this.inputValidate = inputValidate;
  } // constructor

  inputValidate(valueParsed, isInt) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    field.addEventListener("keyup", function(e) {
      if (this.isInt(valueParsed) === false && field.value !== '') { 
        alert('Please enter a valid integer.'); 
      } 

      if(this.isFibonacci(valueParsed)) {
        alert(valueParsed + ' is a Fibonacci Number.');  
      } else {
        alert(valueParsed + ' is not a Fibonacci Number.'); 
      }
    });
  }

  isInt(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
  }

  isPerfectSquare(valueParsed) { 
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
    if (field.value !== '') { 
      return (squaredValue * squaredValue == valueParsed); 
    }
  } 

  isFibonacci(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0)); 
      return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4); 
  } 
} // class

let Fib = new Fibonacci();
console.log(Fib.inputValidate());
Share Improve this question edited Jan 14, 2019 at 23:59 Jack Bashford 44.2k11 gold badges55 silver badges82 bronze badges asked Jan 14, 2019 at 23:38 Rob MyrickRob Myrick 85911 silver badges28 bronze badges 4
  • 1 Just remove all the this.[function] = [function] from the constructor. – ibrahim mahrir Commented Jan 14, 2019 at 23:41
  • I just did this, but then on keyup I get this.isInt is not a function. – Rob Myrick Commented Jan 14, 2019 at 23:42
  • 1 Your constructor is overwriting all its functions with an empty string. – user5734311 Commented Jan 14, 2019 at 23:42
  • @RobMyrick that's a different problem – ibrahim mahrir Commented Jan 14, 2019 at 23:44
Add a ment  | 

5 Answers 5

Reset to default 3

The real problem is that the this inside event handlers is not what you think it is. The this inside the event handler will be the (DOM) element that fired the event, not the instance of your class.

Now while you were trying to fix the real problem, you ended up with another problem, which is that you are shadowing the class methods with properties that have as values the empty string ''.

To fix this, just remove the constructor all together as it doesn't do anything, and fix the issue with the this inside the event listener. To do that you have plenty of ways:

  1. Use a variable outside the event listener scope called, for example, that, assign this to it and use that instead of this inside the event listener.

Like so:

var that = this;
field.addEventListener("keyup", function(e) {
    // use 'that' instead of 'this'
    if(that.isInt(valueParsed) ...
});
  1. Use an arrow function because arrow functions use their surrounding this value:

Like so:

// notice the arrow function passed to addEventListener
field.addEventListener("keyup", e => {
    // you can now use 'this' here with no problems
    if(this.isInt(valueParsed) ...
});
  1. bind your event handler to the instance of your class. binding a function will create a new function that always have its this value set to whatever you set it to.

Like so:

field.addEventListener("keyup", function(e) {
    // you can now use 'this' here with no problems
    if(this.isInt(valueParsed) ...
}.bind(this)); // bind the function to its surronding 'this' value so 'this' inside it will be the same as 'this' outside it

Working code: Using an arrow function

class Fibonacci {
  inputValidate(valueParsed, isInt) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    field.addEventListener("keyup", e => {
      if (this.isInt(valueParsed) === false && field.value !== '') {
        alert('Please enter a valid integer.');
      }

      if (this.isFibonacci(valueParsed)) {
        alert(valueParsed + ' is a Fibonacci Number.');
      } else {
        alert(valueParsed + ' is not a Fibonacci Number.');
      }
    });
  }

  isInt(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
  }

  isPerfectSquare(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
    if (field.value !== '') {
      return (squaredValue * squaredValue == valueParsed);
    }
  }

  isFibonacci(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
    return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
  }
} // class

let Fib = new Fibonacci();
<form id="fibonacci-form" action="" method="post">
  <input id="fibonacci" type="text" name="fibonacci" />
</form>

Enhanced working code:

There still are some issues with your code that are related to the functionality rather than errors:

  1. You declare functions as having arguments but you are not using them. For instance, the valueParsed argument is not used at all by even though it is an argument in all functions, instead you are fetching it from the DOM element every time. Use the argument.
  2. The valueParsed (now used as argument) will be initialized inside inputValidate. It should be fetched from within the event listener not from outside it (every time the event fires we should get a new value for valueParsed).
  3. For validation use Number instead of parseInt if you want to exclude float numbers (using parseInt will let float numbers pass validation as it takes only the integer bit from them). Also if validation fail, return to stop further code from executing. It (validation) still not very good though, I'll leave that to you.
  4. Suggestion: You may want to use a button and listen for clicks on it instead of listening for keydown input on the field which is annoying. Create a button, and when the user clicks the button then check if the number they entered in the field is a Fibbonacci one or not. You would only change a line or two of code to achieve that.

class Fibonacci {
  inputValidate() {
    var field = document.getElementById('fibonacci');

    field.addEventListener("keyup", e => {
      var valueParsed = Number(field.value);
      if (this.isInt(valueParsed) === false) {
        alert('Please enter a valid integer.');
        return;
      }

      if (this.isFibonacci(valueParsed)) {
        alert(valueParsed + ' is a Fibonacci Number.');
      } else {
        alert(valueParsed + ' is not a Fibonacci Number.');
      }
    });
  }

  isInt(valueParsed) {
    return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
  }

  isPerfectSquare(valueParsed) {
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));

    return (squaredValue * squaredValue == valueParsed);
  }

  isFibonacci(valueParsed) {
    var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
    return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
  }
} // class

let Fib = new Fibonacci();
<form id="fibonacci-form" action="" method="post">
  <input id="fibonacci" type="text" name="fibonacci" />
</form>

Remove (or empty) your constructor. Class methods are inherited by instances of the class automatically, and as it is your constructor is simply overriding them with properties whose values are empty strings.

Remove the this.inputValidate and const inputValidate from your constructor. And write your method this way...

inputValidate = (valueParsed, isInt) => {
 // do stuff here
};

Problem

Your constructor is overwriting every single function in your class. Here's what actually happens to each method (I'm using isInt() as an example, but it's exactly the same for each one):

You set isInt to '' (empty string) in the constructor:

const isInt = '';

Then, you create a property named isInt, and set it to isInt the string:

this.isInt = isInt;

So isInt ends up being an empty string. Here's a minified example:

class Fibonacci {

  constructor() {
    const isInt = '';
    this.isInt = isInt;
  } // constructor

  isInt(valueParsed) {
    var field = document.getElementById('fibonacci');
    var valueParsed = parseInt(field.value);
    return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
  }
} // class

let Fib = new Fibonacci();
console.log(Fib);

As you can see, the property isInt is equal to "" (empty string), which is why you can't call it like a function - it's a string.

Solution

Place the function declarations within the constructor:

class Fibonacci {

  constructor() {
    this.inputValidate = function(valueParsed, isInt) {
      var field = document.getElementById('fibonacci');
      var valueParsed = parseInt(field.value);
      field.addEventListener("keyup", function(e) {
        if (this.isInt(valueParsed) === false && field.value !== '') {
          alert('Please enter a valid integer.');
        }

        if (this.isFibonacci(valueParsed)) {
          alert(valueParsed + ' is a Fibonacci Number.');
        } else {
          alert(valueParsed + ' is not a Fibonacci Number.');
        }
      });
    }

    this.isInt = function(valueParsed) {
      var field = document.getElementById('fibonacci');
      var valueParsed = parseInt(field.value);
      return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
    }

    this.isPerfectSquare = function(valueParsed) {
      var field = document.getElementById('fibonacci');
      var valueParsed = parseInt(field.value);
      var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
      if (field.value !== '') {
        return (squaredValue * squaredValue == valueParsed);
      }
    }

    this.isFibonacci = function(valueParsed) {
      var field = document.getElementById('fibonacci');
      var valueParsed = parseInt(field.value);
      var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
      return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
    }
  }
} // class

let Fib = new Fibonacci();
console.log(Fib);

Problem raised for below lines in your code.

const inputValidate = '';

this.inputValidate = inputValidate;

what does it mean, it means const variable inputValidate assigned to this.inputValidate, So this.inputValidate is not a function.

In contrast function inputValidate(valueParsed, isInt) added into prototype of the created object for the class by nature.

So, when you called below lines

let Fib = new Fibonacci(); 
console.log(Fib.inputValidate());

then find Fib.inputValidate in the class/constructor functions first, if not found then find Fib.inputValidate in prototype.

so, when you called Fib.inputValidate() then it finds the function in its constructor function, but it founds Fib.inputValidate is a property like variable but not function.

Thats why showing Uncaught TypeError: Function is not a function

For clear conception you can read the article enter link description here

Actually there is no class in javascript but ES6 introduce a class keyword, Actually this class keyword simply syntactic sugar.

So everyone should keep in mind actual scenario of Class.

Finally some modification of your code:

constructor() {
    const isPerfectSquare = '';
    const isFibonacci = '';
    const isInt = '';
    const inputValidate = '';
  } // constructor

Now Fib.inputValidate() will be reachable.

And finally into the keypress/keyup or any other event this always point the Dom element so if you use arrow function for keypress/keyup or any other event then this will point the class object.

发布评论

评论列表(0)

  1. 暂无评论