语法

以下是js的语法要求

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

关键字和保留字

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

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

变量

js声明变量的方式如下

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

我们看一个例子:

  1. var message = 'hi';
  2. message = 10;

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

再看一个例子:

  1. function test(){
  2. var message = 'hi';
  3. }
  4. test();
  5. 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

03-基本概念 - 图1
显然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没有重载, 但是可以通过判断参数的长度来模拟