浅析JS原生数据类型的方法

Posted by xionghongzhi on August 23, 2017

戳这里,查看原文地址~~~

数据类型

JS中,允许将原生数据类型(字符串,数值等)看作对象,进行操作.

JS还提供了相关大量的方法去进行调用.接下来将要一点一点的学习这些内容,但是,首先,让我们看一看具体是如何工作的,毕竟,原生数据类型并不是对象(这里直接了当的说清楚,记住了,原生数据类型不是对象).

下面,让我们开始了解原生数据类型以及对象之间根本的区别.

原生数据

原生数据,就是一个原生数据类型表示的值,原生数据的类型有下面6种: 字符串(string), 数值类型(number), 布尔值(boolean), symbol(标志), null以及undefined.

对象

对象能够存储多种数据作为它的属性.可以通过大括号{}进行创建,如{name: ‘xiong’, age: 23};

在JS中,有若干种不同的对象,其中函数也是一个对象!

对象的好处之一就是我们可以将一个函数作为它的一个属性,如:

let me = {
    name: 'xiong',
    age: 23,
    sayLove(){
        alert('i love zhangyue');
    }
};

me.sayLove(); // i love zhangyue

上面这个例子我们创建了一个me对象,然后将sayLove这个函数作为它其中的一个属性.

JS存在许多内嵌的对象,包括作用于日期的,错误的以及HTML元素的.它们有着不同的属性和方法.

但是,这些特性是需要代价的.(But features come at a price!)

对象相对于原生数据类型而言是”重量级的”.它们需要一些额外的资源去支持内部的一些机制. 但是属性以及方法在编程中非常有效,因此JS引擎尝试去优化它,所以,这种代价一般而言,也是较为公平的.

作为对象的原生数据类型

关于JS创造者所面临的悖论:

  1. 即便针对与原生数据类型(如字符串,数字等),想要做的事情也很多. 能否更好的操作它们,就好像使用方法一样.
  2. 原生数据类型应当尽可能的轻量和迅速.

结果看起来有那么一点尴尬:

  1. 原生数据类型就是原生数据类型,只有一个单一的值.
  2. JS语言允许字符串,数值,标志以及布尔值操作属性和方法.(除去了null以及undefined,它们会出现问题.)
  3. 当发生上述操作调用的时候,一个特殊的对象包装类将会被创建且用于实现上述相关的功能性,然后将它们摧毁掉.

这些对象包装类与它们对应的原生类型是两码事,并且它们的命名也不相同:String, Number, Boolean, Symbol.因此,它们也提供了一系列不同的方法.

比如,这样一个方法,str.toUpperCase()能够返回str的大写形式.

下面, 来使用它们:

let str = "hello";
alert(str.toUpperCase()); // HELLO

是不是超级简单? 下面,来具体看看这个toUpperCase()方法调用的时候发生了什么:

  1. str这个字符串是原生类型,因此,在操作这个字符串属性的时候,一个特殊对象就被创建了,这个对象不仅拥有这个字符串的字面值,还拥有其他一些额外方法,如toUpperCase().
  2. 方法运行的时候,将会返回一个全新的字符串(使用alert展示出来).
  3. 这个特殊对象在使用完成之后将会被摧毁掉,只剩下str本身.

当然,上述操作被JS引擎各种优化,在内部,完全可以忽略这个特殊对象的创建.但是必须创建这个特殊对象,遵守这样的规范和行为.

数值number也同样有着它的方法,如,toFixed(n),根据指定的精度对数值四舍五入.

let number = 1.23456;
number.toFixed(2); // 1.23 (精确到小数点后两位)

更多的细节,可以仔细阅读Numbers以及Strings章节

使用构造方法创建供内部使用的String/Number/Boolean

一些编程语言,如Java,明确允许通过下述语法创建原生类型的包装对象: new Number(1); / new Boolean(true);

在JS中,由于历史原因,也可以使用这种方式进行创建.但是,绝对不建议这样做,因为这样可能在某些地方会造成不可预测的错误.

比如:

alert(typeof 1); // number
alert(typeof new Number(1)); //object

那么,下面这种情况下,0就是一个对象,相应的if语句中就可以被执行(这其实并不是我们想要的,0在JS中被当成了false)

let zero = new Number(0);
if(zero){
    alert("这不是零,而是一个对象~~");
}

另外,使用相同的String/Boolean/Number函数而不使用new绝对是一个有效且明智(sane)的选择.它们将会为每一个值转换成对应的原生类型:string/boolean/number.

如,下面这种方式是完全正确合法的:

let number = Number("123");  //将字符串转换成数值

空(null)/未定义(undefined)不存在任何方法

原生类型中,null和undefined是两个例外,它们不仅没有对应的包装对象,也没有提供任何方法.在某种意义上(In a sense),它们才是”最原生”的.

如果尝试去访问上述两个原生类型的属性,就会抛出一个错误:

alert(null.test); // Error

总结

  1. 原生类型中,除了null和undefined外,其他的都提供了一些有效的方法和属性.
  2. 正式而言,这些方法都是通过临时对象进行调用的,但是JS引擎优化做的非常好,几乎没有什么额外消耗.

任务

是否可以给字符串增加一个属性?

let str = "Hello";
//给str创建一个属性
str.test = 5;  // (*)
alert(str.test); 上面这段代码可能会出现两种结果:
  1. undefined(这也是大家都可以接受的)
  2. 会抛出一个错误

下面,介绍一下(*)这行代码发生了什么:

  1. 当str的一个属性被访问了,一个对应的包装对象就会被创建出来.
  2. 对这个属性的操作就会基于这个包装对象,所以,这个对象获得了这个test属性.
  3. 当操作结束后,包装对象就会消失.

因此,在最后一段代码中,str根本就追溯不到test这个属性. 只要对字符串进行任何的对象操作,一个新的包装对象就会被创建.

那为什么答案不是undefined呢?因为在有些浏览器中(其实我也不知道是什么浏览器),它对程序员有更深的限制,它根本就不允许给原生数据类型分配任何属性和方法! 这与我们的规范有不同的差异.

这个例子明确指出了,原生数据类型根本不是对象!

因为它们无法存储数据(当然,本身值除外!).

所有对原生数据类型的属性/方法操作,都是通过对应的临时对象(Wrapper Object)的帮助才能完成的!