欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javascript中的_proto_、constructor和prototype详解

程序员文章站 2024-03-13 12:40:45
...

首先,在JavaScript中,任何对象都有一个proto属性;任何方法都有prototype属性,指向一个对象,称为原型对象,这个对象有一个proto属性,另外还有一个constructor属性。

<script>
       function myFunc() {
       }
        var a=new myFunc();
        console.log(a);//a是一个对象
        console.log(myFunc.prototype);
    </script>

javascript中的_proto_、constructor和prototype详解
接下来,看prototype的作用,先来看代码及结果,再做解释

//实例1
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
//实例2
function a(){alert(1)}
a.prototype.constructor;//function a(){alert(1)}

var b={x:1}
a.prototype=b;//改变原型对象的值

var c=new a();

c.x;//1

从实例1中可以看出,利用函数的prototype属性可以添加对象实例所需要的属性信息,也就是说,给这个函数的prototype指针所指向的原型对象添加的方法和属性可以被这个函数的实例所继承(注:之所以成为函数的实例,是因为在JavaScript中,函数就是一个对象,当他通过new操作符调用的时候,就是一个构造函数,可以用来创建一个对象。所以此处也可以理解为对象的实例);从实例2中可以看出,prototype指针的值可以修改。当定义了一个构造函数之后,其原型对象默认只会取得constructor属性,其他的方法和属性都是从Object中继承而来。在实例2中,改变函数a的原型对象之后,a的实例c便可以访问到属性x,因为此时函数a的原型对象变为b,然后通过对象属性查找的过程,c在其构造函数的原型对象中找到属性x。
进一步理解原型对象(如下图片来自《JavaScript高级程序设计》以实例1为例)
javascript中的_proto_、constructor和prototype详解
这幅图展示了Person构造函数,Person的原型对象和Person的两个实例之间的关系。Person.prototype 指向了原型对象,而 Person.prototype.constructor 又指回了 Person。
原型对象中除了包含 constructor 属性之外,还包括后来添加的其他属性。 Person 的每个实例person1 和 person2 都包含一个内部属性,该属性仅仅指向了 Person.prototype。也就是说它们
与构造函数没有直接的关系。此外,要格外注意的是,虽然这两个实例都不包含属性和方法,但可以访问name,age,job属性和sayName方法,这就是通过查找对象属性的过程实现的。
虽然可以通过这一过程访问保存在原型中的属性和方法,但是却不能通过对象实例重写原型对象中的属性值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。这就是为什么同一对象的不同实例之间互不干扰的原因。当然,可以通过delete ObjectName.protetyName 的方式删除实例中的某个属性,而使该实例能够重新访问其构造函数原型对象中的同名属性。

接下来,我们再来看proto属性是什么?先来看个例子:
实例3:

    var obj1={};
    console.log(obj1.__proto__===Object.prototype);

从实例3中可以看出proto属性实际指向该对象的构造函数。再看下js中创建对象的三种方法:
实例4:

//1
var obj=new Object();
//2
var obj1={};
var obj2= Object.create(obj1);
//3
var obj={};

实际上,以上三种方法创建的对象都是通过new+构造函数,顺便解释下new一个对象的原理:首选创建一个空对象给变量obj,将obj的proto属性指向Object()方法的prototype对象,然后执行Object.call(obj)来构建对象obj。
总结一下:js中申明的方法实际上就是一个对象,其构造函数就是它自己,我们可以向它的prototype对象中设置各种属性和方法(即对象),而这些对象可以它的所有实例继承,所有的实例都有一个proto属性指向该构造函数。
现在,我们还要继续探讨proto属性,有这样一个概念:proto属性的作用主要是用来确定当前对象的继承者,在当前对象找不到指定的属性和对象时,会去proto属性指定的对象中寻找,之后依次类推直到找完所有继承或找到要找的属性为止。要解释这句话先来看这个例子。
实例5:

var Dog = function(){};
Dog.prototype.belongto = 'animal';
var wangwang= new Dog();
console.log(wangwang.belongto); //animal

在实例5中,当wangwang对象访问属性belogto时,找不到这个属性,于是去他的proto属性指向的prototype对象中找,之前说过wangwang.proto=Dog.prototype,所以能输出animal。其实这也就是js中原型链的概念,因为在js中没有父类和子类的概念,也就没有子类对父类属性和方法的继承, 因而js中的集成是通过原型链来集成的,每个函数都有prototype属性,可以作为内存块共享,在每个实例*享数据。