环境准备
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))
通过实验,我们可以得出如下结论:
- 遇到字符串,加号就是拼接字符串,所有非字符串隐式转换为字符串。
- 如果没有字符串,加号把其他所有类型都当数字处理,非数字类型隐式转换为数字。undefined特殊,因为它都没有定义值,所以转换数字失败得到一个特殊值NaN
- 如果运算符是逻辑运算符,短路符,返回就是短路时的类型。没有隐式转换
字符串
- 将一个值使用单引号或者双引号引用起来就是字符串,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使用
...
- 可理解为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参数解构,不需要解构后的值个数和参数个数对应
- 于python类似,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