「前端基础」阮一峰JavaScript教程笔记(一)

入门篇

6 条件语句

注意,if后面的表达式之中,不要混淆赋值表达式(=)、相等运算符(==)和严格相等运算符(===)。尤其是赋值表达式不具有比较作用。

var x = 1;
var y = 2;
if (x = y) {
   
  console.log(x);
}
// "2"

上面代码的原意是,当x等于y的时候,才执行相关语句。但是,不小心将严格相等运算符写成赋值表达式,结果变成了将y赋值给变量x,再判断变量x的值(等于2)的布尔值(结果为true)。

这种错误可以正常生成一个布尔值,因而不会报错。为了避免这种情况,有些开发者习惯将常量写在运算符的左边,这样的话,一旦不小心将相等运算符写成赋值运算符,就会报错,因为常量不能被赋值。

if(x = 2) {
    // 不报错
if(2 = x) {
    // 报错 Uncaught SyntaxError: Invalid left-hand side in assignment
const c = 2
if(c = x) {
    //报错 Uncaught TypeError: Assignment to constant variable.

数据类型

概述

  • 1.简介

原始(基础)类型(primitive type):

  • 数值(number)
  • 字符串(string)
  • 布尔值(boolean)
  • symbol
  • null
  • undefined
  • bigInt
    2020.6 新出了一个新的数据类型bigInt

8种数据类型, 7种原始类型和object

对象是最复杂的数据类型,又可以分成三个子类型。

  • 狭义的对象(object)
  • 数组(array)
  • 函数(function)

狭义的对象和数组是两种不同的数据组合方式,除非特别声明,本教程的“对象”都特指狭义的对象。函数其实是处理数据的方法,JavaScript 把它当成一种数据类型,可以赋值给变量,这为编程带来了很大的灵活性,也为 JavaScript 的“函数式编程”奠定了基础。

  • 2.typeof 运算符
    JavaScript 有三种方法,可以确定一个值到底是什么类型。

typeof运算符
instanceof运算符
Object.prototype.toString方法

利用这一点,typeof可以用来检查一个没有声明的变量,而不报错。

v
// ReferenceError: v is not defined

typeof v
// "undefined"

实际编程中,这个特点通常用在判断语句, 检查一个没有声明的变量。

// 错误的写法
if (v) {
   
  // ...
}
// ReferenceError: v is not defined

// 正确的写法
if (typeof v === "undefined") {
   
  // ...
}

对象返回object

typeof window // "object"
typeof {
   } // "object"
typeof [] // "object"

instanceof运算符可以区分数组和对象,

var o = {
   };
var a = [];

o instanceof Array // false
a instanceof Array // true

不能区分函数和对象。

function a(){
   }
undefined
a instanceof Object // true
a instanceof Function // true
a instanceof Array // false

null返回object

typeof null // "object"

null的类型是object,这是由于历史原因造成的。1995年的 JavaScript语言第一版,只设计了五种数据类型(对象、整数、浮点数、字符串和布尔值),没考虑null,只把它当作object的一种特殊值。后来null独立出来,作为一种单独的数据类型,为了兼容以前的代码,typeof null返回object就没法改变了。

null, undefined 和布尔值

1. null 和 undefined

老实说, null, undefined语法效果几乎没区别。

为什么JS有null和undefined?

这与历史原因有关。1995年 JavaScript 诞生时,最初像 Java 一样,只设置了null表示"无"。根据 C 语言的传统,null可以自动转为0。

Number(null) // 0
5 + null // 5
typeof(5 + null) // number

但是,JavaScript 的设计者 Brendan Eich,觉得这样做还不够。

原因:
首先,第一版的 JavaScript 里面,null就像在 Java 里一样,被当成一个对象,Brendan Eich 觉得表示“无”的值最好不是对象。

其次,那时的 JavaScript 不包括错误处理机制,Brendan Eich 觉得,如果null自动转为0,很不容易发现错误。

因此,他又设计了一个undefined。区别是这样的:null是一个表示“空”的对象,转为数值时为0;undefined是一个表示"此处无定义"的原始值,转为数值时为NaN

Number(undefined) // NaN
5 + undefined // NaN
typeof(5 + undefined) // NaN
typeof(NaN) // number

@@@
null表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入null,表示该参数为空。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入null,表示未发生错误。
@@不理解@

undefined表示“未定义”,下面是返回undefined的典型场景。

// 变量声明了,但没有赋值
var i;
i // undefined

// 调用函数时,应该提供的参数没有提供,该参数等于 undefined
function f(x) {
   
  return x;
}
f() // undefined

// 对象没有赋值的属性
var  o = new Object();
o.p // undefined

// 函数没有返回值时,默认返回 undefined
function f() {
   }
f() // undefined
2. 布尔值

布尔: 纪念伟大的数学家布尔来命名的.
注意大小写: true, false

6个falsy值:

  • false
  • null
  • undefined
  • 0
  • NaN
  • ''或""(空字符串)

注意,空数组([])和空对象({})对应的布尔值,都是true

if ([]) {
   
  console.log('true');
}
// true

if ({
   }) {
   
  console.log('true');
}
// true

参考原因:
这是因为 JavaScript 语言设计的时候,出于性能的考虑,如果对象需要计算才能得到布尔值,对于obj1 && obj2这样的场景,可能会需要较多的计算。为了保证性能,就统一规定,对象的布尔值为true

数值

1 概述
  • 1.1 整数和浮点数

JavaScript 内部,所有数字都是以64位浮点数形式储存(国际标准 IEEE 754),即使整数也是如此。所以,11.0是相同的,是同一个数。

1 === 1.0 // true

容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算,参见《运算符》一章的“位运算”部分。

由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心。

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false
  • 1.2 数值精度

精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即 − 2 53 -2^{53} 253 2 53 2^{53} 253 ,都可以精确表示。

Math.pow(2, 53) // 9007199254740992

Math.pow(2, 53) + 1 // 9007199254740992

Math.pow(2, 53) + 2 // 9007199254740994

Math.pow(2, 53) + 3 // 9007199254740996

Math.pow(2, 53) + 4 // 9007199254740996

上面代码中,大于2的53次方以后,整数运算的结果开始出现错误。所以,大于2的53次方的数值(小于-2的53次方的数值),都无法保持精度。由于2的53次方是一个16位的十进制数值,所以简单的法则就是,JavaScript 对15位的十进制数都可以精确处理。

  • 1.3 数值的范围

@@@
为啥正向溢出是Math.pow(2, 1024) // Infinity, 负向溢出是Math.pow(2, -1075) // 0?
@@没搞定

2 数值的表示法

以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

  • 小数点前的数字多于21位。
1234567890123456789012 // 1.2345678901234568e+21

123456789012345678901 // 123456789012345680000 (大于9...992 16位)
  • 小数点后的零多于5个。
// 小数点后紧跟5个以上的零,
// 就自动转为科学计数法
0.0000003 // 3e-7

// 否则,就保持原来的字面形式
0.000003 // 0.000003
3 数值的进制

HEX(Hexadecimal )十六进制。
DEC(Decimal) 十进制。
OCT(Octal) 八进制。
BIN(Binary) ,二进制。

八进制:有前缀0o0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。

JavaScript 不再允许将带有前缀0的数字视为八进制数,而是要求忽略这个0。但是,为了保证兼容性,大部分浏览器并没有部署这一条规定。

4 特殊数值
  • 0
-0 === +0 // true
0 === -0 // true
0 === +0 // true
+0 // 0
-0 // 0   
(-0).toString() // '0'
(+0).toString() // '0'

自测: -0 //-0

唯一有区别的场合是,+0或-0当作分母,返回的值是不相等的。

(1 / +0) === (1 / -0) // false
//等价
+Infinity === -Infinity // false
  • NaN(Not a Number)

主要出现在将字符串解析成数字出错的场合。

5 - 'x' // NaN
5 - '1' // 4

一些数学函数的运算结果会出现NaN。

Math.acos(2) // NaN
Math.log(-1) // NaN
Math.sqrt(-1) // NaN

0除以0也会得到NaN。

0 / 0 // NaN

需要注意的是,NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number,使用typeof运算符可以看得很清楚。

typeof NaN // 'number'

讲故事: 原始社会打到猎物了, 有很多不知道怎么表示NaN. 然后就发明了正整数这样就可以描述今天打了1只猎物还是2只3只. 然后突然有一天没有打到猎物. 然后发现这不符合正整数. 然后不知道用啥表示, 那就先叫NaN吧. 后面商讨后决定用0表示没有.随着社会发展, 出现了1-5这种情况, 然后目前数学知识解决不了那就先叫NaN吧. 数学突破之后, 然后定义这个为负整数.
过了很久遇到了1/2 这种情况以前也没见过,先叫NaN,数学突破之后那规定就叫分数吧,或另一种表示形式0.5(有理数), 随着社会发展后面出现了圆的周长是直径的3.14159…倍先用NaN表示, 数学突破了规定交无理数.
1的平方=1 2的平方=4
然后什么的平方等于-1虚数(复数)

NaN不等于任何值,包括它本身(一个我不知道是啥的数, 和另一个我不知道是啥的数, 怎么可能相等呢?)。

NaN === NaN // false

数组的indexOf方法内部使用的是严格相等运算符,所以该方法对NaN不成立。

[1, NaN, 2].indexOf(NaN) // -1

NaN与任何数(包括它自己)的运算,得到的都是NaN。

NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN
  • Infinity
Math.pow(2, 1024)  // Infinity

Infinity大于一切数值(除了NaN),-Infinity小于一切数值(除了NaN)。

Infinity与NaN比较,总是返回false。

Infinity > NaN // false
-Infinity > NaN // false

Infinity < NaN // false
-Infinity < NaN // false

0乘以Infinity,返回NaN
0除以Infinity,返回0
Infinity除以0,返回Infinity

0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity

Infinity加上或乘以Infinity,返回的还是InfinityInfinity减去或除以Infinity,得到NaN

Infinitynull计算时,null会转成0,等同于与0的计算。

Infinityundefined计算,返回的都是NaN

5 与数值相关的全局方法

parseInt方法用于将字符串转为整数。

但是,isNaN只对数值有效,如果传入其他值,会被先转成数值。比如,传入字符串的时候,字符串会被先转成NaN,所以最后返回true,这一点要特别引起注意。也就是说,isNaNtrue的值,有可能不是NaN,而是一个字符串。出于同样的原因,对于对象和数组,isNaN也返回true

判断NaN更可靠的方法是,利用NaN`为唯一不等于自身的值的这个特点,进行判断。

function myIsNaN(value) {
   
  return value !== value;
}

isFinite方法返回一个布尔值,表示某个值是否为正常的数值。除了Infinity-InfinityNaNundefined这几个值会返回falseisFinite对于其他的数值都会返回true

字符串

1. 概述
  • 1.1定义

由于 HTML 语言的属性值使用双引号,所以很多项目约定 JavaScript 语言的字符串只使用单引号,本教程遵守这个约定。

如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠。

var longString = 'Long \
long \
long \
string';

longString
// "Long long long string"

上面代码表示,加了反斜杠以后,原来写在一行的字符串,可以分成多行书写。但是,输出的时候还是单行,效果与写在同一行完全一样。注意,反斜杠的后面必须是换行符,而不能有其他字符(比如空格),否则会报错。

如果想输出多行字符串,有一种利用多行注释的变通方法。

(function () {
    /*
line 1
line 2
line 3
*/}).toString().split('\n').slice(1, -1).join('\n')

// "line 1
// line 2
// line 3"
  • 1.2 转义

(1)\HHH

反斜杠后面紧跟三个八进制数(000377),代表一个字符。HHH对应该字符的 Unicode 码点,比如\251表示版权符号。显然,这种方法只能输出256种字符。

(2)\xHH

\x后面紧跟两个十六进制数(00FF),代表一个字符。HH对应该字符的 Unicode 码点,比如\xA9表示版权符号。这种方法也只能输出256种字符。

(3)\uXXXX

\u后面紧跟四个十六进制数(0000FFFF),代表一个字符。XXXX对应该字符的 Unicode 码点,比如\u00A9表示版权符号。

'\251' // "©"
'\xA9' // "©"
'\u00A9' // "©"

'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
  • 1.3 字符串与数组

如果方括号中的数字超过字符串的长度,或者方括号中根本不是数字,则返回undefined。

'abc'[3] // undefined
'abc'[-1] // undefined
'abc'['x'] // undefined

但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符。

var s = 'hello';

delete s[0];
s // "hello"

s[1] = 'a';
s // "hello"

s[5] = '!';
s // "hello"
2. 字符集

JavaScript 使用 Unicode 字符集。JavaScript 引擎内部,所有字符都用 Unicode 表示。

Unicode可以叫: 统一码, 万国码.

JavaScript 不仅以 Unicode 储存字符,还允许直接在程序中使用 Unicode 码点表示字符

var s = '\u00A9';
s // "©"
var f\u006F\u006F = 'abc';
foo // "abc"

为什么一般都用4位16进制?
首先2个字节(16位), 用二进制写起来太慢了.

011 1100 0101 1010的16进制3C5A

3. Base64 转码

有时,文本里面包含一些不可打印的符号,比如 ASCII 码0到31的符号都无法打印出来,这时可以使用 Base64 编码,将它们转成可以打印的字符。另一个场景是,有时需要以文本格式传递二进制数据,那么也可以使用 Base64 编码。

所谓 Base64 就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+/这64个字符组成的可打印字符。使用它的主要目的,不是为了加密,而是为了不出现特殊字符,简化程序的处理。

JavaScript 原生提供两个 Base64 相关的方法:

btoa():任意值转为 Base64 编码
atob():Base64 编码转为原来的值

var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"

注意,这两个方法不适合非 ASCII 码的字符,会报错。

btoa('你好') // 报错

要将非 ASCII 码字符转为 Base64 编码,必须中间插入一个转码环节,再使用这两个方法。

function b64Encode(str) {
   
  return btoa(encodeURIComponent(str));
}

function b64Decode(str) {
   
  return decodeURIComponent(atob(str));
}

b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"

对象

1 概述
  • 1.2 键名

对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以(如果键名不符合标识名的条件, 就需要加引号)。

对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。

对象的属性之间用逗号分隔,最后一个属性后面可以加逗号(trailing comma),也可以不加。

  • 1.4 表达式还是语句?

对象采用大括号表示,这导致了一个问题:如果行首是一个大括号,它到底是表达式还是语句?

{
    foo: 123 }

JavaScript 引擎读到上面这行代码,会发现可能有两种含义。第一种可能是,这是一个表达式,表示一个包含foo属性的对象;第二种可能是,这是一个语句,表示一个代码区块,里面有一个标签foo,指向表达式123

疑问: chrome直接输出{ foo: 123 }, 是一个对象.

为了避免这种歧义,JavaScript 引擎的做法是,如果遇到这种情况,无法确定是对象还是代码块,一律解释为代码块。

{
    console.log(123) } // 123

上面的语句是一个代码块,而且只有解释为代码块,才能执行。

如果要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象。

({
    foo: 123 }) // 正确
({
    console.log(123) }) // 报错

这种差异在eval语句(作用是对字符串求值)中反映得最明显。

eval('{foo: 123}') // 123
eval('({foo: 123})') // {foo: 123}
2 属性的操作
  • 2.1 属性的读取

读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

请注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理(数字键可以不加引号,因为会自动转成字符串)。

方括号运算符内部还可以使用表达式。

注意,数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。eg: obj.123

  • 2.3 属性的查看

查看一个对象本身的所有属性,可以使用Object.keys方法。

  • 2.4 属性的删除:delete 命令

delete命令用于删除对象的属性,删除成功后返回true

var obj = {
    p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []

注意,删除一个不存在的属性,delete不报错,而且返回true。因此,不能根据delete命令的结果,认定某个属性是存在的。

var obj = {
   };
delete obj.p // true

只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。

var obj = Object.defineProperty({
   }, 'p', {
   
  value: 123,
  configurable: false
});

obj.p // 123
delete obj.p // false

上面代码之中,对象objp属性是不能删除的,所以delete命令返回false(关于Object.defineProperty方法的介绍,请看《标准库》的 Object 对象一章)。

另外,需要注意的是,delete命令只能删除对象本身的属性,无法删除继承的属性(关于继承参见《面向对象编程》章节)。

var obj = {
   };
delete obj.toString // true
obj.toString // function toString() { [native code] }
  • 2.5 属性是否存在:in 运算符

in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值)

in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。

var obj = {
    p: 1 };
'p' in obj // true
'toString' in obj // true

可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。

var obj = {
   };
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值