学习JavaScript“原生函数”

原生函数

常用的原生函数:

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol()

使用原生函数当作构造函数:

var str = new String('hello');

typeof str;  //"object"                       

str instanceof String;  //true

Object.prototype.toString.call(str); //"[object String]"
复制代码

通过构造函数(如new String(..))创建出来的是封装了基本类型值(如hello)的封装对象。

请注意:typeof在这里返回的是对象类型的子类型。

console.log(str);  //String {"hello world"}
复制代码

通过原生函数创建的是封装对象,而非基本类型。

内部属性 [[Class]]

所有typeof返回值为"object"的对象(如数组)都包含一个内部属性[[Class]],这个属性无法直接访问,一般通过 Object.prototype.toString(..) 来查看。

Object.prototype.toString.call([1,2,3]);  //"[object Array]"
复制代码

多数情况下,对象的内部[[Class]]属性和创建该对象的内建原生构造函数相对应。

基本类型的情况稍微不同:

Object.prototype.toString.call(null);   //"[object Null]"

Object.prototype.toString.call(undefined);  //"[object Undefined]"

Object.prototype.toString.call("hello"); //"[object String]"

Object.prototype.toString.call(2019); //"[object Number]"

Object.prototype.toString.call(true); //"[object Boolean]"
复制代码

虽然Null()Undefined()这样的原生构造函数并不存在,但是内部[[Class]]属性值仍然是"Null""Undefined"

其他基本类型值(如字符串、数字和布尔)通常被各种的封装对象自动包装,所以它们的内部[[Class]] 属性值分别为"String""Number""Boolean"

封装对象包装

封装对象有着十分重要的作用。由于基本类型值没有.length,valueOf().toString()这样的属性和方法,需要通过封装对象才能访问,但是JavaScript会自动帮我们为基本类型值进行包装。

var foo = "hello";

foo.length; //5

foo.toUpperCase(); //"HELLO"
复制代码

使用封装对象时有些地方需要特别注意。

var foo = new Boolean(false);

if(!foo){
    console.log('ok');
}
复制代码

上面代码打印不会被输出,因为其实我们是给false创建一个封装对象,这个对象是一个真值。

如果想要自行封装基本类型值,可以使用Object(..)函数:

var foo = "ok";
var bar = new String(foo);
var baz = Object(foo);

typeof foo; // "string"
typeof bar; // "object"
typeof baz; // "object"

bar instanceof String;   //true
baz instanceof String;   //true

Object.prototype.toString.call(bar); // "[object String]"
Object.prototype.toString.call(baz); // "[object String]"
复制代码

拆封

如果想要得到封装对象中的基本类型值,可以使用valueOf()函数:

var number = new Number(2019);
var str = new String('ok');
var bool = new Boolean(true);

number.valueOf();  //2019
str.valueOf();  //"ok"
bool.valueOf();  //true
复制代码

还有一个需要注意的就是隐式拆封:

var str = new String('ok');

var foo = str + '';

typeof str;   //"object"
typeof foo;   //"string"    
复制代码

原生函数作为构造函数

Array(..)

Array构造函数只带一个数字参数的时候,该参数会被作为数组的预设长度(length),而 非只充当数组中的一个元素。使用Array构造函数时也可以省略new操作符。

var foo = new Array("t","o","m");
foo; //["t", "o", "m"]

var baz = Array("t","o","m"); 
baz; //["t", "o", "m"]

var bar = new Array(10);
bar; //[empty × 10]
复制代码

这样的结果也会带来一些问题:

 var a = new Array(3);
 var b = [ undefined, undefined, undefined ];
 var c = [];
 c.length = 3;
 var d = [ , , , ];
 
 console.log(a);   //[empty × 3]
 console.log(b);   //[undefined, undefined, undefined]
 console.log(c);   //[empty × 3]
 console.log(d);   //[empty × 3]    
复制代码

b 在当前版本的 Chrome 中显示为 [ undefined, undefined, undefined ],而 acd 则显示 为 [ empty x 3 ]d实际得到的是[ , , ]包含三个空单元的数组。

更糟糕的是,上例中 a 和 b 的行为有时相同,有时又大相径庭:

a.join( "-" ); // "--"
b.join( "-" ); // "--"

a.map(function(v,i){ return i; }); // [ undefined x 3 ]
b.map(function(v,i){ return i; }); // [ 0, 1, 2 ]
复制代码

a.map(..) 之所以执行失败,是因为数组中并不存在任何单元,所以 map(..) 无从遍历。而 join(..) 却不一样,join(..) 首先假定数组不为空,然后通过 length 属性值来遍历其中的元 素。而 map(..) 并不做这样的假定,因此结果也往往在预期之外,并可能导致失败。

我们可以通过下述方式来创建包含 undefined 单元(而非“空单元”)的数组:

var foo = Array.apply(null, { length: 3 });
foo; //[undefined, undefined, undefined]
复制代码

Object(..)

创建一个Object可以通过new Object()或者字面量的方式。

var obj = new Object();
obj.name = "tom";
console.log(obj);   //{name: "tom"}

var foo = {
    name:"tom"
}
console.log(foo); //{name: "tom"}
复制代码

在实际情况中没有必要使用new Object()来创建对象,因为这样就无法像常量形式那样一 次设定多个属性,而必须逐一设定。

  • Function(..)

每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

var foo = new Function("num1", "num2", "return num1 + num2")
复制代码

从技术角度,这是一个函数表达式,我们不推荐这种方法定义函数,因为这种语法会导致解析两次代码(第一个是解析常规代码,第二次是解析传入构造函数中的字符串),从而影响性能。

RegExp(..)

JavaScript通过RegExp(..)构造函数来支持正则表达式。

 var foo = new RegExp( "[0-9A-Z]", "g" );
 var bar = /[0-9A-Z]/g;
复制代码

强烈建议使用常量形式(如/[0-9A-Z]/g)来定义正则表达式,这样不仅语法简单,执行效率 也更高,因为 JavaScript 引擎在代码执行前会对它们进行预编译和缓存。

RegExp(..) 有时还是很有用的,比如动态定义正则表达式时:

var name = "Kyle";
var namePattern = new RegExp("(" + name + ")", "ig" );
var a = "Kyle".match(namePattern);
var b = "tom".match(namePattern);

console.log(a);   //["Kyle"]
console.log(b);   //null     
复制代码

Date(..)

创建日期对象必须使用new Date(),这里只简单说明一下。

var date = new Date();
复制代码

Error(..)

构造函数 Error(..)(与前面的 Array() 类似)带不带 new 关键字都可。

创建错误对象主要是为了获得当前运行栈的上下文(大部分 JavaScript 引擎 通过只读属性 .stack 来访问)。栈上下文信息包括函数调用栈信息和产生错误的代码行号, 以便于调试。

function bar (args) {
    if(!args){
        throw new Error("args in empty");
    }
}
复制代码

小结

本小节只是简明扼要的说明了一下JavaScript中的原生函数,形成基本的概念,后续会进行深入说明。

参考

转载于:https://juejin.im/post/5d52c7d9e51d4561f95eea1b

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值