构造函数和原型对象
构造函数
- 所有用同一个构造函数创建的对象都有相同的属性和方法,如果想创建多个相同的对象,就可以创建构造函数。
- 构造函数和普通函数一样,定义方式就是函数名字首字母大写
function Person(){
}
复制代码
- 定义好构造函数后,就可以创建对象了。
var person1 = new Person()
var person2 = new Person()
复制代码
- 即使构造函数Person没有显示的返回任何东西,person1和person2会被认为是一个新的Person类型的实例。new操作符会自动创建给定类型的对象并返回他们。这意味着可以用instanceof 获取对象的类型.
console.log(person1 instanceof Person) //true
console.log(person2 instanceof Person) //true
复制代码
- 每个对象在创建的时候,都会有个构造函数属性(constructor),指向构造函数
console.log(person1.constructor === Person) //true
console.log(person2.constructor === Person) //true
复制代码
- 为构造函数创建属性和方法。
function Person(name){
this.name = name;
this.sayName = function(){
console.log(this.name)
}
}
var person1 = new Person('aaa')
var person2 = new Person('bbb')
console.log(person1.name) //aaa
console.log(person2.name) //bbb
person1.sayName() //aaa
person2.sayName() //bbb
复制代码
- 这样的对象,每个对象都拥有相同的属性和方法,但是每个对象都会被new,造成重复的损失,可以用原型对象来避免,被多次new.
原型对象
- 几乎所有的函数(除了一些内建函数)都有一个名为prototype的属性,该属性是一个原型对象用来创建新的对象实例,所有创建的对象实例共享该原型对象。且这些对象实例可以访问原型对象的属性。例如:hasOwnPrototype()方法被定义在Object的原型对象中,可以被任何对象当作自己的属性访问。
var book = {
title:'111'
}
console.log(title in book) //true
console.log(book.hasOwnPrototype('title')) //true
复制代码
-
虽然book中没有定义hasOwnPrototype(),但是可以使用,因为该方法存在Object.prototype中。
-
鉴别一个属性是否是原型属性
function hasPro(object,name){
return name in object && object.hasOwnPrototype(name)
}
//true 代表是原型属性
//false 代表不是原型属性
复制代码
- Person.prototype就是原型对象
- 写在原型对象的属性和方法会被多个对象实例共用
- isPrototypeOf()检查一个对象是否是另一个对象的原型对象
var object = {}
console.log(Object.prototype.isPrototypeOf(object)) //true
复制代码
- 当读取一个对象的属性时,会先在自身查找有没有该属性,没有的话,会在原型对象中查找,也没有的话,会返回undefined
- delete只能删除对象的自有属性,删除不了原型对象的属性
在构造函数中使用原型对象
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log(this.name)
}
var p1 = new Person('aaa')
var p2 = new Person('bbb')
console.log(p1.name) //aaa
console.log(p2.name) //bbb
p1.sayName() //aaa
p2.sayName() //bbb
复制代码
-
上面把sayName()方法放在了Person的原型对象里面,所以用new创建出来的对象实例可以共用。p1和p2对象都可以使用sayName()方法。this值分别被赋值在p1和p2中。
-
如果在原型对象上添加引用类型的值时,会造成多个对象同时改变。
function Person(name){
this.name = name;
}
Person.prototype.sayName = function (){
console.log(this.name)
}
Person.prototype.favorite = [] //数组,引用类型
var p1 = new Person('aaa')
var p2 = new Person('bbb')
p1.favorite.push('ccc')
p2.favorite.push('ddd')
console.log(p1.favorite) //['ccc','ddd']
console.log(p2.favorite) //['ccc','ddd']
// 因为在原型对象上添加了引用类型,导致p1和p2对象都添加了ccc和ddd
复制代码
- 使用对象字面形式代替原型对象
function Person(name){
this.name = name
}
Person.prototype = {
sayName:function(){
console.log(this.name)
},
toString:function(){
return this.name
}
}
复制代码
- 使用对象字面形式的缺点是对象实例的constructor指向了Object,而不是构造函数
var p1 = new Person('aaa')
console.log(p1 instanceof Person) //true
console.log(p1.constructor === Person) //false
console.log(p1.constructor === Object) //true
// 可以看出来对象实例p1的constructor指向了Objcet
复制代码
- 解决办法:手动重置constructor属性
function Person(name){
this.name = name
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name)
}
}
var p1 = new Person('aaa')
console.log(p1 instanceof Person) //true
console.log(p1.constructor === Person) //true
console.log(p1.constructor === Object) //false
复制代码
- constructor是原型对象的属性
- 对象实例p1可以调用constructor属性
- 对象实例p1的constructor指向构造函数或者Object,并且可以手动改变对象实例的constructor,也就是原型对象的属性constructor
- p1.constructor = Person.prototype.constructor
内建对象也有原型对象
- 比如数组
Array.prototype.sum = function(){
return this.reduce((pre,cur)=>{
return pre + cur
})
}
var num = [1,2,3]
var res = num.sum()
console.log(6)
复制代码
- 通常,共享的方法和原始值的属性被定义在原型对象里面,其他属性被定义在构造函数里
原型对象链和Object.prototype
原型对象链
- 对象继承原型对象。原型对象继承它的原型对象,依次类推。
- 所有对象自动继承Object,确切的是,所有对象继承Object.prototype
继承自Object.prototype的方法
- hasOwnPrototype():检查是否存在一个给定名字的自有属性
- isPrototypeOf():检查一个对象是否是另一个对象的原型对象
- valueOf():返回一个对象的值表达式
- toString():返回一个对象的字符串表达式
对象继承
- 对象继承是最简单的继承类型。只需要做的是指定哪个对象是新对象。对象字面形式会隐士指定Object.prototype为其[[Prototype]],也可用Object.create()显示指定。