乐趣区

关于javascript:快速了解JavaScript基础知识

正文

单行正文:

// 单行正文

多行正文:

/*
 多行
 正文
*/

历史上 JavaScript 能够兼容 HTML 正文,因而 <!----> 也能够是单行正文。

x = 1; <!-- x = 2;
--> x = 3;

只有 x = 1 会执行,其它局部都被正文掉了。

然而 --> 只有在行首才会被当成正文,否则会当作失常的运算。

function countdown(n) {while (n --> 0) console.log(n);
}
countdown(3)

下面代码中,n --> 0 实际上会当作 n-- > 0,因而输入 2、1、0。

语句

JavaScript 程序的执行单位为行(line),也就是一行一行地执行。个别状况下,每一行就是一个语句。

语句(statement)是为了实现某种工作而进行的操作,比方上面就是一行赋值语句。

// 修饰符润饰变量 a 赋值 表达式,以分号完结,联合起来为一个语句
var a = 1 + 3;
// 多个语句能够写在一行内
var a = 1 + 3; var b = 'abc';
// 三个空语句
;;;

变量 & 常量

JavaScript 申明变量需应用关键字 var 来申明:

var i;
var sum;
// 或者
var i, sum;
// 申明变量时赋值
var message = "Hello";
var i = 0, j = 0, k = 0;
// 变量赋值不写 var 也是能够的
a = 1;    // 等同于 var a = 1; 但这样写法不利于表白,且容易人不知; 鬼不觉地创立全局变量

如果未在 var 申明语句时指定初始值,初始值会默认为 undefined

ES6新增 let 来申明变量,是古代的变量申明形式,与 var 大体雷同。但 let 申明的变量只能在 let 命令所在的代码块内无效,无变量晋升(变量晋升就是被晋升至所在作用域的顶部)。

ES6 新增 const 申明常量,不可被批改,无变量晋升。

const pi = 3.14;
pi = 3.15;    // 这里会报错,常量不能被批改。

标识符

标识符就是用来标记各种值的非法名称。大小写敏感。

命名规定:

  • 首字符为字母下划线 (_) 与美元符号($)。
  • 剩下的字符除了上述字符还能够有数字 0-9

但 JavaScript 中的保留字不能够用作标识符。

古代模式

ES5标准新增了新的语言个性,但默认不失效。要应用非凡指令use strict 进行激活。将 use strict 放在脚本文件的顶部,整个脚本文件都将以 “ 古代 ” 模式工作。

"use strict";
// 代码以古代模式工作
...

古代 JavaScript 反对 “classes” 和 “modules”,它们会主动启动 use strict

区块

JavaScript 应用大括号,将多个相干的语句组合在一起,称为 “ 区块 ”(block)。

对于 var 命令来说,JavaScript 的区块不形成独自的作用域(scope)。

{var a = 1;}
a    // 1

下面代码在区块外部,应用 var 命令申明并赋值了变量 a而后在区块内部,变量 a 仍然无效,区块对于 var 命令不形成独自的作用域,与不应用区块的状况没有任何区别。在 JavaScript 语言中,独自应用区块并不常见,区块往往用来形成其它更简单的语法结构,比方 forifwhilefunction 等。

流程管制

条件语句

JavaScript 提供 if 构造和 switch 构造,实现条件判断。

if 语句

if 构造的条件语句有几种状况:

// 第一种
if (布尔值) {语句;}
// 第二种
if (布尔值) {语句;} else {语句;}
// 屡次判断
if (布尔值) {语句;} else if (布尔值) {语句;} else if (布尔值) {语句;} else {语句;}

switch 语句

switch 构造能够代替多个 if语句构造:

switch (fruit) {
    case "banana":
        // ...
        break;
    case "apple":
        // ...
        break;
    ...
    default:
        // ...
}

如果没有 break 语句,代码会始终执行上来。

switch 语句前面的表达式,与 case 语句前面的表达式比拟运算后果时,采纳的是严格相等运算符(===),而不是相等运算符(==),这意味着比拟时不会产生类型转换。

JavaScript 还有一个三元运算符 ?: 能够用于逻辑判断:

var even = (n % 2 === 0) ? true : false;

三元运算符能够被视作 if...else... 的简写。

循环语句

