JavaScript中的原型概念

秉着温故知新的原则,又复习了一遍高程中关于原型的概念,这里做一个学习笔记。

原型概念

无论何时只要创建了一个函数,该函数就会有一个prototype属性,指向函数的原型对象。而原型对象会有一个constructor属性,包含一个指向构造函数的指针。当调用构造函数创建一个实例后,实例内部包含一个指针,指向构造函数的原型对象,浏览器上都支持这个属性为__proto__,这是一个实例与原型对象的联系。

原型链概念

原型链是一种机制,指的是JavaScript每个对象包括原型对象都有一个内置的__proto__属性指向创建它的函数对象的原型对象,即prototype。让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构造了实例与原型的链条,这就是原型链的基本概念。

[[Prototype]]

实例指向构造函数的原型对象的指针,被称为[[Prototype]]。

获取[[Prototype]]:Object.getPrototypeOf()
确定某构造函数的原型对象是不是属于某实例:构造函数.prototype.isPrototype(实例)

原型对象的属性查找和屏蔽

属性查找:当搜索对象上的某个属性时,先从对象实例本身开始,如果再实例中找到了具有给定名字的属性,就会返回这个属性值;如果没有找到,就会继续搜索指针指向的原型对象是否具有这个属性。
属性屏蔽:当我们为实例对象添加一个与原型对象同名的属性时,这个属性会屏蔽原型对象中的同名属性,但是却不会修改这个属性,其它实例对象仍然可以继承原型对象未被修改的属性。
解除属性屏蔽:但是当发生属性屏蔽后,我们就失去了对该原型对象属性的联系,即使将屏蔽属性设置为null也不行,只能使用delete删除对象实例上的该属性,从而可以重新访问原型中的属性。

检测和取得对象属性

hasOwnProperty():检测一个属性是否存在于实例中
in操作符:可以判断对象是否拥有某个属性,无论是实例属性或原型上的属性,in都会返回true
Obeject.keys():可以返回对象的所有可枚举属性的字符串数组
Object.getOwnPropertyNames():可以返回对象所有可枚举和不可枚举的属性

修改原型对象

对象字面量形式重写原型:本质上会完全重写prototype对象,然后constructor属性被改写,不再指向构造函数,而是指向Object;
重写丢失的constructor(字面量形式重写):如果以对象字面量形式来设置对象的原型,当然也可以再单独重新设置constructor的值,如果以对象字面量的形式去重写,会使constructor属性变为可枚举,
重写丢失的constructor(Object.defineProperty):设置的constructor仍然为不可枚举属性。

原型的动态性

由于在原型中查找值的过程是一次搜索,因此对原型对象所做的任何修改都能够立刻反映出来,即使是先创建实例后修改原型也是如此,先创建的原型还是能访问到后修改的原型属性。

function Pen(){
}
var pencil= new Pen();
Pen.prototype = {
    constructor: Pen,
    color: "red",
    printColor: function () {
        alert(this.color);
    }
};
var pencil2=new Pen();
pencil.printColor();   //pencil.printColor() is not a function

但是以上方法会报错,pencil.printColor() is not a function,调用构造函数的时候会为实例添加一个指向最初原型的 [[Prototype]]指针,而把原型重写为另一个对象就等于切断了构造函数与最初原型之间的联系。然而实例的__proto__仍然指向之前的原型对象。

打印两个实例对象的原型,结果如下图:
console.log(Object.getPrototypeOf(pencil2))
console.log(Object.getPrototypeOf(pencil))
image.png
正确的做法是先重写原型,再创建实例。

你可能感兴趣的:(JavaScript中的原型概念)