Javascript 是一门功能强大的语言;其中原型式的面向对象编程更是突出了这门编程语言的特色,为js的项目开发带来了许多便利。那到底什么是原型呢?今天就让我们一起来探究一下Javascript中原型的概念吧。
原型
在JavaScript中,原型(Prototype)是一个核心概念,用于实现继承和对象间共享属性与方法的机制。每个函数和对象都有一个特殊的__proto__属性,它指向构造该对象的构造函数的prototype属性。当尝试访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到这个属性或方法或者到达原型链的终点。
构造函数上的prototype(显示原型)
在JavaScript中,每一个函数都有一个prototype属性,这个属性通常是一个对象,它包含了所有由该函数作为构造函数创建的对象(即“实例”)将要继承的属性和方法。prototype实际上定义了一个公共的“模板”,所有的实例对象可以通过这个模板共享数据和功能。如果没有显式地为这个prototype属性赋值,那么它的默认值会是一个由Object构造函数创建的新对象,这个对象继承自Object.prototype。
上面中,我们往构造函数Person的原型上添加了一个属性age和一个方法say;那我们用new构造函数实例化出来的对象p,当在自身中没有找到属性age就会往其构造函数的原型上查找;找到age为18。
所以说,原型是函数自带的一个属性 prototype,它定义了构造函数创建的实例对象的公共祖先。通过构造函数创建的对象,可以访问到该函数原型上的属性和方法。
实例对象上的__proto__(隐式原型)
在JavaScript中,__proto__属性(通常称为隐式原型)是每个对象(除了那些通过Object.create(null)创建的特殊对象)所具有的一个内部链接,它指向该对象的原型对象。这个原型对象是该对象继承属性和方法的地方,也是当查找属性或方法时,JavaScript引擎沿着原型链向上查找的起点。
当你使用构造函数和new关键字创建一个对象时,这个新对象的__proto__属性会自动设置为构造函数的prototype属性所指向的对象。
构造函数实例化出来的对象隐式具有构造函数原型上的属性,且只是只读属性;也就是说实例对象隐式的继承了其构造函数原型上的属性和方法,但是在实例对象中并不可见,而且不能通过实例化的对象去修改其构造函数原型上的属性值。
可以看到,在对象p中并没有显示到其构造函数原型上的属性like;而后面通过p.like = '撸铁'也没有修改掉原型上的属性,而是直接往对象p中添加了一个like属性。
原型链
JavaScript在查找一个对象的属性时,首先会在该对象自身上查找。如果在对象自身上找不到请求的属性,JavaScript引擎会沿着所谓的“原型链”继续查找,直到找到该属性或到达链的终点——null。
原型链是由一系列__proto__属性链接起来的,这些属性指向对象的原型。如果在某个对象的__proto__上仍然找不到属性,则继续沿着链向上查找,直到到达Object.prototype,这是所有对象原型链的最终父级。如果在Object.prototype上也找不到,那么就认为该属性不存在于对象上。
所有对象都有原型吗?
尽管大多数JavaScript对象都有原型,但是有一种特殊情况,那就是通过Object.create(null)创建的对象。这种对象没有原型,这意味着它的__proto__属性值为null。 这在某些情况下非常有用,比如当需要创建一个对象,但又不希望它继承任何默认的属性和方法时(例如,toString()和valueOf()等)。由于这类对象没有原型,它们的原型链也就终止于此,不会向上追溯至Object.prototype。