循环语句用于反复执行某个操作。JavaScript 反对三种模式:while 循环、do...while 循环、for 循环。

while 循环

var i = 0;
while (i < 100) {i = i + 1;}

do…while 循环

var x = 3, i = 0;
do {i++;} while (i < x);

for 循环

for 语句里有三个表达式:初始化、条件表达式、递增语句。

for (var i = 0; i < 3; i++) {console.log(i);
}

break & continue

breakcontinue 能够用来完结循环语句,但作用不雷同。

break语句用于跳出代码块或循环。

var i = 0;
while (i < 100) {
    i++;
    if (i === 10) break;    // 当 i =10,跳出循环
}

continue 语句用于立刻终止本轮循环,返回循环构造的头部,开始下一轮循环。

var i = 0;
while (i++) {
    i++;
    if (i % 2 === 0) continue;    // 当 i 为偶数时终止本轮循环
}

label

JavaScript 能够在循环语句前增加 label,用于跳转到程序的任意地位。相当于定位。

label 设置的标识符不能是保留字。通常与 breakcontinue 进行配合,跳出循环语句。

top:
    for (var i = 0; i < 3; i++) {for (var j = 0; j < 3; j++) {if (i === 1 && j === 1) break top;
            console.log('i=' + i + ', j=' + j);
        }
    }
// i = 0, j = 0
// i = 0, j = 1
// i = 0, j = 2
// i = 1, j = 0

标签也能够用于跳出代码块。

foo: {console.log(1);
    break foo;
    console.log('本行不会输入');
}
console.log(2);
// 1
// 2

运算符

运算符是解决数据的根本办法,用来从现有的值得到新的值。运算符提供算术运算符、关系运算符、布尔运算符、位运算符与其它运算符来进行运算。上面咱们顺次来介绍它们。

算术运算符

算术运算符提供 加法 (+) 减法 (-) 乘法 (*) 除法 (/) 指数 (**) 余数 (%) 自增 (++) 自减 (--) 正数值 (+X) 负数值(-X) 运算符。

加法运算

应用 + 运算符来实现两数相加。JavaScript 容许非数值相加;而如果与字符串相加,则将字符串相连并返回新的字符串。

1 + 1    // 2
true + true        // 2
1 + true        // 2
'hello,' + 'world!'    // hello, world!
1 + ', 数字转成字符串'        // 1, 数字转成字符串
true + ', 布尔值转成字符串'    // true, 布尔值转成字符串

对象相加

对象相加,必须先转成原始类型的值。

var obj = {p:1};
obj + 2        // [object Object]2

上述代码中,对象 obj 调用 valueOf 办法返回对象本身:

obj.valueOf()    // { p: 1}

在调用 toString 办法,将其转为字符串。而上述对象默认返回 [object Object]

因而,咱们本人定义 valueOf 办法或 toString 办法,失去想要的后果。

var obj = {valueOf: function () {return 1;}
};
obj + 2        // 3

余数运算

余数的运算后果的正负号由第一个除数的正负号决定。

-1 % 2        // -1
1 % -2        // 1

自增与自减

自增和自减运算符,是一元运算符,只须要一个运算子。它们的作用是将运算子首先转为数值,而后加上 1 或者减去 1。它们会批改原始变量。

数值运算符,负数值运算符

数值运算符(+)同样应用加号,但它是一元运算符,而加法运算符是二元运算符。

数值运算符的作用在于能够将任何值转为数值(与Number 函数的作用雷同)。

+true    // 1
+[]        // 0
+{}        // NaN

负数值运算符(-),也同样具备将一个值转为数值的性能,只不过失去的值正负相同。连用两个负数值运算符,等同于数值运算符。

var x = 1;
-x    // -1
-(-x)    // 1

数值运算符号和赋值运算符,都会返回一个新的值,而不会扭转原始变量的值。

指数运算符

指数运算符(**)实现指数运算,前一个运算子是底数,后一个运算子是指数。

2 *** 4    //16

留神,指数运算符是右联合,而不是左联合。即多个指数运算符连用时,先进行最左边的计算。

// 相当于 2 ** (3 ** 2)
2 *** 3 *** 2     // 512

下面代码中,因为指数运算符是右联合,所以先计算第二个指数运算符,而不是第一个。

赋值运算符

