JS红皮书读书笔记-03-基本概念

TOC
  1. 1. 关键字和保留字
  2. 2. 变量
  3. 3. 数据类型
    1. 3.1. typeof 操作符
    2. 3.2. Undefined类型
    3. 3.3. Null类型
    4. 3.4. Boolean类型
    5. 3.5. Number类型
    6. 3.6. String类型
    7. 3.7. Object类型
  4. 4. 操作符
    1. 4.1. 一元操作符
      1. 4.1.1. 自增自减操作符
      2. 4.1.2. 加减操作符
    2. 4.2. 位操作符
    3. 4.3. 布尔操作符
    4. 4.4. 乘性操作符
    5. 4.5. 加性操作符
    6. 4.6. 关系操作符
    7. 4.7. 相等操作符
    8. 4.8. 条件操作符
    9. 4.9. 赋值操作符
    10. 4.10. 逗号操作符
  5. 5. 语句
    1. 5.1. if语句
    2. 5.2. do-while语句
    3. 5.3. while语句
    4. 5.4. for语句
    5. 5.5. for-in语句
    6. 5.6. label语句
    7. 5.7. break和continue语句
    8. 5.8. with语句
    9. 5.9. switch语句
  6. 6. 函数
    1. 6.1. 理解参数
    2. 6.2. 没有重载

语法
以下是js的语法要求

  • 大小写敏感
  • 标识符: 即我们说的变量名 开头必须是字幕/下划线/&的一种
  • 不能把关键字/保留字/true,false,null作为标识符
  • 注释:
    • 单行注销: //
    • 多行注销: /** here is your code **/
  • 严格模式: Es5引入严格模式,在严格模式下,ECMAScript 3 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。
    • 语法: 在顶部 添加 "use strict"; 即可
  • 语句: 建议每一行语句背后加分号, 哪怕这不是必须的-因为解释器会自行补充 , 不过这样会耗费时间和性能.

关键字和保留字

Reserved Words - JavaScript | MDN 可以查看最新的关键字和保留字

以上都是不能作为标识符的.

变量

js声明变量的方式如下

var message; // 松散型变量, 你可以赋任何值, 如果不显示赋值, 默认值为undefined

我们看一个例子:

var message = 'hi';
message = 10;

虽然我们声明了一个名为message的变量, 但并不意味着它就是字符串类型.

再看一个例子:

function test(){
var message = 'hi';
}
test();
alert(message) // Uncaught ReferenceError: message is not defined

上面的实例说明了用var定义的变量, 将会成为当前作用于的局部变量, 通常外部无法访问.

实际上不用var也能直接声明一个变量:


function test(){
message = "hi"; // 挂载到全局
}
test();
alert(message); // "hi"

数据类型

Es中有6种数据类型, 实际上到现在为止(2019)有7种, 不过此系列文章讨论ES5.

基本数据类型:

  • Undefined
  • Null
  • Boolean
  • Number
  • String

复杂数据类型:

  • Object: 数组, 函数, 对象都统称为Object类型

typeof 操作符

js中, 给定任意一个变量, 用typeof操作符去检验, 必然返回以下结果之一:

  • undefined
  • boolean
  • string
  • number
  • object
  • function

Undefined类型

Undefined 类型只有一个值,即特殊的 undefined。在使用 var 声明变量但未对其加以初始化时, 这个变量的值就是 undefined

Null类型

Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回”object”的原因

实际上 undefined是派生于null


undefined == null; // true

Boolean类型

Boolean 类型是 ECMAScript 中使用得最多的一种类型,该类型只有两个字面值:true 和 false。

一个变量想得到对应的Boolean值, 可调用Boolean()函数

对于一个变量s, 如果它满足以下条件之一, Boolean(s)就返回false:

  • Boolean: false
  • String: 空字符串
  • Number: 0和NaN
  • Object: null
  • Undefined: undefined

Number类型

Number类型要注意几个关键点:

  • 浮点数精度问题: 如 0.1+0.2 != 0.3
  • 以0开头八进制数字赋值时的差异: 比如 var num1 = 070, num2 = 071; 表现不一致
  • 科学计数法
  • 数值范围有限
  • NaN: NaN != NaN
  • 数值转换, 有三个能把非数值转换成数值的方法:
    • Number()
    • parseInt(arg1,arg2) //parseInt(“AF”, 16); 转换成10进制
    • parseFloat()

String类型

这是一个拥有属性的基本类型(实际上数值, 布尔值, 对象和字符串值都有toString方法 ,后面的章节复习中会讲到包装类型)

var str = 'hello';
alert(str.length); //6

字符字面量: 保留了一些\n,\t,\b,\r等转义序列, 用于表示非打印字符.

拼接字符串效率问题?

var lang = "Java";
lang = lang + "Script";

