Index: src/wp-includes/category.php
===================================================================
--- src/wp-includes/category.php	(revision 33977)
+++ src/wp-includes/category.php	(working copy)
@@ -323,12 +323,12 @@
  */
 function _make_cat_compat( &$category ) {
 	if ( is_object( $category ) && ! is_wp_error( $category ) ) {
-		$category->cat_ID = &$category->term_id;
-		$category->category_count = &$category->count;
-		$category->category_description = &$category->description;
-		$category->cat_name = &$category->name;
-		$category->category_nicename = &$category->slug;
-		$category->category_parent = &$category->parent;
+		$category->cat_ID = $category->term_id;
+		$category->category_count = $category->count;
+		$category->category_description = $category->description;
+		$category->cat_name = $category->name;
+		$category->category_nicename = $category->slug;
+		$category->category_parent = $category->parent;
 	} elseif ( is_array( $category ) && isset( $category['term_id'] ) ) {
 		$category['cat_ID'] = &$category['term_id'];
 		$category['category_count'] = &$category['count'];
Index: src/wp-includes/class-wp-term.php
===================================================================
--- src/wp-includes/class-wp-term.php	(revision 0)
+++ src/wp-includes/class-wp-term.php	(working copy)
@@ -0,0 +1,150 @@
+<?php
+/**
+ * WordPress Term class.
+ *
+ * @since 4.4.0
+ * @package WordPress
+ * @subpackage Taxonomy
+ *
+ */
+final class WP_Term {
+
+	/**
+	 * Term ID.
+	 *
+	 * @var int
+	 */
+	public $term_id;
+
+	/**
+	 * The term's name.
+	 *
+	 * @var string
+	 */
+	public $name = '';
+
+	/**
+	 * The term's slug.
+	 *
+	 * @var string
+	 */
+	public $slug = '';
+
+	public $term_group = '';
+
+	/**
+	 * Term Taxonomy ID.
+	 *
+	 * @var int
+	 */
+	public $term_taxonomy_id = 0;
+
+	/**
+	 * The term's taxonomy name.
+	 *
+	 * @var string
+	 */
+	public $taxonomy = '';
+
+	/**
+	 * The term's description.
+	 *
+	 * @var string
+	 */
+	public $description = '';
+
+	/**
+	 * ID of a term's parent term.
+	 *
+	 * @var int
+	 */
+	public $parent = 0;
+
+	/**
+	 * Cached object count for this term.
+	 *
+	 * @var int
+	 */
+	public $count = 0;
+
+	/**
+	 * Stores the term object's sanitization level.
+	 *
+	 * Does not correspond to a DB field.
+	 *
+	 * @var string
+	 */
+	public $filter;
+
+	/**
+	 * Retrieve WP_Term instance.
+	 *
+	 * @static
+	 * @access public
+	 *
+	 * @global wpdb $wpdb
+	 *
+	 * @param int $term_id Term ID.
+	 * @return WP_Term|false Term object, false otherwise.
+	 */
+	public static function get_instance( $term_id ) {
+		global $wpdb;
+
+		$term_id = (int) $term_id;
+		if ( ! $term_id ) {
+			return false;
+		}
+
+		$_term = wp_cache_get( $term_id, 'terms' );
+
+		if ( ! $_term ) {
+			$_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE t.term_id = %d LIMIT 1", $term_id ) );
+			if ( ! $_term ) {
+				return false;
+			}
+
+			$_term = sanitize_term( $_term, $_term->taxonomy, 'raw' );
+			wp_cache_add( $term_id, $_term, 'terms' );
+		} elseif ( empty( $_term->filter ) ) {
+			$_term = sanitize_term( $_term, $_term->taxonomy, 'raw' );
+		}
+
+		return new WP_Term( $_term );
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param WP_Term|object $term Term object.
+	 */
+	public function __construct( $term ) {
+		foreach ( get_object_vars( $term ) as $key => $value ) {
+			$this->$key = $value;
+		}
+	}
+
+	/**
+	 * {@Missing Summary}
+	 *
+	 * @param string $filter Filter.
+	 * @return self|array|bool|object|WP_Term
+	 */
+	public function filter( $filter ) {
+		if ( $this->filter == $filter )
+			return $this;
+
+		if ( $filter == 'raw' )
+			return self::get_instance( $this->term_id );
+
+		return sanitize_term( $this, $this->taxonomy, $filter );
+	}
+
+	/**
+	 * Convert object to array.
+	 *
+	 * @return array Object as array.
+	 */
+	public function to_array() {
+		return get_object_vars( $this );
+	}
+}
Index: src/wp-includes/taxonomy-functions.php
===================================================================
--- src/wp-includes/taxonomy-functions.php	(revision 33977)
+++ src/wp-includes/taxonomy-functions.php	(working copy)
@@ -685,40 +685,59 @@
  * @global wpdb $wpdb WordPress database abstraction object.
  * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
  *
- * @param int|object $term     If integer, will get from database. If object will apply filters and return $term.
- * @param string     $taxonomy Taxonomy name that $term is part of.
- * @param string     $output   Constant OBJECT, ARRAY_A, or ARRAY_N
- * @param string     $filter   Optional, default is raw or no WordPress defined filter will applied.
- * @return object|array|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not
- * exist then WP_Error will be returned.
+ * @param int|WP_Term $term     If integer, will get from database. If object will apply filters and return $term.
+ * @param string      $taxonomy Taxonomy name that $term is part of.
+ * @param string      $output   Constant OBJECT, ARRAY_A, or ARRAY_N
+ * @param string      $filter   Optional, default is raw or no WordPress defined filter will applied.
+ * @return WP_Term|array|null|WP_Error Type corresponding to $output on success or null on failure. When $output is OBJECT,
+ *                                     a `WP_Term` instance is returned. If taxonomy does not exist then WP_Error will be returned.
  */
-function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
-	global $wpdb;
-
+function get_term( $term, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
 	if ( empty( $term ) ) {
 		return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
 	}
 
-	if ( ! taxonomy_exists( $taxonomy ) ) {
+	if ( $taxonomy && ! taxonomy_exists( $taxonomy ) ) {
 		return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
 	}
 
-	if ( is_object($term) && empty($term->filter) ) {
-		wp_cache_add( $term->term_id, $term, $taxonomy );
+	if ( $term instanceof WP_Term ) {
 		$_term = $term;
+	} elseif ( is_object( $term ) ) {
+		if ( empty( $term->filter ) ) {
+			$_term = sanitize_term( $term, $taxonomy, 'raw' );
+			$_term = new WP_Term( $_term );
+		} elseif ( 'raw' == $term->filter ) {
+			$_term = new WP_Term( $term );
+		} else {
+			$_term = WP_Term::get_instance( $term->term_id );
+		}
 	} else {
-		if ( is_object($term) )
-			$term = $term->term_id;
-		if ( !$term = (int) $term )
-			return null;
-		if ( ! $_term = wp_cache_get( $term, $taxonomy ) ) {
-			$_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %d LIMIT 1", $taxonomy, $term) );
-			if ( ! $_term )
-				return null;
-			wp_cache_add( $term, $_term, $taxonomy );
+		$_term = WP_Term::get_instance( $term );
+	}
+
+	// If a taxonomy was passed, make sure it matches the taxonomy of the located term.
+	if ( $_term && $taxonomy && $taxonomy !== $_term->taxonomy ) {
+		// If there are two terms with the same ID, split them.
+		$new_term_id = _split_shared_term( $_term->term_id, $_term->term_taxonomy_id );
+
+		// If no split occurred, this is an invalid request.
+		if ( $new_term_id === $_term->term_id ) {
+			return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
+		} else {
+			$_term = WP_Term::get_instance( $new_term_id );
 		}
 	}
 
+	if ( ! $_term ) {
+		return null;
+	}
+
+	// Ensure a taxonomy is set for the filters
+	if ( ! $taxonomy ) {
+		$taxonomy = $_term->taxonomy;
+	}
+
 	/**
 	 * Filter a term.
 	 *
@@ -741,19 +760,14 @@
 	 * @param string     $taxonomy The taxonomy slug.
 	 */
 	$_term = apply_filters( "get_$taxonomy", $_term, $taxonomy );
-	$_term = sanitize_term($_term, $taxonomy, $filter);
+	$_term = sanitize_term( $_term, $taxonomy, $filter );
 
-	if ( $output == OBJECT ) {
-		return $_term;
-	} elseif ( $output == ARRAY_A ) {
-		$__term = get_object_vars($_term);
-		return $__term;
-	} elseif ( $output == ARRAY_N ) {
-		$__term = array_values(get_object_vars($_term));
-		return $__term;
-	} else {
-		return $_term;
-	}
+	if ( $output == ARRAY_A )
+		return $_term->to_array();
+	elseif ( $output == ARRAY_N )
+		return array_values( $_term->to_array() );
+
+	return $_term;
 }
 
 /**
@@ -780,13 +794,13 @@
  * @param string     $taxonomy Taxonomy Name
  * @param string     $output   Constant OBJECT, ARRAY_A, or ARRAY_N
  * @param string     $filter   Optional, default is raw or no WordPress defined filter will applied.
- * @return object|array|null|WP_Error|false Term Row from database.
+ * @return WP_Term|array|null|WP_Error|false Instance of WP_Term.
  *                                          Will return false if $taxonomy does not exist or $term was not found.
  */
-function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') {
+function get_term_by($field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw') {
 	global $wpdb;
 
-	if ( ! taxonomy_exists($taxonomy) )
+	if ( $taxonomy && ! taxonomy_exists($taxonomy) )
 		return false;
 
 	if ( 'slug' == $field ) {
@@ -808,29 +822,18 @@
 		return $term;
 	}
 
-	$term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value ) );
+	if ( $taxonomy ) {
+		$term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value ) );
+	} else {
+		$term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE $field = %s LIMIT 1", $value ) );
+	}
+
 	if ( ! $term )
 		return false;
 
-	wp_cache_add( $term->term_id, $term, $taxonomy );
+	wp_cache_add( $term->term_id, $term, 'terms' );
 
-	/** This filter is documented in wp-includes/taxonomy-functions.php */
-	$term = apply_filters( 'get_term', $term, $taxonomy );
-
-	/** This filter is documented in wp-includes/taxonomy-functions.php */
-	$term = apply_filters( "get_$taxonomy", $term, $taxonomy );
-
-	$term = sanitize_term($term, $taxonomy, $filter);
-
-	if ( $output == OBJECT ) {
-		return $term;
-	} elseif ( $output == ARRAY_A ) {
-		return get_object_vars($term);
-	} elseif ( $output == ARRAY_N ) {
-		return array_values(get_object_vars($term));
-	} else {
-		return $term;
-	}
+	return get_term( $term, $taxonomy, $output, $filter );
 }
 
 /**
@@ -887,7 +890,7 @@
  * @param string $context  Optional, default is display. Look at sanitize_term_field() for available options.
  * @return string|int|null|WP_Error Will return an empty string if $term is not an object or if $field is not set in $term.
  */
-function get_term_field( $field, $term, $taxonomy, $context = 'display' ) {
+function get_term_field( $field, $term, $taxonomy = '', $context = 'display' ) {
 	$term = (int) $term;
 	$term = get_term( $term, $taxonomy );
 	if ( is_wp_error($term) )
@@ -899,6 +902,10 @@
 	if ( !isset($term->$field) )
 		return '';
 
+	if ( ! $taxonomy ) {
+		$taxonomy = $term->taxonomy;
+	}
+
 	return sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context);
 }
 
@@ -914,7 +921,7 @@
  * @param string     $taxonomy Taxonomy name.
  * @return string|int|null|WP_Error Will return empty string if $term is not an object.
  */
-function get_term_to_edit( $id, $taxonomy ) {
+function get_term_to_edit( $id, $taxonomy = '' ) {
 	$term = get_term( $id, $taxonomy );
 
 	if ( is_wp_error($term) )
@@ -1003,7 +1010,7 @@
  *     @type string       $cache_domain      Unique cache key to be produced when this query is stored in an
  *                                           object cache. Default is 'core'.
  * }
- * @return array|int|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies
+ * @return array|int|WP_Error List of WP_Term instances and their children. Will return WP_Error, if any of $taxonomies
  *                        do not exist.
  */
 function get_terms( $taxonomies, $args = '' ) {
@@ -1427,6 +1434,10 @@
 		foreach ( $terms as $term ) {
 			$_terms[ $term->term_id ] = $term->slug;
 		}
+	} else {
+		foreach ( $terms as $term ) {
+			$_terms[] = get_term( $term, $term->taxonomy );
+		}
 	}
 
 	if ( ! empty( $_terms ) ) {
@@ -1521,7 +1532,7 @@
  * @param string     $taxonomy Taxonomy name that $term1 and `$term2` belong to.
  * @return bool Whether `$term2` is a child of `$term1`.
  */
-function term_is_ancestor_of( $term1, $term2, $taxonomy ) {
+function term_is_ancestor_of( $term1, $term2, $taxonomy = '' ) {
 	if ( ! isset( $term1->term_id ) )
 		$term1 = get_term( $term1, $taxonomy );
 	if ( ! isset( $term2->parent ) )
@@ -1552,13 +1563,17 @@
  *                               'display', 'attribute', or 'js'. Default 'display'.
  * @return array|object Term with all fields sanitized.
  */
-function sanitize_term($term, $taxonomy, $context = 'display') {
+function sanitize_term($term, $taxonomy = '', $context = 'display') {
 	$fields = array( 'term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group', 'term_taxonomy_id', 'object_id' );
 
 	$do_object = is_object( $term );
 
 	$term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0);
 
+	if ( ! $taxonomy ) {
+		$taxonomy = $do_object ? $term->taxonomy : ( isset( $term['taxonomy'] ) ? $term['taxonomy'] : '' );
+	}
+
 	foreach ( (array) $fields as $field ) {
 		if ( $do_object ) {
 			if ( isset($term->$field) )
@@ -3185,14 +3200,14 @@
 		foreach ( (array) $terms as $term ) {
 			$taxonomies[] = $term->taxonomy;
 			$ids[] = $term->term_id;
-			wp_cache_delete($term->term_id, $term->taxonomy);
+			wp_cache_delete( $term->term_id, 'terms' );
 		}
 		$taxonomies = array_unique($taxonomies);
 	} else {
 		$taxonomies = array($taxonomy);
 		foreach ( $taxonomies as $taxonomy ) {
 			foreach ( $ids as $id ) {
-				wp_cache_delete($id, $taxonomy);
+				wp_cache_delete( $id, 'terms' );
 			}
 		}
 	}
@@ -3311,7 +3326,7 @@
 		if ( empty($term_taxonomy) )
 			$term_taxonomy = $term->taxonomy;
 
-		wp_cache_add( $term->term_id, $term, $term_taxonomy );
+		wp_cache_add( $term->term_id, $term, 'terms' );
 	}
 }
 
Index: src/wp-includes/taxonomy.php
===================================================================
--- src/wp-includes/taxonomy.php	(revision 33977)
+++ src/wp-includes/taxonomy.php	(working copy)
@@ -9,4 +9,5 @@
  */
 
 require_once( ABSPATH . WPINC . '/taxonomy-functions.php' );
+require_once( ABSPATH . WPINC . '/class-wp-term.php' );
 require_once( ABSPATH . WPINC . '/class-wp-tax-query.php' );
Index: tests/phpunit/tests/term/cache.php
===================================================================
--- tests/phpunit/tests/term/cache.php	(revision 33977)
+++ tests/phpunit/tests/term/cache.php	(working copy)
@@ -103,10 +103,10 @@
 		) );
 
 		$term_object = get_term( $term, 'wptests_tax' );
-		wp_cache_delete( $term, 'wptests_tax' );
+		wp_cache_delete( $term, 'terms' );
 
 		// Affirm that the cache is empty.
-		$this->assertEmpty( wp_cache_get( $term, 'wptests_tax' ) );
+		$this->assertEmpty( wp_cache_get( $term, 'terms' ) );
 
 		$num_queries = $wpdb->num_queries;
 
@@ -128,16 +128,16 @@
 			'taxonomy' => 'wptests_tax',
 		) );
 
-		wp_cache_delete( $term, 'wptests_tax' );
+		wp_cache_delete( $term, 'terms' );
 
 		// Affirm that the cache is empty.
-		$this->assertEmpty( wp_cache_get( $term, 'wptests_tax' ) );
+		$this->assertEmpty( wp_cache_get( $term, 'terms' ) );
 
 		$num_queries = $wpdb->num_queries;
 
 		// Prime cache.
 		$term_object = get_term( $term, 'wptests_tax' );
-		$this->assertNotEmpty( wp_cache_get( $term, 'wptests_tax' ) );
+		$this->assertNotEmpty( wp_cache_get( $term, 'terms' ) );
 		$this->assertSame( $num_queries + 1, $wpdb->num_queries );
 
 		$term_object_2 = get_term( $term, 'wptests_tax' );
@@ -155,16 +155,16 @@
 			'taxonomy' => 'wptests_tax',
 		) );
 
-		wp_cache_delete( $term, 'wptests_tax' );
+		wp_cache_delete( $term, 'terms' );
 
 		// Affirm that the cache is empty.
-		$this->assertEmpty( wp_cache_get( $term, 'wptests_tax' ) );
+		$this->assertEmpty( wp_cache_get( $term, 'terms' ) );
 
 		$num_queries = $wpdb->num_queries;
 
 		// Prime cache.
 		$term_object = get_term_by( 'id', $term, 'wptests_tax' );
-		$this->assertNotEmpty( wp_cache_get( $term, 'wptests_tax' ) );
+		$this->assertNotEmpty( wp_cache_get( $term, 'terms' ) );
 		$this->assertSame( $num_queries + 1, $wpdb->num_queries );
 
 		$term_object_2 = get_term( $term, 'wptests_tax' );
