Js基础语法

tutorial

变量

1
2
let message; //声明
message = 'hello'; //赋值

变量

数据类型

  • number — 可以是浮点数,也可以是整数,
  • bigint — 用于任意长度的整数,
  • string — 字符串类型,
  • boolean — 逻辑值:true/false
  • null — 具有单个值 null 的类型,表示“空”或“不存在”,
  • undefined — 具有单个值 undefined 的类型,表示“未分配(未定义)”,
  • object 和 symbol — 对于复杂的数据结构和唯一标识符,我们目前还没学习这个类型。

值比较

=== 表示比较时,不做任何的类型转换

控制

if

1
2
3
4
5
6
7
8
9
et year = prompt('In which year was ECMAScript-2015 specification published?''');

if (year < 2015) {
  alert'Too early...' );
else if (year > 2015) {
  alert'Too late' );
else {
  alert'Exactly!' );
}

三目运算符:

1
let result = condtion ? value1 : value2;

控制合并运算符

1
2
3
result = a ?? b;
// 等同于
result = (a != null && a != undefined) ? a : b;

如果a是已定义的,则结果为a; 否则结果为 b

循环

1
2
3
while(condition){
// loop body
}
1
2
3
do{
// loop body
}while(condition);
1
2
3
for(begin; condtion; step){
// loop body
}

跳出循环: breakcontinue

break/continue 标签

1
2
3
4
5
6
7
8
9
10
outerfor (let i = 0; i < 3; i++) {
  for (let j = 0; j < 3; j++) {
    let input = prompt(`Value at coords (${i},${j})`'');
    // 如果是空字符串或被取消,则中断并跳出这两个循环。
    if (!input) {
    break outer; // (*)
}
    // other ..
  }
}

单使用break和continue,只能跳出当前循环,
但如果使用标签,就可以跳出到标签的那层

switch

1
2
3
4
5
6
7
8
9
10
11
12
switch(x){
case 'value1':
...
break;
case 'value2':
...
break;
...
default:
...
break;
}

函数

1
2
3
function showMessage() {
alert('Hello a!');
}

形式:

1
2
3
function name(param1, param2, ... parmN) {
... body ...
}

函数表达式

1
2
3
4
5
6
let sayHi = function(){
alert('Hello function expression!');
};

//使用
sayHi();

箭头函数

形式:

1
let func = (arg1, arg2, ..., argN) => expression;

可以认为是如下形式的缩写:

1
2
3
let func = function(arg1, arg2, ..., argN){
return expression;
};

示例:

1
2
3
let sum = (a, b) => a+b;

alert(sum(1,2)); //3

对象

基础语法

1
2
let user = new Object(); //构造函数 语法
let user = {}; //字面量 语法
1
2
3
4
5
6
7
8
9
10
let user = {
name : "John", //key "name", value "John"
age : 30 //key "age", value 30
}

user['like birds'] = true; // 新增 value类型为booleans
user['age'] = 40; //修改值

delete user['like birds']; //删除

”in“操作符:

1
2
3
4
let user = {name:'John', age:30};

alert("age" in user); //true
alert("blabla" in user); //false

for…in :

1
2
3
4
5
6
7
8
9
10
11
let user = {
  name"John",
  age30,
  isAdmintrue
};
for (let key in user) {
  // keys
  alert( key );  // name, age, isAdmin
  // 属性键的值
  alert( user[key] ); // John, 30, true
}

对于对象中的各个属性的顺序
整数属性会被进行排序,其他属性则按照创建的顺序显示。

对象引用和复制

1
2
3
4
5
let user = {name:'John'};
let admin = user; //复制引用
alert(user === admin); //true
admin.name = 'Pete'; //通过 'admin'引用来修改
alert(user.name); //'Pete', 修改能通过"user"引用看到
1
2
3
4
5
6
7
8
9
10
let user = {name:'John', age:30};
let clone = {};
//
for(let key in user)
{
clone[key] = user[key];
}
//修改
clone.name = "Pete";
alert(user.name); //原对象的name属性还是 John

以上的流程,可以通过assign方法来达到,语法:

1
Object.assign(dst, [src1, src2, src3...])
1
2
3
4
5
6
let user = {name:'John'};
let permission1 = {canView: true};
let permission2 = {canEdit: true};
//将 permission1, permission2的所有属性拷贝到user中
Object.assign(user, permission1, permission2);

1
2
3
4
5
let user = {name:'John'};
Object.assign(user, {name, "Pete"}); //属性已存在时,会覆盖
alert(user.name) // "Pete"
//写法也可以是
let clone = Object.assign({}, user); //将user拷贝到空对象并返回赋值给clone

深层克隆
原始类型,上边的复制方式可以完成,但如果属性是对其他对象的引用,则需要使用其他方式进行深层克隆。

