# 理解原型对象

# 1.介绍理解原型对象之前,让我们先看一段代码。

function Person(name, age, job) {
  Person.prototype.name = "somebody";
  Person.prototype.age = 29;
  Person.prototype.job = "tyloo";
  Person.prototype.sayName = function() {
    console.log(this.name);
  };
}
let person1 = new Person();
person1.sayName(); //somebody
let person2 = new Person();
person2.sayName(); //somebody
console.log(person1.sayName === person2.sayName); //true
1
2
3
4
5
6
7
8
9
10
11
12
13

此函数创建是遵循了 js 中原型模式而创建的函数,我们将 sayName()方法和所有属性直接添加到了 Person 的 prototype 属性中。即使如此,我们任然可以通过调用构造函数来创建对象,而且新对象还会具有相同的属性和方法。如果不理解上面代码也没关系,只需记住两点:

  • Person.prototype 即为原型对象
  • 实例(person1 或 person2)可以访问原型对象上的方法和属性。

RUNOOB 图标

图 6-1 展示了 Person 构造函数,Person 的原型属性以及现有的两个实例之间的关系。

function Person() {
  this.color = "yellow";
  this.showColor = function() {
    return this.color;
  };
}

var person1 = new Person();

console.log(Person.prototype.constructor === Person); // 构造函数Person的默认对象的constructor // 属性
console.log(person1.constructor === Person); //person1实例通过__proto__指向了Person的原型,所以具有了 // person1实例可以访问到constructor
Person.prototype = {}; //把Person构造函数的原型进行了重新赋值

// Person.prototype.constructor = Person;

var person2 = new Person(); // person2实例通过__proto__指向了Person的原型,此时Person的原型是{},这个空对象的 // constructor 的值不是Person了,而是Object
console.log("person2.constructor: " + person2.constructor === Person); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

结论

  • 任何一个 prototype 对象(原型对象)都有一个 constructor 属性,指向它的构造函数
  • 原型最初只包含 constructor 属性,而该属性也是共享的,因此可以通过对象实例访问。即所有对象实例可以访问 constructor 属性,是因为原型链的存在,对象实例会沿着原型链继承原型所有属性。

# 2.判断原型的一些方法

function Person(name, age, job) {
  Person.prototype.name = "somebody";
  Person.prototype.age = 29;
  Person.prototype.job = "tyloo";
  Person.prototype.sayName = function() {
    console.log(this.name);
  };
}
let person1 = new Person();
person1.sayName(); //somebody
let person2 = new Person();
person2.name = "Nike";
person2.sayName(); //somebody
console.log(person1.sayName === person2.sayName); //true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 原型对象的isPrototypeOf()
/*
  如果实例的[[Prototype]](_proto_)指向调用
  isPrototypeOf()方法的对象(此处为Person.prototype),
  那么这个方法就返回true
*/
console.log(Person.prototype.isPrototypeOf(person1)); //true
console.log(Person.prototype.isPrototypeOf(person2)); //true
1
2
3
4
5
6
7
  • ES5 新增方法 Object.getPrototypeOf()
/* 
  Object.getPrototypeOf()获取一个对象的原型
*/
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
console.log(Object.getPrototypeOf(person1).name);
1
2
3
4
5

# 2.1 判断对象或实例上是否存在某个属性

  • hasOwnProperty() (这个方法由 Object 继承而来)
/* 
  只有给定的属性存在于实例中,而不是由原型继承而来,才返回true。
*/
console.log(person1.hasOwnProperty("name")); //false
console.log(person2.hasOwnProperty("name")); //true
1
2
3
4
5
  • in 操作符
/* 
  判断对象实例是否包含某个属性,无论该属性存在于实例中还是原型中
*/
console.log("name" in person1); //true
console.log("name" in person2); //true
1
2
3
4
5

# 2.2 判断一个属性到底使用的是实例中还是原型中

使用hasOwnProperty()in 操作符结合判断,即可分清属性到底是在实例中被调用还是原型中被调用。(实例中的属性会覆盖原型中的属性)

/*
  in 操作符只要对象能访问到该属性就返回true
  hasOwnProperty() 只在属性存在于实例中时才返回true
  只要属性存在于对象实例中即返回false
  注意:当原型和对象实例中都存在该属性时,该函数返回false
*/
function hasPrototypePeroperty(object,name) {
  return !object.hasOwnProperty(name) && (name in object);
}
1
2
3
4
5
6
7
8
9

请看下面例子

function Person() {
  Person.prototype.name = "somebody";
  Person.prototype.age = 29;
  Person.prototype.job = "tyloo";
  Person.prototype.sayName = function() {
    console.log(this.name);
  };
}
let person = new Person()
console.log(hasPrototypePeroperty(person,"name")); // true
person.name="Greg"
console.log(hasPrototypePeroperty(person,"name")); // false
1
2
3
4
5
6
7
8
9
10
11
12

WARNING

实例中的属性会覆盖原型中的属性

# 2.3 对象创建 new Object()和 Object.create()比较

Object.create(proto,[propertiesObject])

  • 参数

    proto 新创建对象的原型对象。

    propertiesObject 可选。需要传入一个对象,该对象的属性类型参照 Object.defineProperties()的第二个参数。如果该参数被指定且不为 undefined,该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。

通过构造函数来创建对象, 添加的属性是在自身实例下。 Object.create() es6 创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下。

  • 返回值

    一个新对象,带着指定的原型对象和属性。

  • 例子

// new Object() 方式创建
var a = { rep: "apple" };
var b = new Object(a);
console.log(b); // {rep: "apple"}
console.log(b.__proto__); // {}
console.log(b.rep); // {rep: "apple"}

// Object.create() 方式创建
var a = { rep: "apple" };
var b = Object.create(a);
console.log(b); // {}
console.log(b.__proto__); // {rep: "apple"}
console.log(b.rep); // {rep: "apple"}
1
2
3
4
5
6
7
8
9
10
11
12
13

Object.create()方法创建的对象时,属性是在原型下面的,也可以直接访问 b.rep // {rep: "apple"} , 此时这个值不是 b 自身的,是它通过原型链 proto 来访问到 b 的值。

👍➡️ 其他优秀文章讲解(原型与原型链详解) ⬅️

# 参考文献

Last Updated: 3/4/2024, 3:06:40 PM