原型与原型链
深入理解原型和原型链
每个对象(object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型链:原型对象也有一个自己的原型,层层向上直到找到 null。原型链决定了对象属性的访问方式。
在控制台输出一个对象,都能看到有一个名为[Prototype]的对象,打开后也能找到在这个对象中有__proto__
属性
输出一个函数,除了有上面的带方括号的Prototype,还有一个不带方括号的prototype属性,这些属性都叫原型
什么是原型
遵循 ECMAScript 标准,每一个对象上的 [[Prototype]](内置属性)表示原型,注意:并非所有的对象都有原型。为了方便理解,把**[[Prototype]]** 也称为 隐式原型。
const o1 = {}
const o2 = new Object()
const o3 = Object.create({null})
console.log(o1)
console.log(o2)
console.log(o3)
返回结果是o1与o2都有原型,o3没有,这是因为Object.create()方法会把传入的参数作为原型
原型的访问可以通过对象的__proto__
和Object.getPrototypeOf()来访问
var obj = {}
obj.__proto__ === Object.getPrototypeOf(obj) // true
console,log(obj.__proto__)
console,log(Object.getPrototypeOf(obj))
原型的原型
console.log(obj.__proto__.__proto__)// null
原型链的顶端:null
原型的主要作用: 实现继承
const parent = {
a: 1,
fn() {
console.log('父级的方法')
}
}
const child = {}
// 把parent赋值给child的隐式原型
child.__proto__ = parent
console.log(child.a)
child.fn()
- 输出child的a属性与调用fn方法,发现都能使用。说明child可以从原型上继承到parent的属性与方法
构造函数的prototype
函数的本身(除了箭头函数外)都存在一个prototype属性,该属性是在给定函数被用作构造函数时分配给所有对象实例的 [[Prototype]],为了方便理解,把构造函数的prototype 也称为 显示原型
var fn = function() {}
console.dir(fn)
var f = new fn()
f.__proto__ === fn.prototype // true
实例的原型与构造函数的原型是全等的
构造函数中有一个prototype属性,这个属性指向的就是这个函数的显式原型
寻找这原型的原型:
f.__proto__.__proto__ === Object.prototype// true
所以f的原型的原型,是由Object的原型构造出来的
console.log(f.__proto__.__proto__.__proto__)// null顶端
实例中有constractor属性,指向的是这个实例的构造函数
console.log(f.constractor === fn)// true
实例可以通过这个属性来找到其构造函数
构造函数的原型的constractor属性指向的是其本身
console.log(fn.prototype.constractor === fn)// true
总结:
- 构造函数通过new创建f,f通过constractor属性访问到构造函数fn
- 构造函数fn的prototype属性指向了fn的原型,也是实例f通过
__proto__
属性访问到f的原型 - 实例f的原型通过constractor属性也可以访问到构造函数fn
- 实例f的原型是Object通过new的方式创建的
- Object通过prototype属性能找到Object原型,即f的原型的原型
- 实例f的原型可以用
__proto__
属性继续访问,也可以继续用这种方法接着访问到顶端的null
构造函数是函数,有显式原型,同时也是对象,就有隐式原型
构造函数的隐式原型是Function的prototype(Function是一个关键字)
所有函数的构造函数都是Function,所以构造函数的原型可以用fn.__proto__
访问到
const fn = function() {}
fn.__proto__ === Function.prototype// true
也可以用Function的prototype属性访问到
console.log(Function.__proto__)//
需要注意的是,funtion的__proto__
(隐式原型)与Funtion.prototype(显示原型)指的是同一个东西
总结:
什么是原型链
原型链决定了对象属性的访问方式,对象寻找属性会从本身开始查找,本身找不到会继续沿着原型链往原型上去找,直到找到原型链顶端null,都找不到就返回undefined
- 获取属性的情况
var obj = { foo:1 }
obj.__proto__.foo = 2// 在原型上加一个属性foo,值为2
console.log(obj.foo)// 1从本身找
- 赋值属性的情况
var obj = { foo:1 }
var obj1 = Object.create(obj)
obj1.foo = 2// 给foo属性赋值
console.log(obj1)
obj1以obj为原型,所以obj的foo属性的属性值1,是在obj1的原型上;obj1.foo=2是给obj1创建foo属性赋值为2,因为本身没有,所以创建;
var obj = { foo:[1] }// 把值改为一个数组
var obj1 = Object.create(obj)
obj1.foo[0] = 2// foo属性的值的0号位赋值
console.log(obj1)
obj1以obj为原型,所以obj的foo属性的属性值[1],是在obj1的原型上;obj1.foo[0]在obj1本身找不到,所以会往原型上找,发现原型obj有这个属性,所以obj这个属性值被修改为2
两种写法,第二种却是寻找,因为obj1.foo[0] = 2
这里的写法foo[0]这个寻找元素的写法,所以是在查找对应元素,所以不会创建新属性,本身找不到就往上找