JavaScript语法快速入门指南
变量名:字母_$开头,使用Unicode字符集,可以用各种字符
变量声明用
var / let / const
,var是全局作用域,有变量提升,let
和const
是块级作用域,无提升注释:
//
单行;/**/
多行但不能嵌套数据类型
- 原始类型:
Boolean / Null / Undefined / Number / BigInt / String / Symbol
- Object:Array / RegExp / Date
- 原始类型:
运算符
+ - * / % **
?:
> / < / >= / <= / == / != / === / !===
& | ^ ~ >> << >>>
- 优先级与C类似:成员(
. []
) > 调用(() new
) > 一元(! ~ - + -- ++ typeof void delete
) > 四则 > 移位 > 关系(> < >= <= in instanceof
) > 判等 > 与异或 > 逻辑与或 > 条件(?:
) > 赋值 > 逗号
控制语句
if...else...else if
while(...){} / do{}while(...)
for循环
for(;;)
for(in)
:以任意顺序迭代对象的可枚举属性for(of)
:用于可迭代对象,比如Array / Map / String / TypedArray / arguments
可以在for和while循环前加一个标签,这样在嵌套的时候就可以指定break或者continue哪一个循环了
switch语句中的case可以是任意常量、变量、表达式,在判等时使用的是===操作符,不会进行类型转换
with(location){}
语句可以省去对象名和后面的.,在使用变量时会先查找全局变量,再查找location对象的属性解构赋值
数组
[x, y]=[y, x]
- 可以带默认值
[x=1, y=2]=z
- 可以忽略
[,y]=z
- 可以用剩余参数
[x, ...y]=z
对象
- 定义时解构:
let {a, b}={a:1, b:2};
- 非定义时解构:
({a, b}={a:1, b:2});
必须带括号 - 修改名称:
({a:aa, b:bb}=obj);
- 带默认值:
({a:aa=1, b=2}=obj);
- 定义时解构:
解构赋值可以用于函数定义中,也可用于
for...of
中解构赋值可以嵌套,如
({a:[x,y]}=obj)
判等
===
判断类型和值都相同,不会自动转换,NaN!==NaN
,+0===-0
。用于Array的indexOf和lastIndexOf方法==
自动转换再判等,规则如下表,其中 ToNumber(A) 表示转换为数字,这与 +A 效果相同,ToPrimitive(A) 通过尝试调用.toString()和.valueOf()方法,将A转换为原始值PrimitiveObject.is
同值相等,即二者是否在任何情况下都可以相互替换,+0和-0不同,但NaN和NaN相同- 同值零相等:在同值相等的基础上认为+0和-0相等。用于
Set / Map / TypedArray / ArrayBuffer / Array
和String的includes
方法
A == B | Undefined | Null | Number | String | Boolean | Object |
---|---|---|---|---|---|---|
Undefined | true | true | false | false | false | IsFalsy(B) |
Null | true | true | false | false | false | IsFalsy(B) |
Number | false | false | A === B | A === ToNumber(B) | A=== ToNumber(B) | A== ToPrimitive(B) |
String | false | false | ToNumber(A) === B | A === B | ToNumber(A) === ToNumber(B) | ToPrimitive(B) == A |
Boolean | false | false | ToNumber(A) === B | ToNumber(A) === ToNumber(B) | A === B | ToNumber(A) == ToPrimitive(B) |
Object | false | false | ToPrimitive(A) == B | ToPrimitive(A) == B | ToPrimitive(A) == ToNumber(B) | A === B |
判断类型用typeof操作符,括号可省略,返回值是以下字符串:
undefined、boolean、string、number、object、function、symbol(ES6)
typeof null
是”object”- 对于任意实现了
[[Call]]
方法的对象都会返回”function”,所以除了Function外的构造函数都是”object”
如果想进一步判断是什么类型的object,用instanceof操作符
8进制字面量用0开头,如果无效则按十进制解析,在严格模式下不接受8进制字面量
Number
new Number()返回对象
Number()将非数值转换为数值
- 忽略前导0,与+等
- 对于null、””、false返回0,对于true返回1,不符合规范的字符串和undefined返回NaN,对象会依次调用valueOf和toString方法。
- 还可使用parseInt(str[,radix])、parseFloat,后者只支持十进制,且若结果为整数会返回整数
常量:EPSILON / MAX_SAFE_INTEGER($2^{53}-1$) / MAX_VALUE / MIN_SAFE_INTEGER / MIN_VALUE / NaN / NEGATIVE_INFINITY / POSITIVE_INFINITY
方法:isNaN / isFinite / isInteger / parseInt / parseFloat
BigInt:大整型,在数字后面加n或者用构造函数
Object
new Object() / {}
访问属性:
obj.foo.bar / obj["foo"]["bar"]
列出属性
- for…in只会列出可枚举属性
- Object.keys(o)返回o所有可枚举属性的数组
- Object.getOwnPropertyNames(o)返回o所有属性的数组
用b in a检查属性是否存在,delete a.b删除非继承的属性
getter和setter
- 在对象初始化器中,可以通过在方法前加get表明是在访问属性时调用的函数,加set表明是在设置属性时调用的函数
- 为对象添加属性时用Object.defineProperty(obj, “name“, {get: function() {}, set: function(x) {}});
Object对象的方法
.assign(target, …sources)将源自己的可枚举属性值赋值给目标,会调用源的[[GET]]和目标的[[SET]],是引用的话就是浅拷贝
.create(proto[,property])创建以proto为原型的对象,第二个参数与.defineProperties的第二个参数相同
.defineProperty(target, name, descriptor)定义新属性,也可用.defineProperties(target,{name1:descriptor1,…})
.entries()和.fromEntries可以转换为键值对列表或者转换回来
.getOwnProperty系列
- Descriptor / Descriptors:获取描述符
- Names:获取名称,包括不可枚举属性,但不包括Symbols
- Symbols:获取Symbol属性
.[get/set]PrototypeOf:获取/设置[[Prototype]]属性,和
.__proto__
相同.keys / .values:获取可枚举属性名/值
String
- ‘’ / “” / ``(支持内部使用${}变量替换)
- 字符串不可变,操作只会返回新串
- 使用String()函数或.toString()方法可以转为字符串,但null和undefined没有后者,对字符串使用这个方法会返回一个副本,对数值使用时可以传入一个参数以指定基数
- charAt和charCodeAt返回给定位置的字符/字符值
- fromCharCode接收一串字符编码并转为String
- concat拼接
- 取子串用slice / substring / substr,区别在于前两个参数格式为区间[a,b),第三个的参数格式为(start, length),在传入负数时,slice会加上字符串长度,substr第一个参数会加,第二个参数置零,substring都置零;
- indexOf / lastIndexOf查询子串位置,可选参数为起始搜索位置;
- trim返回一个去除首位空格的副本;
- toLowerCase / toUpperCase / toLocale…大小写;
- match相当于RegExp.exec,search返回位置,replace替换;
- split分割,可选参数为截取的数组长度;
- localeCompare用于比较
Array
创建:
[1,2] / [new] Array(1,2) / Array.of(1,2) / Array.from(_[,mapFn[,this]])
注意
new Array(length)
传入单个值的时候视为数组长度,如果不是整数会报错用数组字面量创建数组时空着的元素为undefined,但结尾的逗号被忽略(早期会报错)
判断:Array.isArray()
索引查询越界返回undefined,对元素赋值或者修改length可以改变数组大小
indexOf() / lastIndexOf()寻找元素位置,includes()判断元素存在
keys / values / entries,返回键/值/键值对迭代器,键为0,1,2,…
flat()展开嵌套数组
数组操作
不修改原数组:slice / concat / join
直接修改:
push / pop / shift / unshift / sort / reverse / splice
splice(start[,delete_num[,items_to_add]])
:从起始位置删除[全部/若干]元素[并插入新元素],支持负值sort([fn])
:将元素转为String后按UTF-16非降序排列,默认fn类似(a,b)=>a-b,ES标准中未要求a=b时ab顺序不变,新版本的浏览器一般支持。- 以上各函数无明显意义时返回array的新长度
遍历方法
- every(),判断是否所有元素都满足条件,但对于空数组返回true;
- some(),判断是否存在元素满足条件
- find(),返回第一个满足条件的元素
- findIndex(),返回第一个满足条件的元素的位置
归并方法:
- filter(),对每一项运行给定函数,返回结果为true的元素构成的数组
map(f(value[,index[,array]])) / flatMap()
,对每一项运行给定函数,返回由结果构成的数组。需要注意传入的参数有3个,运行[“1”,”2”].map(parseInt)会出错,因为parseInt接收两个参数reduce(f(acc, value[,index[,array]])[,initial])
和reduceRight(),接收两个参数,归并函数和可选的归并基础,归并函数接收4个参数,前一个值、当前值、当前索引、数组对象
填充数组用
fill(value[, start[, end]])
Boolean
- new Boolean([value])
- 如果忽略value或者是
+0 / -0 / Null / false / NaN / undefined / "" / document.all
,那么生成的Boolean对象的值为false,否则为true,即使是”false”也一样 - 需要注意的是,在条件语句中(如if),除undefined和null之外的对象都视为true,所以不能用值为false的Boolean对象
- 如果要将某个东西转换为布尔型,用Boolean()或者!!(),不要用构造函数
Symbol
foo=Symbol([description])
- 每次调用会创建一个新的symbol,symbol主要用于表示对象属性,比如a[foo]=bar
Map
创建:new Map([iterable]),如[[1,a], [2,b]]
合并:new Map([…map1, …map2]),如果有重复,后面的会覆盖前面的
访问元素
- set / has / get / delete / clear(清空)
- keys / values / entries,返回键/值/键值对迭代器
- .forEach / for(let [k, v] of map){} / for(let p of map){}
获取大小用size属性
map[‘a’]=1只是设置Map对象的属性,无法被has等方法访问
Map和Object的区别
- 有size属性、有性能优势
- 键可以是任意值,函数、对象、基本类型;Object的键只能是字符串或者Symbols
- 可直接进行迭代;而Object需要先获取它的键数组。
- 键值是有序的,迭代时会按插入顺序迭代;对象中的键则不是 (但自ES6对象保留字符串和Symbol键的创建顺序)
- Object 都有自己的原型,原型链上的键名有可能产生冲突 (自ES5可以用Object.create(null)创建一个没有原型的对象)
Set
创建:new Set([iterable])
转Array:[…set]
访问元素
- add / has / delete / clear(清空)
- keys / values / entries,返回键/值/键值对迭代器(键和值相等)
- .forEach / for(let v of set){}
获取大小用size属性
Function
每个函数事实上都是一个Function对象,
(function(){}).constructor===Function
,可以用new Function ([*arg1*[, *arg2*[, ...*argN*]],] *functionBody*)
创建函数(这样的函数没有闭包,eval创建的有闭包).length属性获取接收的参数个数,方法有apply / bind / call / toString
函数不能重载,后面的函数定义会覆盖之前函数的定义;函数声明存在提升,但如果是用初始化语句方式定义的函数则不会提升。
在被调用时会自动获得this和arguments变量
- this取决于函数的运行时环境,在全局函数或者嵌套函数中为window,如果是作为某个对象的方法调用那么就指向这个对象,在调用时指定this使用
.apply(that, [1, 2, 3])
,.call(that,1,2,3) / .bind(that)
方法返回一个绑定了this的函数 - 箭头函数会捕捉作用域中的this而不重新定义
- arguments是一个类似Array的对象,用于访问参数数组,它的内容与参数值保持同步,修改一个另一个也会改变;arguments.callee属性指向了这个函数(ES5严格模式下无效);调用函数时的参数个数和它的参数列表不一定要相符
- this取决于函数的运行时环境,在全局函数或者嵌套函数中为window,如果是作为某个对象的方法调用那么就指向这个对象,在调用时指定this使用
迭代器与生成器
- 生成器:
function*
,yield
,yield*
将另一个迭代器的值作为返回值。可以直接迭代,f.next()会返回{value:xxx, done:true/false},传入next的参数会作为yield语句的返回值,但第一次调用next时传入的参数被忽略 - 内置迭代器:Set / Map / String / Array / TypedArray
- 自定义迭代器:需要实现@@iterator方法,即有以Symbol.iterator为键的属性,例如
var myIterable = { *[Symbol.iterator]() { yield 1; yield 2; yield 3; } }
- 生成器:
Date对象表示日期
- new Date()返回包含当前时间的对象,Date()返回当前时间的字符串
- Date.now()返回当前时间
- Date.parse()解析日期字符串(不推荐使用)
- Date.UTC()接收的参数为 年月日时分秒,月份从0开始,其余正常;Date()也支持这种用法,只不过是基于本地时间
- 注意:月份从0开始;当某个数值大于合理值时其他的会相应调整,比如new Date(2013,13,1)实际上是2014-02-1
JSON(JavaScript Object Notation)
- 只有parse()和stringify()两个方法
- 属性名称必须是双引号括起来的字符串;最后一个属性后不能有逗号。
- 禁止出现前导零( JSON.stringify 方法自动忽略前导零,而在 JSON.parse 方法中将会抛出 SyntaxError);如果有小数点, 则后面至少跟着一位数字。
基本包装类型:基本类型(Boolean、Number、String)不是对象,逻辑上说不应该有方法,而实际上有,是在后台创建了相应类型的对象,调用其方法,然后销毁,所以我们不能在运行时为它们添加属性
单体内置对象Global和Math
- 不属于其他任何对象的属性和方法就属于Global对象,如isNaN之类的,它还包含encodeURI和encodeURIComponent方法,用于将非标准字符编码为标准字符,但前者不会处理:/?#,相应地还有decode…函数
- 在浏览器中window即为Global
void加表达式,表示没有返回值
- 点击javascript:链接时会用返回值替换当前页面,除非是undefined,所以加void
- 如果要定义函数并立即执行,可以void function(){}();
用
...
可以展开数组和对象,let obj2={...obj1}
进行对象的(浅)拷贝在try…catch…finally结构中finally一定会执行,就算在try/catch中return了也是这样,并且可以覆盖它们的返回值
Promise是一个代理对象,有pending/fulfilled/rejected三种状态,后两者合称settled
- 约定传入的第一个函数在传入的第一个函数在成功时被调用,第二个函数在失败时被调用
- 用.then(f,g)添加回调函数,.catch(g)是.then(null,g)的缩写,回调函数f和g可以返回新的promise,或者用throw将控制权交给下一个处理错误的回调函数
- 在本轮事件循环完成之前,回调函数不会被调用
- 即使promise已经处于settled状态,还是可以用then / catch添加新的回调函数
- 在异步函数中(async function(){})可以使用await语法糖获取promise成功后的返回值,失败值需要用try{}catch(){}捕捉。这样可以简化有多个promise时的写法。
1
2
3
4
5
6
7
8
9function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
};
拓展内容:Reflex / Proxy / WeakSet / WeakMap / TypedArray / ArrayBuffer / DataView