You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
当对函数使用new关键字执行new Person()时,Person就成为了一个构造函数。而let p = new Person()创建出来的p就是Person的实例对象。实例对象有__proto__属性就是隐式原型,也就是实例的内部[[Prototype]]指针。实例对象有**__proto__属性等于构造函数的原型对象**(Person.prototype)。也就是说:
functionPerson(){};
letp = newPerson();
Person.prototype = {
sayHi: ()=>{console.log('Hi')},
constructor: Person//此外由于prototype被重新定义,也没有constructor属性了,必要时需要自己重写
};
p.sayHi(); //Uncaught TypeError: p.sayHi is not a function
原型
每个函数都会创建一个 prototype 属性,这个属性是一个对象(默认是空的Object对象),包含应该由特定引用类型(构造函数创建)的实例共享的属性和方法。上面提到的prototype属性就是通过调用构造函数创建的对象的原型。也就是说,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。
函数的显式原型
如下所示,定义了一个Person函数,JS会为Person函数自动创建prototype属性(空Object对象),并prototype中添加constructor属性指向Person。prototype属性就是函数的显式原型。原型定义的属性和方法可以由所有实例共享。
JS内部操作类似于:
对象实例的隐式原型
当对函数使用new关键字执行new Person()时,
Person
就成为了一个构造函数。而let p = new Person()
创建出来的p
就是Person
的实例对象。实例对象有__proto__
属性就是隐式原型,也就是实例的内部[[Prototype]]
指针。实例对象有**__proto__
属性等于构造函数的原型对象**(Person.prototype
)。也就是说:此外,在实例对象上可以通过隐式原型访问到构造函数的原型,实例对象还能访问到实例的属性和方法。
注意,在构造函数
Person
中通过this创建的属性和方法,最后都是p
实例的属性和方法和Person
没有关系,因为在p
被创建出来的时候调用new Person()
,构造函数中的this
指向的是当前实例p
。原型链
原型链基本思想就是通过原型继承多个引用类型的属性和方法。实例对象的隐式原型等于对应构造函数的显示原型。前面的例子中
Person
函数的prototype
是默认的Object
对象,我们也可以更换为别的自定义对象,扩展原型链。再附上一张究极神图,吃透了这张图原型链就差不多了:
注意事项
原型赋值
由于
prototype
是一个对象,因此在对prototype
对象上进行属性和方法的添加时,采用对象方法赋值会新建对象,把原本指向旧地址的prototype
指向新对象地址。 可以看到第二个代码展示中,由于
p
先实例化,指向原先的Person.prototype
(如地址0x1234
),此时没有sayHi
函数。后续采用对象赋值Person.prototype
添加sayHi
产生的是新的地址(0x2345
)。原型层级
可以通过实例读取原型对象上的值,但不可能通过实例重写这些值(实例.
__proto__
.属性可以重写值)。只要给对象实例添加一个属性,就会在实例上创建这个属性,这个属性就会遮蔽原型对象上的同名属性,也就是虽然不会修改它,但会屏蔽对它的访问。 在通过对象实例访问属性时,会按照这个属性的名称开始搜索。搜索开始于对象实例本身,如果没有找到这个属性,则搜索会沿着原型链寻找,再返回对应的值。也就是实例——》实例.
__proto__
——》...——》null。原型的引用值属性
由于prototype是所有实例所共享的,在prototype上定义的引用值属性存在一定问题。上一个例子中
person1.name = "Greg"
是在实例上添加了 可以看到
colors
被p1
和p2
所共享,对它的更改也是共享的。但有时候需要不同实例拥有独立的属性,这样做就存在问题,需要避免。 可能会存在疑问,前面讲原型层级时,不是说会存在遮蔽吗?这里的colors应该是实例上的属性呀。看代码:
p1.colors.push('black')
,仅仅是操作原型上的colors
对象,没有生成新的实例属性。p1.colors =xxx
的操作才会让实例生成新的属性遮蔽原本的原型属性。p1.colors = p1.__proto__.colors
生成的实例属性,仅仅是把原型的colors
地址值赋值了,并没有新建对象,还是指向原型的colors
数组。p1.colors = ["blue","yellow"]
这种赋值操作 等价于p1.colors = new Array("blue","yellow")
,会让p1.colors
指向新的对象,就切断了和原型colors
的联系。The text was updated successfully, but these errors were encountered: