Context page

What is "this"? Learn about context and build your very own . operator!

Overview

  • Rules for setting context
  • proto & prototype: A visual representation
  • Exercise!

Video

Slides

Exercise: DOT operator

The problem

Write the DOT (.) operator as if it was implemented in JS as a function.

For example, the following uses the DOT operator to read properties from person:

var Person = function(name) {
    this.name = name;
}
Person.prototype.isPerson = true;

var person = new Person('Smith');
DOT(person, 'name');        //person.name
DOT(person, 'isPerson');    //person.isPerson

To solve this problem, DOT should read from the proto chain. While it can be implemented as simple as the following:

const DOT = function(obj, property){
    return obj[property];
}

You should instead only use the [] property accessor when you know the object has that direct property.

To get started, click the button at the bottom of the following:

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>

<script type="module">
/* start DOT code here */
function DOT(object, property) {

}
/* end DOT code */

// Test code. There’s no need to edit the following:
QUnit.test("DOT works", function(){
    var Person = function(name){
        this.name = name;
    }

    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak =function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    }

    var person = new Person('Alexis');
    var species = DOT(person, 'species');
    var toString = DOT(person, 'toString');

    QUnit.equal(species, 'Homo Sapien', 'property accessed');
    QUnit.equal(toString, Object.prototype.toString,
        'Object.prototype.toString accessed');
    QUnit.equal(DOT(person, 'foobar'), undefined, 'property not found');

});
</script>

What you need to know

  • The DOT function will take an object and a property name as a string.
  • hasOwnProperty returns if an object has a direct property:
    var obj = {foo: "bar"};
    console.log( obj.hasOwnProperty("foo") ) // Logs: true
    
  • Object.getPrototypeOf(obj) returns the __proto__ value of the passed obj. This is the recommended way of reading the __proto__ property.
    var date = new Date()
    console.log( Object.getPrototypeOf(date) === Date.prototype ) // Logs: true
    
  • Recursive functions call themselves to answer a sub-problem:
    function factorial(number){
      if (number === 0) {
        return 1;
      } else {
        return number * factorial(number-1);
      }
    }
    console.log( factorial(4) ) // Logs: 24
    

The solution

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>

<script type="module">
/* start DOT code here */
function DOT(object, property) {
    if( Object.prototype.hasOwnProperty.call(object, property) ) {
        return object[property];
    }
    var proto = Object.getPrototypeOf(object);
    if( proto ) {
        return DOT(proto, property);
    }
}
/* end DOT code */

// Test code. There’s no need to edit the following:
QUnit.test("DOT works", function(){
    var Person = function(name){
        this.name = name;
    }

    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak =function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    }

    var person = new Person('Alexis');
    var species = DOT(person, 'species');

    QUnit.equal(species, 'Homo Sapien', 'property accessed');
    QUnit.equal(DOT(person, 'foobar'), undefined, 'property not found');
});
</script>

Exercise: DOTCALL operator

The problem

Write the dot (.) [[call]] operator as if it was implemented in JS.

For example, instead of calling person.speak("Hi"), we will call it as DOTCALL(person,"speak",["Hi"]) as follows:

const Person = function(name) {
    this.name = name;
}

Person.prototype.speak = function(message){
  console.log(message + ' ' + this.name);
}
var person = new Person('Smith');

DOTCALL(person,"speak",["Hi"])  //person.speak("Hi")

DOTCALL( obj, prop, args ) will take:

  • obj - the context of the function to call.
  • prop - the property name to lookup.
  • args - an array of arguments to pass to the function.

To get started, click the Run in your browser button at the bottom of the following code sample:

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script type="module">
// The DOT operator will be useful
function DOT(object, property) {
    if( Object.prototype.hasOwnProperty.call(object, property) ) {
        return object[property];
    }
    var proto = Object.getPrototypeOf(object);
    if( proto ) {
        return DOT(proto, property);
    }
}

function DOTCALL(obj, prop, args){

}
</script>
<script>
QUnit.test('DOTCALL works', function() {

    var Person = function(name){
        this.name = name;
    };
    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak = function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    };

    var person = new Person('Alexis');
    var speak = DOTCALL(person, 'speak', ['Justin']);

    var greet = 'Hello Justin. My name is Alexis.';
    equal(speak, greet, 'method called with argument');
});
</script>

What you need to know

  • Use apply to call a function (also constructor functions) with a this value and an array of arguments.
  • If the call operator (()) is used on a value that is not a function, an error is thrown. For this example, throw:
    throw new Error("${prop} is not a function");
    
    where prop is the name of the property that is being called.

The solution

<div id="qunit"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script type="module">
// The DOT operator will be useful
function DOT(object, property) {
    if( Object.prototype.hasOwnProperty.call(object, property) ) {
        return object[property];
    }
    var proto = Object.getPrototypeOf(object);
    if( proto ) {
        return DOT(proto, property);
    }
}

function DOTCALL(obj, prop, args){
    var fn = DOT(obj, prop);
    if(typeof fn === "function") {
        return fn.apply(obj, args);
    } else {
        throw new Error(prop+" is not a function");
    }
}
</script>
<script>
QUnit.test('DOTCALL works', function() {

    var Person = function(name){
        this.name = name;
    };
    Person.prototype.species = 'Homo Sapien';
    Person.prototype.speak = function(toWhom) {
        return 'Hello ' + toWhom + '. My name is ' + this.name + '.';
    };

    var person = new Person('Alexis');
    var speak = DOTCALL(person, 'speak', ['Justin']);

    var greet = 'Hello Justin. My name is Alexis.';
    equal(speak, greet, 'method called with argument');
});
</script>