diff --git src/wp-includes/category-functions.php src/wp-includes/category-functions.php
index 847dd50..193887f 100644
--- src/wp-includes/category-functions.php
+++ src/wp-includes/category-functions.php
@@ -325,12 +325,12 @@ function clean_category_cache( $id ) {
  */
 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'];
diff --git src/wp-includes/class-wp-term.php src/wp-includes/class-wp-term.php
new file mode 100644
index 0000000..68fb187
--- /dev/null
+++ src/wp-includes/class-wp-term.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ * WordPress Term class.
+ *
+ * @since 4.4.0
+ * @package WordPress
+ * @subpackage Taxonomy
+ *
+ */
+final class WP_Term {
+
+	/**
+	 * Term ID.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var int
+	 */
+	public $term_id;
+
+	/**
+	 * The term's name.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var string
+	 */
+	public $name = '';
+
+	/**
+	 * The term's slug.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var string
+	 */
+	public $slug = '';
+
+	/**
+	 * The term's term_group.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var string
+	 */
+	public $term_group = '';
+
+	/**
+	 * Term Taxonomy ID.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var int
+	 */
+	public $term_taxonomy_id = 0;
+
+	/**
+	 * The term's taxonomy name.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var string
+	 */
+	public $taxonomy = '';
+
+	/**
+	 * The term's description.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var string
+	 */
+	public $description = '';
+
+	/**
+	 * ID of a term's parent term.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var int
+	 */
+	public $parent = 0;
+
+	/**
+	 * Cached object count for this term.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var int
+	 */
+	public $count = 0;
+
+	/**
+	 * Stores the term object's sanitization level.
+	 *
+	 * Does not correspond to a DB field.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @var string
+	 */
+	public $filter = 'raw';
+
+	/**
+	 * Retrieve WP_Term instance.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 * @static
+	 *
+	 * @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.
+	 *
+	 * @since 4.4.0
+	 *
+	 * @param WP_Term|object $term Term object.
+	 */
+	public function __construct( $term ) {
+		foreach ( get_object_vars( $term ) as $key => $value ) {
+			$this->$key = $value;
+		}
+	}
+
+	/**
+	 * Sanitize term fields, according to the filter type provided.
+	 *
+	 * @param string $filter Filter context. Accepts 'edit', 'db', 'display', 'attribute', 'js', 'raw'.
+	 * @return WP_Term
+	 */
+	public function filter( $filter ) {
+		if ( isset( $this->filter ) && $this->filter === $filter ) {
+			return $this;
+		}
+
+		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 );
+	}
+}
diff --git src/wp-includes/taxonomy-functions.php src/wp-includes/taxonomy-functions.php
index a13424b..afd8a40 100644
--- src/wp-includes/taxonomy-functions.php
+++ src/wp-includes/taxonomy-functions.php
@@ -712,40 +712,58 @@ function get_tax_sql( $tax_query, $primary_table, $primary_id_column ) {
  * @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 int|WP_Term|object $term If integer, term data will be fetched from the database, or from the cache if
+ *                                 available. If stdClass object (as in the results of a database query), will apply
+ *                                 filters and return a `WP_Term` object corresponding to the `$term` data. If `WP_Term`,
+ *                                 will 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.
+ * @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 the other one to a new term.
+		$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 {
+			// Refetch the term, now that it's no longer shared.
+			return get_term( $_term->term_id, $taxonomy, $output, $filter );
 		}
 	}
 
+	if ( ! $_term ) {
+		return null;
+	}
+
 	/**
 	 * Filter a term.
 	 *
@@ -768,19 +786,17 @@ function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
 	 * @param string     $taxonomy The taxonomy slug.
 	 */
 	$_term = apply_filters( "get_$taxonomy", $_term, $taxonomy );
-	$_term = sanitize_term($_term, $taxonomy, $filter);
 
