环境准备
js基础知识

准备工作


Nodejs

  • Nodejs是服务器端运行JavaScript的开源、跨平台运行环境。关于nodejs的环境管理可参考之前的文章nvm管理多版本nodejs
  • npm:软件包管理器,通过它,可以方便的发布、分享Nodejs的库和源代码

开发工具

  • Nodejs:官方网站淘宝镜像站
  • Visual Studio Code:推荐的IDE,官方网站
  • Node Exec插件:vs code插件,代码自动完成能力,将写的js跑在nodejs上(F8运行js脚本,F9停止)

相关文档

JS基础


以下内容为个人笔记,便于翻阅,其内容都能从官方文档中获取,建议直接对照官方文档学习即可

js是动态语言,弱类型语言

  • 弱类型,不需要强制类型转换,会隐式类型转换

常量、变量和注释

  • 注释和C、Java一样,//单行注释,/* 注释 */ 多行注释
  • JS的标识符区分大小写
  • 声明和初始化
    • var声明一个变量, var会把变量提升到当前全局或函数作用域,不建议使用,应该先声明后使用
    • let声明一个块作用域中的局部变量,可参考语句块和作用域部分
    • const声明一个常量,不可变,如果明确知道一个标识符定义后不再修改,应该尽量声明成const常量,减少被修改的风险
    • JS中的变量声明和初始化是可以分开的

数据类型

JS是弱类型,不需要强制类型转换,会隐式类型转换

  • number:数值型,包括整型和浮点型
  • boolean:布尔型,true和false
  • string:字符串
  • null:只有一个值null
  • undefined:变量声明未赋值的;对象未定义的属性
  • symbol:ES6 新引入类型,可参看symbol类型
  • object类型:是以上基本类型的复合类型,是容器
  • NaN,即Not a Number,转换数字失败。它和任何值都不等,和自己也不等,只能使用Number.isNaN(NaN)
// 类型转换
// 弱类型
console.log('=====string=====')
console.log(a = 3 + 'jerry', typeof(a), typeof(typeof(a)))
console.log(a = null + 'jerry', typeof(a))
console.log(a = undefined + 'jerry', typeof(a))
console.log(a = true + 'jerry', typeof(a))
// 数字
console.log('=====number=====')
console.log(a = null + 8, typeof(a))
console.log(a = undefined + 8, typeof(a)) //undefined没法转换成一个对应的数字
console.log(a = true + 8, typeof(a))
console.log(a = false + 8, typeof(a))
// boolean
console.log('=====bool=====')
console.log(a = null + true, typeof(a))
console.log(a = null + false, typeof(a))
console.log(a = undefined + true, typeof(a)) //undefined没法转换成一个对应的数字
console.log(a = undefined + false, typeof(a)) // NaN
console.log(a = null & true, typeof(a))
console.log(a = undefined & true, typeof(a))
// 短路
console.log(a = null && true, typeof(a)) // 逻辑运算符,null 直接就是false短路
console.log(a = false && null, typeof(a)) // 逻辑运算符,false短路返回false
console.log(a = false && 'jerry', typeof(a)) // boolean
console.log(a = true && 'jerry', typeof(a)) // 字符串
console.log(a = true && '', typeof(a)) // 字符串
// null
console.log('=====null=====')
console.log(a = null + undefined, typeof(a))

// 短路补充
console.log(a = [] && 'jerry', typeof(a))
console.log(a = {} && 'jerry', typeof(a))

通过实验,我们可以得出如下结论:

  1. 遇到字符串,加号就是拼接字符串,所有非字符串隐式转换为字符串。
  2. 如果没有字符串,加号把其他所有类型都当数字处理,非数字类型隐式转换为数字。undefined特殊,因为它都没有定义值,所以转换数字失败得到一个特殊值NaN
  3. 如果运算符是逻辑运算符,短路符,返回就是短路时的类型。没有隐式转换