赋值运算符(Assignment Operators)用于给变量赋值。能够与其它运算符联合。

var x = 1;
var y = x;
x += y;        // 与加号运算符联合
x >>= y        // 与位运算符联合

比拟运算符

比拟运算符用于比拟两个值之间的大小,而后返回一个布尔值,示意是否满足指定的条件。可用于各种类型的值,不限于数值。

提供 大于 (>) 小于 (<) 小于等于 (<=) 大于等于 (>=) 相等 (==) 严格相等 (===) 不相等 (!=) 严格不相等(!==) 八比拟运算符。

相等与不相等

===== 同是用于比拟两个值是否相等,然而语义的定义不尽相同。== 运算符称作雷同运算符,用于检测两个操作数是否雷同,容许类型转换。=== 运算符称作严格相等运算符,用于检测两个值的类型和值是否都相等,比拟过程不会进行类型转换。而 !=!== 也与下面一样。

对象比拟

如果比拟的是对象,会转成原始类型的值,在进行比拟。先调用 valueOf 办法,如果返回的还是对象,在调用 toString 办法。

var x = [2];
x >= '11'    // true
// 等同于 [2].valueOf

逻辑运算符

逻辑运算符是将表达式转为布尔值,共四个运算符:逻辑非 (!) 与(&&)或 (||) 三元(?:)

逻辑非

取反操作就是将布尔值变成相同值。!true == false。对于非布尔值,取反会将其转为布尔值。

!undefined    // true
!null        // true
!0            // true
!NaN        // true
!""            // true

上述值进行取反操作为 true,其它值就都会成为 false

逻辑与

逻辑与运算符(&&)对多个值进行 AND 运算。该操作只有多个值皆为真,后果才为真。两个值执行 && 操作,只有在第一个值和第二个值都为 true,它才返回true。如果其中一个为值为false,它会返回false。而第一个值为false,它会提前结束AND 操作,并返回后果false,这种行为称为 ” 短路 ”。

3-1 == 2 && 3+2 == 5    // true
3-1 == 2 && 3+2 != 5    // false

逻辑或

逻辑或运算符(||)对多个值进行 OR 运算,该操作是有一个值为真,后果就为真。而值都为假时,才为假。

3-1 == 2 || 3+2 != 5    // true
3-1 != 2 || 3+2 != 5    // false

例如下面的逻辑与操作换成逻辑或操作后,后果为true

三元运算符

三元运算符由 问号 (?) 冒号(:) 组成。如果表达式为 true,则返回第二个表达式的值,否则返回第三个表达式的值。

var x = 2;
var result = x > 1 ? 'true' : 'false';

三元表达式与 if...else 语句的成果差不多。

位运算符

对数字示意的二进制数据进行更低层级的按位运算,一共有 7 个:或 (|) 与(&)否 (~) 异或 (^) 左移 (<<) 右移 (>>) 头部补零的右移(>>>)

位运算符间接对每一个比特位(bit)间接解决,所以是十分底层的运算,速度快但不直观。

位运算符只对 32 位整数起作用,必要时,位运算会将操作数转为 32 位整数再执行。

按位或

或运算符(|)对两个整数的二进制逐位进行 OR 操作。其中一个二进制操作数相应位为 1,或者两个整数比拟的二进制位都为 1,就返回 1,否则返回 0。

5 | 6    // 7

