-
Notifications
You must be signed in to change notification settings - Fork 442
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
We're going to implement a rough analog of JavaScript's prototypical inheritance by hand, | ||
to ensure we fully understand how it fits together. | ||
|
||
######## | ||
# Task # | ||
######## | ||
|
||
Implement the functions `New`, `Create` and `Lookup` to simulate JavaScript's | ||
`new`, `Object.create` and property lookup mechanisms respectively. | ||
|
||
Throughout this exercise, you will avoid using any built-in JavaScript inheritance features. | ||
Instead you will need to use your own New, Create and Lookup functions as well as `__PROTO__` | ||
to represent an instance's prototype, and `PROTOTYPE` to represent a constructor's prototype. | ||
|
||
i.e. | ||
|
||
* New(Apple, 1,2,3) == new Apple(1,2,3) | ||
* `obj.__PROTO__` == obj.__proto__ || Object.getPrototypeOf(obj) | ||
* `Constructor.PROTOTYPE` == `Constructor.prototype` | ||
|
||
################## | ||
# Part 1: Lookup # | ||
################## | ||
|
||
`Lookup` will simulate the behaviour of JavaScript's property lookup mechanism. | ||
When you reference any object's property in JavaScript, it will | ||
'walk up the prototype chain' to find the property, otherwise it will return `undefined`. | ||
|
||
Your `Lookup` function will be passed a context object, and the property String | ||
that we're looking for. If the property is found on the current context, | ||
return that property, otherwise check the context's prototype, `__PROTO__`. | ||
|
||
If a property cannot be found in the object's prototype chain, simply | ||
return `undefined`. | ||
|
||
```js | ||
|
||
var cat = { | ||
color: 'black' | ||
} | ||
|
||
var kitten = { | ||
size: 'small' | ||
} | ||
|
||
var otherKitten = { | ||
size: 'small', | ||
color: 'grey' | ||
} | ||
|
||
kitten.__PROTO__ = cat | ||
otherKitten.__PROTO__ = cat | ||
|
||
Lookup(kitten, 'color') // => 'black' | ||
Lookup(otherKitten, 'color') // => 'grey' | ||
|
||
Lookup(kitten, 'wings') // => undefined | ||
|
||
// changing properties on the prototype should | ||
// affect any instances that inherit from it. | ||
cat.color = 'blue' | ||
|
||
Lookup(kitten, 'color') // => 'blue' | ||
|
||
// overridden properties should still work | ||
Lookup(otherKitten, 'color') // => 'grey' | ||
|
||
``` | ||
|
||
################## | ||
# Part 2: Create # | ||
################## | ||
|
||
`Create` will simulate the behaviour of `Object.create`. | ||
|
||
`Create` will be passed an object, and you must return a new object with its | ||
prototype (`__PROTO__`) set to the supplied object. | ||
|
||
```js | ||
fuction Cat() { | ||
|
||
} | ||
|
||
Cat.PROTOTYPE.speak = function() { | ||
return 'Meow!' | ||
} | ||
|
||
function Kitten() { | ||
Cat.apply(this, arguments) | ||
} | ||
|
||
Kitten.PROTOTYPE = Create(Cat.PROTOTYPE) | ||
|
||
var kitten = New(Kitten) | ||
Lookup(kitten, 'speak')() // => 'Meow!' | ||
|
||
``` | ||
|
||
############### | ||
# Finale: New # | ||
############### | ||
|
||
`New` will simulate the behaviour of JavaScript's `new` keyword. | ||
|
||
The first argument passed to `New` will be a constructor function (i.e. a type). | ||
Subsequent parameters must be passed to the constructor function when creating the | ||
the new object. | ||
|
||
`New` will return new objects using the supplied constructor function. | ||
|
||
```js | ||
|
||
function Cat(color) { | ||
this.color = color | ||
} | ||
|
||
var blackCat = New(Cat, 'black') | ||
blackCat.color // => black | ||
|
||
var brownCat = New(Cat, 'brown') | ||
brownCat.color // => brown | ||
|
||
``` | ||
|
||
The constructor function passed to `New` may have a `.PROTOTYPE` property. | ||
All objects created with this constructor will have their `__PROTO__` | ||
set to the constructor's `.PROTOTYPE` property. | ||
|
||
```js | ||
|
||
function Cat(color) { | ||
this.color = color | ||
} | ||
|
||
Cat.PROTOTYPE.speak = function() { | ||
return 'Meow!' | ||
} | ||
|
||
function Kitten() { | ||
Cat.apply(this, arguments) | ||
} | ||
|
||
Kitten.PROTOTYPE = Create(Cat) | ||
|
||
Kitten.PROTOTYPE.speak = function() { | ||
return 'Mew.' | ||
} | ||
|
||
var blackCat = New(Cat, 'black') | ||
blackCat.color // => black | ||
|
||
var brownCat = New(Cat, 'brown') | ||
brownCat.color // => brown | ||
|
||
``` | ||
|
||
We also need to simulate one other behaviour of the `new` keyword: | ||
If the constructor itself returns a value, `New` will return that value. | ||
|
||
```js | ||
|
||
function Cat(){ | ||
return 3 | ||
} | ||
var cat = new Cat() // 3 | ||
var cat = New(Cat) // 3 | ||
|
||
``` | ||
|
||
############## | ||
# Conditions # | ||
############## | ||
|
||
* Do not use any built-in javascript prototypical inheritance features. | ||
* Do not call `new` | ||
* Do not use `__proto__`, `Object.getPrototypeOf` or `Object.setPrototypeOf` | ||
|
||
######### | ||
# Hints # | ||
######### | ||
|
||
* Use `hasOwnProperty` to discover if an object has a property. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
"use strict" | ||
|
||
var input = require('../../input') | ||
|
||
module.exports = input().wrap(function(input, submission) { | ||
var New = submission.New | ||
var Lookup = submission.Lookup | ||
var Create = submission.Create | ||
|
||
var cat = { | ||
color: 'black' | ||
} | ||
|
||
var kitten = { | ||
size: 'small' | ||
} | ||
|
||
var otherKitten = { | ||
size: 'small', | ||
color: 'grey' | ||
} | ||
|
||
kitten.__PROTO__ = cat | ||
otherKitten.__PROTO__ = cat | ||
|
||
console.log() | ||
console.log('Testing Lookup:') | ||
console.log() | ||
console.log("Lookup(kitten, 'color')", Lookup(kitten, 'color')) | ||
console.log("Lookup(otherKitten, 'color')", Lookup(otherKitten, 'color')) | ||
console.log("Lookup(kitten, 'wings')", Lookup(kitten, 'wings')) | ||
|
||
|
||
console.log("cat.color = 'blue'") | ||
cat.color = 'blue' | ||
|
||
console.log("Lookup(kitten, 'color')", Lookup(kitten, 'color')) | ||
console.log("Lookup(otherKitten, 'color')", Lookup(otherKitten, 'color')) | ||
|
||
console.log() | ||
console.log('Testing Create:') | ||
console.log() | ||
|
||
console.log("var cat = Create({color: 'black'})") | ||
console.log("var otherCat = Create(cat)") | ||
var cat = Create({color: 'black'}) | ||
var otherCat = Create(cat) | ||
|
||
console.log("Lookup(cat, 'color')", Lookup(cat, 'color')) | ||
console.log("Lookup(otherCat, 'color')", Lookup(otherCat, 'color')) | ||
console.log("otherCat.__PROTO__ === cat", otherCat.__PROTO__ === cat) | ||
|
||
console.log() | ||
console.log('Testing New:') | ||
console.log() | ||
|
||
var rootProto = Object.create(null) | ||
|
||
var Cat = function Cat(color) { | ||
this.color = color | ||
} | ||
|
||
Cat.PROTOTYPE = rootProto | ||
|
||
Cat.PROTOTYPE.speak = function() { | ||
return 'Meow!' | ||
} | ||
var Kitten = function Kitten() { | ||
Cat.apply(this, arguments) | ||
} | ||
|
||
Kitten.PROTOTYPE = Create(Cat.PROTOTYPE) | ||
|
||
Kitten.PROTOTYPE.speak = function() { | ||
return 'Mew.' | ||
} | ||
|
||
console.log("var cat = New(Cat, 'red')") | ||
var cat = New(Cat, 'red') | ||
|
||
console.log("var kitten = New(Kitten, 'blue')") | ||
var kitten = New(Kitten, 'blue') | ||
|
||
console.log("Lookup(cat, 'color')", Lookup(cat, 'color')) | ||
console.log("Lookup(cat, 'speak')()", Lookup(cat, 'speak')()) | ||
console.log("Lookup(kitten, 'color')", Lookup(kitten, 'color')) | ||
console.log("Lookup(kitten, 'speak')()", Lookup(kitten, 'speak')()) | ||
console.log() | ||
console.log("Changing Cat prototype to return MEOW from speak()...") | ||
|
||
Cat.PROTOTYPE.speak = function() { | ||
return 'MEOW.' | ||
} | ||
|
||
console.log("Lookup(cat, 'speak')()", Lookup(cat, 'speak')()) | ||
console.log("Lookup(kitten, 'speak')()", Lookup(kitten, 'speak')()) | ||
console.log() | ||
console.log('Testing constructor can return any object...') | ||
console.log('e.g. Dog constructor returns 3.') | ||
function Dog() { | ||
return 3 | ||
} | ||
console.log("New(Dog)", New(Dog)) | ||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
function Lookup(context, property) { | ||
if (!context) return undefined | ||
if (Object.prototype.hasOwnProperty.call(context, property)) return context[property] | ||
return Lookup(context.__PROTO__, property) | ||
} | ||
|
||
function Create(proto) { | ||
return { | ||
__PROTO__: proto | ||
} | ||
} | ||
|
||
function New(Type) { | ||
var obj = Create(Type.PROTOTYPE) | ||
var args = [].slice.call(arguments, 1) // remove Type arg | ||
var result = Type.apply(obj, args) | ||
if (result) return result | ||
return obj | ||
} | ||
|
||
module.exports = { | ||
Lookup: Lookup, | ||
Create: Create, | ||
New: New | ||
} |