字符串

  • 将一个值使用单引号或者双引号引用起来就是字符串,ES6提供了反引号定义一个字符串,可以支持多行和插值
  • 转义字符
let a = 'abc'
let b = "123"
let c = `line1
line2
line3
` 
console.log(c)

let name="jerry", age = 20
console.log(`Hi, my name is ${name}. I am ${age}`)
  • 操作方法,与python类似,可在IDE中具体实验各个方法即可
let s = 'abcdefg'
console.log(s.charAt(2))
console.log(s[2])
console.log(s.toUpperCase())
console.log(s.concat('hij')) // 连接
console.log(s.slice(3)) // 切片,支持负索引
console.log(s.slice(3,5))
console.log(s.slice(-2, -1))
console.log(s.slice(-2))
let url = "www.monkeyjerry.top"
console.log(url.split('.'))
console.log(url.substr(7,2)) // 返回子串从何处开始,取多长
console.log(url.substring(7,10)) // 返回子串,从何处开始,到什么为止
console.log(url.indexOf('mo'))
console.log(url.indexOf('jer', 4))
console.log(url.replace('.top', '.com'))
s = ' \tmonkey jerry \r\n'
console.log(s.trim()) // 去除两端的空白字符。trimLeft、trimRight是非标函数,不建议使用
console.log('-'.repeat(30))

数值型

  • 二进制0b/0B,八进制0o,十六进制0x/0X,指数如1E3,1e-2
  • 数字类型还有三种符号值:+Infinity(正无穷)、-Infinity(负无穷)和 NaN (not-a-number)(非数字)
  • 常量属性:Number.MAX_VALUE,Number.MIN_VALUE,Number.POSITIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NaN
  • 常用方法:Number.parseFloat()(把字符串参数解析成浮点数,和全局方法 parseFloat() 作用一致)、Number.parseInt()(把字符串解析成特定基数对应的整型数字,和全局方法 parseInt() 作用一致)、Number.isFinite()、Number.isInteger()、Number.isNaN()(NaN唯一判断方式)
  • 内置数学对象Math:Math提供了绝对值(Math.abs())、对数指数运算(Math.log2()等)、三角函数运算、最大值、最小值、随机数(Math.random())、开方(Math.sqrt())等运算函数,提供了PI值(Math.PI)

