一、原型概述
任何对象都有一个原型对象,这个原型对象由对象的内置属性_proto_指向它的构造函数的prototyoe指向的对象,即任何对象都是由一个构造函数创建的,被创建的对象都可以获得构造函数的prototype属性,注意:对象是没有prototype属性,只有方法才有prototype属性。
任何对象都有一个constructor属性,指向创建此对象的构造函数,比如说{}对象,它的构造函数是function Object(){}。
function Person() {
}
var p = new Person();
//方法才有prototype,普通对象无prototype
console.log(Person.prototype); // Object{}
console.log(p.prototype); // undifined
//任何对象都是有构造函数constructor,由构造函数创建的对象也可以获得构造函数的引用
//此处只是打印下列对象的构造函数是什么。
console.log(p.constructor); //function Person(){}
console.log(Person.constructor); //function Function(){}
console.log({}.constructor); // function Object(){}
console.log(Object.constructor); // function Function() {}
console.log([].constructor); //function Array(){}
那什么是构造函数呢?
用function声明的都是函数,而如果直接调用的话,那么Person()就是一个普通函数,只有用函数new产生对象时,这个函数才是new出来对象的构造函数。
二、创建对象的过程
2.1 、声明方法的过程
首先,当我们声明一个function关键字的方法时,会为这个方法添加一个prototype属性,指向默认的原型对象,并且此prototype的constructor属性就是此方法。此二个属性会在创建对象时被对象的属性引用。
function Hello() {
}
console.log(Hello.prototype); // Object {} -- > 内部的constructor 指向Hello方法
console.log(Hello.prototype.constructor); // function Hello(){}
2.2、创建一个对象,从构造函数继承了什么属性?
console.log(h.constructor); // function Hello(){}
console.log(Object.getPrototypeOf(h)==Hello.prototype); // true 备注:getPrototypeOf是获取_proto_
我们惊喜的发现,new出来的对象,它的constructor指向了方法对象,它的_proto_和prototype相等。
即new一个对象,它的_proto_属性指向了方法的prototype属性,并且constructor指向了prototype的constructor属性。
由构造函数创建一个对象,此对象多了一个_proto_属性指向构造函数的prototype,一个constructor属性指向构造函数的prototype的constructor属性。
2.3 、创建一个对象的过程
function Hehe(name) {
this.name = name;
}
var h = new Hehe("笑你妹");
//伪代码:
function newObj(name){
var obj = {};
obj.__proto__ = Hehe.prototype;
obj.constructor = Hehe.prototype.constructor;
var result = Hehe.call(obj, name);
return typeof result==='object'&& result!=null ? result : obj; //当无返回对象或默认时返回obj。
}
var hh = newObj("笑你妹");
console.log(hh);
console.log(h);
//虽然hh!=h,但是可以看到这个hh就和h的结构一样了。
过程:先创建一个空对象,设置一个_proto_指向方法的原型,设置constructor,用新对象做this指向方法,返回新对象。
2.4、总结
从上面说明的过程中,我们发现只要是对象就是有构造函数来创建的,并且内部二个属性是从构造函数的prototype衍生的一个指向,而构造函数的prototype也是一个对象,那么它应该肯定也有一个构造函数,首先它是一个Object {} 对象,那么它的构造函数肯定是Object,所以就会有一个指针_proto_指向Object.prototype。最后Object.prototype因为没有_proto_,指向null,这样就构成了一个原型链。
三、原型链分析
什么是原型链?
原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有_proto_指向了。
如何分析原型链?
因为_proto_实质找的是prototype,所以我们只要找这个链条上的构造函数的prototype。其中Object.prototype是没有_proto_属性的,等于null。
3.1、最简单的原型链分析
function Person(name){
this.name = name;
}
var p = new Person();
//p ---> Person.prototype --->Object.prototype---->null
上面原型链的分析:p的构造函数是Person创建的,那么Person.prototype就是继承的第一个原型,而Person.prototype没有自定义设置,默认就是一个Object对象,即是Object构造函数创建的,那么就是继承了Object.prototype,而Object.prototype再往上就没有_proto_指向了,等于null。
属性搜索原则:
1.当访问一个对象的成员的时候,会现在自身找有没有,如果找到直接使用。
2.如果没有找到,则去原型链指向的对象的构造函数的prototype中找,找到直接使用,没找到就返回undifined或报错。
3.2、原型继承
//原型继承的基本案例
function Person(name, age) {
this.name = name;
this.age = age;
}
//1.直接替换原型对象
var parent = {
sayHello : function() {
console.log("方式1:替换原型对象");
}
}
Person.prototype = parent;
var p = new Person("张三", 50);
p.sayHello();
//2.混入式原型继承
console.log(".............混入式原型继承..............");
function Student(name, age) {
this.name = name;
this.age = age;
}
var parent2 = {
sayHello : function() {
console.log("方式2:原型继承之混入式加载成员");
}
}
for ( var k in parent2) {
Student.prototype[k] = parent2[k];
}
var p = new Student("张三", 50);
p.sayHello();
3.3 原型链案例
// 查询原型链上的对象的方法
function findProtoType(obj) {
var arr = [];
while (obj != null) {
obj = Object.getPrototypeOf(obj);
arr.push(obj);
}
return arr;
};
function Root() {}
function Child() {}
Child.prototype = new Root();
Child.prototype.constructor = Child; // 这个步骤是为了让原型对象打印显示成自身(继承prototype同时也继承了constructor,因此替换成自身)
function Item() {}
Item.prototype = new Child();
Item.prototype.constructor = Item;
var result = findProtoType(new Item());
console.log(result);
// [ Item { constructor: [Function: Item] },Child { constructor: [Function: Child] }, Root {}, {}, null ]
谢谢观看!
end!