-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use spread operator instead of apply when supported
Introduces non-backward compatible changes to automatic constructor detection in createFactory.
- Loading branch information
Krzysztof Chrapka
committed
Apr 11, 2016
1 parent
4185eb1
commit b365c09
Showing
10 changed files
with
278 additions
and
40 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,45 @@ | ||
(function(define) { | ||
'use strict'; | ||
define(function() { | ||
/** | ||
* Carefully sets the instance's constructor property to the supplied | ||
* constructor, using Object.defineProperty if available. If it can't | ||
* set the constructor in a safe way, it will do nothing. | ||
* | ||
* @param instance {Object} component instance | ||
* @param ctor {Function} constructor | ||
*/ | ||
function defineConstructorIfPossible(instance, ctor) { | ||
try { | ||
Object.defineProperty(instance, 'constructor', { | ||
value: ctor, | ||
enumerable: false | ||
}); | ||
} catch(e) { | ||
// If we can't define a constructor, oh well. | ||
// This can happen if in envs where Object.defineProperty is not | ||
// available, or when using cujojs/poly or other ES5 shims | ||
} | ||
} | ||
|
||
return function(func, thisObj, args) { | ||
var result = null; | ||
|
||
if(thisObj && typeof thisObj[func] === 'function') { | ||
func = thisObj[func]; | ||
} | ||
|
||
// detect case when apply is called on constructor and fix prototype chain | ||
if (thisObj === func) { | ||
thisObj = Object.create(func.prototype); | ||
defineConstructorIfPossible(thisObj, func); | ||
func.apply(thisObj, args); | ||
result = thisObj; | ||
} else { | ||
result = func.apply(thisObj, args); | ||
} | ||
|
||
return result; | ||
}; | ||
}); | ||
})(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }); |
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,28 @@ | ||
/* jshint esversion: 6 */ | ||
(function(define) { | ||
'use strict'; | ||
define(function() { | ||
return function(func, thisObj, args) { | ||
var result = null; | ||
|
||
if(thisObj === func || (thisObj && thisObj.constructor === func)) { | ||
/* jshint newcap: false */ | ||
result = new func(...(args||[])); | ||
|
||
// detect broken old prototypes with missing constructor | ||
if (result.constructor !== func) { | ||
Object.defineProperty(result, 'constructor', { | ||
enumerable: false, | ||
value: func | ||
}); | ||
} | ||
} else if(thisObj && typeof thisObj[func] === 'function') { | ||
result = thisObj[func](...args); | ||
} else { | ||
result = func.apply(thisObj, args); | ||
} | ||
|
||
return result; | ||
}; | ||
}); | ||
})(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }); |
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 |
---|---|---|
@@ -1,11 +1,13 @@ | ||
(function(define) { | ||
define(function() { | ||
|
||
var universalApply = require('./universalApply'); | ||
|
||
return function(methodName, args) { | ||
return function(target) { | ||
return target[methodName].apply(target, args); | ||
return universalApply(target[methodName], target, args); | ||
}; | ||
}; | ||
|
||
}); | ||
})(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }); | ||
})(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }); |
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,45 @@ | ||
(function(){ | ||
'use strict'; | ||
|
||
(function(define){ | ||
|
||
function evaluates (statement) { | ||
try { | ||
/* jshint evil: true */ | ||
eval(statement); | ||
/* jshint evil: false */ | ||
return true; | ||
} catch (err) { | ||
return false; | ||
} | ||
} | ||
|
||
// we have to know it synchronously, we are unable to load this module in asynchronous way | ||
// we cannot defer `define` and we cannot load module, that would not compile in browser | ||
// so we can't delegate this check to another module | ||
function isSpreadAvailable() { | ||
return evaluates('Math.max(...[ 5, 10 ])'); | ||
} | ||
|
||
var requires = []; | ||
if (typeof('process') !== 'undefined' && 'ES_VERSION' in process.env) { | ||
requires.push('./es'+process.env.ES_VERSION+'Apply'); | ||
} else { | ||
if(isSpreadAvailable()) { | ||
requires.push('./es6Apply'); | ||
} else { | ||
requires.push('./es5Apply'); | ||
} | ||
} | ||
|
||
define('universalApply', requires, function(apply){ | ||
return apply; | ||
}); | ||
})( | ||
typeof define === 'function' | ||
? define | ||
: function(name, requires, factory) { | ||
module.exports = factory.apply(null, requires.map(require)); | ||
} | ||
); | ||
})(); |
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 |
---|---|---|
@@ -1,8 +1,42 @@ | ||
'use strict'; | ||
|
||
require('gent/test-adapter/buster'); | ||
|
||
function evaluates (statement) { | ||
try { | ||
/* jshint evil: true */ | ||
eval(statement); | ||
/* jshint evil: false */ | ||
return true; | ||
} catch (err) { | ||
return false; | ||
} | ||
} | ||
|
||
function isClassAvailable() { | ||
return evaluates('class es6TestClass_ibyechBaloodren7 {}'); | ||
} | ||
|
||
function isSpreadAvailable() { | ||
return evaluates('parseInt(...["20", 10])'); | ||
} | ||
|
||
var tests = ['node/**/*-test.js']; | ||
|
||
console.log('class operator %savailable', isClassAvailable() ? '' : 'not '); | ||
console.log('spread operator %savailable', isSpreadAvailable() ? '' : 'not '); | ||
|
||
if( | ||
isClassAvailable() | ||
&& isSpreadAvailable() | ||
&& !('ES_VERSION' in process.env && parseFloat(process.env.ES_VERSION) < 6) | ||
) { | ||
tests.push('node-es6/**/*-test.js'); | ||
} | ||
|
||
module.exports['node'] = { | ||
environment: 'node', | ||
tests: ['node/**/*-test.js'] | ||
tests: tests | ||
// TODO: Why doesn't this work? | ||
//, testHelpers:['gent/test-adapter/buster'] | ||
}; | ||
}; |
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,97 @@ | ||
/* jshint esversion: 6 */ | ||
(function(buster, context) { | ||
'use strict'; | ||
|
||
var assert, refute, fail, sentinel; | ||
|
||
assert = buster.assert; | ||
refute = buster.refute; | ||
fail = buster.fail; | ||
|
||
sentinel = {}; | ||
|
||
function createContext(spec) { | ||
return context.call(null, spec, null, { require: require }); | ||
} | ||
|
||
class es6Class | ||
{ | ||
constructor () { | ||
this.constructorRan = true; | ||
this.args = Array.prototype.slice.call(arguments); | ||
} | ||
|
||
someMethod() { | ||
|
||
} | ||
} | ||
|
||
buster.testCase('es6/lib/plugin/basePlugin', { | ||
'clone factory': { | ||
'should call constructor when cloning an object with a constructor': function() { | ||
class FabulousEs6 { | ||
constructor () { | ||
this.instanceProp = 'instanceProp'; | ||
} | ||
} | ||
FabulousEs6.prototype.prototypeProp = 'prototypeProp'; | ||
|
||
return createContext({ | ||
fab: { | ||
create: FabulousEs6 | ||
}, | ||
copy: { | ||
clone: { $ref: 'fab' } | ||
} | ||
}).then( | ||
function(context) { | ||
assert.defined(context.copy, 'copy is defined'); | ||
assert.defined(context.copy.prototypeProp, 'copy.prototypeProp is defined'); | ||
assert.defined(context.copy.instanceProp, 'copy.instanceProp is defined'); | ||
refute.same(context.copy, context.fab); | ||
}, | ||
fail | ||
); | ||
} | ||
}, | ||
|
||
'create factory': { | ||
'should call es6 constructor': function() { | ||
return createContext({ | ||
test: { | ||
create: { | ||
module: es6Class, | ||
isConstructor: true | ||
} | ||
} | ||
}).then( | ||
function(context) { | ||
assert(context.test.constructorRan); | ||
}, | ||
fail | ||
); | ||
}, | ||
|
||
'should call es6 constructor functions with args': function() { | ||
return createContext({ | ||
test: { | ||
create: { | ||
module: es6Class, | ||
isConstructor: true, | ||
args: [1, 'foo', 1.7] | ||
} | ||
} | ||
}).then( | ||
function(context) { | ||
assert(context.test instanceof es6Class); | ||
assert.equals(context.test.args, [1, 'foo', 1.7]); | ||
}, | ||
fail | ||
); | ||
}, | ||
} | ||
}); | ||
})( | ||
require('buster'), | ||
require('../../../../lib/context') | ||
); |
Oops, something went wrong.