JavaScript中有基本类型和复杂类型的区分。当我们在声明一个基本类型时:var n1= 1;console.log(n1);//1这时我们可以用Number方法将1包装为对象,即声明一个对象1。var n2= new Number(1);console.log(n2);//Number {1}//[[PrimitiveValue]]:1//△proto: Number//constructor: ƒ Number()//toExponential: ƒ toExponential()//toFixed: ƒ toFixed()//toLocaleString: ƒ toLocaleString()//toPrecision: ƒ toPrecision()//toString: ƒ toString()//valueOf: ƒ valueOf()//proto: Objectn2的PrimitiveValue(初始值)为1,其实此时它就是一个hash。此时对象n2现在有很多方法,可以调用对应各自的函数。但是,我们发现有一点:在我们直接声明基本数据类型n1时,也可以使用这些方法调用函数。比如toString方法:var n1= 1;n1.toString();//“1"这就要涉及到JavaScript的发明历史。在设计之初,它被要求:“长得像” Java。当时的Java声明一个对象,就是如此:var n= new Number(1)设计师在设计的同时,为了简便快捷,也制定了我们最常用的声明方式:var n= 1当然,这种方法肯定被JavaScript程序员所喜爱,第一种方法几乎没有人用。但是有一个问题,如果直接声明n,那么它就是一个数字。而基本数据类型是没有属性的。这时候该如何调取各种方法呢?声明一个临时对象,我们暂将它称为temp。比如在对n进行toString方法引用时,会声明一个临时对象,对n进行复杂封装,将其变为对象。var n= 1;n.toString();其实是:var temp= new Number(n);temp.toString();//1将temp.toString的值赋予n.toString。在操作结束后,临时对象将从内存中抹除。共用属性number,string,boolean,object都有一些共同的方法,比如toString,valueof等等。为了避免重复声明和内存浪费,这些方法“归纳”与一个共用属性之中。JavaScript在声明一个对象后,并不是先默认复制一遍共用属性在内存中的存放地址,再在引用时调取对应的函数。而是:在声明对象时,就存入一个隐藏的属性:proto。该属性,对应其共用属性。var obj= { name: ‘Jack’, age: 18};console.log(obj);//△{name:“Jack”, age:18} //age: 18 //name: “Jack” //△proto: Object //constructor: ƒ Object() //hasOwnProperty: ƒ hasOwnProperty() //isPrototypeOf: ƒ isPrototypeOf() //propertyIsEnumerable: ƒ propertyIsEnumerable() //toLocaleString: ƒ toLocaleString() //toString: ƒ toString() //valueOf: ƒ valueOf() //defineGetter: ƒ defineGetter() //defineSetter: ƒ defineSetter() //lookupGetter: ƒ lookupGetter() //lookupSetter: ƒ lookupSetter() //get proto: ƒ proto() //set proto: ƒ proto()那我们在调用toString方法时:JavaScript首先会检查数据类型是否是对象;若不是,则包装为对象作临时的转换。然后,在检查对象中是否有toString这个属性;若没有,才进入共用属性进行检查。两个空对象是不相等的,但他们的共有属性是相等的。var obj1= {};console.log(obj1);//{}var obj2= new Object();console.log(obj2);//{}obj1 === obj2;//falseobj1.proto === obj2.proto;//true但是,当我们声明一个number为对象时,它就有自己区别于普通对象的独特的共有属性。比如toFixed,toExponential等等。那么它的__proto__属性就对应自己独有的共同属性,在共同属性中还有另一个隐藏属性__proto__对应一般对象的共有属性。这样,number类型的对象就可以调用所有的函数。原型与原型链这里,就引入了两个新的概念。那么,这个共有属性,就被称为原型(对象)。原型对象就是用来存放声明对象中共有的那部分属性。JavaScript中所有的对象都可以继承其原型对象的属性。而原型对象自身也是一个对象,它也有自己的原型对象。这样层层上溯,就形成了一个类似链表的结构,这就是原型链。为了避免对原型对象在没有被使用时被内存清理,JavaScript通过prototype来默认引用。Object.prototypeObject.prototype;//constructor: ƒ Object()//hasOwnProperty: ƒ hasOwnProperty()//isPrototypeOf: ƒ isPrototypeOf()//propertyIsEnumerable: ƒ propertyIsEnumerable()//toLocaleString: ƒ toLocaleString()//toString: ƒ toString()//valueOf: ƒ valueOf()//defineGetter: ƒ defineGetter()//defineSetter: ƒ defineSetter()//lookupGetter: ƒ lookupGetter()//lookupSetter: ƒ lookupSetter()//get proto: ƒ proto()//set proto: ƒ proto()Number.prototypeNumber.prototype;//constructor: ƒ Number()//toExponential: ƒ toExponential()//toFixed: ƒ toFixed()//toLocaleString: ƒ toLocaleString()//toPrecision: ƒ toPrecision()//toString: ƒ toString()//valueOf: ƒ valueOf()//proto: Object//[[PrimitiveValue]]: 0如此,还有:String.prototypeBoolean.prototype等等。Object.prototype.proto === null我们制作一个简单的示意图,以便更直观地理解这些概念。那么我们可以得到关系:var obj= {};obj.proto === Object.prototype;//truevar n= new Number(1);n.proto === Number.prototype;//truen.proto.proto === Object.prototype;//true总结起来,得到的结论就是:Object.prototype是Object的共用属性obj.__proto__是Object的共用属性的引用同理,对于String、Boolean、Symbol和Number也是如此