Let's say I have a class named Human
.
function Human(name, gender, age, personality){
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
}
and I have some functions for the class such as greeting
and introduce
. So I create them like this:
Human.prototype.introduce = function() {
var _gender = {"M": "Boy", "F": "Girl"};
switch(this.personality)
{
case "passionate":
alert("Hi, how are you? My name is " + this.name + ". I'm " + this.age + " years old " + _gender[this.gender] + ". ");
break;
case "aggressive":
alert("I'm " + this.name + ". What you want? ");
break;
}
}
Human.prototype.greeting = function() {
alert("Hi!");
}
Since introduce
and greeting
can be grouped by the same category (let's name it speak
), how can I simply wrap this two functions with an object? I've tried this:
Human.prototype.speak = {};
Human.prototype.speak.greeting = function(){
alert("Hi!");
}
Human.prototype.speak.introduce = function(){
var _gender = {"M": "Boy", "F": "Girl"};
switch(this.personality)
{
case "passionate":
alert("Hi, how are you? My name is " + this.name + ". I'm " + this.age + " years old " + _gender[this.gender] + ". ");
break;
case "aggressive":
alert("I'm " + this.name + ". What you want? ");
break;
}
}
Now the question is, when a function is wrapped by an object, this
in the introduce function is no longer referring to the instance. So how can I work this out?
I would like to call the function as this:
var eminem = new Human("Marshall Mathers", "M", 45, "aggressive");
eminem.speak.introduce();
Let's say I have a class named Human
.
function Human(name, gender, age, personality){
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
}
and I have some functions for the class such as greeting
and introduce
. So I create them like this:
Human.prototype.introduce = function() {
var _gender = {"M": "Boy", "F": "Girl"};
switch(this.personality)
{
case "passionate":
alert("Hi, how are you? My name is " + this.name + ". I'm " + this.age + " years old " + _gender[this.gender] + ". ");
break;
case "aggressive":
alert("I'm " + this.name + ". What you want? ");
break;
}
}
Human.prototype.greeting = function() {
alert("Hi!");
}
Since introduce
and greeting
can be grouped by the same category (let's name it speak
), how can I simply wrap this two functions with an object? I've tried this:
Human.prototype.speak = {};
Human.prototype.speak.greeting = function(){
alert("Hi!");
}
Human.prototype.speak.introduce = function(){
var _gender = {"M": "Boy", "F": "Girl"};
switch(this.personality)
{
case "passionate":
alert("Hi, how are you? My name is " + this.name + ". I'm " + this.age + " years old " + _gender[this.gender] + ". ");
break;
case "aggressive":
alert("I'm " + this.name + ". What you want? ");
break;
}
}
Now the question is, when a function is wrapped by an object, this
in the introduce function is no longer referring to the instance. So how can I work this out?
I would like to call the function as this:
var eminem = new Human("Marshall Mathers", "M", 45, "aggressive");
eminem.speak.introduce();
Share
Improve this question
edited Mar 8, 2018 at 19:43
Roman
5,2403 gold badges24 silver badges33 bronze badges
asked Nov 14, 2017 at 7:52
Johnny CheukJohnny Cheuk
2373 silver badges15 bronze badges
1
- What's the end goal? How do you want to call the functions? – StudioTime Commented Nov 14, 2017 at 7:58
4 Answers
Reset to default 5Make Speak to A Class
Make Speak
to a class, because logical grouping of variables and functionality in OOP are classes.
Speak
function Speak(human) {
this.human = human
}
Speak.prototype.greeting = function () {
// ...
}
Speak.prototype.introduce = function () {
// ..
}
Human
function Human(name, gender, age, personality, greetWord) {
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
this.speak = new Speak(this)
}
Example
function Human(name, gender, age, personality, greetWord) {
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
this.greetWord = greetWord;
this.speak = new Speak(this)
}
function Speak(human) {
this.human = human
}
Speak.prototype.greeting = function () {
alert(this.human.greetWord + "!");
}
Speak.prototype.introduce = function () {
var _gender = { "M": "Boy", "F": "Girl" };
switch (this.human.personality) {
case "passionate":
alert("Hi, how are you? My name is " + this.human.name + ". I'm " + this.human.age + " years old " + _gender[this.human.gender] + ". ");
break;
case "aggressive":
alert("I'm " + this.human.name + ". What you want? ");
break;
}
}
var peter = new Human('Peter', 'M', 35, 'aggressive', 'Hi')
peter.speak.greeting()
peter.speak.introduce()
An other way to solve the solution would be to use apply.
Apply
The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).
Code
var peter = new Human('Peter', 'M', 35, 'aggressive')
console.log(peter.speak.introduce.apply(peter))
Example
function Human(name, gender, age, personality){
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
}
Human.prototype.speak = {};
Human.prototype.speak.greeting = function(){
alert("Hi!");
}
Human.prototype.speak.introduce = function(){
var _gender = {"M": "Boy", "F": "Girl"};
switch(this.personality)
{
case "passionate":
alert("Hi, how are you? My name is " + this.name + ". I'm " + this.age + " years old " + _gender[this.gender] + ". ");
break;
case "aggressive":
alert("I'm " + this.name + ". What you want? ");
break;
}
}
var peter = new Human('Peter', 'M', 35, 'aggressive')
console.log(peter.speak.introduce.apply(peter))
You can bind
the context of this
in the function in its constructor
function Human(name, gender, age, personality){
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
this.speak.introduce = this.speak.introduce.bind(this);
}
This way, this
in your introduce
function will always be the current instance of Human
function Human(name, gender, age, personality){
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
var self = this; //this is how
this.speak = {
introduce: function() {
var _gender = {"M": "Boy", "F": "Girl"};
switch(self.personality)
{
case "passionate":
alert("Hi, how are you? My name is " + self.name + ". I'm " + self.age + " years old " + _gender[self.gender] + ". ");
break;
case "aggressive":
alert("I'm " + self.name + ". What you want? ");
break;
}
},
greeting: function() {
alert("Hi!");
}
};
}
var hulk = new Human("Hulk", "M", 33, "aggressive");
hulk.speak.introduce();
Based on the accepted answer you could write a few lines of code to automatically rebind all your functions using Object.keys and forEach.
function Human(name, gender, age, personality){
this.name = name;
this.gender = gender;
this.age = age;
this.personality = personality;
Object.keys(this.speak).filter(function(key) {
return typeof this.speak[key] === 'function';
}, this).forEach(function(key) {
this.speak[key] = this.speak[key].bind(this);
}, this);
}
Also you could easily improve this code to not only iterate the functions of the 'speak' object.