以上示例中的变量 lang 开始时包含字符串”Java”。而第二行代码把 lang 的值重新定义为”Java” 与”Script”的组合,即”JavaScript”。实现这个操作的过程如下:

  • 首先创建一个能容纳 10 个字符的新字符串
  • 然后在这个字符串中填充”Java”和”Script”
  • 最后一步是销毁原来的字符串”Java”和字符串”Script”

以上是旧浏览器(例如版本低于 1.0 的 Firefox、IE6 等)存在的问题, 目前的浏览器已经不存在字符串拼接效率问题, 所以请大胆使用.


把其他数据类型(不含null, undefined)转换成字符串:

  • toString()

    var num = 10;
    alert(num.toString()); // "10"
    alert(num.toString(2)); // "1010"
    alert(num.toString(8)); // "12"
    alert(num.toString(10)); // "10"
    alert(num.toString(16)); // "a
  • String(): 可以转换null和undefined

    var value1 = 10; 
    var value2 = true;
    var value3 = null;
    var value4;

    alert(String(value1)); // "10"
    alert(String(value2)); // "true"
    alert(String(value3)); // "null"
    alert(String(value4)); // "undefined"

Object类型

创建Object对象实例通常方法:

  • 构造函数:

    var obj1 = new Object; // 没有参数是可以省略括号的
  • 对象字面量

    var obj2 = { };

Object 的每个实例都具有下列属性和方法。

  • constructor:保存若用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就 是 Object()。
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如:o.hasOwnProperty(“name”))。
  • isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型(第 5 章将讨论原型)。
  • propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语旬(本章后面将会讨论)来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符串形式指定。
  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值相同。

由于在 ECMAScript 中 Object 是所有对象的基础,因此所有对象都具有这些基本的属性和方法。

操作符

开始之前, 我们来看看一道题:


var a = 1, b = 2, c = 3, d=4;
var e1 = a<b || b>c && c<d || d<a; //?
var e2 = a<b && b>c || c<d && d<a; //?

所以e1 , e2到底是true还是false呢? 这里就涉及到操作符优先级的问题

一元操作符

只能操作一个值的符号就叫做一元操作符, 也是ES里面最简单的操作符

自增自减操作符

递增和递减操作符, 其中又分前置和后置的情况, 我们看两个有意思的例子:

前置:

var num1 = 2; 
var num2 = 20;
var num3 = --num1 + num2; // 等 于 21
var num4 = num1 + num2; // 等 于 21

后置:

var num1 = 2; 
var num2 = 20;
var num3 = num1-- + num2; // 等 于 22 , num1的值是计算完num3之后再修改的
var num4 = num1 + num2; // 等 于 21

对于这种蛋疼的现象(计算机中称为副效应), 我们可以这么记忆:

在运算时, 前置先修改(因为前置的优先级和执行语句的优先级相等), 后置后修改 (笑)

自增自减符, 对于任何值都适用:

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字符串变量变成数值变量。
  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN(第 4 章将详细讨论)。字符串变量变成数值变量。
  • 在应用于布尔值 false 时,先将其转换为 0 再执行加减 1 的操作。布尔值变量变成数值变量。
  • 在应用于布尔值 true 时,先将其转换为 1 再执行加减 1 的操作。布尔值变量变成数值变量。
  • 在应用于浮点数值时,执行加减 1 的操作。
  • 在应用于对象时,先调用对象的 valueOf()方法(第 5 章将详细讨论)以取得一个可供操作的值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString()方法后再应用前述规则。对象变量变成数值变量。

这里是一些示例:

var s1 = "2"; 
var s2 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};

s1++; // 值变成数值 3
s2++; // 值变成 NaN
b++; // 值变成数值 1
f--; // 值变成 0.10000000000000009(由于浮点舍入错误所致)
o--; // 值变成数值-2


加减操作符

位操作符

讲这个之前, 自行了解下一下二进制码, 反码, 补码的概念. 可以点击此处查看相关内容, 我这里只简单说说

我们首先记住一个规定(反正是计算机科学家规定的):

用32为的二进制数才存储数值 , 从右往左开始, 为第1位到第32位, 第32位是用来做符号位, 0表示正数, 1表示负数.

正数以纯二进制格式存储.

如18这个十进制数:
它的二进制数表示为: 10010

至于-18, 则用经过三个计算步骤用补码表示:

  1. 计算该数绝对值的二进制码: 00000000 00000000 00000000 00010010
  2. 求反: 11111111 11111111 11111111 11101101
  3. 反码加1: 11111111 11111111 11111111 11101110

所以 -18 的二进制表示是: 11111111 11111111 11111111 11101110

你可能看到这里头都大了, 为啥看起来这么复杂, 那我们就来讨论一下这个问题, 这回我们不用32位的二进制数, 我们用8位二进制数来做demo:

