Created
May 21, 2013 16:55
-
-
Save dtao/5621367 to your computer and use it in GitHub Desktop.
How 'this' in JavaScript is like 'instance_eval' in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Agent(name) { | |
this.name = name; | |
this.command = function(subordinate, description, instructions) { | |
console.log(this.name + " tells " + subordinate.name + ": '" + description + "'"); | |
instructions.apply(subordinate); | |
}; | |
this.getCoffee = function() { | |
var recipients = Array.prototype.slice.call(arguments); | |
var names = recipients.map(function(agent) { return agent.name; }); | |
console.log(this.name + " gets coffee for " + names.join(" and ")); | |
}; | |
this.drinkCoffee = function() { | |
console.log(this.name + " drinks coffee."); | |
}; | |
}; | |
var barack = new Agent("Barack"); | |
var joe = new Agent("Joe"); | |
var dan = new Agent("Dan"); | |
(function() { | |
// Here 'this' is Barack. | |
this.command(joe, "Tell Dan to get us some coffee.", function() { | |
// Here 'this' is Joe. | |
this.command(dan, "Get us some coffee, Dan.", function() { | |
// Here 'this' is Dan. | |
this.getCoffee(joe, barack); | |
}); | |
// Joe again. | |
this.drinkCoffee(); | |
}); | |
// Back to Barck. | |
this.drinkCoffee(); | |
}).apply(barack); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Agent | |
attr_reader :name | |
def initialize(name) | |
@name = name | |
end | |
def command(subordinate, description, &instructions) | |
puts "#{self.name} tells #{subordinate.name}: '#{description}'" | |
subordinate.instance_eval(&instructions) | |
end | |
def get_coffee(*recipients) | |
puts "#{self.name} gets coffee for #{recipients.map(&:name).join(' and ')}." | |
end | |
def drink_coffee | |
puts "#{self.name} drinks coffee." | |
end | |
end | |
barack = Agent.new("Barack") | |
joe = Agent.new("Joe") | |
dan = Agent.new("Dan") | |
barack.instance_eval do | |
# Here 'self' is Barack. | |
self.command(joe, "Tell Dan to get us some coffee.") do | |
# Here 'self' is Joe. | |
self.command(dan, "Get us some coffee, Dan.") do | |
# Here 'self' is Dan. | |
self.get_coffee(joe, barack) | |
end | |
# Joe again. | |
self.drink_coffee() | |
end | |
# Back to Barack. | |
self.drink_coffee() | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// We don't necessarily need to use the constructor function pattern in JavaScript | |
// to make sense of 'this'. Here's an alternate way of implementing almost exactly | |
// the same functionality as agent.js, but without making Agent a constructor. | |
function createAgent(name) { | |
return { | |
name: name, | |
command: function(subordinate, description, instructions) { | |
console.log(this.name + " tells " + subordinate.name + ": '" + description + "'"); | |
instructions.apply(subordinate); | |
}, | |
getCoffee: function() { | |
var recipients = Array.prototype.slice.call(arguments); | |
var names = recipients.map(function(agent) { return agent.name; }); | |
console.log(this.name + " gets coffee for " + names.join(" and ")); | |
}, | |
drinkCoffee: function() { | |
console.log(this.name + " drinks coffee."); | |
} | |
}; | |
}; | |
var barack = createAgent("Barack"); | |
var joe = createAgent("Joe"); | |
var dan = createAgent("Dan"); | |
(function() { | |
// Here 'this' is Barack. | |
this.command(joe, "Tell Dan to get us some coffee.", function() { | |
// Here 'this' is Joe. | |
this.command(dan, "Get us some coffee, Dan.", function() { | |
// Here 'this' is Dan. | |
this.getCoffee(joe, barack); | |
}); | |
// Joe again. | |
this.drinkCoffee(); | |
}); | |
// Back to Barck. | |
this.drinkCoffee(); | |
}).apply(barack); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you run
ruby agent.rb
andnode agent.js
ornode agent2.js
, you should get identical output.I wouldn't argue that the
this
keyword in JavaScript isn't confusing sometimes; but I don't think it's quite the anathema of a language feature it's often made out to be. If you understandinstance_eval
in Ruby, it isn't really so different.My hypothesis is that the main reasons
this
in JavaScript is perceived as so much worse are:this
does not change meaning in either an anonymous type (Java) or a closure (C#), end up also reading/writing JavaScript than end up reading/writing Ruby.this
in JavaScript is much greater than that ofinstance_eval
in Ruby; so it's considered a more "core" part of the language as opposed to a magical but seldom-used feature.this
-related bugs) than code written in other languages.All that said, it's very possible I have no idea what I'm talking about, and
this
is actually way more confusing than I realize.