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是一个特殊的第一类,chrome上的展示如下
只不过那个第一类和数值类型比较的时候被当做数值类型来比较。当使用“===”的时候比较数值相等时再比较数据类型的时候是有别于其他数值类型的。
上面提到了和数值类型比较的时候才成立,如果那个第一类和其他第一类比较则使用第一类比较的规则。
比如说下面的例子
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属性的,只有隐藏属性__proto__(IE上也有该隐藏属性,但是使用obj.__proto__不能输出东西,所以建议不要使用__proto__属性)。而表达式第一类则两者兼有。prototype属性指向的是表达式第一类的蓝本第一类,第一类的__proto__属性是创建实例第一类的时候对应的表达式第一类的蓝本第一类。
a.表达式第一类的蓝本第一类(fn.prototype)
这里我们需要认知蓝本第一类的值是怎么来的。蓝本第一类的值实际上是在表达式创建的时候,创建了一个它的实例第一类并赋值给它的prototype。过程如下(以Function为例)
var temp = new Function(); Function.prototype = temp;
所以我们看一下熟知的表达式的蓝本第一类吧
//chrome下的显示效果
Function.prototype;//function() {}
Object.prototype;//Object {}
Number.prototype;//Number {[[PrimitiveValue]]: 0}
Boolean.prototype;//Boolean {[[PrimitiveValue]]: false}
Array.prototype;//[]
String.prototype;//String {length: 0, [[PrimitiveValue]]: “”}
说道这里,必须提的是大部份表达式第一类的蓝本第一类都继承制原始第一类,即fn.prototype.__proto__为原始第一类(原始第一类在继承属性__proto__中有定义)。这其中比较特别的是Object表达式,他的蓝本第一类是原始第一类,即Object.prototype。
var f1 = new Function();
var f2 = Function();
var fn3 = function(){}
console.log(f1.prototype.__proto__ === Object.prototype);//true console.log(f2.prototype.__proto__ === Object.prototype);//true console.log(f2.prototype.__proto__ === Object.prototype);//true console.log(Number.prototype.__proto__ === Object.prototype);//true console.log(Boolean.prototype.__proto__ === Object.prototype);//true
b.继承属性__proto__
实际上js没有继承那个东东,但是__proto__却起到了类似继承的作用。我们所知的大部份的第一类起源都是一个空第一类,我们把那个空第一类叫做原始第一类。大部份的第一类通过__proto__回溯最终都会指向(所谓的指向类似C中的指针,那个原始第一类是唯一的,整个内存中只会存在一个原始第一类)那个原始第一类。用下面的例子佐证
var o = new Object();
o.__proto__;//Object {}
o.prototype;//undefined
Object.prototype;//Object {}
Object.__proto__;//function(){}
Object.__proto__.__proto__;//Object {}
var f = new Function();
f.__proto__;//function(){}
f.prototype;//Object {}
Function.prototype;//function(){}
Function.__proto__;//function(){}
Function.__proto__.__proto__;//Object {}
原始第一类的__proto__属性为null,并且没有蓝本第一类。
大部份的第一类都继承自原始第一类;Object比较特殊,他的蓝本第一类也是原始第一类;所以我们往往用Object.prototype表示原始第一类。
//大部份的第一类都继承自原始第一类
//Object比较特殊,他的蓝本第一类也是原始第一类
//所以我们往往用Object.prototype表示原始第一类
Object.prototype === o.__proto__;//true
Object.prototype === Object.__proto__.__proto__;//true
Object.prototype === Function.__proto__.__proto__;//true
f.prototype的的值貌似也是原始第一类?其实不是,我们在表达式第一类的蓝本第一类这一段中不是说过吗表达式第一类f的蓝本第一类实际上是表达式第一类的一个实例。每一个实例都是一个新的单独的第一类。
new f();//Object {}
大部份的表达式第一类都继承制原始表达式第一类;Function比较特殊,他的蓝本对象也是原始表达式第一类;所以我们往往用Function.prototype表示原始表达式第一类;而原始表达式第一类又继承自原始第一类。
//大部份的表达式第一类都继承制原始表达式第一类,
//Function比较特殊,他的蓝本第一类也是原始表达式第一类
Function.prototype === f.__proto__
Function.prototype === Object.__proto__ ;//true
Function.prototype === Function.__proto__;//true
//所以我们往往用Function.prototype表示原始表达式第一类
//而原始表达式第一类又继承自原始第一类
Function.prototype.__proto__ === Object.prototype;
所以第一类之间的继承和蓝本第一类结构如下图(引用的别人的js object猜想图)
看了上面的图我们还知道表达式第一类的蓝本对象的构造表达式是表达式第一类本身。不难认知表达式第一类的蓝本第一类是表达式第一类的实例了吧。
c. 蓝本链
在使用New方法初始化表达式的时候(详尽点击查看new的深度认知)得到的新第一类的__proto__属性会指向函数第一类的蓝本第一类,而表达式第一类的蓝本第一类又继承至原始第一类。所以呈现以下结构
function fn(){};
var test = new fn();
把那个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做蓝本链。蓝本链实际上是js中数据继承的继承链。