> [] + []
''
> [] + {}
'[object Object]'
> {} + []
0
> {} + {}
NaN
// "Default values" are assigned at *run time*, unlike the classic python example:
function foo(x=[]) { x.push(1); return x; }
console.log(foo()); // [1]
console.log(foo()); // [1], in python: [1, 1]
// And the default values can be any expression!
var x = 0;
var incX = () => x++;
function blah(a=incX()) { return x; }
console.log(blah()); // 0
console.log(blah()); // 1
// And you can reference any previously declared parameter
let isWut = (a, b=a || 12) => b;
isWut(true); // true
isWut(''); // 12
let sum = (a, b, s=a+b) => s;
sum(10, 20); // 30
let fib = (n, s=n > 2 ? sum(fib(n-1), fib(n-2)) : 1) => s;
let f = (a=(() => wut(12))(), b=(function (x, ...[{ dis: { is: y } }]) {
return x ? ami(y) : 3
})(true instanceof {
[Symbol.hasInstance()](x) { return !!x }
}), { dis: { is: 'wat' } }) => false;
var Y = function(F) {
return function(f) {
return f(f);
}(function(f) {
return F(
function(x) { return (f(f))(x); }
);
});
};
// I'm curious, what about eval?
function foo(a=eval('var x = 12; x')) {
console.log(a); // 12
console.log(x); // ReferenceError: x is not defined
// where's x?
}
foo();
// Maybe it's only defined in the parameters?
function foo(a=eval('var x = 12; x'), b=x) {
console.log(b);
}
foo(); // ReferenceError: x is not defined
// What about shadowing declarations?
var x = 17;
function foo(a=x, x=12) {
console.log(a);
}
foo(); // ReferenceError: x is not defined
// !?
// One of strict mode's implications:
function sloppyFoo() { 'use sloppy'; return this; }
function strictFoo() { 'use strict'; return this; }
sloppyFoo(); // global object
strictFoo(); // undefined
// But what about this:
function foo(a=this) {
'use strict';
return a;
}
'use sloppy';
foo(); // undefined? global object?
// And what about this?
function foo(a=(function() {
let x = 12;
with ({ x: 42 })
return x;
// with is a syntax error in strict mode
})) {
'use strict';
return a;
}
Syntax Error
// Allows you to override `instanceof`
let awesomeObject = {
[Symbol.hasInstance](thing) {
return thing.x > thing.y;
}
};
{ x: 80, y: -1 } instanceof awesomeObject; // true
{ x: 10, y: 79 } instanceof awesomeObject; // false
// Let's abuse it
// Randomly switch my mind about my children
var thing = {
[Symbol.hasInstance]() { return Math.random() > 0.5; }
};
4 instanceof thing; // true? false? Who knows!
const Even = {
[Symbol.hasInstance](x) { return !(x % 2); }
};
4 instanceof Even; // true
5 instanceof Even; // false
// Something I couldn't figure out: Why isn't it called on functions?
function foo() {}
foo[Symbol.hasInstance] = function (x) {
console.log('wut', x);
return x === 4;
};
4 instanceof foo; // false, no logs
// v8 source
MaybeHandle<Object> Object::InstanceOf(Isolate* isolate, Handle<Object> object,
Handle<Object> callable) {
// The {callable} must be a receiver.
if (!callable->IsJSReceiver()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kNonObjectInInstanceOfCheck),
Object);
}
// Lookup the @@hasInstance method on {callable}.
Handle<Object> inst_of_handler;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, inst_of_handler,
JSReceiver::GetMethod(Handle<JSReceiver>::cast(callable),
isolate->factory()->has_instance_symbol()),
Object);
if (!inst_of_handler->IsUndefined(isolate)) {
// Call the {inst_of_handler} on the {callable}.
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, inst_of_handler, callable, 1, &object),
Object);
return isolate->factory()->ToBoolean(result->BooleanValue());
}
// The {callable} must have a [[Call]] internal method.
if (!callable->IsCallable()) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kNonCallableInInstanceOfCheck),
Object);
}
// Fall back to OrdinaryHasInstance with {callable} and {object}.
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
JSReceiver::OrdinaryHasInstance(isolate, callable, object), Object);
return result;
Function.prototype[Symbol.hasInstance]
is defined as non-writable!
Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance)
// { writable: false, enumerable: false, configurable: false, value: function [Symbol.hasInstance]() }
// Writing raises an error in strict mode:
(() => {
"use strict";
function foo() {}
foo[Symbol.hasInstance] = function(){};
// TypeError: Cannot assign to read only property
})();
// Defined on RegExp.prototype, called in str.replace
'foobar'.replace({
[Symbol.replace](subj, rep) {
return `wut${rep}`;
}
}, 123); // wut123
// Friends & Family: Symbol.{match,search,split}
function countedReplace(pattern, count) {
return {
pattern, count,
[Symbol.replace](subj, rep) {
let c = this.count;
// In This Function: Avoiding subclassing RegExp
let p = new RegExp(this.pattern, this.pattern.flags.replace(/g|$/, 'g'));
return subj.replace(p, $0 => c <= 0 ? $0 : (c--, rep));
}
}
}
'foo bar baz quz qux'.replace(countedReplace(/\w+/, 2), 'wut');
// wut wut baz quz qux
Window.prototype[Symbol.isConcatSpreadable] = true;
[].concat(window); // list of frames
// functions have length too!
function foo(a, b, c){}
[].concat(foo); // [foo]
Function.prototype[Symbol.isConcatSpreadable] = true;
[].concat(foo); // [ undefined ✗ 3 ], notice that it's not undefined values but keys
// Maybe flipping it to false?
var foo = [0, 1, 2];
[-2, -1].concat(foo); // [-2, -1, 0, 1, 2]
foo[Symbol.isConcatSpreadable] = false;
[-2, -1].concat(foo); // [-2, -1, [0, 1, 2]]
// But then why not put the argument in an array?
var thing = {
foo: 4
};
with (thing) {
console.log(foo); // 4
}
thing[Symbol.unscopables] = { foo: true };
with (thing) {
console.log(foo); // ReferenceError: foo is not defined
}
let arrowFunc = (a, b) => { things }
var obj = { method() { things } };
function *gen() { yield things }
async function neat() { await things }
class Thingy { methods }
// Classes can't be called:
class Foo {}
Foo(); // TypeError
// Additionally, they're not hoisted, just like `let`:
(function() {
new Foo(); // ReferenceError
class Foo {}
})();
// Methods, generators, async functions and arrows can't be `new`ed:
var obj = {
meth() {}
};
function *gen() {}
async function asy() {}
var arrow = () => {};
new obj.meth(); // TypeError
new gen(); // TypeError
new asy(); // TypeError
new arrow(); // TypeError
// ...but generators do have a prototype
obj.meth.prototype; // undefined
arrow.prototype; // undefined
asy.prototype; // undefined
gen.prototype; // Generator
// wut?
// Generators also have an extra step in their inheritance hierarcy:
Object.getPrototypeOf(gen); // GeneratorFunction
Object.getPrototypeOf(Object.getPrototypeOf(gen)); // Function.prototype
// GeneratorFunction is not exposed on the global object :(
// It can still be used, just like the regular Function constructor!
var GeneratorFunction = Object.getPrototypeOf(gen).constructor;
var gen = GeneratorFunction('let x = yield 4; return x;');
var g = gen();
console.log(g.next()); // { value: 4, done: false }
console.log(g.next(12)); // { value: 12, done: true }
// This also applies to async functions:
var AsyncFunction = Object.getPrototypeOf(asy).constructor;
var wut = AsyncFunction('return await 4')
wut().then(r => console.log(r)); // 4
(function () {
yield 4; // SyntaxError: Unexpected number
})();
(function () {
var x = yield; // ReferenceError: yield is not defined
return x;
})();
var yield = 4;
console.log(yield); // 4
// wut?
function let() { return 4; }
console.log(let()); // 4
var let = 4;
console.log(let); // 4
// The right operand (RHS) of extends can be any expression
class Foo extends (false || Number) {}
// Remember writing bad code abusing default arguments? :D
// Extending null has a special meaning, similar to Object.create(null)
class Regular {}
Object.getPrototypeOf(Regular.prototype); // Object.prototype
class Foo extends null {}
Object.getPrototypeOf(Regular.prototype); // null
// ...but leads to funky shit
new Foo; // TypeError: super is not a constructor
class Bar extends null { constructor() {} }
new Bar; // ReferenceError: this is not defined
// wut?
// Open question: How do we properly implement Foo or Bar?
class Foo extends 4 {} // TypeError, legit
class Foo {} // SyntaxError: Foo already declared
Foo // ReferenceError: Foo is not defined
// wut?
// Also works with the other temporal-dead-zone annhiliating declarations, let and const
let foo = (() => { throw 'wut'; })(); // UncaughtError
foo; // ReferenceError
foo = 4; // SyntaxError: foo has already been declared
super
// Usually used inside classes
// Inspired by http://www.sitesbay.com/java/java-super-keyword
class Student {
message() {
console.log("Good Morning Sir");
}
}
class Faculty extends Student {
message() {
console.log("Good Morning Students");
}
display() {
this.message();//will invoke or call current class message() method
super.message();//will invoke or call parent class message() method
}
}
// I had to fix a bug in this part, s/Student/Faculty/ sorry sitesbay.com
let f = new Faculty();
f.display();
// Good Morning Students
// Good Morning Sir
super
outside classes
var parent = {
x: 4
};
var child = {
__proto__: parent,
x: 7,
whatsX() {
console.log(super.x); // 4
},
heynow: function () {
console.log(super.x); // SyntaxError, super can only be used inside methods
}
};
// super is computed from a hidden property called [[HomeObject]] which refers
//to the object at *function creation time*, so the following will not work as
//you expect:
var riddle =
wut() {
console.log(super.x); // Object.getPrototypeOf(riddle).x === undefined
}
};
Object.assign(child, riddle);
super
var foo = {
x: 4,
whatsX() {
return super.x;
}
};
var p = new Proxy(foo, {});
Object.setPrototypeOf(foo, p);
// Inside of `foo`'s methods, `super` refers to the proxy, which delegates to `foo`
// Meaning: We sort of get a reference to [[HomeObject]]!
foo.whatsX(); // 4
foo.whatsX.call({ x: 12 }); // 4
var wut = foo.whatsX.bind({ x: 13 });
wut(); // 4
// win
Elephants in the room since 1999
// `break` and `continue` can be used with labels to control loops:
outer: for (...) {
for (...) {
break outer;
}
}
// Not frequently used, regarded as bad practice.
// Now tell me, what does this do (hint: not a syntax error)?
foo: {
console.log(1);
break foo;
console.log(2);
}
foo: function foo() {
foo: {
console.log(1);
break foo;
console.log(2);
}
}
foo: try { throw 'wut'; }
catch (e) {
console.log(1);
break foo;
console.log(2);
}
<script type="text/javascript">
<!-- <![CDATA[
// awesome code goes here
]] -->
</script>
console.log(0);
<!-- console.log('wtf is this!?')
console.log(1);
--> console.log('i will not run!');
console.log(2);
<!--
console.log('wat');
--> <b>wat</b>
console.log('wat')
<b>wat</b>
Questions?