面试题【Day06】
本篇绪论
1,原型、原型链
1,原型、原型链
创建对象的几种方法:
- 1,字面量
let obj = {}
let obj1 = new Object()
- 2, 构造函数
let Person = function(name) {
this.name = name
}
let obj3 = new Person('xiaowang')
- 3, Object.create
let obj4 = Object.create({})
Object.create是用原型链的方式连接的
所有对象都有一个proto(隐式原型)属性,属性值是一个普通的对象,所有的函数都有一个prototype(原型)属性,属性值也是一个普通的对象。
let arr = []
arr.__proto__ === Array.prototype // true
let obj = {}
obj.__proto__ === Object.prototype // true
对象的__proto__属性指向它构造函数的prototype
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果找不到,则会去他的proto上找,即它的构造函数的prototype,如果还没找到,就会去构造函数的prototype的proto中查找,这样一层一层向上查找就会形成一个链式结构,这称之为原型链
function Person(name) {
this.name = name
}
let p = new Person('xiaowang')
console.log(p.name) // xiaowang
console.log(p.age) // undefined
在P1中查找某个属性,执行步骤图
一直向上查找,直到查找到null还没有找到,则返回 undefined
Object.prototype.proto === null
原型
- 所有的引用类型(数组、对象、函数)都可以自由扩展属性(除null外)
- 所有的引用类型都有一个proto属性(隐式原型,它是一个普通的对象)
- 所有的函数都有一个prototype(原型)属性,它也是一个普通的对象
- 所有的引用类型,它的proto属性都指向它的构造函数的prototype属性
- 当试图得到一个对象的属性的时候,如果这个对象本身不存在这个属性,那么就会去它的proto属性(也就是它的构造函数的prototypr属性)中去寻找
我们来看个原型的例子
// 这是一个构造函数
function Person(name, age) {
this.name = name
this.age = age
}
// 所有的函数都具有一个prototype属性,这个属性是一个对象,所有的对象都可以自由扩展属性
Person.prototype = {
sayName () {
console.log(this.name) // this是什么要看执行的时候谁调用了这个函数
}
}
const p1 = new Person('xiaowang', 18)
p1.sayName() // xiaowang
以上就是原型, 那为什么使用原型呢?试想,如果我们要通过Person()创建很多很多个对象:
function Person (name, age) {
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
}
那么我们创建出来的每一个对象,里面都有一个sayName方法,这样就会占用很多的资源。
而通过原型来实现的话,只需要在构造函数里给属性赋值,而把方法写在Person.prototype属性里面,这样每个对象都可以使用prototype属性里面的sayName方法,并且节约了不少的资源。
原型链
当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它构造函数的prototype属性中去寻找。又因为prototype属性是一个对象,所以它也是一个proto属性
function Person(name, age) {
this.name = name
this.age = age
}
Object.prototype.toString = function () {
console.log(this.name + this.age) // this是什么要看执行的时候谁调用了这个函数
}
let p1 = new Person('xiaowang', 18)
p1.toString() // xiaowang18
console.log(p1.toString === Person.prototype.__proto__.toString) // true
console.log(p1.__proto__ === Person.prototype) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null) // true
以上代码,p1的构造函数是Person(),所以p1.proto === Person.prototype
因为Person.prototype是一个普通的对象,它的构造函数是Object,所以:Person.prototype.proto === Object.prototype
通过上面的代码,我们知道这个toString()方法是在Object.prototype里面的,当调用这个对象本身并不存在的方法时候,就会一层一层的往上查找,一直到null为止。
所以当p1调用toString()时,JS发现p1中没有这个方法,于是就会去Person.prototype中去找,发现还是没有这个方法,就会去Object.prototype中去找,找到了,就调用Object.prototype中的toString()方法。
这就是原型链,p1能够调用Object.prototype中的方法正是因为存在原型链的机制。
tips:
在使用原型的时候,一般推荐将需要拓展的方法写在构造函数的prototype属性中,避免写在proto属性里。