js的类型分为两大类,基本类型和引用类型,对应内存模型中的栈和堆,这也是OOP目前主流的分类方法(Java和C#),可能叫法上面有不同,js最常用的三种基本类型
let a = "China No.1";
let b = 99999999;
let c = true;
特点是值复制,使用const修饰会锁值,不能用成员操作符增加字段,具体可以参考我写的小白都能看懂的javascript内存模型,
console.warn("a.toString", a.toString());
console.warn("b.toString", b.toString());
console.warn("c.toString", c.toString());
乍一看好像会报错,实际是正常打印出结果,难道是因为使用了let产生了什么副作用,再试一下直接用字面值
console.warn("toString", "China No.1".toString());
console.warn("toString", (99999999).toString());
console.warn("toString", true.toString());
这回总该报错了吧,基本类型压根没有toString这个方法呀,然而顺利打印出结果,这回我想到是不是有类似的包装机制,查一下构造函数
console.warn("toString", "China No.1".constructor);
console.warn("toString", (99999999).constructor);
console.warn("toString", true.constructor);
果然是包装成对象,所以也就能调用原型中的方法了
demo.html:25 toString ƒ String() { [native code] }
demo.html:26 toString ƒ Number() { [native code] }
demo.html:27 toString ƒ Boolean() { [native code] }
如果展开constructor.prototype可以看到详细的信息
demo.html:31 toString String {"", constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, ...}
demo.html:32 toString Number {0, constructor: ƒ, toExponential: ƒ, toFixed: ƒ, ...}
demo.html:33 toString Boolean {false, constructor: ƒ, toString: ƒ, valueOf: ƒ}
但是又有疑问了,包装会不会改变类型,也就是说基本类型会不会因为调用toString()之后就变成引用类型了,还是说只是临时包装,我猜测应该是临时的
let a = "China No.1";
let b = 99999999;
let c = true;
console.warn("toString", a.toString());
console.warn("toString", b.toString());
console.warn("toString", c.toString());
console.warn("toString", a instanceof Object);
console.warn("toString", b instanceof Object);
console.warn("toString", c instanceof Object);
终端打印出了三个大大的false,也就是说包装并未改变对象类型,只是临时借用了原型中的方法罢了,三个对象都不是Object的实例,如果我非要使用包装类呢,肯定是不能直接用字面值赋值,唯一想到的是用构造方法
let a = new String("China No.1");
let b = new Number(99999999);
let c = new Boolean(true);
console.warn("toString", a.toString());
console.warn("toString", b.toString());
console.warn("toString", c.toString());
console.warn("toString", a instanceof Object);
console.warn("toString", b instanceof Object);
console.warn("toString", c instanceof Object);
这回输出是三个大大的true,也就是说如果基本类型的包装类需要用构造方法,本着刨根问底的精神,常用的创建对象的方法是用字面值{},这和用new加构造方法的区别是啥
const d1 = {
name: "韩宇",
dance: "hiphop & locking"
};
function Dancer(name, dance){
this.name = name;
this.dance = dance;
}
const d2 = new Dancer("亮亮", "hiphop");
表面上看没啥区别,都是存放两个字段,我们输出控制台
demo.html:33 d1 {name: "韩宇", dance: "hiphop $ locking"}
demo.html:34 d2 Dancer {name: "亮亮", dance: "hiphop"}
这就清楚了,d2的类型信息是Dancer而d1是默认的,也就是Object,进一步展开prototype
{constructor: ƒ}
constructor: ƒ Dancer(name, dance)
__proto__: Object
Dancer的原型是Object,或者说Dancer被绑定到了Object的原型上,Dancer向下扩展了Object的原型,而使用字面值是没有这种效果的
总结:使用字面值赋值的基本类型可以调用包装类的方法,但是要转成包装类要调用构造方法,不管是基本类型还是引用类型,被const修饰都不能重新赋值,也不能改变其类型,但是引用类型可以用"."增添字段,使用"."添加的字段和函数只对当前的对象生效,除非加在prototype里面,基本类型独占一个内存,每一次赋值都是独立的,引用类型多个对象可以共享一个内存,值对于每一个对象都是可读的,使用构造方法创建的对象独占一个prototype,而使用字面值则没有