├── .editorconfig ├── .eslintrc ├── .github ├── FUNDING.yml └── workflows │ ├── node-aught.yml │ ├── node-pretest.yml │ ├── node-tens.yml │ ├── rebase.yml │ └── require-allow-edits.yml ├── .gitignore ├── .npmrc ├── .nycrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── implementation.js ├── index.js ├── isArguments.js ├── package.json └── test ├── isArguments.js └── shim.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab; 5 | insert_final_newline = true; 6 | quote_type = auto; 7 | space_after_anonymous_functions = true; 8 | space_after_control_statements = true; 9 | spaces_around_operators = true; 10 | trim_trailing_whitespace = true; 11 | spaces_in_brackets = false; 12 | end_of_line = lf; 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | 4 | "extends": "@ljharb", 5 | 6 | "rules": { 7 | "complexity": [2, 23], 8 | "id-length": [2, { "min": 1, "max": 40 }], 9 | "max-statements": [2, 23], 10 | "max-statements-per-line": [2, { "max": 2 }], 11 | "no-restricted-syntax": [2, "BreakStatement", "ContinueStatement", "LabeledStatement", "WithStatement"], 12 | }, 13 | 14 | "overrides": [ 15 | { 16 | "files": "test/**", 17 | "rules": { 18 | "max-params": [2, 3], 19 | "no-invalid-this": 1, 20 | "no-magic-numbers": 0, 21 | }, 22 | }, 23 | ], 24 | } 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ljharb] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: npm/object-keys 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/node-aught.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js < 10' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/node.yml@main 8 | with: 9 | range: '< 10' 10 | type: minors 11 | command: npm run tests-only 12 | 13 | node: 14 | name: 'node < 10' 15 | needs: [tests] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - run: 'echo tests completed' 19 | -------------------------------------------------------------------------------- /.github/workflows/node-pretest.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: pretest/posttest' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/pretest.yml@main 8 | -------------------------------------------------------------------------------- /.github/workflows/node-tens.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js >= 10' 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | tests: 7 | uses: ljharb/actions/.github/workflows/node.yml@main 8 | with: 9 | range: '>= 10' 10 | type: minors 11 | command: npm run tests-only 12 | 13 | node: 14 | name: 'node >= 10' 15 | needs: [tests] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - run: 'echo tests completed' 19 | -------------------------------------------------------------------------------- /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | 3 | on: [pull_request_target] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | _: 10 | permissions: 11 | contents: write # for ljharb/rebase to push code to rebase 12 | pull-requests: read # for ljharb/rebase to get info about PR 13 | 14 | name: "Automatic Rebase" 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: ljharb/rebase@master 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/require-allow-edits.yml: -------------------------------------------------------------------------------- 1 | name: Require “Allow Edits” 2 | 3 | on: [pull_request_target] 4 | 5 | jobs: 6 | _: 7 | name: "Require “Allow Edits”" 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: ljharb/require-allow-edits@main 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gitignore 2 | 3 | node_modules 4 | 5 | # Only apps should have lockfiles 6 | npm-shrinkwrap.json 7 | yarn.lock 8 | package-lock.json 9 | 10 | .nyc_output/ 11 | coverage/ 12 | 13 | .npmignore 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "check-coverage": false, 4 | "reporter": ["text-summary", "text", "html", "json"], 5 | "exclude": [ 6 | "coverage", 7 | "test" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.1.1 / 2019-04-06 2 | ================= 3 | * [Fix] exclude deprecated Firefox keys (#53) 4 | 5 | 1.1.0 / 2019-02-10 6 | ================= 7 | * [New] [Refactor] move full implementation to `implementation` entry point 8 | * [Refactor] only evaluate the implementation if `Object.keys` is not present 9 | * [Tests] up to `node` `v11.8`, `v10.15`, `v8.15`, `v6.16` 10 | * [Tests] remove jscs 11 | * [Tests] switch to `npm audit` from `nsp` 12 | 13 | 1.0.12 / 2018-06-18 14 | ================= 15 | * [Fix] avoid accessing `window.applicationCache`, to avoid issues with latest Chrome on HTTP (#46) 16 | 17 | 1.0.11 / 2016-07-05 18 | ================= 19 | * [Fix] exclude keys regarding the style (eg. `pageYOffset`) on `window` to avoid reflow (#32) 20 | 21 | 1.0.10 / 2016-07-04 22 | ================= 23 | * [Fix] exclude `height` and `width` keys on `window` to avoid reflow (#31) 24 | * [Fix] In IE 6, `window.external` makes `Object.keys` throw 25 | * [Tests] up to `node` `v6.2`, `v5.10`, `v4.4` 26 | * [Tests] use pretest/posttest for linting/security 27 | * [Dev Deps] update `tape`, `jscs`, `nsp`, `eslint`, `@ljharb/eslint-config` 28 | * [Dev Deps] remove unused eccheck script + dep 29 | 30 | 1.0.9 / 2015-10-19 31 | ================= 32 | * [Fix] Blacklist 'frame' property on window (#16, #17) 33 | * [Dev Deps] update `jscs`, `eslint`, `@ljharb/eslint-config` 34 | 35 | 1.0.8 / 2015-10-14 36 | ================= 37 | * [Fix] wrap automation equality bug checking in try/catch, per [es5-shim#327](https://github.com/es-shims/es5-shim/issues/327) 38 | * [Fix] Blacklist 'window.frameElement' per [es5-shim#322](https://github.com/es-shims/es5-shim/issues/322) 39 | * [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG 40 | * [Tests] up to `io.js` `v3.3`, `node` `v4.2` 41 | * [Dev Deps] update `eslint`, `tape`, `@ljharb/eslint-config`, `jscs` 42 | 43 | 1.0.7 / 2015-07-18 44 | ================= 45 | * [Fix] A proper fix for 176f03335e90d5c8d0d8125a99f27819c9b9cdad / https://github.com/es-shims/es5-shim/issues/275 that doesn't break dontEnum/constructor fixes in IE 8. 46 | * [Fix] Remove deprecation message in Chrome by touching deprecated window properties (#15) 47 | * [Tests] Improve test output for automation equality bugfix 48 | * [Tests] Test on `io.js` `v2.4` 49 | 50 | 1.0.6 / 2015-07-09 51 | ================= 52 | * [Fix] Use an object lookup rather than ES5's `indexOf` (#14) 53 | * [Tests] ES3 browsers don't have `Array.isArray` 54 | * [Tests] Fix `no-shadow` rule, as well as an IE 8 bug caused by engine NFE shadowing bugs. 55 | 56 | 1.0.5 / 2015-07-03 57 | ================= 58 | * [Fix] Fix a flabbergasting IE 8 bug where `localStorage.constructor.prototype === localStorage` throws 59 | * [Tests] Test up to `io.js` `v2.3` 60 | * [Dev Deps] Update `nsp`, `eslint` 61 | 62 | 1.0.4 / 2015-05-23 63 | ================= 64 | * Fix a Safari 5.0 bug with `Object.keys` not working with `arguments` 65 | * Test on latest `node` and `io.js` 66 | * Update `jscs`, `tape`, `eslint`, `nsp`, `is`, `editorconfig-tools`, `covert` 67 | 68 | 1.0.3 / 2015-01-06 69 | ================= 70 | * Revert "Make `object-keys` more robust against later environment tampering" to maintain ES3 compliance 71 | 72 | 1.0.2 / 2014-12-28 73 | ================= 74 | * Update lots of dev dependencies 75 | * Tweaks to README 76 | * Make `object-keys` more robust against later environment tampering 77 | 78 | 1.0.1 / 2014-09-03 79 | ================= 80 | * Update URLs and badges in README 81 | 82 | 1.0.0 / 2014-08-26 83 | ================= 84 | * v1.0.0 85 | 86 | 0.6.1 / 2014-08-25 87 | ================= 88 | * v0.6.1 89 | * Updating dependencies (tape, covert, is) 90 | * Update badges in readme 91 | * Use separate var statements 92 | 93 | 0.6.0 / 2014-04-23 94 | ================= 95 | * v0.6.0 96 | * Updating dependencies (tape, covert) 97 | * Make sure boxed primitives, and arguments objects, work properly in ES3 browsers 98 | * Improve test matrix: test all node versions, but only latest two stables are a failure 99 | * Remove internal foreach shim. 100 | 101 | 0.5.1 / 2014-03-09 102 | ================= 103 | * 0.5.1 104 | * Updating dependencies (tape, covert, is) 105 | * Removing forEach from the module (but keeping it in tests) 106 | 107 | 0.5.0 / 2014-01-30 108 | ================= 109 | * 0.5.0 110 | * Explicitly returning the shim, instead of returning native Object.keys when present 111 | * Adding a changelog. 112 | * Cleaning up IIFE wrapping 113 | * Testing on node 0.4 through 0.11 114 | 115 | 0.4.0 / 2013-08-14 116 | ================== 117 | 118 | * v0.4.0 119 | * In Chrome 4-10 and Safari 4, typeof (new RegExp) === 'function' 120 | * If it's a string, make sure to use charAt instead of brackets. 121 | * Only use Function#call if necessary. 122 | * Making sure the context tests actually run. 123 | * Better function detection 124 | * Adding the android browser 125 | * Fixing testling files 126 | * Updating tape 127 | * Removing the "is" dependency. 128 | * Making an isArguments shim. 129 | * Adding a local forEach shim and tests. 130 | * Updating paths. 131 | * Moving the shim test. 132 | * v0.3.0 133 | 134 | 0.3.0 / 2013-05-18 135 | ================== 136 | 137 | * README tweak. 138 | * Fixing constructor enum issue. Fixes [#5](https://github.com/ljharb/object-keys/issues/5). 139 | * Adding a test for [#5](https://github.com/ljharb/object-keys/issues/5) 140 | * Updating readme. 141 | * Updating dependencies. 142 | * Giving credit to lodash. 143 | * Make sure that a prototype's constructor property is not enumerable. Fixes [#3](https://github.com/ljharb/object-keys/issues/3). 144 | * Adding additional tests to handle arguments objects, and to skip "prototype" in functions. Fixes [#2](https://github.com/ljharb/object-keys/issues/2). 145 | * Fixing a typo on this test for [#3](https://github.com/ljharb/object-keys/issues/3). 146 | * Adding node 0.10 to travis. 147 | * Adding an IE < 9 test per [#3](https://github.com/ljharb/object-keys/issues/3) 148 | * Adding an iOS 5 mobile Safari test per [#2](https://github.com/ljharb/object-keys/issues/2) 149 | * Moving "indexof" and "is" to be dev dependencies. 150 | * Making sure the shim works with functions. 151 | * Flattening the tests. 152 | 153 | 0.2.0 / 2013-05-10 154 | ================== 155 | 156 | * v0.2.0 157 | * Object.keys should work with arrays. 158 | 159 | 0.1.8 / 2013-05-10 160 | ================== 161 | 162 | * v0.1.8 163 | * Upgrading dependencies. 164 | * Using a simpler check. 165 | * Fixing a bug in hasDontEnumBug browsers. 166 | * Using the newest tape! 167 | * Fixing this error test. 168 | * "undefined" is probably a reserved word in ES3. 169 | * Better test message. 170 | 171 | 0.1.7 / 2013-04-17 172 | ================== 173 | 174 | * Upgrading "is" once more. 175 | * The key "null" is breaking some browsers. 176 | 177 | 0.1.6 / 2013-04-17 178 | ================== 179 | 180 | * v0.1.6 181 | * Upgrading "is" 182 | 183 | 0.1.5 / 2013-04-14 184 | ================== 185 | 186 | * Bumping version. 187 | * Adding more testling browsers. 188 | * Updating "is" 189 | 190 | 0.1.4 / 2013-04-08 191 | ================== 192 | 193 | * Using "is" instead of "is-extended". 194 | 195 | 0.1.3 / 2013-04-07 196 | ================== 197 | 198 | * Using "foreach" instead of my own shim. 199 | * Removing "tap"; I'll just wait for "tape" to fix its node 0.10 bug. 200 | 201 | 0.1.2 / 2013-04-03 202 | ================== 203 | 204 | * Adding dependency status; moving links to an index at the bottom. 205 | * Upgrading is-extended; version 0.1.2 206 | * Adding an npm version badge. 207 | 208 | 0.1.1 / 2013-04-01 209 | ================== 210 | 211 | * Adding Travis CI. 212 | * Bumping the version. 213 | * Adding indexOf since IE sucks. 214 | * Adding a forEach shim since older browsers don't have Array#forEach. 215 | * Upgrading tape - 0.3.2 uses Array#map 216 | * Using explicit end instead of plan. 217 | * Can't test with Array.isArray in older browsers. 218 | * Using is-extended. 219 | * Fixing testling files. 220 | * JSHint/JSLint-ing. 221 | * Removing an unused object. 222 | * Using strict mode. 223 | 224 | 0.1.0 / 2013-03-30 225 | ================== 226 | 227 | * Changing the exports should have meant a higher version bump. 228 | * Oops, fixing the repo URL. 229 | * Adding more tests. 230 | * 0.0.2 231 | * Merge branch 'export_one_thing'; closes [#1](https://github.com/ljharb/object-keys/issues/1) 232 | * Move shim export to a separate file. 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2013 Jordan Harband 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # object-keys [![Version Badge][npm-version-svg]][package-url] 2 | 3 | [![github actions][actions-image]][actions-url] 4 | [![coverage][codecov-image]][codecov-url] 5 | [![dependency status][deps-svg]][deps-url] 6 | [![dev dependency status][dev-deps-svg]][dev-deps-url] 7 | [![License][license-image]][license-url] 8 | [![Downloads][downloads-image]][downloads-url] 9 | 10 | [![npm badge][npm-badge-png]][package-url] 11 | 12 | An Object.keys shim. Invoke its "shim" method to shim Object.keys if it is unavailable. 13 | 14 | Most common usage: 15 | ```js 16 | var keys = Object.keys || require('object-keys'); 17 | ``` 18 | 19 | ## Example 20 | 21 | ```js 22 | var keys = require('object-keys'); 23 | var assert = require('assert'); 24 | var obj = { 25 | a: true, 26 | b: true, 27 | c: true 28 | }; 29 | 30 | assert.deepEqual(keys(obj), ['a', 'b', 'c']); 31 | ``` 32 | 33 | ```js 34 | var keys = require('object-keys'); 35 | var assert = require('assert'); 36 | /* when Object.keys is not present */ 37 | delete Object.keys; 38 | var shimmedKeys = keys.shim(); 39 | assert.equal(shimmedKeys, keys); 40 | assert.deepEqual(Object.keys(obj), keys(obj)); 41 | ``` 42 | 43 | ```js 44 | var keys = require('object-keys'); 45 | var assert = require('assert'); 46 | /* when Object.keys is present */ 47 | var shimmedKeys = keys.shim(); 48 | assert.equal(shimmedKeys, Object.keys); 49 | assert.deepEqual(Object.keys(obj), keys(obj)); 50 | ``` 51 | 52 | ## Source 53 | Implementation taken directly from [es5-shim][es5-shim-url], with modifications, including from [lodash][lodash-url]. 54 | 55 | ## Tests 56 | Simply clone the repo, `npm install`, and run `npm test` 57 | 58 | [package-url]: https://npmjs.org/package/object-keys 59 | [npm-version-svg]: https://versionbadg.es/ljharb/object-keys.svg 60 | [deps-svg]: https://david-dm.org/ljharb/object-keys.svg 61 | [deps-url]: https://david-dm.org/ljharb/object-keys 62 | [dev-deps-svg]: https://david-dm.org/ljharb/object-keys/dev-status.svg 63 | [dev-deps-url]: https://david-dm.org/ljharb/object-keys#info=devDependencies 64 | [es5-shim-url]: https://github.com/es-shims/es5-shim/blob/master/es5-shim.js#L542-589 65 | [lodash-url]: https://github.com/lodash/lodash 66 | [npm-badge-png]: https://nodei.co/npm/object-keys.png?downloads=true&stars=true 67 | [license-image]: https://img.shields.io/npm/l/object-keys.svg 68 | [license-url]: LICENSE 69 | [downloads-image]: https://img.shields.io/npm/dm/object-keys.svg 70 | [downloads-url]: https://npm-stat.com/charts.html?package=object-keys 71 | [codecov-image]: https://codecov.io/gh/ljharb/object-keys/branch/main/graphs/badge.svg 72 | [codecov-url]: https://app.codecov.io/gh/ljharb/object-keys/ 73 | [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/object-keys 74 | [actions-url]: https://github.com/ljharb/object-keys/actions 75 | -------------------------------------------------------------------------------- /implementation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global window */ 4 | 5 | var keysShim; 6 | if (!Object.keys) { 7 | // modified from https://github.com/es-shims/es5-shim 8 | var has = Object.prototype.hasOwnProperty; 9 | var toStr = Object.prototype.toString; 10 | var isArgs = require('./isArguments'); // eslint-disable-line global-require 11 | var isEnumerable = Object.prototype.propertyIsEnumerable; 12 | var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); 13 | var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); 14 | var dontEnums = [ 15 | 'toString', 16 | 'toLocaleString', 17 | 'valueOf', 18 | 'hasOwnProperty', 19 | 'isPrototypeOf', 20 | 'propertyIsEnumerable', 21 | 'constructor' 22 | ]; 23 | var equalsConstructorPrototype = function (o) { 24 | var ctor = o.constructor; 25 | return ctor && ctor.prototype === o; 26 | }; 27 | var excludedKeys = { 28 | $applicationCache: true, 29 | $console: true, 30 | $external: true, 31 | $frame: true, 32 | $frameElement: true, 33 | $frames: true, 34 | $innerHeight: true, 35 | $innerWidth: true, 36 | $onmozfullscreenchange: true, 37 | $onmozfullscreenerror: true, 38 | $outerHeight: true, 39 | $outerWidth: true, 40 | $pageXOffset: true, 41 | $pageYOffset: true, 42 | $parent: true, 43 | $scrollLeft: true, 44 | $scrollTop: true, 45 | $scrollX: true, 46 | $scrollY: true, 47 | $self: true, 48 | $webkitIndexedDB: true, 49 | $webkitStorageInfo: true, 50 | $window: true 51 | }; 52 | var hasAutomationEqualityBug = (function () { 53 | if (typeof window === 'undefined') { return false; } 54 | for (var k in window) { 55 | try { 56 | if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { 57 | try { 58 | equalsConstructorPrototype(window[k]); 59 | } catch (e) { 60 | return true; 61 | } 62 | } 63 | } catch (e) { 64 | return true; 65 | } 66 | } 67 | return false; 68 | }()); 69 | var equalsConstructorPrototypeIfNotBuggy = function (o) { 70 | if (typeof window === 'undefined' || !hasAutomationEqualityBug) { 71 | return equalsConstructorPrototype(o); 72 | } 73 | try { 74 | return equalsConstructorPrototype(o); 75 | } catch (e) { 76 | return false; 77 | } 78 | }; 79 | 80 | keysShim = function keys(object) { 81 | var isObject = object !== null && typeof object === 'object'; 82 | var isFunction = toStr.call(object) === '[object Function]'; 83 | var isArguments = isArgs(object); 84 | var isString = isObject && toStr.call(object) === '[object String]'; 85 | var theKeys = []; 86 | 87 | if (!isObject && !isFunction && !isArguments) { 88 | throw new TypeError('Object.keys called on a non-object'); 89 | } 90 | 91 | var skipProto = hasProtoEnumBug && isFunction; 92 | if (isString && object.length > 0 && !has.call(object, 0)) { 93 | for (var i = 0; i < object.length; ++i) { 94 | theKeys.push(String(i)); 95 | } 96 | } 97 | 98 | if (isArguments && object.length > 0) { 99 | for (var j = 0; j < object.length; ++j) { 100 | theKeys.push(String(j)); 101 | } 102 | } else { 103 | for (var name in object) { 104 | if (!(skipProto && name === 'prototype') && has.call(object, name)) { 105 | theKeys.push(String(name)); 106 | } 107 | } 108 | } 109 | 110 | if (hasDontEnumBug) { 111 | var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); 112 | 113 | for (var k = 0; k < dontEnums.length; ++k) { 114 | if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { 115 | theKeys.push(dontEnums[k]); 116 | } 117 | } 118 | } 119 | return theKeys; 120 | }; 121 | } 122 | module.exports = keysShim; 123 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var slice = Array.prototype.slice; 4 | var isArgs = require('./isArguments'); 5 | 6 | var origKeys = Object.keys; 7 | var keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation'); 8 | 9 | keysShim.shim = function shimObjectKeys() { 10 | if (Object.keys) { 11 | var keysWorksWithArguments = (function () { 12 | // Safari 5.0 bug 13 | var args = Object.keys(arguments); 14 | return args && args.length === arguments.length; 15 | }(1, 2)); 16 | if (!keysWorksWithArguments) { 17 | Object.keys = function keys(object) { // eslint-disable-line func-name-matching 18 | if (isArgs(object)) { 19 | return origKeys(slice.call(object)); 20 | } 21 | return origKeys(object); 22 | }; 23 | } 24 | } else { 25 | Object.keys = keysShim; 26 | } 27 | return Object.keys || keysShim; 28 | }; 29 | 30 | module.exports = keysShim; 31 | -------------------------------------------------------------------------------- /isArguments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var toStr = Object.prototype.toString; 4 | 5 | module.exports = function isArguments(value) { 6 | var str = toStr.call(value); 7 | var isArgs = str === '[object Arguments]'; 8 | if (!isArgs) { 9 | isArgs = str !== '[object Array]' 10 | && value !== null 11 | && typeof value === 'object' 12 | && typeof value.length === 'number' 13 | && value.length >= 0 14 | && toStr.call(value.callee) === '[object Function]'; 15 | } 16 | return isArgs; 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object-keys", 3 | "version": "1.1.1", 4 | "author": { 5 | "name": "Jordan Harband", 6 | "email": "ljharb@gmail.com", 7 | "url": "http://ljharb.codes" 8 | }, 9 | "funding": { 10 | "url": "https://github.com/sponsors/ljharb" 11 | }, 12 | "contributors": [ 13 | { 14 | "name": "Jordan Harband", 15 | "email": "ljharb@gmail.com", 16 | "url": "http://ljharb.codes" 17 | }, 18 | { 19 | "name": "Raynos", 20 | "email": "raynos2@gmail.com" 21 | }, 22 | { 23 | "name": "Nathan Rajlich", 24 | "email": "nathan@tootallnate.net" 25 | }, 26 | { 27 | "name": "Ivan Starkov", 28 | "email": "istarkov@gmail.com" 29 | }, 30 | { 31 | "name": "Gary Katsevman", 32 | "email": "git@gkatsev.com" 33 | } 34 | ], 35 | "description": "An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim", 36 | "license": "MIT", 37 | "main": "index.js", 38 | "scripts": { 39 | "prepack": "npmignore --auto --commentLines=autogenerated", 40 | "prepublishOnly": "safe-publish-latest", 41 | "prepublish": "not-in-publish || npm run prepublishOnly", 42 | "pretest": "npm run lint", 43 | "test": "npm run tests-only", 44 | "posttest": "npx npm@\">= 10.2\" audit --production", 45 | "tests-only": "nyc tape 'test/**/*.js'", 46 | "lint": "eslint --ext=.js,.mjs ." 47 | }, 48 | "repository": { 49 | "type": "git", 50 | "url": "git://github.com/ljharb/object-keys.git" 51 | }, 52 | "keywords": [ 53 | "Object.keys", 54 | "keys", 55 | "ES5", 56 | "shim" 57 | ], 58 | "devDependencies": { 59 | "@ljharb/eslint-config": "^21.1.1", 60 | "eslint": "=8.8.0", 61 | "in-publish": "^2.0.1", 62 | "indexof": "^0.0.1", 63 | "is": "^3.3.0", 64 | "npmignore": "^0.3.1", 65 | "nyc": "^10.3.2", 66 | "safe-publish-latest": "^2.0.0", 67 | "tape": "^5.9.0" 68 | }, 69 | "testling": { 70 | "files": "test/index.js", 71 | "browsers": [ 72 | "iexplore/6.0..latest", 73 | "firefox/3.0..6.0", 74 | "firefox/15.0..latest", 75 | "firefox/nightly", 76 | "chrome/4.0..10.0", 77 | "chrome/20.0..latest", 78 | "chrome/canary", 79 | "opera/10.0..latest", 80 | "opera/next", 81 | "safari/4.0..latest", 82 | "ipad/6.0..latest", 83 | "iphone/6.0..latest", 84 | "android-browser/4.2" 85 | ] 86 | }, 87 | "engines": { 88 | "node": ">= 0.4" 89 | }, 90 | "publishConfig": { 91 | "ignore": [ 92 | ".github/workflows", 93 | "test/*" 94 | ] 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /test/isArguments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var isArguments = require('../isArguments'); 5 | 6 | test('is.arguments', function (t) { 7 | t.notOk(isArguments([]), 'array is not arguments'); 8 | (function () { t.ok(isArguments(arguments), 'arguments is arguments'); }()); 9 | (function () { t.notOk(isArguments(Array.prototype.slice.call(arguments)), 'sliced arguments is not arguments'); }()); 10 | var fakeOldArguments = { 11 | callee: function () {}, 12 | length: 3 13 | }; 14 | t.ok(isArguments(fakeOldArguments), 'old-style arguments is arguments'); 15 | t.end(); 16 | }); 17 | -------------------------------------------------------------------------------- /test/shim.js: -------------------------------------------------------------------------------- 1 | /* global window */ 2 | 3 | 'use strict'; 4 | 5 | var test = require('tape'); 6 | var is = require('is'); 7 | var keysShim; 8 | var reRequire = function reRequireImplementation() { 9 | delete require.cache[require.resolve('../implementation')]; 10 | delete require.cache[require.resolve('../')]; 11 | keysShim = require('../'); // eslint-disable-line global-require 12 | }; 13 | reRequire(); 14 | 15 | var indexOf = require('indexof'); 16 | var has = Object.prototype.hasOwnProperty; 17 | var enumerable = Object.prototype.propertyIsEnumerable; 18 | 19 | var obj = { 20 | aNull: null, 21 | arr: [], 22 | bool: true, 23 | num: 42, 24 | obj: {}, 25 | str: 'boz', 26 | undef: undefined 27 | }; 28 | var objKeys = ['aNull', 'arr', 'bool', 'num', 'obj', 'str', 'undef']; 29 | 30 | var returnEmptyArray = function () { return []; }; 31 | var preserve = function preserveProperty(object, property) { 32 | reRequire(); 33 | var original = object[property]; 34 | return function restorePreserved() { 35 | // eslint-disable-next-line no-param-reassign 36 | object[property] = original; 37 | if (object[property] !== original) { throw new EvalError('should never happen'); } 38 | reRequire(); 39 | }; 40 | }; 41 | 42 | test('exports a "shim" function', function (t) { 43 | t.equal(typeof keysShim.shim, 'function', 'keysShim.shim is a function'); 44 | 45 | t.test('when Object.keys is present', function (st) { 46 | st.teardown(preserve(Object, 'keys')); 47 | 48 | Object.keys = returnEmptyArray; 49 | st.equal(Object.keys, returnEmptyArray, 'Object.keys has been replaced'); 50 | var shimmedKeys = keysShim.shim(); 51 | st.notEqual(Object.keys, keysShim, 'Object.keys is not overridden to the shim'); 52 | st.notEqual(Object.keys, returnEmptyArray, 'Object.keys is overridden, is not returnEmptyArray'); 53 | st.equal(shimmedKeys, Object.keys, 'Object.keys is returned'); 54 | st.end(); 55 | }); 56 | 57 | t.test('when Object.keys is not present', { skip: Object.keys }, function (st) { 58 | st.teardown(preserve(Object, 'keys')); 59 | 60 | st.notOk(Object.keys, 'Object.keys does not exist'); 61 | var shimmedKeys = keysShim.shim(); 62 | 63 | st.equal(Object.keys, keysShim, 'Object.keys is overridden'); 64 | st.equal(shimmedKeys, keysShim, 'shim is returned'); 65 | 66 | st.end(); 67 | }); 68 | 69 | t.test('when Object.keys has arguments bug', function (st) { 70 | st.teardown(preserve(Object, 'keys')); 71 | 72 | st.deepEqual(arguments.length, 1, 'arguments has expected length'); 73 | 74 | var originalObjectKeys = Object.keys; 75 | var fakeKeys = function keys(object) { 76 | if (is.args(object)) { return []; } 77 | return originalObjectKeys(object); 78 | }; 79 | Object.keys = fakeKeys; 80 | st.deepEqual(Object.keys(arguments), [], 'Object.keys has arguments bug'); 81 | var shimmedKeys = keysShim.shim(); 82 | st.notEqual(fakeKeys, shimmedKeys, 'Object.keys is not original fake value'); 83 | st.equal(Object.keys, shimmedKeys, 'Object.keys is overridden'); 84 | st.deepEqual(Object.keys(arguments), ['0'], 'Object.keys now works with arguments'); 85 | st.end(); 86 | }); 87 | 88 | t.end(); 89 | }); 90 | 91 | test('working with actual shim', function (t) { 92 | t.notEqual(Object.keys, keysShim, 'keys shim is not native Object.keys'); 93 | t.end(); 94 | }); 95 | 96 | test('works with an object literal', function (t) { 97 | var theKeys = keysShim(obj); 98 | t.equal(is.array(theKeys), true, 'returns an array'); 99 | t.deepEqual(theKeys, objKeys, 'Object has expected keys'); 100 | t.end(); 101 | }); 102 | 103 | test('works with an arguments object', function (t) { 104 | (function () { 105 | t.equal(arguments.length, 3, 'arguments has length of 3'); 106 | t.deepEqual(keysShim(arguments), ['0', '1', '2'], 'returns keys of arguments'); 107 | }(1, 2, 3)); 108 | t.end(); 109 | }); 110 | 111 | test('works with a boxed primitive', function (t) { 112 | t.deepEqual(keysShim(Object('hello')), ['0', '1', '2', '3', '4'], 'object string returns proper keys'); 113 | /* eslint-disable no-new-wrappers */ 114 | t.deepEqual(keysShim(new String('hello')), ['0', '1', '2', '3', '4'], 'String object returns proper keys'); 115 | 116 | var x = new String('x'); 117 | /* eslint-enable no-new-wrappers */ 118 | x.y = 1; 119 | t.deepEqual(keysShim(x).sort(), ['0', 'y'].sort(), 'String object with extra properties returns proper keys'); 120 | 121 | t.end(); 122 | }); 123 | 124 | test('works with an array', function (t) { 125 | var arr = [1, 2, 3]; 126 | var theKeys = keysShim(arr); 127 | t.equal(is.array(theKeys), true, 'returns an array'); 128 | t.deepEqual(theKeys, ['0', '1', '2'], 'Array has expected keys'); 129 | t.end(); 130 | }); 131 | 132 | test('works with a function', function (t) { 133 | var foo = function () {}; 134 | foo.a = true; 135 | 136 | t.doesNotThrow(function () { return keysShim(foo); }, 'does not throw an error'); 137 | t.deepEqual(keysShim(foo), ['a'], 'returns expected keys'); 138 | t.end(); 139 | }); 140 | 141 | test('returns names which are own properties', function (t) { 142 | var theKeys = keysShim(obj); 143 | for (var i = 0; i < theKeys.length; ++i) { 144 | t.equal(has.call(obj, theKeys[i]), true, theKeys[i] + ' should be returned'); 145 | } 146 | t.end(); 147 | }); 148 | 149 | test('returns names which are enumerable', function (t) { 150 | var k; 151 | var loopedValues = []; 152 | for (k in obj) { 153 | if (enumerable.call(obj, k)) { 154 | loopedValues.push(k); 155 | } 156 | } 157 | var theKeys = keysShim(obj); 158 | for (var i = 0; i < theKeys.length; ++i) { 159 | t.notEqual(indexOf(loopedValues, theKeys[i]), -1, theKeys[i] + ' is not enumerable'); 160 | } 161 | t.end(); 162 | }); 163 | 164 | test('throws an error for a non-object', function (t) { 165 | var expectedError; 166 | try { 167 | Object(null); 168 | } catch (e) { 169 | expectedError = e; 170 | } 171 | t['throws']( 172 | function () { return keysShim(null); }, 173 | expectedError, 174 | 'throws on a non-object' 175 | ); 176 | t.end(); 177 | }); 178 | 179 | test('works with an object instance', function (t) { 180 | var Prototype = function () {}; 181 | Prototype.prototype.foo = true; 182 | var instance = new Prototype(); 183 | instance.bar = true; 184 | var theKeys = keysShim(instance); 185 | t.equal(is.array(theKeys), true, 'returns an array'); 186 | t.deepEqual(theKeys, ['bar'], 'Instance has expected keys'); 187 | t.end(); 188 | }); 189 | 190 | test('works in iOS 5 mobile Safari', function (t) { 191 | var Foo = function () {}; 192 | Foo.a = function () {}; 193 | 194 | // the bug is keysShim(Foo) => ['a', 'prototype'] instead of ['a'] 195 | t.deepEqual(keysShim(Foo), ['a'], 'has expected keys'); 196 | t.end(); 197 | }); 198 | 199 | test('works in environments with the dontEnum bug (IE < 9)', function (t) { 200 | var Foo = function () {}; 201 | Foo.prototype.a = function () {}; 202 | 203 | // the bug is keysShim(Foo.prototype) => ['a', 'constructor'] instead of ['a'] 204 | t.deepEqual(keysShim(Foo.prototype), ['a'], 'has expected keys'); 205 | t.end(); 206 | }); 207 | 208 | test('shadowed properties', function (t) { 209 | var shadowedProps = [ 210 | 'dummyControlProp', /* just to be sure */ 211 | 'constructor', 212 | 'hasOwnProperty', 213 | 'isPrototypeOf', 214 | 'propertyIsEnumerable', 215 | 'toLocaleString', 216 | 'toString', 217 | 'valueOf' 218 | ]; 219 | shadowedProps.sort(); 220 | var shadowedObject = {}; 221 | for (var i = 0; i < shadowedProps.length; ++i) { 222 | shadowedObject[shadowedProps[i]] = i; 223 | } 224 | var shadowedObjectKeys = keysShim(shadowedObject); 225 | shadowedObjectKeys.sort(); 226 | t.deepEqual(shadowedObjectKeys, shadowedProps, 'troublesome shadowed properties are keys of object literals'); 227 | t.end(); 228 | }); 229 | 230 | test('host objects on `window` constructor.prototype equal to themselves', { skip: typeof window === 'undefined' }, function (t) { 231 | var keys, exception; 232 | var excludedKeys = { 233 | $applicationCache: true, 234 | $console: true, 235 | $external: true, 236 | $frame: true, 237 | $frameElement: true, 238 | $frames: true, 239 | $innerHeight: true, 240 | $innerWidth: true, 241 | $onmozfullscreenchange: true, 242 | $onmozfullscreenerror: true, 243 | $outerHeight: true, 244 | $outerWidth: true, 245 | $pageXOffset: true, 246 | $pageYOffset: true, 247 | $parent: true, 248 | $scrollLeft: true, 249 | $scrollTop: true, 250 | $scrollX: true, 251 | $scrollY: true, 252 | $self: true, 253 | $webkitIndexedDB: true, 254 | $webkitStorageInfo: true, 255 | $window: true 256 | }; 257 | for (var k in window) { 258 | keys = undefined; 259 | exception = undefined; 260 | if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { 261 | try { 262 | keys = keysShim(window[k]); 263 | } catch (e) { 264 | exception = e; 265 | } 266 | t.ok(is.array(keys), 'keys of window["' + k + '"] is an array'); 267 | t.equal(exception, undefined, 'there is no exception: window["' + k + '"]'); 268 | } 269 | } 270 | t.end(); 271 | }); 272 | --------------------------------------------------------------------------------