原型与原型链的详细理解
js中的对象分为两种:普通对象object和函数对象function。
function fn1(){};
var fn2 = function(){};
var fn3 = new Function();
var fn4 = Function();
var obj1 = new fn1();
var obj2 = {};
var obj3 = new Object();
var obj4 = Object();
console.log(typeof fn1);//function
console.log(typeof fn2);//function
console.log(typeof fn3);//function
console.log(typeof fn4);//function
console.log(typeof obj1);//object
console.log(typeof obj2);//object
console.log(typeof obj3);//object
console.log(typeof obj4);//object
还有就是所有的构建函数比如Function、Object、Number等等都是函数对象,这个共知的。
//所有的构建函数都是function类型的
console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof Number);//function
所有的这类构建函数使用new或者直接调用方式都能构建出一个新的数据类型。但是构建出来的数据有区别的。
var o = new Object(),
o1 = Object();
console.log(o == o1);//false
console.log(o === o1);//false
var f = new Function(),
f1 = Function();
console.log(f == f1);//false
console.log(f === f1);//false
var a = new Array(),
a1 = Array();
console.log(a == a1);//false
console.log(a === a1);//false
var n = new Number(),
n1 = Number();
console.log(n == n1);//**********************true
console.log(n === n1);//false
var s = new String(),
s1 = String();
console.log(s == s1);//**********************true
console.log(s === s1);//false
var b = new Boolean(),
b1 = Boolean();
console.log(b == b1);//**********************true
console.log(b === b1);//false
//数据类型null/undefined是没有构造函数的
上面的例子中Object/Function/Array的对比结果都好说,因为他们构建出来的都是新的对象,对象比较是要比较根源(数据是否是同一个)。无论是使用new还是直接调用生成一个新的对象是要开辟新的空间存储的,不会和任何一个对象相等。但是对于数值类型,比较符“==”只是比较值相等,比较符"==="除了比较值以外还要比较数据类型。
我们以Number为例。new Number()构建出来的变量n是一个特殊的对象和数值类型比较的时候被当做数值类型来比较。当使用“===”的时候比较数值相等时再比较数据类型的时候是有别于其他数值类型的。
var num = new Number(0);
var str = new String(0);
var str1 = String(0);
console.log(num == str);//false
console.log(num == str1);//true
console.log(num === str1);//false
num和str都是比较特殊的对象,str1为数值类型。num和str比较实用对象比较的规则来,num和str1比较实用数值比较的规则来。
构造函数,实例,原型与原型对象之间的关系:
每创建一个函数,该函数都会自动带有一个prototype属性。该属性是一个指针,指向一个对象,该对象称之为原型对象(后期我们可以使用这个原型对象帮助我们在js中实现继承)。 原型对象上默认有一个属性constructor,该属性也是一个指针,指向其相关联的构造函数。 通过调用构造函数产生的实例对象,都拥有一个内部属性prototype,指向了原型对象。其实例对象能够访问原型对象上的所有属性和方法。
三者的关系是,每个构造函数都有一个原型对象,原型对象上包含着一个指向构造函数的指针,而实例都包含着一个指向原型对象的内部指针。通俗的说,实例可以通过内部指针访问到原型对象,原型对象可以通过constructor找到构造函数。
如上图,p1 ,p2的的实例都有Person的属性和方法,并且prototype都指向原型对象,p1\p2共享prototype原型对象的属性和方法,各自的constructor都指向Peson,这便是构造函数、实例、原型(对象)三者的关系。
普通对象是没有prototype属性的,只有隐藏属性__proto__(IE上也有该隐藏属性,但是使用obj.__proto__不能输出东西,所以建议不要使用__proto__属性)。而函数对象则两者兼有。prototype属性指向的是函数对象的原型对象,对象的__proto__属性是创建实例对象的时候对应的函数对象的原型对象。
原型链
所有的实例都有一个内部指针指向他的原型对象,并且可以访问到原型对象上的所有属性和方法。person实例对象指向了People的原型对象,可以访问People原型对象上的所有属性和方法。如果People原型对象变成了某一个类的实例aaa,这个实例又会指向一个新的原型对象AAA,那么person此时能访问aaa的实例属性和AAA原型对象上的所有属性和方法了。同理新的原型对象AAA碰巧又是另外一个对象的实例bbb,这个对象实例指向原型对象BBB,那么person就能访问bbb的实例属性和BBB原型上的属性和方法了。
当重写了Woman.prototype指向的原型对象后,实例的内部指针也发生了改变,指向了新的原型对象,然后就能实现类与类之间的继承了。
function People(){
this.type='人'
}
People.prototype.showType=function(){
alert(this.type);
}
function Woman(){
this.sex='女';
this.age=34;
}
Woman.prototype=new People();
var w=new Woman();
console.log('大家好,我的种类是:'+w.type+",我的年龄是:"+w.age+",我的性别是:"+w.sex);
//输出结果:
//大家好,我的种类是:人,我的年龄是:34,我的性格是:女
//w.type是People上面定义的type
解释一下以上代码.以上代码,首先先定义了People构造函数,通过new People()得到实例,会包含一个实例对象type和一个原型属性showType。另外定义一个Woman构造函数,然后情况发生变化,本来构造函数woman的prototype会执行Woman的原型对象,但是我们这里稍有改变,将Woman构造函数的prototype指向了People实例对象覆盖了woman的原型对象。当Woman的实例对象woman去访问type属性时,js首先在woman实例属性中查找,发现没有定义,接着去woman的原型对象上找,woman的原型对象这里已经被我们改成了People实例,那就是去People实例上去找。先找People的实例属性,发现没有type,最后去People的原型对象上去找,终于找到了。这个查找就是这么一级一级的往上查找。