原型和原型链(prototype chain)
2021/4/29
- 通过原型这种机制,JavaScript 中的对象从其他对象继承功能特性;这种继承机制与经典的面向对象编程语言的继承机制不同。
一. 基于原型的语言
-
JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象
-
对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。
-
这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
-
准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的
prototype
属性上,而非对象实例本身。
建立连接
-
在传统的面向对象程序设计(Object Oriented Programming,OOP)中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。
-
在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的
prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。 -
注意:__proto__是每个实例上都有的属性,prototype是构造函数的属性,也就是说
-
Object.getPrototypeOf(new Foobar())===new Foobar().__proto__===Foobar.prototype 复制代码
-
二. 查找过程
1. 创建一个实例对象
-
function doSomething(){} doSomething.net="ninini"; doSomething.prototype.foo = "bar"; var doSomeInstancing = new doSomething(); doSomeInstancing.prop = "some value"; 复制代码
-
可见,构造函数的prototype中的constructor指向自己本身,构造函数是继承函数而来的,构造函数的prototype对象是继承对象的
2.沿着原型链往上查找
- 当你访问
doSomeInstancing
的一个属性, 浏览器首先查找doSomeInstancing
是否有这个属性 - 如果
doSomeInstancing
没有这个属性, 然后浏览器就会在doSomeInstancing
的__proto__
中查找这个属性(也就是 doSomething.prototype). - 如果 doSomeInstancing 的
__proto__
(也就是 doSomething.prototype)有这个属性, 那么 doSomeInstancing 的__proto__
上的这个属性就会被使用. - 否则, 浏览器就会去查找 doSomeInstancing 的
__proto__
的__proto__
,看它是否有这个属性.
3. prototype 属性:继承成员被定义的地方
4. create()
-
var person2 = Object.create(person1); 复制代码
-
**
Object.create()
**方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。这里以person1
为原型对象创建了person2
对象
5. constructor 属性
-
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
-
你可以在
constructor
属性的末尾添加一对圆括号(括号中包含所需的参数),从而用这个构造器创建另一个对象实例。毕竟构造器是一个函数,故可以通过圆括号调用;只需在前面添加new
关键字,便能将此函数作为构造器使用。 -
function Person(name) { this.name=name } let person1=new Person('1') var person2 = new person1.constructor('2') 复制代码
6. 修改原型
-
你很少看到属性定义在 prototype 属性中,因为如此定义不够灵活。
-
当想根据实例某些属性值设置原型属性值时
-
Person.prototype.fullName = this.name.first + ' ' + this.name.last; // 注意,这样设置是没有效果的,因为这里的this默认指向window 复制代码
-
Person.prototype.fullName =function(){return this.name.first + ' ' + this.name.last;} // 这样设置会让this指向调用原型中该方法的实例对象 复制代码