数字 5 转为二进制为 101,数字 6 转为二进制为 110,所以进行或运算失去 111即(7

按位与

与运算符(&)对两个整数的二进制逐位进行 AND 操作。只有两个整数的二进制为皆为 1,能力返回 1,否则返回 0。

5 & 6    // 4

数字 5 转为二进制为 101,数字 6 转为二进制为 110,所以进行或运算失去 100即(4

按位否

否运算符(~)是一元运算符,位于一个整型参数之前,它将操作数的二进制位取反,即 0 转为 11 转为0

~3    // -4

数字 3 转为二进制为 32 整数模式的 0..011,对它进行按位取反则为 32 整数模式的1..100,转为十进制为-4

异或

异或运算(^)对两个操作数的二进制执行 XOR 操作。只有二进制位不同时返回1,雷同是则返回0

3 ^ 4    // 7

数字 3 转为二进制为 32 模式的 0..011,数字4 转为二进制为 32 模式的0..100,所以进行异或运算失去0..111,转为十进制为7

左移

左移运算符(<<)示意将一个数的二进制值向左挪动指定的位数,尾部补 0,即乘以2 的指定次方。要挪动二进制的数有第一个操作数决定,而挪动的位数有第二个操作数决定。

3 << 4    // 48

数字 3 向左挪动 4 位,从二进制中看0..011,失去0..11000,转为十进制为48

右移

右移运算符(>>)示意将一个数的二进制值向右挪动指定的位数,即除以 2 的指定次方,为负数,最高位补0;为正数,最高位补1。要挪动二进制的数有第一个操作数决定,而挪动的位数有第二个操作数决定。

-48>>4    // -3

数字 -48 向右挪动 4 位,从二进制中看10..011000,失去10..011,转为十进制为-3

头部补零的右移

头部补零的右移(>>>)与右移(>>)只有一个差异,就是 >>> 操作的头部一律补零,与原先符号无关。该运算失去的值皆为负数。

-48>>>4    // 268435453

数据类型

数据类型分为两类:原始类型(primitive type)和对象类型(object type)。

原始类型分为 数字 (Number) 字符串 (String) 布尔值 (Boolean)。还有两个非凡的原始值为undefinednull

ES6新增了 Symbol 类型。BigInt 是在Symbol 之后新增加的数字类型。这两个也是原始类型。

对象类型是 Object类型,它能够存储数据汇合和更简单的实体。

Number

Number 类型代表整数和浮点数,因为 JavaScript 外部都是以 64 位浮点数模式存储数字类型。

let pi = 3.14159;
0.1+0.2 === 0.3    // false

浮点数不是准确的值,因而小数的运算要小心。例如下面的例子 0.1+0.2 等于 0.300000000000000040.3 不相等。

Number 类型能够做四则运算等等。除了惯例的数字,还包含 ” 非凡数值 ”:无穷大 (Infinity) 负无穷大 (-Infinity) 非数字(NaN)

1 / 0    // Infinity
1 / -0    // -Infinity
1 - 'A'    // NaN    次要呈现在将字符串解析成数字出错的场合

BigInt

JavaScript 中 Number 无奈示意 $2^{53}-1$ 或小于 $-(2^{53}-1)$ 的整数。

有时须要加密或奥妙读取的工夫戳时,这个范畴就不够了。

BigInt 类型是最近被增加到 JavaScript 中的,用于示意任意长度的整数。能够通过 n 附加到整数字段的末端来创立 BigInt 值。

const bigInt = 98765432100123456789n;

String

String 类型代表字符串,必须应用引号括起来,单引号或双引号皆可,能够应用 length 计算字符串长度。

let name = "Jam";
name.length        // 3

JavaScript 应用 Unicode 字符集存储字符,还能够间接应用 Unicode 示意字符。例如 \172,输入时是 z

Boolean

boolean 类型仅蕴含 truefalse。用于示意正确与否。

let isReal = 2 != 3;    // true

会被转为 false 的值有 undefinednullfalse0NaN 和 空字符串。其它值会被转为 true

null & undefined

null 属于原始类型中的非凡值,代表 ” 无 ”、” 空 ” 等未知值。不是其它编程语言中的 “ 对不存在的 object 的援用 ” 或者 “null指针 ”。

undefined 也属于原始类型中的非凡值,代表 ” 未定义 ”。与 null的用法差不多,都代表值不存在,带语义不一样。对于没有初始化的变量、函数调用时候提供的函数参数、缺失的对象属性,它们的默认值就是 undefined

let name;    // undefined
name = "Any";
name = undefined;    // 不倡议
name = null;    // 倡议

变量被申明,但未被赋值,那它的值就是 undefined。而在有值后要批改为无值,不倡议应用 undefined。能够应用 nullnull将一个 ” 空 ” 或者 ” 未知 ” 的值写入变量。而 undefined 保留作为未进行初始化的变量的默认初始值。

Symbol

Symbol 类型的实例是惟一且不可变的;一种惟一标识符,可用作对象的惟一属性名。

let id1 = Symbol('id');    // id 是形容为 "id" 的 Symbol
let id2 = Symbol('id');
id1 == id2;    // false

能够调用 Symbol.description 属性来显示形容信息。

“ 暗藏 ” 属性

Symbol 容许创建对象的 “ 暗藏 ” 属性,这些属性都不能拜访或重写。

let student = {
    name: 'XiaoMing',
    age: 23
};
let sid = Symbol('sid');    // 学号
student[sid] = 2020;
student[sid];    // 2020 能够应用 Symbol 作为键拜访数据

Symbolfor...in 循环中会被跳过,不会显示。

for (var val in student) {console.log(val);
}
/*
name
age
*/

全局 Symbol

通常所有的 Symbol 都不同,即便它们由雷同的名字。但为了实现雷同的 Symbol 具备雷同的实体。有一个全局 Symbol 注册表。咱们能够创立并拜访,确保每次拜访雷同名字的 Symbol时,返回的都是雷同的 Symbol

let id = Symbol.for('id');    // 从全局注册表中读取,如果不存在,则创立
let idAgain = Symbol.for('id');    // 再次读取
id == idAgain;    // true, 雷同的 Symbol

还能够应用 Symbol.keyFor(sym) 通过 Symbol 返回该 Symbol 的名称。

let name = Symbol.for('name');
Symbol.keyFor(name);    // name

Object

Object 类型是重要的数据类型,它能够存储 “ 键值对 ”(key-value),是无序的合乎数据汇合。

var o1 = {
    val1: 123,
    val2: function() {...}
}
// 动态创建
var o2 = {}
o2.val1 = 123;
o2.val2 = function() {...}

咱们能够应用点运算符读取对象的属性 o.val1 或者应用方括号o['val1']

类型转换

typeof 运算符能够返回参数的类型。对解决不同类型或类型测验时有用。反对两种语法:运算符模式typeof x 或 函数模式typeof(x)

typeof undefined    // underfined
typeof 3            // number
typeof 3n            // bigint
typeof true            // boolean
typeof "string"        // string
typeof null            // object
typeof Math            // object

应用 String(value) 将值转换为字符串类型。应用 Number(value) 将值转换为 Number 类型。应用 Boolean(value) 将值转换为布尔类型。对 undefined 进行数字转换时,输入为 NaN,而非 0。而 "0" 和只有空格的字符串进行布尔转换时,输入为 true

Array

Array 数组类型是值的有序汇合。数组中的元素都是依照插入程序排序的。

数组申明

创立数组的办法总共有如下几种:

let arr = new Array();
let arr = [];
// 也能够间接增加元素
let arr = ['Jan', 'Aug', 'Mar'];

任何类型的数据都能够放入数组。通过下标获取数组的元素 arr[0]

数组是一种非凡的对象。应用 typeof 效验 arr 属于的类型。

typeof arr    // object

遍历

数组能够应用 for...in 遍历。

arr.test = 23;
for (var i in arr) {console.log(i);
}
/*
0
1
2
test
*/

然而 for...in 还会遍历非数字键。举荐应用 while 或者 for 循环遍历。

存取方法

Array 数组反对push/popshift/unshift 两种存取形式。

push 办法会在末端增加一个元素;pop 办法会在末端取出一个元素;shift 会在首端取出一个元素;unshift 办法会在首端增加一个元素。

队列是一种先进先出的线性构造,咱们能够应用 push/shift 两种办法来进行实现。

栈是一种后进先出的数据结构,咱们能够应用 push/pop 来实现。

这就是数组的两种利用。

函数

咱们会在许多中央执行许多类似的操作。而这些操作能够应用函数来进行构建。而在 JavaScript 中函数实际上是对象。每个函数都是 Function 类型的实例。

函数申明

应用 function 关键字来申明代码块。

function add(x, y) {    // add 为函数名
    return x + y;
}

函数还能够通过一个表达式申明。

var add = function(x, y) {return x + y;};
// 调用形式
add(10, 20);    // 30

函数表达式实际上就是一个匿名函数,存储在变量中,不须要函数名,通过变量名调用。

咱们还能够应用 “ 箭头函数 ” 来代替函数表达式:

var add = (x, y) => {return x + y;};
// 或者
var add = (x,y) => x + y;
add(10, 20);    // 30

下面的函数是两种写法:(...args) => expression(...args) => {statements}

咱们也能够应用 Function 构造函数来申明。

var sub = new Function(
    'x',
    'y',
    'return x - y'
);
// 调用形式
sub(20, 10);    // 10

反复申明

同一函数被屡次申明,后面的函数会被前面的函数笼罩。

function add(x, y) {return x - y;}
add(10, 20);    // 30
function add(x, y) {return x + y;}
add(10, 20);    // 30

函数作为值

JavaScript 中函数名自身就是变量,所以函数能够作为值来应用。但凡应用值的中央,都能够应用函数。

function sub(func, x) {return func - x;}
sub(add(10, 20), 25);    // 5

下面的函数将 add 函数作为参数传递给了它。

函数晋升

函数会像申明变量一样,被晋升到代码头部。

add(10, 20);    // 30
function add(x, y) {return x + y;}

因而,如上的 add 调用不会报错。

函数外部属性

函数外部有两个属性:argumentsthis

arguments 是一个类数组对象,蕴含传入的所有参数。

function min(x, y) {console.log(arguments);
    return Math.min(x, y);
}
min(10, 20);
/*
[Arguments] {'0': 10, '1': 20}
10
*/

arguments 带有一个 callee 属性,返回它所对应的原函数。

function factorial(num) {if (num <= 1) {return 1;}
    return num * arguments.callee(num - 1);
}
factorial(3);    // 6

咱们通过 arguments.callee,达到调用函数本身的目标。

this 就是函数运行时所在的环境对象。

在全局执行环境中应用 this,浏览器中指window 对象。

在没有对象的状况下调用 this 的值为 undefined

例如,这里雷同的函数被调配给两个不同的对象,在调用中有着不同的 “this” 值:

let user = {name: 'John'};
let admin = {name: 'Admin'};

function sayHi() {return this.name;}
// 两个对象中应用雷同的函数
user.func = sayHi;
admin.func = sayHi;

// 调用不同的 this 值
user.func();    // John
admin.func();    // Admin

函数属性和办法

name 属性

name 用于返回申明函数的名称。

sub.name;    // sub
length 属性

length 用于返回函数的形参个数。

sub.length;    // 2
prototype 属性

每个函数都有一个prototype 属性,这个属性指向一个对象,这个对象就是原型对象。原型对象的作用就是定义所有实例对象共享的属性和办法。实例对象能够看作从原型对象衍生进去的子对象,原型对象上的所有属性和办法都被实例对象共享。

toString()办法

toString 用于返回对于函数源码的字符串。

sub.toString()    
/* function sub(func, x) {return func - x;}
*/

闭包

闭包(closure)是一个函数读取其它函数外部变量的函数。变量的作用域分为全局变量和局部变量,局部变量是只有被函数外部读取,内部读取不了,因而就会用到闭包。

function clos1() {
    var x = 10;
    function clos2() {return x++;}
    return clos2;
}
var result = clos1();
result();    // 10

上述代码中,函数 clos1 的返回值就是clos2 函数,clos2读取了 clos1 中的外部变量 x,在调用函数时,会获得clos1 中的外部变量。函数 clos2 就是闭包。

闭包最大的两个用途就是读取函数外部的变量和让这些变量的值始终保持在内存中。

result();    // 11

result 实际上就是闭包 clos2 函数,它一共运行两次,第一次返回 10 之后,做了 ++ 操作的 x 的值为11;下一次调用的值就会返回 11

起因是 clos2 被赋给了一个全局变量,导致 clos2 始终在内存中,而 clos2 依赖于 clos1,因而 clos1 也始终在内存中,不会在调用完结后,被垃圾回收机制(garbage collection)回收。

立刻调用的函数表达式(IIFE)

依据 JavaScript 语法,在下面的例子中 funcname() 函数名 +() 就能够调用函数。

function iife() {console.log("立刻调用");
}();    // 报错 SyntaxError: Unexpected token ')' 

而想要解决该问题就是让 function 不要呈现的行首,让引擎将其了解成一个表达式。

(function iife() {console.log("立刻调用");
}());
// 或者
(function iife() {console.log("立刻调用");
})();
// 了解调用

上述两种都以( 圆括号结尾,引擎会认为前面跟的是表达式,而不是函数定义语句,所以会防止谬误。这就叫 “ 立刻调用的函数表达式 ”(Immediately-Invoked Function Expression)。

如果想要查看更多文章,请关注公总号「海人的博客」

退出移动版