Strategies for prototype detection - gregswindle/eslint-plugin-crc GitHub Wiki

1. JavaScript object prototypes

Contrary to ES6, ES7, and ES8 keywords and nomenclature, JavaScript has no classes. JavaScript is a prototype-based language in which properties and methods are defined on the Objects' constructor functions, not the object instances themselves.

Prototypes are the mechanism by which JavaScript objects inherit features from one another, and they work differently than inheritance mechanisms in classical object-oriented programming languages...each object has a prototype object, whose constructor defines its properties and methods. The object's prototype acts as a template object. An object's prototype object may also have a prototype object, which it inherits methods and properties from, and so on. This is often referred to as a prototype chain, and explains why different objects have properties and methods defined on other objects available to them. [1]

Please reference the MDN article "Object prototypes" for an overview of object prototypes and how they differ from Classes and their instances.

2. Definitions for identifying objects' prototypes

In JavaScript runtime contexts, we can discover an object's prototype using either Object.getPrototypeOf or Reflect.getPrototypeOf. These methods are not available for execution during static code analysis, however, so we need to define strategies for identifying an object's prototype based on a program's abstract syntax tree.

The article "JSClassFinder: A Tool to Detect Class-like Structures in JavaScript" prescribes two definitions for identifying what the authors call "classes":[2]

Note: In the following definitions, the variable A (object attributes) is synonymous with the official ECMAScript term property.

2.1. Definition 1

An object is a tuple (C, A, M), where

  • C is the object name,
  • A = {a1, a2, . . . , ap} are the attributes defined by the object, and
  • M = {m1, m2, . . . , mq} are the methods.

Moreover, an object (C, A, M), defined in a program P, must respect the following conditions:

  • P must have a function with name C.

  • P must include at least one expression of type new C() or Object.create(C.prototype).

  • For each aA,

    • The function C must include an assignment this.a = Exp or
    • P must include an assignment C.prototype.a = Exp.
  • For each mM,

    • function C must include an assignment this.m = function {Exp} or
    • P must include an assignment C.prototype.m = function {Exp}.

2.2. Definition 2

Assuming that (C1, A1, M1) and (C2, A2, M2) are objects in a program P, we define that C2 is a prototype of C1 if one of the following conditions holds:

  • P includes an assignment C2.prototype = new C1().
  • P includes an assignment C2.prototype = Object.create(C1.prototype).
  • P includes an assignment C2.prototype.constructor = C2. [3]

2.3. Definition 3 - extends

ECMAScript 2015 (aka ECMAScript 6) introduced the Class syntax, which is a form of syntactic-sugar around Object prototypes. When using Classes, we express prototypal inheritance with the extends keyword [4], e.g.,

class CrcModelMarkdownFormatter extends CrcModelFormatter {
    // stuff here
}

Assuming that (C1, A1, M1) and (C2, A2, M2) are objects in a program P, we define that C2 is a prototype of C1 if one of the following conditions holds:

  • P includes a declaration class C2 extends C1.

2.4. Definition 4 - Object.setPrototypeOf

ECMAScript 6 also introduced the static method Object.setPrototypeOf to enable prototypal inheritance.

Assuming that (C1, A1, M1) and (C2, A2, M2) are objects in a program P, we define that C2 is a prototype of C1 if P includes an expression Object.setPrototypeOf(C2.prototype, C1).

3. Identifying objects' prototypes in an (Espree-based) abstract syntax tree (AST)

Let's shift out gazes away from the fields of Academe and cast them down to our keyboards.

3.1. Definition 1 JavaScript examples

View the full example's abstract syntax tree on AST Explorer. You can also review the full source code in es5-object-prototypes.js, which is the fixture for eslint-plugin-crc's Mocha specifications.

// _P_ must have a function with name `C`.
// FunctionDeclaration
function Person() {
  this.canTalk = true;
};

// VariableDeclaration > VariableDeclarator > FunctionExpression
var Person = function () {
  this.canTalk = true;
};

// Person    === Program / body[2] / ExpressionStatement / AssignmentExpression / left:MemberExpression / object:MemberExpression / object: Identifier
// prototype === Program / body[2] / ExpressionStatement / AssignmentExpression / left:MemberExpression / object:MemberExpression / object:prototype
Person.prototype.greet = function() {
  if (this.canTalk) {
    console.log('Hi, I am ' + this.name);
  }
};

// Employee === Program / body[3] / VariableDeclaration / declarations[0]:VariableDeclarator
var Employee = function(name, title) {
  Person.call(this);
  this.name = name;
  this.title = title;
};

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

Employee.prototype.greet = function() {
  if (this.canTalk) {
    console.log('Hi, I am ' + this.name + ', the ' + this.title);
  }
};

var bob = new Employee('Bob', 'Builder');
bob.greet();
// Hi, I am Bob, the Builder

References

[1] Silva, L. H., Hovadick, D., Valente, M. T., Bergel, A., Anquetil, N., & Etien, A. (2016, February 18). JSClassFinder: A Tool to Detect Class-like Structures in JavaScript. Retrieved March 12, 2017, from https://arxiv.org/pdf/1602.05891.pdf
[2] Mozilla Developer Network and individual contributors. (2017, March 11). Object prototypes. Retrieved March 12, 2017, from https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
[3] Mozilla Developer Network and individual contributors. (2017, March 8). Inheritance in JavaScript. Retrieved March 18, 2017, from https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance#Setting_Teacher()'s_prototype_and_constructor_reference
[4] Mozilla Developer Network and individual contributors. (2017, March 10). Classes. Retrieved March 15, 2017, from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
⚠️ **GitHub.com Fallback** ⚠️