Comments (17)
Perhaps,
var needsPatch;
try {
var result = (function (a) {return a}).apply(null, {0: 10, length: 1});
needsPatch = result !== 10;
} catch (exception) {
needsPatch = true;
}
if (needsPatch) {
Function.prototype.apply = (function (apply) {
return function (thisp, args) {
return apply.call(this, thisp, Array.prototype.slice.call(args));
};
})(Function.prototype.apply);
}
Your Array.from(x)
is equivalent to the generic Array.prototype.slice.call(x)
. The initial index of undefined
(by omission) is coerced to 0 internally, though it would be a good thing™ if Array.from
became a standard, especially if it handled Harmony iterables.
from es5-shim.
@kriskowal
Due this patch is only actual in IE < 9 I have to note that IE < 9 throw exception if we try to Array.prototype.slice.call(x)
on DOM object, eg NodeList: "this" is not a JavaScript object
And for performance we have to try original function first.
Also Array.prototype.slice.call
don't throw exception if args
is a String
, that is not valid.
Thx for the syntax idea!
from es5-shim.
Allowing array-like object in Function.prototype.apply
is very important to me due DOM4 mutation methods:
var node = document.querySelector("div");
node.append.apply(node, document.querySelectorAll("p"));
This code should append all <p>
nodes into a given <div>
node.
Since I implement DOM4 mutation methods in IE6+ I have to path Function.prototype.apply
to allow array-like object as a second parameter - link to path
If you find it useful, I can make Pull Request
My previous Pull Request #100 contains a error which I fixed
from es5-shim.
Native ES5 apply, doesn't skip holes in array-like objects as your approach. If you are going to use it, you should fix it.
from es5-shim.
@abozhilov Yes you are right. And I fixed this. My Array.from function now is:
Array.from = function(iterable) {
if(iterable instanceof Array || Array.isArray(iterable))return iterable;
if(iterable.toArray)return iterable.toArray();
var object = Object(iterable),
length = object.length >>> 0,
result;
try {
result = Array.prototype.slice.call(object);//Old IE throw error here when object is NodeList
}
catch(e) { }
if(result && result.length === length)return result;
result = [];
for(var key = 0 ; key < length ; key++) {
if(key in object)
result[key] = object[key];
}
return result;
};
And here is a new Function.prototype.apply
fix without try/catch
:
var /** @const */
_Function_apply = Function.prototype.apply;
var canUseArrayLikeObjectInApply = false;
try {
canUseArrayLikeObjectInApply = isNaN.apply(null, {})
}
catch(e) { }
if(!canUseArrayLikeObjectInApply) {
Function.prototype.apply = function(contexts, args) {
// 1. simple case, no args at all
if(!args) {
return _Function_apply.call(this, contexts);
}
// 2. args is not an array-like object or just an array
if (
typeof args !== "object" // when args is not an object -> IE throw error for this case and would be right
|| args instanceof Array // if args is array -> IE can eat this case
|| !("length" in args) // if args is an abject but not array and also has no "length" prop -> IE throw error for this case and would be right
) {
return _Function_apply.call(this, contexts, args);
}
// 3. args is array-like object
return _Function_apply.call(this, contexts, Array.from(args));
};
}
from es5-shim.
@termi Array.from is part of ES6 - I suggest using a different name to avoid collision, or following the spec. A tested implementation that does so is available here: https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js#L214-L237
Considering Array.from is in ES6 already, this might be an issue better fixed in the ES6 shim? Please feel free to file it there as well - I'll leave this open too.
from es5-shim.
@ljharb I will update my Array.from as soon as i am starting using my es6-transpiler in a real project. At the time when I wrote Array.from, it conforms to the specifications. Now it needs refactoring as well as my entire es5/6 shim project (I started it when I first started learning javascript).
As far as I see, es6-shim implementation doesn't match the specification since it doesn't support iterator protocol. In my es6-transpiler I want to use Array.from for spread emulation (I suggest that native implementation would be much faster than something like this var iter = obj.iterator(), v; while(v = iter.next().done !== false){ /*do*/ }
- but it's only an assumption, that must be verified on native Array.from).
But back to es6-shim implementation - since it not support iterator protocol, it's breaks the code that generated by my transpiler, so I must to reshim it again:
var obj = {a: 9};
obj.iterator = function() {
var iterableObject = this;
var first = true;
return {
next: function() {
var result = {
value: first ? iterableObject.a : void 0
, done: !first
}
first = false;
return result;
}
}
}
function Array_from_test() {
var result;
try {
result = Array.from(obj);
}
catch(){}
return Array.isArray(result) && result.length === 1 && result[0] === obj.a;
}
if( !Array_from_test() ) {
Array.from = function() {
// my implementation
}
}
Considering Array.from is in ES6 already, this might be an issue better fixed in the ES6 shim? Please feel free to file it there as well - I'll leave this open too.
Array.from is not related to the topic of this issue. As I mentioned before, Function#apply in IE < 9 doesn't allow array-like object as second parameter. It's doesn't match the specification of ecmascript 5! So I think it's must be fixed in es5-shim.
from es5-shim.
Can you file an issue on es6-shim about the iterator stuff? We'll verify that separately.
I think you're right; fixing apply should be done here. However, shimming Array.from should decidely not be here, since it's not part of ES5. In addition, there should be no code in the es5-shim that has anything to do with iterators, for the same reason - if it works, great, but the code shouldn't attempt to deal with it.
from es5-shim.
@ljharb, lol, why? if this code is needed, it could be used.
from es5-shim.
@ljharb paulmillr/es6-shim#182
from es5-shim.
@Yaffle this is the ES5 shim. ES6 doesn't belong in it. If you want to get any ES6 behavior, you'd use both the ES5 and ES6 shims together (they're designed to be layered)
from es5-shim.
@ljharb , better fix es6-shim
from es5-shim.
That was my suggestion, but @termi's point about apply being broken in ES5 is still valid here. Please see the discussion in paulmillr/es6-shim#182 on why iterators can't be shimmed.
from es5-shim.
This affects node 0.6 as well
from es5-shim.
This also affects OmniWeb v5.11.2
from es5-shim.
This is a subject that I have been looking at for some of my own code and I thought I'd share my investigation code with you as it may be of some help (maybe). Sorry about the size of it, I don't know how to make the code block scrollable. :)
Available on jsFiddle
/*global Date, Function, Math, Object, String */
/*properties
abs, apply, call, floor, length, log, max, min, prototype, random
*/
/*jslint browser: true, devel: true, node: true, sloppy: true*/
(function (global) {
/**
* Returns an arguments object of the arguments supplied.
*
* @private
* @function $returnArgs
* @param {...*} [varArgs]
* @return {Arguments}
*/
function $returnArgs() {
return arguments;
}
(function (call, apply) {
'use strict';
/*jslint vars: true */
/**
* @name forceCallShim
* @type {boolean}
*/
var forceCallShim = false;
/**
* @name forceCallPatch
* @type {boolean}
*/
var forceCallPatch = false;
/**
* @name forceApplyShim
* @type {boolean}
*/
var forceApplyShim = false;
/**
* @name forceApplyPatch
* @type {boolean}
*/
var forceApplyPatch = false;
/**
* @name MAX_SAFE_INTEGER
* @type {number}
* @const
* @default 9007199254740991
*/
var MAX_SAFE_INTEGER = 9007199254740991;
/**
* Shortcut
* The Object constructor creates an object wrapper.
* Keeps jslint happy
*
* @private
* @function $Object
* @param {*} inputArg
* @return {Object}
*/
var $Object = Object;
/**
* Shortcut
* The String constructor creates an object wrapper.
* Keeps jslint happy
*
* @private
* @function $String
* @param {*} inputArg
* @return {Object}
*/
var $String = String;
/**
* Shortcut
*
* @private
* @function $min
* @param {number} number
* @return {number}
*/
var $min = Math.min;
/**
* Shortcut
*
* @private
* @function $max
* @param {number} number
* @return {number}
*/
var $max = Math.max;
/**
* Shortcut
*
* @private
* @function $floor
* @param {number} number
* @return {number}
*/
var $floor = Math.floor;
/**
* Shortcut
*
* @private
* @function $abs
* @param {number} number
* @return {number}
*/
var $abs = Math.abs;
/*
* Shortcut
*
* @private
* @function $random
* @returns {number}
*/
var $random = Math.random;
/**
* Shortcut
* Returns true if the operands are strictly equal with no type conversion.
*
* @private
* @function $strictEqual
* @param {*} a
* @param {*} b
* @return {boolean}
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4
*/
function $strictEqual(a, b) {
return a === b;
}
/**
* Shortcut
* Returns true if the operand inputArg is undefined.
*
* @private
* @function $isUndefined
* @param {*} inputArg
* @return {boolean}
*/
function $isUndefined(inputArg) {
return $strictEqual(typeof inputArg, 'undefined');
}
/**
* The abstract operation converts its argument to a value of type string
*
* @private
* @function $toString
* @param {*} inputArg
* @return {string}
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.8
*/
function $toString(inputArg) {
var val;
if (inputArg === null) {
val = 'null';
} else if ($isUndefined(inputArg)) {
val = 'undefined';
} else {
val = $String(inputArg);
}
return val;
}
/**
* The function evaluates the passed value and converts it to an integer.
*
* @private
* @function $toInteger
* @param {*} inputArg The object to be converted to an integer.
* @return {number} If the target value is NaN, null or undefined, 0 is returned. If the target value is false, 0 is returned and if true, 1 is returned.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
*/
function $toInteger(inputArg) {
var number = +inputArg,
val = 0;
if ($strictEqual(number, number)) {
if (!number || number === Infinity || number === -Infinity) {
val = number;
} else {
val = (number > 0 || -1) * $floor($abs(number));
}
}
return val;
}
/**
* The abstract operation ToLength converts its argument to an integer suitable for use as the length
* of an array-like object.
*
* @private
* @function $toLength
* @param {*} inputArg The object to be converted to a length.
* @return {number} If len <= +0 then +0 else if len is +Infinity then 2^53-1 else min(len, 2^53-1).
* @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
*/
function $toLength(inputArg) {
return $min($max($toInteger(inputArg), 0), MAX_SAFE_INTEGER);
}
/**
* Returns the this context of the function.
*
* @private
* @function $returnThis
* @return {*}
*/
var $returnThis = function () {
return this;
};
/**
* Indicates if running in strict mode.
* True if we are, otherwise false.
*
* @private
* @name isStrictMode
* @type {boolean}
*/
var isStrictMode = !$returnThis();
console.log('isStrictMode', isStrictMode);
/**
* Indicates if the this argument used with call does not convert to an object when not strict mode.
* True if it does, otherwise false.
*
* @private
* @name hasCallBug
* @type {boolean}
*/
var hasCallBug = (function () {
var rtn;
// try in case call is missing
try {
rtn = !isStrictMode && typeof $returnThis.call('foo') === 'string';
} catch (eHasCallBug) {
rtn = false;
}
console.log('hasCallBug', rtn);
return rtn;
}());
/**
* Indicates if the this argument used with apply does not convert to an object when not strict mode.
* True if it does not, otherwise false.
*
* @private
* @name hasApplyBug
* @type {boolean}
*/
var hasApplyBug = (function () {
var rtn;
// try in case apply is missing
try {
rtn = !isStrictMode && typeof $returnThis.apply('foo') === 'string';
} catch (eHasApplyArrayBug) {
rtn = false;
}
console.log('hasApplyBug', rtn);
return rtn;
}());
/**
* Indicates if apply works with ArrayLike objects.
* True if it does not, otherwise false.
*
* @private
* @name supportsApplyArrayLike
* @type {boolean}
*/
var supportsApplyArrayLike = (function () {
var returnThis,
rtn;
returnThis = function (arg) {
return arg;
};
// try in case apply is missing
try {
rtn = returnThis.apply('foo', $returnArgs(1)) === 1;
} catch (eHasApplyArrayBug) {
rtn = false;
}
console.log('supportsApplyArrayLike', rtn);
return rtn;
}());
/**
* @private
* @function $getName
* @param {Object} object
* @param {string} name
* @return {string}
*/
function $getName(object, name) {
while (!$isUndefined(object[name])) {
name += $toString($toInteger($random() * 100));
}
return name;
}
/**
* @private
* @function $evalCallApply
* @param {*} thisArg
* @param {string} name
* @param {Function} func
* @param {ArrayLike} args
* @param {number} start
* @return {*}
*/
function $evalCallApply(thisArg, name, func, args, start) {
var length = $toLength(args.length),
last = length - 1,
argsStrings = '',
index,
rtn;
for (index = start; index < length; index += 1) {
argsStrings += 'args[' + index + ']';
if (index < last) {
argsStrings += ',';
}
}
name = $getName(thisArg, name);
thisArg[name] = func;
/*jslint evil: true */
rtn = new Function('fn', 'name', 'args', 'return function () { return fn[name](' + argsStrings + '); }();')(thisArg, name, args);
/*jslint evil: false */
delete thisArg[name];
return rtn;
}
/**
* Sets the arguments as per ES3 or ES5 non-strict spec.
*
* @private
* @function $toObjectThisArg
* @param {*} thisArg
* @return {Object}
*/
function $toObjectThisArg(thisArg) {
if (!isStrictMode) {
if (thisArg === null || $isUndefined(thisArg)) {
thisArg = global;
} else {
thisArg = $Object(thisArg);
}
}
return thisArg;
}
/**
* @global
* @function Function.prototype.call
*/
if (forceCallShim || !call) {
// ES3 spec
Function.prototype.call = function (thisArg) {
return $evalCallApply($toObjectThisArg(thisArg), '__call__', this, arguments, 1);
};
console.log('Function.prototype.call: ES3 shim');
} else if (forceCallPatch || hasCallBug) {
// ES5 patch
Function.prototype.call = (function () {
return function (thisArg) {
var name = '__call_',
args = [],
length = $toLength(arguments.length),
index,
rtn;
name = $getName(this, name);
this[name] = apply;
args.length = length - 1;
for (index = 1; index < length; index += 1) {
args[index - 1] = arguments[index];
}
rtn = this[name]($toObjectThisArg(thisArg), args);
delete this[name];
return rtn;
};
}());
console.log('Function.prototype.call: ES5 patch');
}
/**
* @global
* @function Function.prototype.apply
*/
if (forceApplyShim || !apply) {
// ES3 spec
Function.prototype.apply = function (thisArg, arrayLike) {
return $evalCallApply($toObjectThisArg(thisArg), '__apply__', this, arrayLike, 0);
};
console.log('Function.prototype.apply: ES3 shim');
} else if (forceApplyPatch || hasApplyBug || !supportsApplyArrayLike) {
// ES5 patch
Function.prototype.apply = (function () {
return function (thisArg, arrayLike) {
var object = $Object(arrayLike),
name = '__apply_',
args,
length,
index,
rtn;
name = $getName(this, name);
this[name] = apply;
if (supportsApplyArrayLike) {
args = object;
} else {
args = [];
length = $toLength(object.length);
args.length = length;
for (index = 0; index < length; index += 1) {
args[index] = object[index];
}
}
rtn = this[name]($toObjectThisArg(thisArg), args);
delete this[name];
return rtn;
};
}());
console.log('Function.prototype.apply: ES5 patch');
}
}(Function.prototype.call, Function.prototype.apply));
(function () {
console.log('\nNon-Strict Mode\n');
/**
* Function to test call and apply
*
* @private
* @function testFn
* @param {...*} [varArgs]
*/
var testFn = function (title) {
console.log('\n' + title + '\n');
console.log('typeof this: ', typeof this);
console.log('this: ', this);
console.log('arguments: ', arguments);
};
testFn.call('foo', 'call', undefined, null, 1, '2', true, Object, new Date());
testFn.apply('foo', $returnArgs('apply', undefined, null, 1, '2', true, Object, new Date()));
testFn.call(undefined, 'call', undefined, null, 1, '2', true, Object, new Date());
}());
(function () {
'use strict';
console.log('\nStrict Mode\n');
/**
* Function to test call and apply
*
* @private
* @function testFn
* @param {...*} [varArgs]
*/
var testFn = function (title) {
console.log('\n' + title + '\n');
console.log('typeof this: ', typeof this);
console.log('this: ', this);
console.log('arguments: ', arguments);
};
testFn.call('foo', 'call', undefined, null, 1, '2', true, Object, new Date());
testFn.apply('foo', $returnArgs('apply', undefined, null, 1, '2', true, Object, new Date()));
testFn.call(undefined, 'call', undefined, null, 1, '2', true, Object, new Date());
}());
}(this));
from es5-shim.
@ljharb I now have apply
working on IE8
https://rawgit.com/Xotic750/es5-shim/apply/tests/index.html
from es5-shim.
Related Issues (20)
- All V8 versions have the Date shim loaded. HOT 1
- Rounding errors with toExponential in Edge 15/16 HOT 4
- IE9 error: Redeclaration of const property HOT 5
- Something in the shim causes Chrome to throw a warning HOT 3
- 4.5.11 got a tag, but not a full release HOT 2
- `hasAutomationEqualityBug` triggers deprecation warnings in Firefox
- Cannot convert undefined or null to object PropTypes.shape() HOT 9
- Question: Why concatenate Symbols with a string without assigning it to a variable? HOT 6
- Why not set bound.prototype = target.prototype directly? HOT 3
- in ie8 Object.keys failed this conversion HOT 3
- Action required: Greenkeeper could not be activated 🚨
- String#lastIndexOf has a shallow-wrapper shim that does nothing and always gets applied HOT 1
- Dependency Dashboard
- Conflict ES5-shim(sham) 4.5.15 and ES6-shim(sham) 0.35.x HOT 19
- Please change the name of the default branch HOT 1
- External URL linking to spam websites HOT 2
- errors with Object.defineProperty under ie8 since 4.6.3 HOT 6
- Incompatibility with awilix HOT 17
- Invalid Date on Safari HOT 2
- ⚠️ `webkitStorageInfo` is going away HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from es5-shim.