/** * @license Apache-2.0 * * Copyright (c) 2018 The Stdlib Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; // MODULES // var isObjectLike = require( '@stdlib/assert-is-object-like' ); var hasOwnProp = require( '@stdlib/assert-has-own-property' ); var isArguments = require( '@stdlib/assert-is-arguments' ); var HAS_ENUM_PROTO_BUG = require( './has_enumerable_prototype_bug.js' ); var HAS_NON_ENUM_PROPS_BUG = require( './has_non_enumerable_properties_bug.js' ); var isConstructorPrototype = require( './is_constructor_prototype_wrapper.js' ); var NON_ENUMERABLE = require( './non_enumerable.json' ); // MAIN // /** * Returns an array of an object's own enumerable property names. * * @private * @param {*} value - input object * @returns {Array} a list of own enumerable property names * * @example * var obj = { * 'beep': 'boop', * 'foo': 3.14 * }; * * var k = keys( obj ); * // e.g., returns [ 'beep', 'foo' ] */ function keys( value ) { var skipConstructor; var skipPrototype; var isFcn; var out; var k; var p; var i; out = []; if ( isArguments( value ) ) { // Account for environments which treat `arguments` differently... for ( i = 0; i < value.length; i++ ) { out.push( i.toString() ); } // Note: yes, we are precluding the `arguments` array-like object from having other enumerable properties; however, this should (1) be very rare and (2) not be encouraged (e.g., doing something like `arguments.a = 'b'`; in certain engines directly manipulating the `arguments` value results in automatic de-optimization). return out; } if ( typeof value === 'string' ) { // Account for environments which do not treat string character indices as "own" properties... if ( value.length > 0 && !hasOwnProp( value, '0' ) ) { for ( i = 0; i < value.length; i++ ) { out.push( i.toString() ); } } } else { isFcn = ( typeof value === 'function' ); if ( isFcn === false && !isObjectLike( value ) ) { return out; } skipPrototype = ( HAS_ENUM_PROTO_BUG && isFcn ); } for ( k in value ) { if ( !( skipPrototype && k === 'prototype' ) && hasOwnProp( value, k ) ) { out.push( String( k ) ); } } if ( HAS_NON_ENUM_PROPS_BUG ) { skipConstructor = isConstructorPrototype( value ); for ( i = 0; i < NON_ENUMERABLE.length; i++ ) { p = NON_ENUMERABLE[ i ]; if ( !( skipConstructor && p === 'constructor' ) && hasOwnProp( value, p ) ) { out.push( String( p ) ); } } } return out; } // EXPORTS // module.exports = keys;