原生函数是什么
JavaScript的原生函数也叫内建函数
例如 String(),Number(),Boolean()等
原生函数可以被当做构造函数来使用
但通过构造函数(如new String("abc"))创建出来的是封装了基本类型值("abc")的封装对象
var abc = new String("abc");
console.log(typeof abc); //object 不是string
[[class]]
[[class]]是所有typeof返回值为"object"的对象的一个内部属性(函数的typeof返回值为"function"),这个属性无法被直接访问
但可以通过Object.prototype.toString(..)来查看,可以把它看做是一个内部的分类
Object.prototype.toString.call(abc);//[object String]
多数情况下,对象内部的[[class]]属性和创建改对象的内建原生构造函数相对应
Object.prototype.toString.call([1, 2, 3]); //[object Array]
但并非总是如此
Object.prototype.toString.call(undefined); //[object Undefined]
可以看到undefined不存在Undefined()这样的原生构造函数,但是[[class]]仍然是"Undefined"(null的[[class]]是"Null")
而其他的基本类型则存在一个被称为包装(boxing)的行为
Object.prototype.toString.call("123"); //[object String]
Object.prototype.toString.call(123); //[object Number]
Object.prototype.toString.call(true); //[object Boolean]
上例中基本类型被各自的封装对象自动包装,所以它们的内部[[Class]]值分别为"String","Number","Boolean"
封装对象包装
由于基本类型没有.length这样的属性和.toString()这样的方法
需要通过封装对象才能访问,此时JavaScript会自动为基本类型值包装(box或者wrap)一个封装对象
var a = "abc";
a.length; //3
想要自行封装基本类型值可以用Object(..)函数(不加new)
拆封
由于封装对象是一个对象,所以不管基本类型值是什么,进行判断的时候返回的都是一个真值
当我们需要得到封装对象中的基本类型值时,可以用valueOf(..)函数
var a = Object("abc");
console.log(a.valueOf());//abc
在需要用到封装函数中的基本类型值的时候会发生隐式解封
var a = Object("abc");
var b = a + "";
console.log(b, typeof b); //abc string
原生函数作为构造函数
使用Array(..)构造一个数组和直接声明一个数组的效果是一样的,创建的值都是通过封装对象来包装
使用Array(..)构造函数时候带new和不带new效果是一样的,不带new的时候会自动补上
只带一个数字参数的时候,该参数会被当做预设长度,而不是当做数组中的一个元素
如果一个数组没有任何单元,但是length属性却显示有,这样会出现空单元的情况
var a = new Array(3);
console.log(a); //(3) [empty × 3]
我们将包含至少一个空单元的数组称之为稀疏数组
在某些方法中空单元的行为和undefined类似,但在另一些方法中又完全不同
var a = new Array(3);
var b = [undefined, undefined, undefined];
console.log(a); //(3) [empty × 3]
console.log(b); //(3) [undefined, undefined, undefined]
console.log(
a.join("-"), //--
b.join("-") //--
);
console.log(
a.map(function(value, index) {
return index;
}), //(3) [empty × 3]
b.map(function(value, index) {
return index;
}) //(3) [0, 1, 2]
);
这是因为join方法会先假定数组不为空,然后通过length来遍历
而map方法则不会
由于空单元存在的种种问题,因此我们不建议在任何情况下创建和使用空单元
我们可以用另一种方法来更安全创建undefined值的数组
var a = Array.apply(null, {
length: 3
});
console.log(a); //(3) [undefined, undefined, undefined]
Array.apply把{length:3}作为参数调用Array()方法
在传入参数时假设apply有个for循环来遍历传入的类数组对象
从0开始循环到length
但是由于传入的类数组对象中并没有[0],[1],[2]等属性,于是返回的是undefined
因此实际上执行的就变成了Array(undefined,undefined,undefined)
原生函数的原型
原生构造函数也有自己的.prototype对象,如Array.prototype
这些对象包含其子对象所特有的行为特征
比如String.prototype.indexOf(..)
根据文档约定,这类方法一般简写为String#indexOf(..)
而原生的函数的原型同样也是不错的默认值
因为类似于Array.prototype已经被创建过一次了
如果将默认值设定为 [] 的话会导致资源的浪费,因为每次使用都需要创建一次
但是如果默认值随后会改变的话,就不要使用这种方法
因为修改原型会导致很多问题