文章目录
在学习原型链之前我们需要先学习三个属性Prototype,constructor以及__proto__
一、 原型Prototype概述
1.概述
JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,
可以定义在构造函数内部。
2.实例
<script>
function Cat(name, color) {
this.name = name;
this.color = color;
}
var cat1 = new Cat('大毛', '白色');
cat1.name // '大毛'
cat1.color // '白色'
</script>
上面代码中,Cat函数是一个构造函数,函数内部定义了name属性和color属性,所有实例对象(上
例是cat1)都会生成这两个属性,即这两个属性会定义在实例对象上面。通过构造函数为实例对象定义
属性,虽然很方便,但是有一个缺点。
- 构造函数优点是,构造函数创建的属性和方法可以在实例之间共享。
- 缺点为如果实例之间有相同的方法,这个方法会在每个实例上创建一遍,这样显然会造成系统
资源的浪费。
二、constructor 属性
1.概述
prototype对象(原型对象)有一个constructor属性,默认指向prototype对象所在的构造函数。
由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。
2.实例
<script>
function Person() { }
var p1 = new Person();
console.log(p1.constructor === Person); // true
console.log(p1.constructor === Person.prototype.constructor); // trure
console.log(p1.hasOwnProperty('constructor')); // false
</script>
上面代码中,p1是构造函数Person的实例对象,但是p1自身没有constructor属性,该属性其实是
读取原型链上面的Person.prototype.constructor属性。
三、 __proto__属性
1.概述
__ proto __ 属性是每一个对象以及函数都隐含的一个属性。 __ proto __ 属性指向的是创建他的构造函数的prototype。原型链就是通过这个属性构件的。
2.实例
<script>
function Person() { }
var p1 = new Person();
console.log(p1.__proto__ === Person.prototype); //true
console.log(Person.prototype.constructor === Person); //true
console.log(p1.constructor === Person); //true
</script>
原型也是一个对象,既然是对象就会有 proto 属性,该属性指向原型对象的原型。
Object.prototype. proto 的值为 null ,也就是说Object.prototype 没有原型。所以查找属性的时候查到 Object.prototype 就可以停止查找了。
四、 原型链
1.概述
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充
当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……
图片为完整原型链
读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,
如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回
undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。
当你尝试访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的末端(即原型为null)。
<script>
// Object 内置对象
var o = new Object()
console.log(o.__proto__ === Object.prototype) // true
console.log(Object.prototype.constructor === Object) // true
// Person 自定义对象
function Person() {}
var p = new Person()
console.log(p.__proto__ === Person.prototype) // true
console.log(Person.prototype.constructor === Person) // true
// Function 特殊对象(也可以作为构造函数使用) 自己是自己的实例
// 注意:在js中所有的函数(构造函数)都属于Function的实例
var f = new Function()
// console.log(f.__proto__ === f.prototype) // false
console.log(f.prototype.constructor === f) // true
// console.log(f.prototype.constructor === Function) // false
// ------------------上面是三个对象的小原型链-----------------------
// Function的原型对象__proto__指向的是Object.prototype的其中一个实例
console.log(Function.prototype.__proto__ === Object.prototype) // true
// Person的原型对象__proto__指向的是Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype)
// 所有的(构造)函数都是Function的一个实例
console.log(Object.__proto__ === Function.prototype) // true
console.log(Person.__proto__ === Function.prototype) // true
// Function的原型对象指向的Object的原型对象
console.log(Function.prototype.__proto__ === Object.prototype) // true
// Object的原型对象__proto__属性指向的是null
console.log(Object.prototype.__proto__) // null
// 面试官:请你谈一谈js中的原型链吧?
// 候选人:
// 1、使用构造函数创建一个实例对象
// 2、每个实例对象有一个属性__proto__(隐式原型)指向的是构造函数的prototype(原型对象)
// 3、这个原型对象本身也是一个对象,也有自己的原型prototype,这个原型还是对象,所以它也有自己的prototype
// 4、最后找到的是Object的原型对象,如果再往上就是null了
// 5、我们在项目中如果要读取对象的某个属性会先从它本身去找,没找到会从原型对象上找,再没找到会从原型对象的原型对象上找,直接最后找到的null
// 6、这种形式类似于链条的形式,所以称为原型链
</script>
实例alice—> Person.prototype —> Object.prototype —> null
这意味着,如果alice尝试访问一个属性或方法,它会首先在自己的属性中查找,如果没有找到,就会去Person.prototype中查找,如果还是没有找到,就会去Object.prototype中查找,最后如果还是没找到,就会返回undefined。
总结
我们可以将原型链看作一个族谱,当要追溯你的特征时你大多数特征都是继承父亲的但是如果你父亲没有这个特征,你就要去找你爷爷的特征,一直找找找,找到了就是继承了那个人的特征,没找到就会返回null,说明没找到。