原型和原型链的名次解释
prototype-> 原型__proto__-> 原型链
原型和原型链的从属关系
首先我们要理解,原型和原型链究竟是什么东西?
prototype:是函数的一个属性。专业的叙述应该是:原型是函数的prototype属性。它就是一个普通的对象{}.
function Test() {}
console.log(Test.prototype)
//{ constructor: {}, prototype: ...} 不管里面是什么,它就是一个普通的对象。
__proto__:是对象(Object)的一个属性。也是一个普通的对象{}.
const test = new Test() // test是一个实例对象,__proto__是实例对象test的一个属性
console.log(test.__proto__)//{ constructor: {}, prototype: ...}
那么两者的从属关系就是:
- 实例对象的
__proto__保存着该对象构造函数的prototype什么意思呢?
console.log(Test.prototype === test.__proto__) // true
构造函数Test()的prototype保存在它实例对象test的__proto__中。这样说你应该能够明白了。
那接下来问题来了。上面讲__proto__时说它是对象的一个属性。既然如此,prototype也是一个对象,那它有__proto__属性吗?答案当然是肯定的。
我们尝试输出:
console.log(Test.prototype.__proto__)
得到了下图的结果:
那
Test.prototype.__proto__里面保存的又是哪一个构造函数的prototype呢?
console.log(Test.prototype.__proto__ === Object.prototype) // true
我们对比发现,保存的是Object.prototype对象。打破砂锅问到底,我们再往上找
console.log(Object.prototype.__proto__) // null
发现Object.prototype.__proto__为null.
而这么一条,从实例对象开始,到Object.prototype.__proto__结尾的链条就是原型链。
原型链
说了这么多好像还没说到原型链。让我们实际操作再了解一下: 写下下面的代码,并输出。
function Test() {
this.name = 'test'
}
Test.prototype.age = 18
Object.prototype.sex = 'Man'
const test = new Test()
console.log(test.name) // test
console.log(test.age) // 18
console.log(test.sex) // Man
我们看到,在Test函数中并没有声明age和sex,但是我们仍然可以输出结果。这是为什么?我们画出上面代码对应的原型链。
test { //实例
name: 'test',
__proto__: Test.prototype = { //构造函数原型
age: 18,
__proto__: Object.prototype = { // Object原型
sex: 'Man',
__proto__: null
}
}
}
当我们输出test.age时,实例对象test中没有age属性,就会自动去实例的__proto__保存的构造函数的prototype对象中查找该属性,直到找到最顶层。这就是为什么,我们可以输出,Test函数中没有声明,而在test实例和Object中声明的属性了。
判断当前对象包含某个属性
- 使用
hasOwnProperty属性来判断当前对象本身(不包括原型链)上有没有该属性。 - 使用
in来检测对象原型链上有没有该属性。
console.log(test.hasOwnProperty('name')) // true
console.log('Man' in test) // true
constructor
constructor指向的是实例化test的构造函数Test
console.log(test.constructor === Test) // true
并且constructor可以被修改。这点要注意!