构造函数原型——prototype(显示原型)
原型是函数 function 对象的一个属性,也是一个对象,它定义了构造函数制造出来的对象的公共祖先。通过构造函数产生的对象,可以继承到该原型的属性和方法。 我们先来看一个例子:
Car.prototype = {
brand = 'BMW',
wheels = 4
}
function Car(){
}
var car1 = new Car()
console.log(car1.brand, car1.wheels) //'BMW' 4
var car2 = new Car()
console.log(car2.brand, car2.wheels) //'BMW' 4
通过上面的例子,我们可以看出,构造函数 Car 的内部并没有 brand、wheels 属性,但是 Car 的实例对象 car1 和 car2 却都能访问到这些属性,这是因为当实例对象去访问 brand、wheels 属性时,由于自身没有该属性,就会往构造函数的原型上去取,且原型上的属性是公有的, car1 和 car2 取到的是同一个属性。
通过实例对象能修改或删除原型上的属性吗?
Person.prototype.lastName = '李' // 实例化对象都是访问公共的原型对象,指向同一块地址
function Person(name) {
this.name = name
}
var person = new Person('小一')
console.log(person.lastName); // 李
var person2 = new Person('小二')
console.log(person2.lastName); // 李
// 修改
person.lastName = '吴' // 相当于往person上添加了一个 lastname 属性,说明**实例改不动原型上的属性**
console.log(person.lastName); // 吴 因为person上添加了一个 lastname 属性,所以访问到的并不是原型上的属性
console.log(person2.lastName); // 李 自身没有 lastname 属性,所以访问到的是原型上的属性
Person.prototype.lastName = '吴' // **一定要在原型上才能改变原型上的属性**
console.log(person2.lastName); // 吴 原型上的属性改变
// 删除
delete person.lastName // **通过实例无法删除原型上的属性**
console.log(person2.lastName); // 吴 仍然存在
delete Person.prototype.lastName // **必须在原型上删除**
console.log(person2.lastName); // undefined 不存在
constructor
构造器属性(constructor)为了让构造出来的所有对象都能找到它自己的构造器,是为了记录下对象是被谁创建的(函数没有constructor,只有原型和对象才有),原型中的 constructor 属性指向构造函数。如:
当我们将构造函数 Car 的原型上的 constructor 属性设置为 Bus 时,可以发现 car.constructor 的值也相应改变,此时认为 car 是由 Bus 创建出来的
对象原型__proto__(隐式原型)
每一个对象都具有__proto__属性,它和构造函数中的 prototype 属性有着什么样的联系呢?
Person.prototype = {
name: '小吴'
}
function Person() {
// var this = {
// __proto__: Person.prototype
// }
// return this
}
var person = new Person()
console.log(person.__proto__); // { name: '小吴' }
var obj = {
name: '小李'
}s
person.__proto__ = obj // 将实例对象的隐式原型修改为另一个对象
console.log(person.__proto__); // { name: '小李' }
由上例我们可得出——Person.prototype === person._proto_(构造函数的显示原型 === 实例对象的隐式原型),所以实例对象可以通过__proto__来访问构造函数原型上的属性,这也就是我们上文中实例对象为何可以访问到构造函数原型上的属性的原因。在构造函数上添加的属性,都会被实例对象显式地“继承”到;在构造函数的原型上添加的属性,都会被实例对象隐式地“继承”到。对象的原型就等于这个对象的创建者的显示原型。
原型链
如果你已经将上文的内容全部掌握,那么原型链对你来说简直小菜一碟。先让我们来看看下面这张很经典的原型链图:
虽然这张图看着有些许的复杂,但千万不要被吓到,让我们来仔细分析一下。 (1)构造函数 Foo 上存在 Foo.prototype 原型,而 Foo.prototype 原型中的 constructor 属性又指向构造函数,就有了如图:
(2)f1、f2 是由 Foo 函数构建出来的实例对象,所以它们的隐式原型__proto__等于 Foo 函数的显式原型,也就是 Foo.prototype ,故而有了如图:
(3) Foo 函数的显式原型也是个对象,所以可以理解为 Foo.prototype 由 Object 构建,故而 Foo.prototype 的隐式原型__proto__等于 Object 的显式原型 Object.prototype ,如图:
(4) Object.prototype 到了顶端,它的隐式原型为null
(5)构造函数 Object 上存在着 Object.prototype 原型,同样, Object.prototype 原型中的 constructor 属性又指向构造函数 Object :
(6)o1、o2 是由 Object 函数构建出来的实例对象,所以它们的隐式原型__proto__等于 Object 函数的显式原型,也就是 Object.prototype :
(7)函数 Function 上存在着 Function.prototype 原型,Function.prototype 原型中的 constructor 属性又指向构造函数 Function :
(8)由于函数比较特殊,函数的隐式原型就等于函数的显式原型 Function.prototype ,所以函数 Foo 、Object、Function 的隐式原型__proto__都指向 Function.prototype。
(9) 函数的显式原型也是对象,所以可以理解为 Function.prototype 由 Object 构建,故而 Function.prototype 的隐式原型__proto__也指向 Object 的显式原型 Object.prototype。
至此,大图详解结束。
希望这篇文章能给您带来些许帮助,若有不对,还望指正,不胜感激!