-	if ( $output == OBJECT ) {
-		return $_term;
-	} elseif ( $output == ARRAY_A ) {
-		$__term = get_object_vars($_term);
-		return $__term;
+	// Sanitize term, according to the specified filter.
+	$_term = $_term->filter( $filter );
+
+	if ( $output == ARRAY_A ) {
+		return $_term->to_array();
 	} elseif ( $output == ARRAY_N ) {
-		$__term = array_values(get_object_vars($_term));
-		return $__term;
-	} else {
-		return $_term;
+		return array_values( $_term->to_array() );
 	}
+
+	return $_term;
 }
 
 /**
@@ -808,7 +824,7 @@ function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
  * @param string     $taxonomy Taxonomy name. Optional, if `$field` is 'term_taxonomy_id'.
  * @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' ) {
@@ -822,17 +838,17 @@ function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter
 	$tax_clause = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
 
 	if ( 'slug' == $field ) {
-		$field = 't.slug';
+		$_field = 't.slug';
 		$value = sanitize_title($value);
 		if ( empty($value) )
 			return false;
 	} elseif ( 'name' == $field ) {
 		// Assume already escaped
 		$value = wp_unslash($value);
-		$field = 't.name';
+		$_field = 't.name';
 	} elseif ( 'term_taxonomy_id' == $field ) {
 		$value = (int) $value;
-		$field = 'tt.term_taxonomy_id';
+		$_field = 'tt.term_taxonomy_id';
 
 		// No `taxonomy` clause when searching by 'term_taxonomy_id'.
 		$tax_clause = '';
@@ -844,7 +860,7 @@ function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter
 		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 $field = %s $tax_clause LIMIT 1", $value ) );
+	$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 $tax_clause LIMIT 1", $value ) );
 	if ( ! $term )
 		return false;
 
@@ -853,25 +869,9 @@ function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter
 		$taxonomy = $term->taxonomy;
 	}
 
-	wp_cache_add( $term->term_id, $term, $taxonomy );
-
-	/** 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);
+	wp_cache_add( $term->term_id, $term, 'terms' );
 
-	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 );
 }
 
 /**
@@ -1048,7 +1048,7 @@ function get_term_to_edit( $id, $taxonomy ) {
  *     @type array        $meta_query             Meta query clauses to limit retrieved terms by.
  *                                                See `WP_Meta_Query`. Default empty.
  * }
- * @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 = '' ) {
@@ -1489,6 +1489,8 @@ function get_terms( $taxonomies, $args = '' ) {
 		foreach ( $terms as $term ) {
 			$_terms[ $term->term_id ] = $term->slug;
 		}
+	} else {
+		$_terms = array_map( 'get_term', $terms );
 	}
 
 	if ( ! empty( $_terms ) ) {
@@ -3422,14 +3424,14 @@ function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) {
 		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' );
 			}
 		}
 	}
@@ -3552,7 +3554,7 @@ function update_term_cache( $terms, $taxonomy = '' ) {
 		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' );
 	}
 }
 
diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
index e29e65c..13b50fd 100644
--- src/wp-includes/taxonomy.php
+++ src/wp-includes/taxonomy.php
@@ -10,5 +10,8 @@
 /** Core taxonomy functionality */
 require_once( ABSPATH . WPINC . '/taxonomy-functions.php' );
 
+/** WP_Term class */
+require_once( ABSPATH . WPINC . '/class-wp-term.php' );
+
 /** WP_Tax_Query class */
 require_once( ABSPATH . WPINC . '/class-wp-tax-query.php' );
diff --git tests/phpunit/tests/term/cache.php tests/phpunit/tests/term/cache.php
index 1a31df0..a620986 100644
--- tests/phpunit/tests/term/cache.php
+++ tests/phpunit/tests/term/cache.php
@@ -103,10 +103,10 @@ class Tests_Term_Cache extends WP_UnitTestCase {
 		) );
 
 		$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 @@ class Tests_Term_Cache extends WP_UnitTestCase {
 			'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 @@ class Tests_Term_Cache extends WP_UnitTestCase {
 			'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' );
diff --git tests/phpunit/tests/term/getTerm.php tests/phpunit/tests/term/getTerm.php
index 6c84d68..db895a0 100644
--- tests/phpunit/tests/term/getTerm.php
+++ tests/phpunit/tests/term/getTerm.php
@@ -35,19 +35,6 @@ class Tests_Term_GetTerm extends WP_UnitTestCase {
 		$this->assertSame( $num_queries, $wpdb->num_queries );
 	}
 
-	public function test_passing_term_object_should_not_skip_database_query_when_filter_property_is_set() {
-		global $wpdb;
-
-		$term = $this->factory->term->create_and_get( array( 'taxonomy' => 'wptests_tax' ) );
-		clean_term_cache( $term->term_id, 'wptests_tax' );
-
-		$num_queries = $wpdb->num_queries;
-
-		$term_a = get_term( $term, 'wptests_tax' );
-
-		$this->assertSame( $num_queries + 1, $wpdb->num_queries );
-	}
-
 	public function test_passing_term_string_that_casts_to_int_0_should_return_null() {
 		$this->assertSame( null, get_term( 'abc', 'wptests_tax' ) );
 	}
