JavaScript 原型

一、原型概念

1、原型的出现

基于原型的面向对象系统通过“复制”的方式创建新对象。原型系统的“复制操作”有两种实现思路:

  • 不是真的去复制一个原型对象,而是使得新对象持有一个原型的引用(比如 JavaScript)
  • 真的复制对象,从此两个对象再无关联。

2、原型的定义

  • 每个函数都有一个 prototype 属性(或如果所有对象都有私有字段[[prototype]],就是对象的原型),它指向一个对象,这个对象就是原型对象。(默认情况下,原型对象上都有一个 constructor 属性,它是一个指向 prototype 属性所在函数的指针。)
  • 读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。

二、实例

1、实例和原型的关系

当调用构造函数创建一个实例后,该对象实例内部有个 __proto__ 属性,它指向构造函数的原型对象。

1
2
3
4
5
6
7
8
Object.__proto__ === Function.prototype //true
Function.__proto__ === Function.prototype //true

Function.prototype.__proto__ === Object.prototype //true
Object.prototype.__proto__ === null //true

Object.__proto__ === Object.prototype // false
Function.__proto__ === Object.prototype //false

2、实例属性

1
2
3
4
5
6
7
8
function Person () {}
Person.prototype.name = 'Tom';

const person = new Person();
console.log(person.name) // Tom

person.name = 'Nike'
console.log(person.name) // Nike

yuanxingduixiangshilli

get

当读取实例上的某个属性时,先在实例上找,如果没找到就在原型中找,直到原型为空或者找到为止。

set

可以通过实例访问保存在原型中的值,但不能通过实例重写原型中的值。

通过”实例.属性 = 值’’的形式修改对象属性时,假如这个属性在原型对象上,则它所执行的操作是在实例上创建了一个相同属性名的属性,并没有改变原型对象上的属性,在访问时候,实例会屏蔽原型对象上相同属性名的属性而读取实例上的属性。

3、api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Object.create(proto,[propertiesObject]) 根据指定的原型创建新对象,原型可以是 null

Object.getPrototypeOf(obj) 获得一个对象的原型;

Object.setPrototypeOf(obj, prototype) 设置一个对象的原型。

isPrototypeOf():返回布尔值

hasOwnProperty():在实例上则返回 true。检测一个属性存在于实例还是原型中

hasPrototypeProperty():当实例中没有给定属性而原型上有返回true,当实例重写了给定属性后就找不到了(因为原型中的该属性让屏蔽了),此时返回false

单独使用 in 操作符:当通过对象能够访问到给定属性时返回 true
// 通过 hasOwnProperty() 和 in 操作符可以确定给定属性在对象实例还是原型对象上
console.log('name' in person) // true

使用 for-in 循环时,返回所有能通过对象访问的、可枚举的属性。(包括实例和原型上的属性)

Object.keys():接受一个对象作为参数,返回该对象上所有可枚举属性的字符串数组。(不向上找)

Object.getOwnPropertyNames():返回所有对象属性,无论是否可枚举。(不向上找)

三、原型的重写

  • 为了简写每次添加属性都要写一次 “构造函数.prototype“ 的写法,可以直接一次赋给它一个对象,然后将要添加的属性封装到那个对象中。但是问题是这样会导致原型对象的 constructor 不在指向该构造函数了,因为此操作相当于重写了默认的原型对象,所以此时的 constructor 指向的是 Object 构造函数,即无法用 constructor 确定对象类型了。所以在重写原型对象后,要同时修改它的 constructor 指向。
  • 重写原型对象会切断现有原型和任何之前已经存在的实例对象之间的联系,那些实例对象指向的仍然是之前的那个原型。

四、原型链

让一个原型对象等于另一个类型的实例,原型对象将包含一个指向另一个原型对象的指针,另一个原型中也包含着一个指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例,这样层层递进,就构成了实例和原型的链条。

五、示例

1
2
3
4
5
6
Function.prototype.a = 1;
Object.prototype.b = 2;
function A() { }
var a = new A();
// console.log(a.a, a.b); // undefined, 2
// console.log(A.a, A.b); // 1, 2
微信打赏