1
2
3
4
5
6
7
8
9
let user = {
  name"John",
  sizes: {
    height182,
    width50
  }
};
let clone = Object.assign({}, user);
alert(user.sizes === clone.sizes); //true, 表示引用的是同一对象

深层克隆,需要考虑使用其他实现,例如: lodash 库的 _.cloneDeep(obj)

对象方法 this

1
2
3
4
5
6
7
8
9
10
let user = {
name : "John",
age : 30,
sayHi() {
alert(this.name); //this 指的是当前对象
}
};

//使用
user.sayHi();

构造

_构造函数_:

1
2
3
4
5
6
7
8
9
10
function User(name) {
//this = {}; {隐式创建}
//添加属性到this
this.name = name;
this.isAdmin = false;
// return thi; (隐式返回)
}

//使用new 进行构造
let user = new User('Jack');

在一个函数内部,我们可以使用 new.target 属性来检查它是否被使用 new 进行调用了。

可选链

可选链 ?. 语法有三种形式:

  1. obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined
  2. obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined
  3. obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined
1
2
3
let user = null;
alert(user?.address); //undefined
alert(user?.address.street); //undefined

symbol类型

根据规范,只有两种原始类型可以用作对象属性键:

  • 字符串类型
  • symbol 类型

symbol 有两个主要的使用场景:

  1. “隐藏” 对象属性。
    如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 symbol 并使用它作为属性的键。symbol 属性不会出现在 for..in 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。

    因此我们可以使用 symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。

  2. JavaScript 使用了许多系统 symbol,这些 symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内建行为。例如,在本教程的后面部分,我们将使用 Symbol.iterator 来进行 迭代 操作,使用 Symbol.toPrimitive 来设置 对象原始值的转换 等等。

属性的getter和setter

访问器属性由 “getter” 和 “setter” 方法表示。在对象字面量中,它们用 get 和 set 表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let user = {
  name"John",
  surname"Smith",

  get fullName() {
    return `${this.name} ${this.surname}`;
  }

set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
};

// set fullName 将以给定值执行
user.fullName = "Alice Cooper";
alert(user.name); // Alice
alert(user.surname); // Cooper

数据类型

数组

1
2
3
//创建
let arr = new Array();
let arr = [];
1
2
3
4
5
6
7
8
9
10
11
12
let fruits = ["Apple", "Orange", "Plum"];
alert(fruits[0]); //Apple
alert(fruits[1]); //Orange
alert(fruits[2]); //Plum

fruits[2] = "Pear"; //修改
fruits[3] = "Lemon"; //新增

alert(fruits.pop()); //将 Lemon 移除出去
fruits.push("Plum"); //末尾添加
alert(fruits.shift()); //取出首端第一个 Apple
fruits.unshift("Apple"); //首端添加元素

遍历:

1
2
3
4
5
6
7
8
9
10
let fruits = ["Apple", "Orange", "Plum"];
for(let i=0; i<arr.length; i++){
alert(arr[i]);
}
for(let fruit of fruits){
alert(fruit);
}
for(let key in fruits){
alert(fruits[key]);
}

Map和Set

Map

  • new Map() —— 创建 map。
  • map.set(key, value) —— 根据键存储值。
  • map.get(key) —— 根据键来返回值,如果 map 中不存在对应的 key,则返回 undefined
  • map.has(key) —— 如果 key 存在则返回 true,否则返回 false
  • map.delete(key) —— 删除指定键的值。
  • map.clear() —— 清空 map。
  • map.size —— 返回当前元素个数。
  • map.keys() —— 遍历并返回一个包含所有键的可迭代对象,
  • map.values() —— 遍历并返回一个包含所有值的可迭代对象,
  • map.entries() —— 遍历并返回一个包含所有实体 [key, value] 的可迭代对象,for..of 在默认情况下使用的就是这个。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    let recipeMap = new Map([
      ['cucumber'500],
      ['tomatoes'350],
      ['onion',    50]
    ]);

    // 遍历所有的键(vegetables)
    for (let vegetable of recipeMap.keys()) {
      alert(vegetable); // cucumber, tomatoes, onion
    }

    // 遍历所有的值(amounts)
    for (let amount of recipeMap.values()) {
      alert(amount); // 500, 350, 50
    }

    // 遍历所有的实体 [key, value]
    for (let entry of recipeMap) { // 与 recipeMap.entries() 相同
      alert(entry); // cucumber,500 (and so on)
    }

从一个已有普通对象创建一个map:

1
2
3
4
5
6
7
let obj = {
  name"John",
  age30
};
//Object.entries 返回键/值对数组:[ ["name","John"], ["age", 30] ]
let map = new Map(Object.entries(obj));
alert( map.get('name') ); // John

