JavaScript 101

JavaScript语法快速入门指南


  • 变量名:字母_$开头,使用Unicode字符集,可以用各种字符

  • 变量声明用var / let / const,var是全局作用域,有变量提升,letconst是块级作用域,无提升

  • 注释://单行;/**/多行但不能嵌套

  • 数据类型

    • 原始类型: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转换为原始值Primitive
    • Object.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严格模式下无效);调用函数时的参数个数和它的参数列表不一定要相符
  • 迭代器与生成器

    • 生成器:function*yieldyield*将另一个迭代器的值作为返回值。可以直接迭代,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
      9
      function 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

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×