8位二进制数, 如果只用来表示正数 那么:

  • 最小值: 0000 0000
  • 最大值: 1111 1111
    看着没毛病

但是要表示正负数, 并且计算基只认识0和1, 那我们只好用第8位来做符号位 :

  • 非负数数值范围: [0000 0000, 0111 1111], 也就是[0,127]
  • 负数范围: [1000 0000, 11111111], 也就是[0,-127]

根据常识, 一个正数要加上另一个数字等于0 , 那么这个数字肯定是它的相反数
比如 1+(-1) = 0

我们再回到八位二进制数:
1表示成: 0000 0001
-1表示成: 1000 0001(我知道长这样它才是你心目中的-1)

那么: 0000 0001 + 1000 0001 = 1000 0010

黑人问号

1000 0010等于0吗?

显然1000 0010长得又不像你心目中的0(实际上也不是)

我们假设: 0000 0001+ x = 0000 0000 (注意这里并不是1000 0000)
得到x = 1111 1111 也就是-1

此处参考了 wenxinwukui234的博客

实际上 0000 0001+ 1111 1111 = 1 0000 0000 产生了溢出问题

但是根据前面计算补码的规则:
绝对值: 0000 0001
求反: 1111 1110
加一: 1111 1111 (和我们之前计算的一致)

  • 非(NOT): ~ , 求反码
  • 与(AND): & , 二进制同时有1才取1
  • 或(OR): | , 二进制有1取1
  • 按位异或(XOR): ^ , 二进制相同的数取0 , 不同则取1
  • 左移:<<
  • 右移:>>

布尔操作符

  • 逻辑非(!)
  • 逻辑与(&)
  • 逻辑或(||)

乘性操作符

  • 乘法(*)
  • 除法(/)
  • 求模(%)

加性操作符

  • 加法(+): 数值 + 字符串 == 字符串
  • 减法(-): 数值 - 字符串 == 数值

关系操作符

字符串比较大小的时候, 是比较字符编码位置的大小 , 如果是对象比较, 会先调用valueOf方法, 如果没有valueOf方法, 就调用toString方法

  • 大于(>)
  • 小于(>)

相等操作符

以下的操作符会进行强制类型转换, 再比较相等性

  • 相等(==)
  • 不相等(!=)

在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值 false 转换为 0,而true 转换为 1;
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类型值按照前面的规则进行比较
  • null == undefined
  • NaN != NaN
  • 对象和对象的相等检测, 会判断是不是指向同一个对象

  • 全等(===) : 不会进行强制类型转换

条件操作符

也就是通常我们所说的三元操作符: var s = bool? true : false;

赋值操作符

以下是常见的赋值操作符, 不过使用这些并不会带来性能上的提升, 取决于你的爱好

  • +=
  • -=
  • *=
  • /=
  • %=
  • <<=
  • >>=

逗号操作符

只需要记住

  • var num1=1, num2=2, num3=3; // 声明变量时候的简约方式
  • var num = (5, 1, 4, 8, 0); // num 的值为 0

语句

if语句

do-while语句

do-while循环至少会执行一次

while语句

for语句

for-in语句

for-in 语旬是一种精准的迭代语旬,可以用来枚举对象的属性。

以下是 for-in 语旬的语法:

for (var propName in window) { 
console.log(propName);
}

在这个例子中,我们使用 for-in 循环来显示了 BOM 中 window 对象的所有属性。每次执行循环时,都会将 window 对象中存在的一个属性名赋值给变量 propName。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。

与 for 语旬类似,这里控制语旬中的 var 操作符也不是必需的。但是, 为了保证使用局部变量,我们推荐上面例子中的这种做法。

ECMAScript 对象的属性没有顺序。因此,通过 for-in 循环输出的属性名的顺序是不可预测的。具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。但是,如果表示要迭代的对象的变量值为 null 或 undefined,for-in 语旬会抛出错误, ECMAScript 5 更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。

label语句

不建议使用

break和continue语句

不建议使用

with语句

不建议使用, 会改变上下文环境, 而且有性能问题

switch语句

如果你的if..else if..不合理, 那需要考虑用这个

函数

函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语旬,而且可以在任何地方、任何时候调用执行。ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数以及函数体。

以下是一个函数示例:

function sayHi(name, message) { 
alert("Hello " + name + "," + message);
//return ; //不必指定是否有返回值
}

理解参数

你可以传无限个参数, 也不介意你的参数是什么类型, 因为函数内部有一个叫arguments的类数组对象( arguments instanceof Array == false)

我们来看一个例子:

function doAdd(num1, num2) { 
arguments[1] = 10;
console.log(arguments[0] + num2);
}
doAdd(10); //NaN , 说明形参和arguments[n]都是独立的, 但是值会根据实参同步个数
doAdd(10,0); // 20

没有重载

ES没有重载, 但是可以通过判断参数的长度来模拟

访客评论