从Map创建对象:

1
2
3
4
5
6
7
8
9
let prices = Object.fromEntries([
  ['banana'1],
  ['orange'2],
  ['meat'4]
]);
// 现在 prices = { banana: 1, orange: 2, meat: 4 }
alert(prices.orange); // 2

//使用Object.fromEntries从Map得到一个普通对象

Set

  • new Set(iterable) —— 创建一个 set,如果提供了一个 iterable 对象(通常是数组),将会从数组里面复制值到 set 中。
  • set.add(value) —— 添加一个值,返回 set 本身
  • set.delete(value) —— 删除值,如果 value 在这个方法调用的时候存在则返回 true ,否则返回 false
  • set.has(value) —— 如果 value 在 set 中,返回 true,否则返回 false
  • set.clear() —— 清空 set。
  • set.size —— 返回元素个数。
  • set.keys() —— 遍历并返回一个包含所有值的可迭代对象,
  • set.values() —— 与 set.keys() 作用相同,这是为了兼容 Map
  • set.entries() —— 遍历并返回一个包含所有的实体 [value, value] 的可迭代对象,它的存在也是为了兼容 Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let set = new Set();
let john = { name"John" };
let pete = { name"Pete" };
let mary = { name"Mary" };
// visits,一些访客来访好几次
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// set 只保留不重复的值
alert( set.size ); // 3
for (let user of set) {
  alert(user.name); // John(然后 Pete 和 Mary)
}

日期和时间

1
2
3
4
5
6
7
8
9
10
11
// 0 表示 01.01.1970 UTC+0
let Jan01_1970 = new Date(0);
alertJan01_1970 );
// 现在增加 24 小时,得到 02.01.1970 UTC+0
let Jan02_1970 = new Date(24 * 3600 * 1000);
alertJan02_1970 );
// 31 Dec 1969
let Dec31_1969 = new Date(-24 * 3600 * 1000);
alert(Dec31_1969);
// 自动解析
let date = new Date("2017-04-25");

从 Date 对象中访问年、月等信息有多种方式:
getFullYear()
获取年份(4 位数)
getMonth()
获取月份,从 0 到 11
getDate()
获取当月的具体日期,从 1 到 31,这个方法名称可能看起来有些令人疑惑。
getHours()getMinutes()getSeconds()getMilliseconds()
获取相应的时间组件。

getTime()
返回日期的时间戳 —— 从 1970-1-1 00:00:00 UTC+0 开始到现在所经过的毫秒数。
getTimezoneOffset()
返回 UTC 与本地时区之间的时差,以分钟为单位:
getDay()
获取一周中的第几天,从 0(星期日)到 6(星期六)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function diffSubtract(date1, date2) {
  return date2 - date1;
}
function diffGetTime(date1, date2) {
  return date2.getTime() - date1.getTime();
}

function bench(f) {
  let date1 = new Date(0);
  let date2 = new Date();
  let start = Date.now();
  for (let i = 0; i < 100000; i++) f(date1, date2);
  return Date.now() - start;
}
alert'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );
alert'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );

Json方法

JSON.stringfy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let student = {
  name'John',
  age30,
  isAdminfalse,
  courses: ['html''css''js'],
  spousenull
};

let json = JSON.stringify(student);
alert(typeof json); // we've got a string!
alert(json);
/* JSON 编码的对象:
{
  "name": "John",
  "age": 30,
  "isAdmin": false,
  "courses": ["html", "css", "js"],
  "spouse": null
}
*/

函数进阶

基本语法

1
2
3
4
5
6
class MyClass{
constructor(){...}
method1(){...}
method2(){...}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
class User{
constructor(name){
this.name = name;
}
sayHi(){
alert(this.name)
}
}

//用法:
let user = new User("John");
user.sayHi();

类继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal{
constructor(name){
this.name=name;
this.speed=0;
}
run(speed){
this.speed = speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
stop(){
this.speed = 0;
alert(`${this.name} stands still.`);
}
}

class Rabbit extends Animal {
hide() {
alert(`${this.name} hides!`);
}
}

let rabbit = new Rabbit("little white");
rabbit.run(5);
rabbit.hide();

Promise、async/await

模块

什么是模块?

一个模块(module)就是一个文件。一个脚本就是一个模块。

  • export 关键字标记了可以从当前模块外部访问的变量和函数。
  • import 关键字允许从其他模块导入功能。
1
2
3
4
//sayHi.js
export function sayHi(user){
alert(`Hello, ${user}!`);
}
1
2
3
4
5
// main.js
import {sayHi} from './sayHi.js';

alert(sayHi); // fucntion..
sayHi('John'); // Hello, John