API Docs for: v2.11.1
Show:

File: packages/ember-runtime/lib/copy.js

import { assert } from 'ember-metal';
import EmberObject from './system/object';
import Copyable from './mixins/copyable';

function _copy(obj, deep, seen, copies) {
  let ret, loc, key;

  // primitive data types are immutable, just return them.
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  // avoid cyclical loops
  if (deep && (loc = seen.indexOf(obj)) >= 0) {
    return copies[loc];
  }

  assert(
    'Cannot clone an Ember.Object that does not implement Ember.Copyable',
    !(obj instanceof EmberObject) || (Copyable && Copyable.detect(obj))
  );

  // IMPORTANT: this specific test will detect a native array only. Any other
  // object will need to implement Copyable.
  if (Array.isArray(obj)) {
    ret = obj.slice();

    if (deep) {
      loc = ret.length;

      while (--loc >= 0) {
        ret[loc] = _copy(ret[loc], deep, seen, copies);
      }
    }
  } else if (Copyable && Copyable.detect(obj)) {
    ret = obj.copy(deep, seen, copies);
  } else if (obj instanceof Date) {
    ret = new Date(obj.getTime());
  } else {
    ret = {};

    for (key in obj) {
      // support Null prototype
      if (!Object.prototype.hasOwnProperty.call(obj, key)) {
        continue;
      }

      // Prevents browsers that don't respect non-enumerability from
      // copying internal Ember properties
      if (key.substring(0, 2) === '__') {
        continue;
      }

      ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key];
    }
  }

  if (deep) {
    seen.push(obj);
    copies.push(ret);
  }

  return ret;
}

/**
  Creates a shallow copy of the passed object. A deep copy of the object is
  returned if the optional `deep` argument is `true`.

  If the passed object implements the `Ember.Copyable` interface, then this
  function will delegate to the object's `copy()` method and return the
  result. See `Ember.Copyable` for further details.

  For primitive values (which are immutable in JavaScript), the passed object
  is simply returned.

  @method copy
  @for Ember
  @param {Object} obj The object to clone
  @param {Boolean} [deep=false] If true, a deep copy of the object is made.
  @return {Object} The copied object
  @public
*/
export default function copy(obj, deep) {
  // fast paths
  if ('object' !== typeof obj || obj === null) {
    return obj; // can't copy primitives
  }

  if (Copyable && Copyable.detect(obj)) {
    return obj.copy(deep);
  }

  return _copy(obj, deep, deep ? [] : null, deep ? [] : null);
}