运算符

  • 运算符优先级从高到低. []() new! ~ - + ++ -- typeof void delete* / %+ -<< >> >>>< <= > >= in instanceof== != === !==&^|&&||?:= += -= *= /= %= <<= >>= >>>= &= ^= |=,
  • 几点补充说明
    • == 宽松相等,进行类型转换;=== 严格相等,不进行类型转换;建议比较的时候,一律使用 ===!==
    • instanceof 判断是否属于指定类型,注意如下例子中的实验,区别于python。它必须明确使用类型定义变量,就是对象必须是new关键字声明创建的。它可以用于继承关系判断
    • typeof 返回类型为字符串
    • delete 删除一个对象(an object)或一个对象的属性(an object's property)或者一个数组中某一个键值(an element at a specified index in an array)
    • in 如果指定的属性在对象内,则返回true,注意如下例子中的实验,区别于python(在JS中可以理解为key是否在对象中,Array对象对应有key值0,1,2......),对比参考for...in和for...of
console.log('============instanceof============')
console.log('a' instanceof String) // false
console.log(1 instanceof Number) // false
a = new String('b')
console.log(a instanceof String) // true
console.log(new Number(1) instanceof Number) // true
console.log(a instanceof Object) // true
console.log(typeof('a')) //string
console.log(typeof 'a') //string
console.log(typeof a) //object

console.log('==========delete==============')
x = 42;
var y = 43;
let z = 60;
myobj = new Number();
myobj.h = 4; // create property h
console.log(delete x); // returns true (can delete if declared implicitly)
console.log(delete y); // returns false (cannot delete if declared with var)
console.log(delete z); // returns false

console.log(delete Math.PI); // returns false (cannot delete predefined properties)
console.log(delete myobj.h); // returns true (can delete user-defined properties)
console.log(delete myobj); // returns true (can delete if declared implicitly)
console.log('~~~~~~~~~~~~~~~~~~~~')
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
for(var i=0;i<trees.length;i++)
    console.log(trees[i])
console.log('~~~~~~~~~~~~~~~~~~~~')
delete trees[3]; // 数组中元素被删除,但空着的位置是undefined
trees.pop(); // 如果需要真的删除可使用pop(),但只能删除数组中的最后一个元素
for(var i=0;i<trees.length;i++)
    console.log(trees[i])

console.log('==========in==============')
console.log(0 in trees); // returns true ,0在数组对象的index中
console.log(3 in trees); // returns true ,3在数组对象的index中
console.log(6 in trees); // returns false,6不在数组对象的index中
console.log("bay" in trees); // return false,bay不是属性,它是值
console.log("length" in trees); // returns true,length是对象的属性
console.log('~~~~~~~~~~~~~~~~~~~~')
delete trees[3];
console.log(3 in trees); // return false
for(var i=0;i<trees.length;i++)
console.log(trees[i]);
console.log('~~~~~~~~~~~~~~~~~~~~')
// Custom objects
let mycar = {
    color: "red",
    year: 1998
};
console.log("color" in mycar); // returns true
console.log("model" in mycar); // returns false
console.log('year' in mycar) // true

表达式

  • 基本表达式,和Python差不多
  • 生成器推荐使用生成器函数,ES6开始支持
function * inc() {
    let i = 0, j = 7
    while (true) {
    yield i++
    if (!j--) return 100
    }
}

let gen = inc()
for (let i=0;i<10;i++)
    console.log(gen.next())

每次调用next() 方法返回一个对象,这个对象包含两个属性:value 和 done,value 属性表示本次yield 表达式的返回值,done 属性为布尔类型。done是false表示后续还有yield语句执行,如果执行完成或者return后,done为true

控制语句

语句块和作用域

  • JS使用大括号{}构成语句块,从ES6开始支持 块作用域 ,let只能在块作用域内可见
  • 严格模式"use strict",若需要使用,这句必须放到首行。如a = 100隐式声明不能提升声明,在“严格模式”下则会报错
// "use strict"   //若需要使用严格模式,这句必须放到首行
function hello() {
    let a = 1;
    var b = 2;
    c = 3  // 若为严格模式,则会报错
}

// let d = 100

{
    let d = 4;
    var e = 5;
    f = 6
    if (true) {
    console.log(d)
    console.log(e)
    console.log(f)
    console.log('-------------')
    g = 10
    var h = 11
    }
}
hello();

// console.log(a) // 不可访问,局部变量
// console.log(b) // 不可访问,局部变量
console.log(c) 
console.log(d) // 块作用域使用let,不可访问;如果在块外,可访问
console.log(e) // 块作用域使用var,可访问
console.log(f) // 块作用域隐式声明,可访问
console.log(g) // 可访问
console.log(h) // 可访问

流程控制

  • if...else
if (cond1){
}
else if (cond2) {
}
else if (cond3) {
}
else {
}

false、undefined、null、0、NaN、空字符串等等效为false,其他值均视为true

  • switch...case
switch (expression) {
    case label_1:
        statements_1
        [break;]
    case label_2:
        statements_2
        [break;]
        ...
    default:
        statements_def
        [break;]
}
  • for
for ([initialExpression]; [condition]; [incrementExpression])
{
    statement
}
  • while和do...while
// while
while (condition)
    statement
// do...while
do
    statement
while (condition);
  • for...in

    • 对象操作语句for...in来遍历对象的属性
    for (variable in object) {
        statements
    }
    
    • 举例
        let arr = [10, 20, 30, 40]
    
    for (let x in arr)
        console.log(x) // 返回索引
    
    for (let index in arr)
        console.log(`${index} : ${arr[index]}`); //插值
    
    // C风格
    for(let i=0;i<arr.length;i++)
        console.log(arr[i]);
    
    
    let obj = {
        a:1,
        b:'jerry',
        c:true
    }
    
    console.log(obj.a);
    console.log(obj['b']);
    console.log(obj.d); // undefined
    
    for (let x in obj)
        console.log(x); // 属性名
    
    for (key in obj)
        console.log(`${key} : ${obj[key]}`);
    
    • for...in 循环返回的是索引或者key,需要间接访问到值
    • 若为数组,用for...in返回的是索引,C风格for循环操作可能更方便点
    • 若为对象,用for...in比较合适。
  • for...of

    • ES6的新语法
    • for ... of 不能迭代一个普通对象
    • 举例
    let arr = [1,2,3,4,5]
    let obj = {
        a:1,
        b:'magedu',
        c:true
    }
    for (let i of arr) { // 返回数组的元素
        console.log(i)
    }
    
    // 异常,不可迭代
    // for (let i of obj) {
    //    console.log(i)
    // }
    
  • break、continue

    • break 结束当前循环
    • continue 中断当前循环,进入下一次循环

Symbol

  • ES6提供Symbol类型,内建原生类型
  • 常用于构建常量
let sym1 = Symbol()
let sym2 = Symbol('key')
let sym3 = Symbol('key')
console.log(sym2 == sym3) // false,symbol值是唯一的

// 以前用法
var COLOR_RED = 'RED';
var COLOR_ORANGE = 'ORANGE';
var COLOR_YELLOW = 'YELLOW';
var COLOR_GREEN = 'GREEN';
var COLOR_BLUE = 'BLUE';
var COLOR_VIOLET = 'VIOLET';

// 现在可用Symbol
const COLOR_RED = Symbol();
const COLOR_ORANGE = Symbol();
const COLOR_YELLOW = Symbol();
const COLOR_GREEN = Symbol();
const COLOR_BLUE = Symbol();
const COLOR_VIOLET = Symbol();

函数

函数表达式

  • 使用表达式来定义函数,表达式中的函数名可以省略
  • 如果这个函数名不省略,也只能用在此函数内部
// 匿名函数
const add = function(x, y){
    return x + y;
};
console.log(add(4, 6));

// 有名字的函数表达式
const sub = function fn(x, y){
    return x - y;
};
console.log(sub(5, 3));
//console.log(fn(3, 2)); // fn只能用在函数内部

// 有名字的函数表达式
const sum = function _sum(n) {
    if (n===1) return n;
    return n + _sum(--n) // _sum只能内部使用
}
console.log(sum(4));

高阶函数及箭头函数

  • 高阶函数:函数作为参数或返回值是一个函数
// 返回值是函数
const counter = function (){
    let c = 0;
        return function(){
            return ++c;
        };
};

const c = counter()
console.log(c())
console.log(c())
console.log(c())

// 函数作为参数
const map = function (arr, fn) {
    let newarr = [];
    for (let i in arr) {
        newarr[i] = fn(arr[i]);
    }
    return newarr
}
console.log(map([1, 2, 3, 4], function (x) { return x++ }))
console.log(map([1, 2, 3, 4], function (x) { return ++x }))
console.log(map([1, 2, 3, 4], function (x) { return x + 1 }))
console.log(map([1, 2, 3, 4], function (x) { return x += 1 }))
  • 箭头函数:就是匿名函数,它是一种更加精简的格式
// 函数作为参数
const map = function (arr, fn) {
    let newarr = [];
    for (let i in arr) {
        newarr[i] = fn(arr[i]);
    }
    return newarr
}
console.log(map([1, 2, 3, 4], (x) => { return x * 2 }));
console.log(map([1, 2, 3, 4], x => { return x * 2 }));
console.log(map([1, 2, 3, 4], x => x * 2));
  • 箭头函数参数
    • 如果一个函数没有参数,使用()
    • 如果只有一个参数,参数列表可以省略小括号(),多个参数不能省略小括号,且使用逗号间隔
  • 箭头函数返回值
    • 如果函数体部分有多行,就需要使用{},如果有返回值使用return
    • 如果只有一行语句,可以同时省略大括号和return
    • 只要有return语句,就不能省略大括号,必须有大括号
    • 如果只有一条非return语句,加上大括号,函数就成了无返回值了,例如 console.log(map([1,2,3,4], x => {x*2})); 加上了大括号,它不等价于 x =>{return x*2}

函数参数

  • 普通参数
    • 一个参数占一个位置,支持默认参数
    • JS中并没有Python中的关键字传参
    • JS只是做参数位置的对应
    • JS并不限制默认参数的位置
const add = (x,y) => x + y
console.log(add(1, 2)); // 3
console.log(add(1, 3, 5, 7)); // 4
console.log(add(1)) // NaN

const add1 = (x=1,y) => x + y   // 语法正确 JS并不限制默认参数的位置
console.log(add1(2, 4)); // 6 
console.log(add1(2)); // NaN // 相当于add1(2, undefined)
console.log(add1(y=2)); // NaN  // JS没有关键字传参
console.log(add1(x=2, y=4)); // 6
console.log(add1(y=2, x=4)); // 6 //JS只是做参数位置的对应
console.log(add1(a=2, b=4)); // 6  
console.log(add1(a=2)); // NaN
console.log(add1(a=2, b=4, c=10)); // 6

虽然JS并不限制默认参数的位置,但是还是建议默认参数写到后面,这是一个好的习惯

  • 剩余参数rest parameters
    • 可理解为python的可变参数,python使用*收集多个参数;JS使用...
  • arguments对象
    • 函数的所有参数会被保存在一个arguments的键值对对象中
    • ES6开始,不推荐,建议使用可变参数。为了兼容而保留
const sum = function (...args) {
    let result = 0
    for (let x in args) {
        result += args[x]
    }
    console.log('----------------')
    for (let y of arguments)
        console.log(y)
    return result
};

console.log(sum(3, 6, 9, 10))
  • 参数解构
    • 于python类似,JS是用...而已
    • JS参数解构,不需要解构后的值个数和参数个数对应
const add = (x, y) => {console.log(x,y);return x + y};
console.log(add(...[100,200]))
console.log(add(...[100,200,300,3,5,3]))
console.log(add(...[100]))
  • 函数返回值
    • JS的函数返回值是单值,如果return是赋值表达式则为等号右边的值;如果return是逗号表达式则为最后一个表达式的值(类C语言)

数组解构和对象解构

  • 数组解构
    • 变量从左到右和元素对齐,剩余参数必须放到最右边
    • 能对应到数据就返回数据,对应不到数据的返回默认值,如果没有默认值返回undefined
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a, b, c);

