在javaScript中,常常会说到原型(prototype
)和原型链(prototype chain
)。我曾经觉得我彻底理解了,可是后来老是会发现不一样的地方,此次进行一次系统的整理。java
看一下权威指南中的说明数组
每个javaScript对象(null除外)都和另外一个对象相关联。“另外一个”对象就是咱们熟知的原型,每个对象都从原型继承属性。浏览器
若是是经过关键字new
和构造函数(constructor)
调用建立的对象,好比let x = new Date()
,此时x
的原型就是constructor的prototype属性。函数
若是是经过对象直接量建立的对象的时候,好比let y = {}
。此时的y
会和Object.prototype
相关联。工具
若是是经过Object.create()
建立的对象,好比let z = Object.create(obj)
。此时z
的原型就是它的第一个参数obj
(也能够是null)。测试
let x = new Date(), y1 = {}, y2 =[], obj = {a:1}, z = Object.create(obj); Object.getPrototypeOf(x) === Date.prototype // true Object.getPrototypeOf(y1) === Object.prototype // true // 注意这里是数组 Object.getPrototypeOf(y2) === Array.prototype // true Object.getPrototypeOf(z) === obj // true
能够在浏览器调试工具中打印下Object.prototype
到底是什么样,会看到不少常见的方法。prototype
上面都是内置构造函数,自定义的函数也是同样调试
function Test(){ console.log('constructor Test'); } let test = new Test(); Object.getPrototypeOf(test) === Test.prototype // true
实例在被建立的时候,就已和本身的原型相关联。原型指向constructor的prototype,固然也能够是指定的对象。那么如此重要的一个特性是作什么的呢?简单来讲,是用来实现继承的。code
思考一个简单的问题,定义一个对象let x = {a:1}
。x
中没有定义任何方法,可是却可使用一些方法(好比:toString
),那么这些方法来自哪里呢?对象
let x = {a:1}, y = new String('123'); // toString继承自Object.prototype x.toSting() // "[object Object]" // split继承自String.prototype y.split('') // ["1", "2", "3"]
实例对象的原型中会有公共的方法,每一个实例均可以访问到,这样无需再重复实现,也就是继承。因此JavaScript中的继承,不是经过复制而来的,而是经过原型继承的,而访问的过程,就是所谓的“原型链”了。
咱们能够把须要实例继承的方法定义在它的原型上。
function Test(){ } Test.prototype.sayHi = function(){ console.log('Hi world!') } let test1 = new Test(); let test2 = new Test(); // 实例使用原型上的方法 test1.sayHi() // 'Hi world!' test2.sayHi() // 'Hi world!'
每一个实例对象(
object
)都有一个私有属性(可称之为__proto__
)指向它的构造函数的原型对象(prototype
)。该原型对象也有一个本身的原型对象(__proto__
) ,层层向上直到一个对象的原型对象为null
。根据定义,null
没有原型,并做为这个原型链中的最后一个环节。
注意: __proto__
是JavaScript 的非标准属性,但许多浏览器都已实现。
以上面的对象为例进行分析:对x={a:1}
这个对象调用toString
方法,在x
对象中没有找到此方法,就会去x.__proto__
指向的原型对象(也就是Object.prototype对象)中找,在Object.prototype中找到了toString
,就调用了。若是调用x.aa()
方法,x
自身没有,x.__proto__
中也没有,再往上一层x.__proto__.__proto__
发现是null
(Object.prototype.__proto__ === null
)了,在自身和原型链中都没有找到,就会报错了。
实例对象在建立的时候就和原型对象相关联,除了__proto__
这个非标准属性外,能够经过如下方法访问原型对象。
Object.getPrototypeOf()
方法返回指定对象的原型(内部[[Prototype]]属性的值)。Object.setPrototypeOf()
方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另外一个对象或 null。isPrototypeOf()
方法用于测试一个对象是否存在于另外一个对象的原型链上。prototypeObj.isPrototypeOf(object)
__proto__
和 prototype
属性要注意区分__proto__
和prototype
:
__proto__
是实例对象的私有属性,指向它的构造函数的prototype
属性prototype
是构造函数的属性,在调用构造函数建立实例对象时,实例对象经过__proto__
和prototype
相关联。一样的,prototype
对象中会有__proto__
,指向它本身的原型,这样一层层连接,直到指向null
,造成原型链Function
的实例prototype
属性,是定义公共方法的地方;prototype
中有constructor
属性,是指向函数自己。// Test为构造函数 function Test(){}; // x为Test的实例对象 let x = new Test(); // x的原型链为:x -> Test.prototype -> Object.prototype -> null x.__proto__ === Test.prototype; // true x.__proto__.__proto__ === Object.prototype; // true x.__proto__.__proto__.__proto__ === null; // true // 构造函数也算是Function的实例 // Test的原型链为:Test -> Function.prototype -> Object.prototype -> null Test.__proto__ === Function.prototype // true Test.__proto__.__proto__ === Object.prototype // true // 内置的构造函数也都算是Function的实例 Array.__proto__ === Function.prototype // true Date.__proto__ === Function.prototype // true Function.__proto__ === Function.prototype // true Object.__proto__ === Function.prototype // true //constructor Test.prototype.constructor === Test // true x.constructor === Test // true 注意,这里用的x原型(Test.prototype中的属性)
最后放一张网图来总结下