咱们建立的每一个函数都有一个prototype(原型)对象,这个属性是一个指针,指向一个对象。在默认状况下,全部原型对象都会自动得到一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。浏览器
function Person () {}
console.log(Person.prototype)
复制代码
打印结果以下:bash
当调用构造函数建立一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象。ECMA-262第5版中管这个指针叫[[Prototype]],双中括号表示该属性为内部属性,在JavaScript中不能直接访问。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox、Safari和Chrome在每一个对象上都支持一个属性__proto__来访问[[Prototype]],但__proto__的[[Enumerable]]值为false,不能够经过for-in或Object.keys()枚举出来。函数
function Person (name, age) {
this.name = name
this.age = age
}
var person = new Person('Jim', 21)
console.log(person)
console.log(Object.keys(person))
console.log(person.__proto__)
console.log(person.__proto__ === Person.prototype)
复制代码
打印结果以下:测试
由于constructor属性在构造函数的原型里,而且指向构造函数,那咱们就能够利用constructor属性来判断一个实例对象是由哪一个构造函数构造出来的,也能够说判断它属于哪一个类。this
function Person (name, age) {
this.name = name
this.age = age
}
function Car () {}
var person = new Person('Jim', 21)
console.log(person.constructor) //Person
console.log(person.constructor === Person) //true
console.log(person.constructor === Car) // false
复制代码
但有一点咱们是要注意的,当咱们将Person.prototype设置为等于一个以对象字面量形式建立的新对象时,constructor属性再也不指向Person。由于上述作法把Person默认的prototype覆盖掉,指向Person的constructor就不复存在。spa
function Person () {}
var person1 = new Person()
Person.prototype = {
name: 'Jim',
age: '21'
}
var person2 = new Person()
console.log(person1)
console.log(person2)
console.log(person1.constructor)
console.log(person2.constructor)
复制代码
打印结果以下:prototype
访问person2时,Person.prototype里已经没有了constructor属性,因此会继续沿着原型链往上找到Person.prototype.prototype中的constructor属性,它是指向Object的,所以person2.constructor指向Object。指针
那若是咱们对 Person.prototype从新赋值后但愿constructor仍指向Person的话,咱们能够在字面对象里加一个constructor属性让它指向Personcode
function Person () {}
var person1 = new Person()
Person.prototype = {
constructor: Person,
name: 'Jim',
age: '21'
}
var person2 = new Person()
console.log(person1)
console.log(person2)
console.log(person1.constructor)
console.log(person2.constructor)
console.log(Object.keys(Person.prototype))
复制代码
打印结果以下:对象
咱们发现person2.constructor从新指向Person,但同时咱们也发现constructor变成了可枚举属性,上文说到constructor属性默认是不可枚举的,即[[Enumerable]]的值为false 咱们能够经过Object.defineProperty()把constructor定义为不可枚举属性
function Person () {}
var person1 = new Person()
Person.prototype = {
name: 'Jim',
age: '21'
}
// 重设构造函数,只使用与ECMAScript5兼容的浏览器
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value: Person
})
var person2 = new Person()
console.log(person1)
console.log(person2)
console.log(person1.constructor)
console.log(person2.constructor)
console.log(Object.keys(Person.prototype))
复制代码
打印结果以下:
咱们能够看到constructor已经变成了不可枚举属性
2、instanceof instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。通俗来将就是判断一个实例对象是否由某个构造函数构造而来。
function Person () {}
function Car () {}
var person = new Person()
console.log(person instanceof Person) //true
console.log(person instanceof Car) //false
复制代码
这一点与constructor有着相同的做用,但instanceof相对于constructor更为可靠
function Person () {}
Person.prototype = {
name: 'Jim',
age: '21'
}
var person = new Person()
console.log(person instanceof Person) //true
console.log(person.constructor === Person) //false
复制代码
可见Person.prototype对象被重写并不影响instanceof的判断,由于instanceof是根据原型链来判断构造函数的,只要对象实例的原型链不发生变化,instanceof即可以正确判断
function Person () {}
var person = new Person()
console.log(person instanceof Person) //true
console.log(person instanceof Object) //true
person.__proto__ = {}
console.log(person instanceof Person) //false]
console.log(person instanceof Object) //true
复制代码
instanceof不只能够判断实例对象直接的构造函数,并且还能判断原型链上全部的构造函数,上面代码中Object在person的原型链中,因此返回true。 当咱们把空字面对象{}赋值给person.__proto__后,至关于切断了person原来的原型链(person -> Person -> Object),因此person instanceof Person返回false,那为何person instanceof Object会返回true呢,由于空字面对象是Object的实例,即原型链修改成(person -> Object)。这里顺带说一下Object的原型是什么?答案是null,由于Object是万物之源,因此对象都是Object的实例,处于原型链的末端,而它没有原型。