写在前面的总结:
JS当中创建一个对象有好几种方式,大体上就是以下几种:
①通过var obj ={...}
这种方式一般称为字面量方式,{}直接写需要定义的字段
②var obj = new Object()
Object对象是JS的内建对象
③通过构造函数创建对象
例如:
function father(){ this.name = "I'm your father!" } var f = new father();
这里通过构造函数的方式创建了一个对象实例f
④通过Object.create创建对象
Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的。
1.原型是什么?
首先应该知道我们创建的每一个函数都有一个prototype属性,这个属性是一个指向原型对象的指针,这个原型对象的用途是包含由特定类型所有实例共享的属性和方法。
也就是说,每创建一个函数,就会创建一个对应的原型对象。
一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。ECMA引入了标准对象原型访问器Object.getPrototype(object),到目前为止只有Firefox和chrome实现了此访问器。除了IE,其他的浏览器支持非标准的访问器__proto__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。
还是挺抽象,举个例子吧:
function Person(){ this.name="大橙子"; this.age = 26; this.sex = "纯爷们"; } var p = new Person(); console.log(p.name); console.log(p.age); console.log(p.sex); console.log(p.career); Person.prototype.career = "程序员"; console.log(p.career); 输出结果: [Web浏览器] "大橙子" [Web浏览器] "26" [Web浏览器] "纯爷们" [Web浏览器] "undefined" [Web浏览器] "程序员"
上面Person.prototype.career = "程序员";就是通过原型来添加的属性,所有继承这个Person的对象,都具有career这个属性。
这个过程是这样的,当输出p.career的时候,首先会在对象p找,因为p对象没有career这个属性,这就是为什么会输出undefined的原因,然后继续向上查找,在Person.prototype中找到了career,然后就输出了“程序员”。
要了解原型的概念,应该知道_proto_、prototype以及constructor他们之间是什么联系
那么就来分析一下var p = new Person(); 这个过程
上面这个分析图可以很好的展示,三者的关系,
首先要知道几个地方,
第一点:就是Empty()函数,这个函数是所有函数的原型,并且他很特殊没有prototype
第二点:__proto__是内部原型,prototype是构造器原型(构造器其实就是函数,所以上面才说这个是函数对象才有的,而_proto_是每个对象都有的。我的理解它才是构成原型链的原因)
①.从中间开始看,首先我创建了一个函数person(),同时JS帮我创建了一个原型对象person.prototype
并把我的person()的prototype指针指向了它,原型对象中有一个constructor属性,指向了我的person()函数。
②.当我new一个person实例的时候,新创建的实例对象p的_proto_指向了person.prototype
这也是为什么,我在person.prototype中添加属性,p也能反映出来的根本原因。
③.person.prototype也是一个对象,那么他的_proto_是谁呢,如上图就是Object.prototype,这也不难理解,因为Object是Javascript中所有对象的父级对象,我们创建的所有对象都继承于此,包括内建对象。同样因为它也是对象,那么它也拥有_proto_,图上表示了它的原型就是null
我们可以打印每一属性的值来进行确认:
//Person函数的原型对象 console.log(Person.prototype); //Person原型对象的Constructor console.log(Person.prototype.constructor); //判断实例对象p的原型,是不是Person.prototype console.log(p.__proto__ === Person.prototype?true:false); //函数对象Person的_proto_,是不是Empty() console.log(Person.__proto__); //Empty()是不是没有prototype console.log(Person.__proto__.prototype); //Empty()的__proto__是不是Object.prototype console.log(Person.__proto__.__proto__ === Object.prototype?true:false); //Person.prototype的_proto_是不是Object.prototype console.log(Person.prototype.__proto__ === Object.prototype?true:false); //Object.prototype的_proto_是不是null console.log(Object.prototype.__proto__); 输出结果: [Web浏览器] "[object Object]" [Web浏览器] "function Person(){ this.name="大橙子"; this.age = 26; this.sex = "纯爷们"; }" [Web浏览器] "true" [Web浏览器] "function Empty() {}" [Web浏览器] "undefined" [Web浏览器] "true" [Web浏览器] "true" [Web浏览器] "null"
2.原型链
上面的原型我们举的例子没有特别明显的显示这个链的过程,看不到具体的继承关系,所以再分析一个例子
Object.prototype.dead = true; function animal(){} animal.prototype.eat=true; animal.prototype.sleep=true; function bird(){ this.fly = true; } bird.prototype = new animal(); var a = new animal(); var b = new bird(); console.log(a.fly); console.log(a.eat); console.log(a.sleep); console.log(a.dead); console.log(b.fly); console.log(b.eat); console.log(b.sleep); console.log(b.dead); 测试输出: [Web浏览器] "undefined" [Web浏览器] "true" [Web浏览器] "true" [Web浏览器] "true" [Web浏览器] "true" [Web浏览器] "true" [Web浏览器] "true" [Web浏览器] "true"
下面我再画图分析一下这个
①下图是在bird.prototype = new animal();之前,创建好函数之后的状态
②继续执行程序
从上图就可以明显看到
实例对象b->bird.prototype->animal.prototype->Object.prototype->null有一个链式的继承关系
也就是为什么
console.log(b.eat);
console.log(b.sleep);
console.log(b.dead);
会打印出来true的原因了。