I-第3章 原生函数
学习原生函数,封装对象包装,拆分。
常用的原生函数
• String()
• Number()
• Boolean()
• Array()
• Object()
• Function()
• RegExp()
• Date()
• Error()
• Symbol()
原生函数可以被当作构造函数来使用。通过构造函数(如new String("abc"))创建出来的是封装了基本类型值(如"abc")的封装对象。
var a = new String( "abc" );
typeof a; // 是"object",不是"String"
a instanceof String; // true
Object.prototype.toString.call( a ); // "[object String]"
内部属性class
所有 typeof 返回值为 "object" 的对象(如数组)都包含一个内部属性Class。这个属性无法直接访问,通过 Object.prototype.toString(..) 来查看。
Object.prototype.toString.call( [1,2,3] ); // "[object Array]"
Object.prototype.toString.call( /regex-literal/i ); // "[object RegExp]"
Object.prototype.toString.call( null ); // "[object Null]"
Object.prototype.toString.call( undefined ); // "[object Undefined]"
Object.prototype.toString.call( "abc" ); // "[object String]"
Object.prototype.toString.call( 42 ); // "[object Number]"
Object.prototype.toString.call( true ); // "[object Boolean]"
封装对象包装
由于基本类型值没有.length和.toString()这样的属性和方法,需要通过封装对象才能访问。JS会自动为基本类型包装一个封装对象。
var a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"
一般情况下,不需要直接使用封装对象。最好的办法是让 JavaScript 引擎自己决定什么时候应该使用封装对象。
如果要自行封装基本类型值,可以用Object()函数(不带new关键字)
var a = "abc";
var b = new String( a );
var c = Object( a );
typeof a; // "string"
typeof b; // "object"
typeof c; // "object"
b instanceof String; // true
c instanceof String; // true
Object.prototype.toString.call( b ); // "[object String]"
Object.prototype.toString.call( c ); // "[object String]"
拆封
要拆封封装对象中的基本类型值,可以用valuesOf()函数。
var a = new String( "abc" );
var b = new Number( 42 );
var c = new Boolean( true );
a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true
在需要用到封装对象中的基本类型值的地方会发生隐式拆封。
var a = new String( "abc" );
var b = a + ""; // b拥有了拆封后的值abc
typeof a; // "object"
typeof b; // "string"
原生函数作为构造函数
-
数组
永远不要创建和使用空单元数组。
var a = new Array( 3 ); var b = [ undefined, undefined, undefined ]; var c = []; c.length = 3; -
Object(..)、Function(..) 和 RegExp(..)
不建议使用Object和Function
强烈建议使用常量形式(如 /^a*b+/g)来定义正则表达式,这样不仅语法简单,执行效率也更高,因为 JavaScript 引擎在代码执行前会对它们进行预编译和缓存。 与前面的构造函数不同,RegExp(..) 有时还是很有用的,比如动态定义正则表达式时:
var name = "Kyle"; var namePattern = new RegExp( "\\b(?:" + name + ")+\\b", "ig" ); var matches = someText.match( namePattern );上述情况在JavaScript编程中时有发生,这时new RegExp("pattern","flags")就能派上用场。
-
Date(..) 和 Error(..)
创建日期必须使用
new Date()。 Date(..) 主要用来获得当前的 Unix 时间戳(从 1970 年 1 月 1 日开始计算,以秒为单位)。 该值可以通过日期对象中的 getTime() 来获得。用
Date.now()即可获取创建错误对象(error object)主要是为了获得当前运行栈的上下文(大部分 JavaScript 引擎通过只读属性 .stack 来访问)。 栈上下文信息包括函数调用栈信息和产生错误的代码行号, 以便于调试(debug)。
function foo(x) { if (!x) { throw new Error( "x wasn’t provided" ); } // .. }通常错误对象至少包含一个 message 属性,有时也不乏其他属性(必须作为只读属性访问),如 type。 除了访问 stack 属性以外,最好的办法是调用toString() 来获得经过格式化的便于阅读的错误信息。
-
Symbol
可以使用 Symbol(..) 原生构造函数来自定义符号。但它比较特殊,不能带 new 关键 字,否则会出错:
var mysym = Symbol( "my own symbol" ); mysym; // Symbol(my own symbol) mysym.toString(); // "Symbol(my own symbol)" typeof mysym; // "symbol" var a = { }; a[mysym] = "foobar"; Object.getOwnPropertySymbols( a ); // [ Symbol(my own symbol) ] -
原生原型
原生构造函数有自己的 .prototype 对象,如 Array.prototype、String.prototype 等。
这些对象包含其对应子类型所特有的行为特征。
Function.prototype 是一个空函数,RegExp.prototype 是一个“空”的正则表达式(无任何匹配),而 Array.prototype 是一个空数组。对未赋值的变量来说,它们是很好的默认值。
function isThisCool(vals = Array.prototype,fn = Function.prototype,rx = RegExp.prototype) { return rx.test( vals.map( fn ).join( "" ) ); } isThisCool(); // true isThisCool( ["a","b","c"], function(v){ return v.toUpperCase(); }, /D/ );这种方法的一个好处是 .prototype 已被创建并且仅创建一次。相反,如果将 []、function(){} 和 /(?:)/ 作为默认值, 则每次调用 isThisCool(..) 时它们都会被创建一次 (具体创建与否取决于 JavaScript 引擎,稍后它们可能会被垃圾回收),这样无疑会造成内存和 CPU 资源的浪费。