const [d, e, f, g] = arr;
console.log(d, e, f, g);
// d = 100; // TypeError: Assignment to constant variable.

const [h, i] = arr;
console.log(h, i);

// [j, ...args, k] = arr; // SyntaxError: Rest element must be last element
const [j, k, ...args1] = arr;
console.log(j, k, args1);

const [l, m, n, o, ...args2] = arr;
console.log(l, m, n, o, args2);

const [p = 10,q,,,,s = 100] = arr;
console.log(p, q, s);
----------------- 输出结果-----------------
1 2 3
1 2 3 undefined
1 2
1 2 [ 3 ]
1 2 3 undefined []
1 2 100
  • 对象解构
    • 根据属性名找到对应的值
    • 没有找到的返回缺省值,没有缺省值则返回undefined
const obj = {
    a: 100,
    b: 200,
    c: 300,
};

var {a, b, c} = obj;
console.log(a, b, c);

var {a, c} = obj;
console.log(a, c);

var {a, b, c, d} = obj;
console.log(a, b, c, d);

var {x, y, z} = obj;
console.log(x, y, z);

var {a:m, b:n, c} = obj; // 重命名,别名
console.log(m, n, c);

var {a:m, b:n, d:x=400} = obj; // 缺省
// console.log(a, b, d); // 不可这么访问,不报错是因为上面定义了a,b,d,否则包undefined错误
console.log(m, n, x); // 必须用别名访问
--------------------- 输出结果 ------------------------
100 200 300
100 300
100 200 300 undefined
undefined undefined undefined
100 200 300
100 200 400
  • 嵌套结构解构
