1、原型以及原型链
1.1、理解原型
-
无论何时,只要创建一个函数,就会按照特定的规则为这个函数构建一个prototype属性,prototype属性的值是一个对象,这个对象包含了可以被该构造函数的所有实例所共享的属性和方法。在ES6中,这个对象就是通过调用构造函数创建的对象的原型。
-
当创建函数后,函数的prototype属性,指向原型对象。默认情况下,所有原型对象会自动获得一个名为constructor的属性,指回与之关联的构造函数。
-
在自定义构造函数时,原型对象默认只会获得constructor属性,其他所有的方法都继承自Object。
-
每次调用构造函数创建一个实例,这个实例的内部会有一个指针,即[[prototype]],这个指针指向构造函数的原型对象。脚本中没有访问这个指针的标准方式,但是在浏览器中提供了__proto__属性(在proto左右各两个下划线),通过这个属性可以访问到原型对象。
-
因为这个不是规范的,所以最好不用,ES5中新增了Object.getPrototypeOf()的方法,可以使用这个方法来获取对象的原型
-
通过isPrototypeOf()方法也可以检查一个对象是否位于另一个对象的原型链上
-
通过上述我们可以理解到:实例与构造函数原型之前有直接的联系,但是实例和构造函数之间没有直接联系
-
下面是一些例子
//创建构造函数Person
function Person(){}
//在Person的原型上添加属性
Person.prototype.name = '张三'
//调用构造函数创建实例
let person1 = new Person()
let person2 = new Person()
//原型对象上的属性和方法被构造函数的所有实例所共享
console.log(person1.name);//'张三'
console.log(person2.name);//'张三'
//原型对象上的constructor属性指回与之关联的构造函数
console.log(Person.prototype.constructor === Person);//true
//通过__proto__属性,实例对象可以访问到原型
console.log(person1.__proto__ === Person.prototype);//true
console.log(person2.__proto__ === Person.prototype);//true
//通过Object.getPrototypeOf(),实例对象可以访问到原型
console.log(Object.getPrototypeOf(person1) === Person.prototype);//true
console.log(Object.getPrototypeOf(person2) === Person.prototype);//true
//原型对象通过isPrototypeOf(),来检查person1和person2
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//true
1.2、原型链:
- 一个对象的原型,也可能是另外一个构造函数的实例,那么这个对象的原型又会有自己的原型。
- 当访问一个对象的属性的时候,如果这个对象内部不存在这个属性,那么引擎就会去这个对象的原型上去找这个属性,如果还是不存在,就会去这个原型的原型上去找,就这样一直找下去。这便是原型链的概念
- 一般来说,正常的原型链都会终止于Object的原型对象
- Object的原型对象是null
console.log(Object.getPrototypeOf(Person.prototype)===Object.prototype);//true
console.log(Object.getPrototypeOf(Object.prototype)===null);//true
1.3、补充:constructor。
- 上文提到,每个构造函数都是prototye属性,这个prototype属性还会自带有constructor属性指回构造函数本身,利用这一点我们可以用来判断变量数据类型
console.log((2).constructor === Number)
console.log((true).constructor === Boolean);
console.log(('帅哥'.constructor === String))
console.log(([1]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({1:1}).constructor === Object); // true
- 但是用constructor来判断数据类型最好还是不要用,因为有时候,如果创建一个对象来改变构造函数的原型,constructor就不能用来判断数据类型了
function Fn(){};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true