Javascript原型钩沉

写在前面的总结:

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,这也不难理解,因为ObjectJavascript中所有对象的父级对象,我们创建的所有对象都继承于此,包括内建对象。同样因为它也是对象,那么它也拥有_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的原因了。

 

原文地址:https://www.cnblogs.com/dcz2015/p/5391971.html