const arr = [1, [2, 3], 4];
const [a, [b, c], d] = arr;
console.log(a, b, c, d); //1 2 3 4

const [e, f] = arr;
console.log(e, f); //1 [ 2, 3 ]

const [g, h, i, j = 18] = arr;
console.log(g, h, i, j); //1 [ 2, 3 ] 4 18

const [k, ...l] = arr;
console.log(k, l); //1 [ [ 2, 3 ], 4 ]
  • 对象的嵌套例子:提取出所有的a的值
var data = {
    a: 100,
    b: [
        {
            c: 200,
            d: [],
            a: 300
        },
        {
            c: 1200,
            d: [1],
            a: 1300
        },
    ],
    c: 500
};

var { a: m, b: [{ a: n }, { a: n1 }] } = data;
console.log(m, n, n1)

数组和对象操作

  • 数组操作
    • push(...items): 尾部增加多个元素
    • pop():移除最后一个元素,并返回它
    • map():引入处理函数来处理数组中每一个元素,返回新的数组
    • filter():引入处理函数处理数组中每一个元素,此处理函数返回true的元素保留,否则该元素被过
      滤掉,保留的元素构成新的数组返回
    • foreach():迭代所有元素,无返回值
const arr = [1, 4, 6]
const newarr = arr.forEach(x => x+10); // 无返回值, 不能得到foreach处理后的数组
console.log(newarr, arr);

