JS高程笔记 1

Keywords: NaN Number String Unary+Operators 2’s+Complement

杂话在前:
啊,昨天找到了 Professional JavaScript for web developers 的mobi,开心(❁´ω`❁)✲゚。相比显示器,还是喜欢kindle的阅读体验。

清明假期第二天,看了小半天这本书。
之前的部分都没有记录,从Page 37开始。
(其实写这个笔记的时候是大晚上了,白天最开始的时候看的内容有点遗忘了,在纠结要从哪开始记。然后在女友的鼓励下,还是决定重新浏览一下,好好记录,毕竟偷懒不记的话……)
(说起来,边看边实践的过程中,发现这本书还是有一些跟目前标准有出入的地方,啊,希望早日出第四版,毕竟这都是ES5时候的书了…)

零零散散的记录:

NaN

typeof NaN;     // "number"

NaN是Number型里特殊的一个存在,如字面意思:Not a Number。

isNaN

MDN上所言,

we could think of isNaN as:

var isNaN = function(value) {
return Number.isNaN(Number(value));
}

这个理解用到了Number.isNaN(两者基本都一样),理解说起来就是,先将尝试将参数转换成Number,然后用Number.isNaN判断是否时isNaN。

正因为用到了Number(),所以isNaN(“10”)也会返回false。(关于Number()会在之后说到)

引用书上的几个例子:

isNaN(NaN);       //true 
isNaN(10);        //false - 10 is a number 
isNaN(10);      //false - can be converted to number 10 
isNaN(“blue”);    //true - cannot be converted to a number 
isNaN(true);      //false - can be converted to number 1

// isNaN() can also be applied to objects
// isNaN()会先调用valueOf(),看其是否可以转换成Number;如果不行,将会调用toString()。
let o = {
    valueOf: function() {
        return 1;
    }
};
isNaN(o);                       //false
isNaN(o.valueOf);               //true
isNaN(new Date());              // false
isNaN(new Date().toString);     // true

由此,例如isNaN()就可以用来检测是否是数字,检测用户输入的数字是否合法;另外,isNaN()也存在一个坑。

isNaN('I am a string');             // true
Number.isNaN('I am a string');      // false

正如一开始说的,NaN 属于 Number 型,”Am a string” 属于 String 型,从数据类型上说,String不是Number型,应返回false。ES6中的 Number.isNaN 则解决了这个问题,它并不强制将参数转换成Number,它只有是Number型且是NaN时才返回true。所以Number.isNaN是一种更安全的判断是否为NaN的方法。

Number()

还是举几个例子理解一下:

// apply to number, it is simply passed through and return
// number will get converted into integer or floating-point number, respectively
// (我到底是打中文还是英文好呢…)
Number(10);             // 10
Number(11.11);          // 11.11
Number(0xff);           // 255


Number(null);           // 0
Number(undefined);      // NaN **
Number(true);           // 1

// 字符串
// 字符串为只包含数字时,类似对数字的处理↑↑,(前置的'0'会被忽略)
Number('10');           // 10
Number('-11.11');       // -11.11
Number('00003115');     // 3115
Number('0xff');         // 255
Number('');             // 0 如果是空字符,将会被转换成 0 。

// 如果包含其它字符,结果将会是NaN
Number('10test');       // NaN   
Number('10/12');        // NaN

// 应用到object时,会先调用valueOf(), 如果返回值时NaN,将会调用toString。
let o = {
valueOf: function() {
    return 1;
}
};
Number(o);              // 1
Number(new Date());     // 1491292015962

这时候再回到isNaN()一开始的位置,就不难理解它的原理,以及会有那个坑的理由。

parseInt()

相比起来,Number()规矩过多,所以一般来说,用parseInt来转换整数是一个更好的选择。

如果字符串只包含数字,两者的处理是一致的,(一样会忽略前置的’0’)。

不同之处在于:

parseInt('1234test');   // 1234
Number('1234test');     // NaN
// 如果字符串首字符不是数字或者加减号,统统会返回NaN
parseInt('');           // NaN

说起来,ES3, ES5, ES6 的parseInt存在差异之处:

var num = parseInt("070");  // 56 (octal) in ES3, 0 (decimal) in ES5, 70 (decimal) in ES6.

所以parseInt提供了第二个参数 radix 来决定如何解释所输入的数据(另外,正因为如此,建议都传入radix来避免意想不到的错误,哪怕是十进制转换)。

parseInt('0xAF', 16);       // 175
parseInt('AF', 16);         // 175
parseInt('AF');             // NaN 由于并没有传入radix,第一个字符不是不是数字、加减号,所以……
parseFloat()
parseFloat()与parseInt()是相似的。
有几点需要注意的:

parseFloat('12.34.5');      // 12.34 第二个小数点是非法的,且之后的内容将会被忽略
parseFloat(12.34.5);        // Error
parseFloat('1.234e3');      // 1234 科学计数法

// if the string represents a whole number
// (no decimal point or only a zero after the decimal point)
// parseFloat() returns an integer
parseFloat('1234test');     // 1234 integer
parseFloat('0.0');          // 0    integer

接下来,

字符串的单双引号

书介绍了一下字符串的单双引号(没有差异),不像PHP,双引号中的内容会编译后输出(所以写PHP的时候偶尔要想想这个输出到底要双引号还是单引号orzzz……)

字符的转义

之后是字符的转义,转义的字符.length中也只占一位长度。

字符串的性质

字符串在JS中是不可改变的(immutable),意味着,一个字符串一旦被创建,它的值是不可变的。

一个例子:

let lang = 'Java';
lang = lang + 'Script';

变量 lang 被定义包含 ‘Java’,在下一行中,lang 被重新定义为 ‘JavaScript’。

而第二行中具体的步骤是,创建一个足够容纳’JavaScript’的空间,然后用’Java’和’Script’将其填♂满(233…);最后一步,因为不再需要,所以将’Java’和’Script’销毁。

当当当当,从从雷峰塔中生出雷锋啦~(欸?似乎不小心还把雷峰塔给毁了,罪过罪过orzzz)

toString()

tostring()可以将数字、布尔值、对象、字符串转换成字符串,null, undefined不适用。

另外,在应用到数字时,也可以传入一个radix来指定转换的基数。

let num = 10;
num.toString();      // '10' 
num.toString(2);     // '1010'
num.toString(8);     // '12'
num.toString(10);    // '10'
num.toString(16);    // 'a'

String()

如果值有可能是null或者undefined,String()就可以出场啦~

两者用法有点不同,而且没有radix参数可以供传入。

String(null);           // null
String(undefined);      // undefined

也可以自己写一个简单的function来统一一下用法…

function myString(value, radix = 10) {
if(value === null || value === undefined)
    return String(value);
else
    return value.toString(radix);
}

当然还有一种简单的方法来将值转换陈字符串,就是加上一个空字符串。

let test1 = null;
let test2 = undefined;
test1 += '';    // 'null'
test2 += '';    // 'undefined'

之后简单介绍了一下对象。这一块,等Chapters 5, 6的时候再写吧。

一元运算符

++ / –
the same as C.

值得一提的是,这个 ++ / – 还可以用来将别的类型的值转换成 Number 型:

var s1 = '2';
var s2 = 'z';
var b = false;
var f = 1.1;
var o = {
    valueOf: function() {
        return -1;    
    }
};
s1++;   //value becomes numeric 3 
s2++;   //value becomes NaN 
b++;    //value becomes numeric 1 
f--;    //value becomes 0.10000000000000009 (due to floating-point inaccuracies) 
o--;    //value becomes numeric -2

类似的, + / - 也可以达到类似的效果。

Bitwise Operators

讲到了原码、反码、补码,之前科协导论课的时候听补码不是很明白,现在有点了解了。
计算机运算中,引入这三码(赛克)🐴是为了解决减法问题。因为CPU中只有加法器,所以要把减法转化成加法来运算。

试想:

A = +16 → 原码 = 00010000
B = -19 → 原码 = 10010011
A + B (原码)   = 10100011 = -35     // 得不到我们想要的 -3

而引入补码之后:

B = -19 → 反码 = 01101100
B = -19 → 补码 = 01101101
A + B (补码)   = 01111101 → 原码 = 10000011 = -3    // TADA!

负整数转换成补码的步骤:

  • 先将负整数的绝对值转换成二进制的原码
  • 然后将原码转换成反码, 0 ⇆ 1
  • 然后将反码 + 1 = 补码

在JS中,当 bitwise operators 应用到数字上时,64-bit的数将会被转换成32-bit的数来进行操作,然后再将32-bit的结果转换回64-bit的数据。不过这种转换的副作用是,NaN 和 Infinity 将会被转换成 0。

如果 bitwise operators 应用到非数字的值时,会先用Number()将值转换成 Number 型,然后进行操作,最后返回一个 Number 型的值。

啊 ~ 终于写完了呢,写了大半天了,中途查查MDN,自己又再次确认写下的内容跟目前的标准基本吻合,用了好久时间啊,不过还是挺有成就感的 (√ ζ ε:)。

This blog is under a CC BY-NC-SA 4.0 Unported License
Link to this article: http://nhh.ink/2017/04/04/professional-javascript-1/