构造函数
构造函数也是函数的一种,只不过构造函数作用是用来生成对象实例,且函数名首字母一般大写。一般通过new关键字生成对象。
function Person (name, age) {
this.name = name
this.age = age
this.add = '北京'
this.say = function () {
console.log('say hello)
}
}
let p = new Person('lucy', 18)
原型对象
js中,只要是函数类型的数据,都有prototype属性,指向一个特殊对象,叫原型对象。
原型对象一般存放着构造函数new出实例的公共方法和属性。
构造函数的prototype属性指向原型对象,原型对象有constructer属性,指向构造函数本身。
作用
存放构造函数new的实例的公共方法和属性
如上例子中,add 和 say 所有实例都一样,没有必要放在构造函数中,每次实例化都占用额外空间,我们就可以放在原型对象上。
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.add = '北京'
Person.prototype.say = function () {
console.log('say hello)
}
const p1 = new Person('lucy', 18)
const p2 = new Person('lily', 22)
console.log(p1.add, p1.say()) // lucy, say hello
这里p1没有add 属性和 say方法,那为什么能够通过点访问到?是因为js中,如果本身访问不到的属性,会去该对象的构造函数的原型对象上去找。如果找到,那么就会返回对象属性或者调用对应方法。这也是为什么我们能进行以下访问?
p1.constructor === Person
因为查找顺序为p1=>Person.prototype
**注意:**p1后Person.prototype怎么建立联系?proto
那么如果原型对象上找不到怎么办,这里就涉及到原型链,找不到就会顺着原型链一直往上找,直到原型链末尾。
原型链
显式原型
- 函数类型数据才有
- 通过prototype访问
隐式原型
- 对象类型数据才有
- 通过__proto__访问,一般通过实例去访问
- 指向构造函数的原型对象。
p1.__proto__ === Person.prototype
p2.__proto__ === Person.prototype
因此,实例__proto__,prototype,constructor关系如下:
隐式原型链
- 既然隐式原型是对象的属性,那么Person.prototype也是对象,那么他也有__proto__属性。
- __proto__指向构造函数的原型对象,那么要知道普通对象的构造函数都是Object()构造函数。
那么就可以回答前边问题,如果对象本身以及原型上都没有对应属性,那么就会去原型的原型上找,找到则返回,找不到就会到Object.prototype.proto=null末尾,没有则返回undefined。这个查找顺序叫做原型链。
可以看到,查找变量的过程是顺着途中蓝色的线,顺着对象的__proto__属性查找的过程,因此原型链也叫做隐式原型链。
正因如此,我们创建的一些内置对象类型,如数组、Date,正则等数据结构都有公共的属性和方法,因为存在他们的原型对象上。
函数也是一种对象
- 函数也是一种特殊对象
- 因此函数也有__proto__属性
- 所有函数的构造函数都是Function(),包括Object(),Function()本身
- 函数公共属性的查找也是顺着隐式原型链查找
Person.constructor => Person.__proto__.constructor => Function.prototype.constructor === Function
Object.constructor = Function
Function.constructor = Function
总结
- 所有函数都有prototype对象,指向原型对象。构造函数原型对象的constructor指向构造函数本身。
- 所有函数都是Function()的实例,或者说所有函数的构造函数都是Function
- 所有对象都有__proto__,指向构造函数的原型对象。
- 对象的构造函数是Object()。函数的构造函数是Function()
- 原型链是隐式原型链,都是根据__proto__去查找属性。
- 原型链末尾Object.prototype.proto=null
练习题
function Person(name) {
this.name = name
}
var p2 = new Person('king');
console.log(p2.__proto__) // Person.prototype
console.log(p2.__proto__.__proto__) // Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
// console.log(p2.__proto__.__proto__.__proto__.__proto__) // null后面报错
// console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__) // null后面报错
console.log(p2.constructor) // Person
console.log(p2.prototype) // undefined
console.log(Person.constructor) // Function
console.log(Person.prototype) // 打印出Person.prototype里面的属性和方法
console.log(Person.prototype.constructor) // Person
console.log(Person.prototype.__proto__) // Object.prototype
console.log(Person.__proto__) // Function.prototype
console.log(Function.prototype.__proto__) // Object.prototype
console.log(Function.__proto__) // Function.prototype
console.log(Object.__proto__) // Function.prototype
console.log(Object.prototype.__proto__) // null