var narr = [];
arr.forEach(x => narr.push(x+10));
console.log(narr, arr);
  • 对象操作(Object的静态方法)
    • Object.keys(obj):ES5开始支持。返回所有key
    • Object.values(obj):返回所有值,试验阶段,支持较差
    • Object.entries(obj):返回键值对,试验阶段,支持较差
    • Object.assign(target, ...sources):使用多个source对象,来填充target对象,返回target对象
const obj={
    a: 100,
    b: 200,
    c: 300
};

for(let x in obj) {
    console.log(x, obj[x])
};

console.log(Object.keys(obj));
console.log(Object.values(obj));
console.log(Object.entries(obj));
---------------输出结果--------------------
a 100
b 200
c 300
[ 'a', 'b', 'c' ]
[ 100, 200, 300 ]
[ [ 'a', 100 ], [ 'b', 200 ], [ 'c', 300 ] ]
const obj={
    a: 100,
    b: 200,
    c: 300
};

var obj1 = Object.assign({}, obj);
var obj2 = Object.assign({f:100, c:200}, obj, {d:400, e:500}, {a: 900});
console.log(obj1);
console.log(obj2);

var obj3 = new Object(obj);
console.log(obj3);
-------------输出结果--------------------
{ a: 100, b: 200, c: 300 }
{ f: 100, c: 300, a: 900, b: 200, d: 400, e: 500 }
{ a: 100, b: 200, c: 300 }

异常处理

  • Js的异常语法和Java相同,使用throw关键字抛出
  • 使用throw关键字可以抛出任意对象的异常
  • try...catch 语句捕获异常
  • try...catch...finally 语句捕获异常,finally保证最终一定执行
  • catch不支持类型,也就是说至多一个catch语句。可以在catch的语句块内,自行处理异常
try {
    //throw new Error('new error');
    //throw new ReferenceError('Ref Error');
    //throw 1;
    //throw new Number(100);
    // throw 'not ok';
    // throw [1,2,3];
    // throw {'a':1};
    throw () => { }; // 函数
} catch (error) {
    console.log(error);
    console.log(typeof (error));
    console.log(error.constructor.name);
} finally {
    console.log('===end===')
}

参考


  • magedu