本文讲解JS的原型和原型链。JavaScript是如何通过一个构造函数生成对象实例的?JavsScript是如何读取对象的属性的?
1、构造函数
通常用构造函数来创建具有一定统一性的对象。可以通过new
操作符创建一个实例对象。
/*
工厂生产酸奶可以分成三部;
1、原料 2、加工 3、出厂
【注】满足上述三个步骤的函数,我们把它叫做工厂函数。
在JavaScript中我们可以创建和使用这样的构造函数来创建具有一定的统一性的对象。
*/
function Person(name, sex){
//1、原料
// var person = new Object();
//this = new Object();
//2、加工
//添加属性和方法
this.name = name;
this.sex = sex;
this.showName = function(){
alert("我叫" + this.name);
}
this.showSex = function(){
alert("我是" + this.sex + "的");
}
//3、出厂
// return person;
//return this;
}
var p1 = new Person("blue", "男");
p1.showName(); //我叫blue
p1.showSex(); //我是男的
var p2 = new Person("red", '女');
p2.showName(); //我叫red
p2.showSex();; //我是女的
alert(p1.showName == p2.showName); //false
2、实例
通过构造函数生成的对象就叫做实例。上面例子的 p1
和 p2
都是通过 Person()
这个构造函数。当使用构造函数new
一个实例的时候,这个函数里面的this指向当前创建的新对象。上面例子中,第一步原料和第三部出场被注掉的原因就是,当时用 new
调用函数的时候,JS会自动完成1、3两步操作。
使用new操作符创建对象的过程:第一步,创建一个新对象。第二步,将构造函数的 this 指向这个新创建的对象(这样就相当于把构造函数的作用域赋给了新对象)。第三步,执行这个构造函数的代码,继承属性。第四步,返回一个新的对象。
3、原型(prototype
)
每一个构造函数都有一个 prototype
属性,这个属性指向一个对象,这个对象就是通过这个构造函数创建的实例的原型。
每一个 JavaScript
对象(或者说实例, null
除外)在创建的时候都会和 prototype
指向的对象相关联,并从上面继承属性。
/*
【注】如果我们想给一类的对象,添加方法,那么我们可以通过原型prototype,
将这个方法添加给这个对象的构造函数。
*/
Array.prototype.sum = function(){
var res = 0;
for(var i = 0; i < this.length; i++){
res += this[i];
}
return res;
}
var arr1 = [10, 20, 30, 40, 50];
var arr2 = [1, 2, 3, 4, 5];
alert(arr1.sum());
alert(arr2.sum());
alert(arr1.sum === arr2.sum); //true
4、原型链
既然每一个JS对象在创建的时候都会关联一个原型对象,而且原型对象也是一个JS对象,所以原型对象在创建的时候也同样会关联一个原型对象的原型对象。这样一层层的追根溯源,就形成了一条原型链。
用上面的例子解释就是, arr1
的原型是Array的 prototype
属性,而Array的 prototype
属性指向的也是一个对象,这个对象也是某一个构造函数的实例(比如Object()
),所以Array的 prototype
属性指向的对象(即arr1
的原型)也会有一个原型,它的原型就是生成它的构造函数的prototype
属性。这样一条原型链就生成了。
“继承”的说法。众所周知,对象会继承构造函数的属性,但是此继承非彼继承,原因就是,他并不是真正的继承,而是在创建的对象和构造函数之间会建立一种关联,这些关联组成一个集合,我们现在可以把这个集合理解成原型链,对象与构造函数的原型都是在这条长长的原型链上,它们一环扣一环,当我们在访问某一个对象的属性的时候,首先判断的是这个对象的直接挂载属性有没有,如果没有就会去原型链的上一环(也就是该对象的__proto__
)寻找这个属性,如果还没有就会继续访问当前环的上一环,知道找到这个属性或者是走到这个链子的尽头为止。
访问属性,会一环一环的向上查找,但是原型链不是无限长的,它的终点就是Object.prototype
。因为Object.prototype
的下一环是null
。
alert(Object.prototype.__proto__ === null) // true
当访问到Object.prototype
还没有找到该属性的时候,就会报错undefined
。
5、__proto__
每一个 JavaScript
对象(或者说实例, null
除外)都有一个属性,即__proto__
,它指向的就是该对象的原型(即生成该对象的构造函数的 prototype
属性)。
alert(arr1.__proto__ == Array.prototype) //true
既然实例的属性都是继承自构造函数,那么所有的对象都有的属性__proto__
是承自哪里呢?嘿嘿,既然是所有的对象都有的属性,当然是承自Object.prototype
。
与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)。
6、constructor
constructor
的英文意思是构造器,每一个原型都会有这么一个属性指向与之相关联的构造函数。
alert(Array == Array.prototype.constructor) //ture
附图一张:
(蓝色的线就是一条原型链)