JavaScript基础

用看得懂的话写de教程。

数据类型

JavaScript的原始数据类型包括Number、String、Boolean、Null、Undefined、Symbol(ES6新增)、BigInt(最近新增),此外有Object类型。

Number

不区分整数和浮点,注意浮点计算结果是不精确的(由于浮点数的计算方式),对浮点数不建议直接用等号判断两值相等,应当看他们的差是否小于某个值(如0.001)。Infinity(超过Number最大值)、NaN(Not a Number)也是合法的Number。

关于NaN

NaN是特殊的Number,它也不等于自己,只能通过isNaN()判断。

String

单引号‘’或双引号""包裹的字符串。

Boolean

Boolean包括'true'、'false'两种取值。比较运算符、&&、||与!都会产生布尔值。

转换结果为false的取值

NaN、null、undefined、0与空字符串转Boolean的结果都为false。

null&undefined

null表示空值,undefined表示未赋值,undefined可以用于判断函数参数是否传递。他们是两种数据类型。

null和undefined的注意事项

  1. null是保留字,而undefined不是。

  2. 如果访问不存在的变量,会报错"var is not defined";访问已声明但未赋值的变量才会得到undefined。

  3. typeof null的结果是object,但null也是一种基本类型而非object,这个混淆是由于typeof以内存低位判断数据类型,object和null的低3位都是0。

Object

键-值对的无序集合。键(key)只能是字符串类型,值(value)可以是任意类型。'.'可以用于表示键路径,比如obj.key或obj.obj.a。

Object是引用类型,存储的是指针,而其他基本类型存储值。

let obj = {key:'value',
           obj:{
             a:'a'
           }}

Array

用[]或new Array()创建,数组可以包含任意类型元素并且提供了相当多的方法。Array属于Object类型。

Date

Date类型提供了丰富的与时间、日期相关的方法,Date()返回当前日期的字符串。Date也属于Object类型。

Map&Set ES6

ES6新增的数据结构。

Map是一组key-value对结构,key不能重复,否则只保留最新的值。与对象只支持string与symbol相比,Map 的key支持任意类型。

let students = new Map([['Lucy',90],['Peter',80],['Bill',85]])
students.get('Lucy')//90
students.has('Peter')//true
students.delete('Peter')//true
students.get('Peter')//undefined
students.set('Bill',90)//Map { 'Lucy' => 90, 'Bill' => 90 }
students.get('Bill')//90

Set类似集合,由一组不重复的key组成,否则只保留一个。

let foods = new Set(['Chicken','Noodles','Rice'])
foods.add('fish')//Set { 'Chicken', 'Noodles', 'Rice', 'fish' }
foods.delete('Noodles')//true
foods.has('Rice')//true

Function

有趣的是,函数是Object的实例,而Object也是函数的实例。

Symbol ES6

用于产生唯一标识,除了自己等于自己,两个完全相同的symbol不相等,常用于对象属性、声明唯一常量、定义私有属性。也可以用Symbol.for()创建symbol,如果参数一致,创建的symbol相等。

let s = Symbol("")
let s1 = Symbol("")
s == s1//false
s === s//true
let s2 = Symbol.for("")
let s3 = Symbol.for("")
s2 == s3//true

你可以通过以下方式获取Symbol的description(无需记忆)。

let s2 = Symbol("love")
let obj = {[s2]:"and peace"}
obj//{ [Symbol(love)]: 'and peace' }
Object.getOwnPropertySymbols(obj)//[ Symbol(love) ]
Reflect.ownKeys(obj)//[ Symbol(love) ]

Symbol定义了对象的许多实用方法,包括[Symbol.Iterator]、[Symbol.match]、[Symbol.replace]、[Symbol.split]、[Symbol.toPrimitive]、[Symbol.toStringTag]等。toStringTag方法甚至能改变对象的toString方法。

BigInt ES6

在数字末尾加n可声明BigInt,其可操作大于Number所能表示最大数的数(2^53)。该类型尚在提案过程中,在新版Chrome与Node中得到实现。

带小数的运算会被取整。

let Num = BigInt(Math.pow(2,63))
let Num2 = 100n

typeof&instanceof

typeof返回字符串有以下结果“number”、“string”、“boolean”、“object”、“function”、“undefined”、“symbol”,null打印为“object”。而instanceof用于判断检测对象的类型,包括"Array"、"Function"、"Object"及自定义类/构造函数等。

此外Object.prototype.toString.call()可以准确打印出Null的类型。也可以通过访问".constructor"获取构造函数判断类型。

let a = []
typeof a//'object'
a instanceof Array//true

class y{}
let t = new y()
t instanceof y//true

动态类型&类型转换

作为动态语言,JS允许同一个变量在不同时间用作不同类型。

使用JavaScript函数转换

例如全局方法(构造函数)String()、Number()、Date()以及变量的toString()方法等。不同类型还会有独有的方法比如Date变量的getDate()、getDay(),Number变量的toPrecision()等。

使用JavaScript自动转换

变量类型会根据需要发生类型转换,例如:

5 + null //5 因为null=0
5 + undefined //NaN 因为undefined转为数字是NaN
"0"+ null //"0null" 因为null="null"
"5" + 1 //"51" 因为1="1"
1 + "5" //"51" 因为1="1"
"5" - 1 //4 因为"5"=5
[1,2] + 1//"1,21" 数组先转字符串,再加"1"
if("str"){
  //这里的代码将会执行,因为“str”可以转为true
}

可以观察到含字符串类型会转为字符串,没有或不能转字符串的话转数字(除加号以外,结果转数字)。

自动转换有一些基础规则,比如Boolean值的转换:true等于1,false等于0,空字符串、空数组和null等于0,非纯数字字符串转为NaN等。

有趣的是,"0"可以转为Boolean的“true”,但“0”转为数字0之后再转Boolean就会变成“false”。空数组也可以转“true”,转数字之后也为0。

基本类型&基本类型包装

除了Object类型存储的是引用,所有类型都是基本类型(存储值),但除了null和undefined,他们都像对象一样拥有自己的方法。这不是因为基本类型具有方法,而是在调用基本类型的方法时,JS引擎自动包装了基本类型,调用结束后销毁对象。

因此,向基本类型添加属性是无效的,因为添加完成后临时对象即被销毁,但可以向其原型添加属性和方法。

var str = 'str'
str.toUpperCase()
/*
相当于做了这些事
var _str = new String(str)
str = _str.toUpperCase()
*/

变量声明 Declaration

var

变量用var声明,不用var则作为全局变量。var声明的变量处于全局作用域或函数作用域。

变量提升 Hoisting

用var声明的变量,可以在声明语句之前使用,但不会初始化(赋值)。因此访问他们虽然不会报错,但会得到undefined。

let&const ES6

ES6中新增了let与const关键字,分别代表块级作用域中的变量与常量,同时不允许重复声明,没有变量提升。

const定义的对象并非常量,const仅保持变量的值(即指针)不变,如果要声明对象常量,则应该使用Object.freeze()。

块级作用域

由{}包裹的代码块。在for循环中,()与{}是父子块级作用域,也就是说{}用let或const声明的变量不会影响for循环计数。

块级作用域没有变量提升,可以防止在函数内使用上级变量时,后面声明的变量意外覆盖上级变量。

使用var声明变量:

var h=10;
function print(){
console.log(h)
var h;//覆盖了上级变量
}
print()//undefined

使用let声明变量:

var h=10;
function print(){
console.log(h)//暂时性死区
let h;//与当前作用域绑定,声明之前不可读取
}
print()//ReferenceError: Cannot access 'h' before initialization

同时块间的隔离有助于减少冲突和出错。此前,JS只能用函数作用域来隔离变量,常用的方式就是匿名立即执行函数(匿名IIFE)。

(function(){var hours=12}())
//等同于
{let hours=12}

暂时性死区 temporal dead zone

let和const声明的变量会与代码块绑定,在声明前不能使用同名的上级环境变量,否则会引发报错。

function

function关键字用于声明函数。

class ES6

class关键字用于声明类。

import ES6

import关键字用于导入其他模块的变量。

全局变量

var和function声明的全局变量会成为顶级对象(如window、global、self)的属性,而let、const、class、import不会。

判断变量是否存在

由于直接使用未声明的变量会报错,可以用try{}包裹代码,也可以用typeof variable == "undefined"判断。

命名规范

变量名由26字母的大小写、数字、“$“和”_“组成,不能用数字开头。甚至支持中文,但不建议使用,避免引发麻烦。

函数声明

推荐使用函数表达式let func=()=>{}let func=function(){}为变量赋值,因此函数声明也遵循以上规则。如果用function func(){}直接声明函数,ES5中函数声明能完整地提升,ES6虽然规定了行为类似let,但实际可能会先赋值为undefined,不同环境可能有不同的处理。

运算符

比较运算符 ==与===

==表示在类型转换后相等,===表示类型和值都一样。除非需要用到==的特性,否则建议用===比较。

比较对象

对象的比较与原始值不同,比较的是引用,因此两个完全相同的数组不相等,除非他们是对同一处的引用。

'与'运算符 &&

‘与’运算符,如果左边表达式的值是false或可以转为false则返回左边表达式的值,否则返回右边表达式的值。

Boolean角度:&&只有当两边都为true,结果才为true,如果左边结果为false,右边不会判断。

'或'运算符 ||

‘或’运算符,如果左边表达式的值是true或可以转为true则返回左边表达式的值,否则返回右边表达式的值。

Boolean角度:||只有当两边都为false,结果才为false,如果左边结果为true,右边不会判断。

扩展运算符 ... ES6

ES6中的扩展运算符,用在数组或对象前表示取出所有项或属性。

用于对象

let obj={a:1,b:2}
let newObj={...obj,c:3}//{ a: 1, b: 2, c: 3 }

用于数组

赋值

生成数组的拷贝。

let arr=[0,1,2]
let newArr=[...arr,3,4]//[ 0, 1, 2, 3, 4 ]
let arr_copy=[...arr]
解构赋值

结合解构赋值,它还提供了生成数组的方法。

let [...arr_copy]=arr

let [ar1,...ar2]=[0,1,2,3]
ar1//0
ar2//[ 1, 2, 3 ]

let [...ar3,ar4]=[0,1,2,3]
//SyntaxError: Rest element must be last element

扩展运算符只能用在最后一项。

一个分号引发的血案

let val = func()//;
[a,b]=[b,a][a[c],b[c]]=[b[c],a[c]]

如果缺少分号,JS解析会出现错误,原因是两行连在一起也符合语法规则。这种情况下

用于函数参数

除了为数组赋值,还支持作为函数参数。

let arr=[0,1,2]
function add(a,b){
  return a+b
}
add(...arr)//1

任何含Iterator接口的对象都可以通过扩展运算符转为真正的数组。详情见iterable接口

基本运算 + - * /

可以用于各种类型间的运算。

方括号 []

属性访问器。最常用的是表示数组[1,2,3]与数组下标arr[1],也可以作为对象的属性名obj['key'],支持使用变量作为属性名obj[key](key不仅可以是符合规则的字符串,也可以是Symbol)。

点运算符 .

属性访问器。点运算符的功能是[]的子集,当属性名为常量时可以用于设置、获取对象属性obj.key

关键字

new

从构造函数派生出对象,构造函数的this指向创建的对象。

delete

用于删除对象属性,不可用于删除对象。

throw

抛出异常。通常结合try...catch使用。

循环体

forEach

Array.prototype.forEach(function(currentValue, index, arr), thisValue)

由于是以传入函数的形式遍历,forEach无法使用return从外部函数体返回,由于是数组的一种方法,也不支持break跳出循环。

for…in

for(let item in obj)

for…in支持遍历各种对象,它的item是对象的key,总是string类型。这种方法不稳定,不同时候结果的顺序可能不一致。如果用于数组,还有一个问题是所有属性也包括数组元素以外的自定义属性。

for…of ES6

for(let item of obj)

首先,它没有for…in的缺点,其次,也没有forEach的缺点。for…of支持数组、字符串、Set、Map和其他有iterable接口的对象,但不支持普通对象(会提示is not iterable)。

iterable接口 ES6

对象的[Symbol.iterator]属性指向其默认的遍历器,其不仅提供了统一的接口,还能按规定的顺序排列。在ES6中,Array、String、Set和Map、arguments等类数组对象都具有iterable接口。而解构赋值扩展运算符、yield和任何与接受数组的场合都调用了Iterator接口。

let myIterable = {}
myIterable[Symbol.iterator] = function* (){
  yield 1;
  yield 2;
  yield 3;
}
[...myIterable]//[ 1, 2, 3 ]

借用数组的iterator:

let fakeArray={0:'apple',1:'peach',2:'pancake',length:3,
               [Symbol.iterator]:Array.prototype[Symbol.iterator]}
for(let it of fakeArray){console.log(it)}//apple peach pancake

待填坑 async写法、next()写法

内置对象

全局对象

浏览器中windowself指代全局对象,node环境中用global,webWorker中用self

Math

常量

E、PI、LN2(loge2或In2)、LN10、SQRT1_2(2的平方根的倒数)、SQRT2、LOG2E、LOG10E。

Math.pow()

Math.ceil()

Math.floor()

Math.round()

Math.log()

Math.sqrt()

Math.sin()

Math.cos()

Math.tan()

Math.random()

Math.exp()

Math.max()

Math.min()

Math.acos()

Math.asin()

Math.atan()

Math.atan2()

JSON

JSON不是JS,也用在很多其他语言中,作为一种简单的传递对象数据的格式。

JSON.parse()

将标准JSON格式的字符串转为JS对象,不符合格式会提示‘unexpected token',最常见的是'unexpected token u',也就是undefined,因此写接口一定要注意确定请求成功再转换对象。

JSON.stringify()

将JS对象转为JSON格式,结合parse可以用于对象深拷贝。

Date

实例化Date对象

let now = new Date()

返回Date对象,控制台输出如2019-08-16T06:06:36.238Z

仅获取表示时间的字符串

let now = Date()

如果不作为构造函数,那么Date()仅返回字符串如'Fri Aug 16 2019 13:49:46 GMT+0800 (China Standard Time)'

getFullYear

从Date对象返回年份(如2019)。

getDate

从Date对象返回天数(1~31)。

getDay

从Date对象返回星期(0~6,0是星期天)。

getHours

从Date对象返回小时(0~23)。

getMinutes

从Date对象返回分钟(0~59)。

getSeconds

从Date对象返回秒数(0~59)。

getMiliseconds

从Date对象返回毫秒数(0~999)。

getTime

从Date对象返回返回从1970年1月1日0时起的毫秒数。与valueOf方法返回一致。

setXXX

以上方法均有set版本,如setDate、setTime。

toDateString

把Date对象的日期部分转化成可读性强的字符串,如Fri Aug 16 2019

toString

把Date转化成可读性强的字符串,如Fri Aug 16 2019 14:06:36 GMT+0800 (China Standard Time),格式与直接使用Date()一致。

UTC

静态方法。UTC( Universal Coordinated Time )即国际协调时间,与GMT(格林尼治时)相同,此方法接受从年到毫秒的7个参数,返回距1970年1月1日0时的毫秒数。此外,从get/set(UTC)FullYear到get/set(UTC)Miliseconds也都有对应的UTC方法。

RegExp

Array

[String](#string 字符串)

函数简介

普通函数都是Function对象的实例,而构造函数生成的函数是Object的实例。相比普通对象,函数多了可被调用的特征。

'Function'与‘function’的不同

‘Function’是JS的内置对象,而'function'是一个声明函数的关键字。

和其他语言函数的区别

由于JS是弱类型语言,函数无法指定形参类型与返回类型,同时也无法限制传入参数的个数,因此没有重载的特性。函数内部可以通过arguments对象获取实参。

this

this指向调用当前函数的对象,如果没有对象调用,那么this==window。在箭头函数中,this指向与环境中相同。bind、call、reply方法都可以修改this。

绑定this

绑定this的4种方法,按优先级从低到高排序分别是默认绑定、隐式绑定、显式绑定、new绑定。其中apply()与call()属于显式绑定。

默认绑定

全局环境中、函数独立调用时(即使被嵌套、包括立即执行函数),this指向window。有时候被嵌套的函数想获得上层函数的this,可以使用var that = this语句传递this值。

隐式绑定/方法调用

this的值是调用该函数的对象。如a.b()中b内部的this指向a。只有直接调用obj.func()时才会传递this,否则this仍指向window。函数虽然可以属于一个对象,但函数不会与对象绑定this。

这些不能传递this的场景包括赋值、传参、内置函数(如setTimeout)、间接引用(对象的属性赋值时的立即执行函数,如(p.foo = o.foo)(),this为window)等。

var val='window'
var func = function(){
  console.log(this.val)
}
var obj={val:'obj',func:func};

func()//window
obj.func()//obj

//赋值会丢失this
var func1 = obj.func
func1()//window

//作为参数传递也会丢失this
var func2 = function(func){
  func()
}

func2(obj.func)//window

显示绑定

即使用call、apply、bind方法绑定this到对象上。JS还新增了许多内置函数提供thisValue选项,如数组的迭代方法map、forEach、filter、some、every。

new绑定

new的作用是从构造函数返回新对象。

function constructorA(){
  this.a=0
}
let obj = new constructorA()
obj.a//0

需要注意如果constructor没有返回值,那么构造的对象就是返回值。而其中的this永远指向obj,即新生成的对象,即使let newObj = new obj.constructorA(),this也会指向newObj,而不是obj。

this与函数

this的四种绑定方法其实也对应函数的四种调用方式,包括函数调用(func)、方法调用(obj.func)、间接调用(call、apply)、构造函数调用(new)。

实现bind

Function.prototype.bind(thisArg[,arg1[,arg2[,...]]])

bind提供了两个功能,一个是传递this,另一个是传递参数(之后传参数会位于这些参数后面),它返回绑定后的函数。

首先,我们要传递this,需要通过方法调用来完成,bind的this是原函数,我们要怎么返回绑定好的函数呢?🤔

可以想到,我们先把函数绑定到那个环境上去,但这就是bind的功能啊。实际上,我们应该返回一个全新函数,而这个新函数的执行结果与原函数+thisArg一致。

那么先不考虑传递参数,只考虑this的情况。

Function.prototype.myBind = function(thisValue){
  thisValue._func = this
  return function(){
    return thisValue._func()
  }
}

这样我们就已经实现了bind最基本的功能——绑定this,但我们向thisValue添加了属性。其实我们只用保证结果正确,因此可以删除添加的属性,用临时变量替代。

Function.prototype.myBind = function(thisValue){
  let func = this
  let getResult = (func,thisValue)=>{
    thisValue._func = func
    let result = thisValue._func()
    delete thisValue._func
    return result
  }
  return function(){
    return getResult(func,thisValue)
  }
}

细心的朋友不难看出,getResult和call、apply有着相似的功能,即返回绑定某个this值后的执行结果。也就是说,bind其实是基于call和apply实现的。但为了方便理解整个概念,我先介绍了bind。

call和apply

明白了基本原理之后,我们来研究如何传入参数。首先,我们应该知道所有参数都可以在aruguments对象中找到,我们先看一下它的结构:

function printArguments(){return arguments}
printArguments(1,2,3)//[Arguments] { '0': 1, '1': 2, '2': 3 }

Arguments是一个特殊的对象,它不是数组,但具有数组的许多特征,这里把它当作数组。

先看function.call(thisArg,arg1,arg2,...),我们需要把后面的参数传入函数。

Function.prototype.myCall = function(){
  let [thisValue,...parameters] = arguments
  thisValue._func = this
  let result = thisValue._func(...parameters)
  delete thisValue._func
  return result
}

再看看function.apply(thisArg,[argsArray]),我们转换一下参数形式就行。需要注意我们的argsArray不能为空,如果为空,parameters为undefined,再使用扩展运算符就会报错is not iterable

Function.prototype.myApply = function(){
  let [thisValue,parameters] = arguments
  thisValue._func = this
  let result = thisValue._func(...parameters)
  delete thisValue._func
  return result
}

可以完善的一些地方

如果没有传this值,我们可以让默认值为window,如果没有传argsArray,让默认值为[],这里拿apply作为例子。

Function.prototype.myApply = function(){
  let [thisValue,parameters] = arguments
  if(!thisValue)thisValue = window
  if(!parameters)parameters = []
  thisValue._func = this
  let result = thisValue._func(...parameters)
  delete thisValue._func
  return result
}

我们的bind也可以改造成使用apply的模式。

Function.prototype.myBind = function(){
  let func = this
  let [thisValue,...parameters] = arguments
  return function(){
    return func.myApply(thisValue,parameters)
  }
}

现在还可以继续考虑两个问题:如果不能使用…运算符怎么办?如果绑定的对象已经有了_func属性或者不能设置属性怎么办?

先看如何替代…运算符。如果不能使用…运算符,我们就不能方便地向函数传递任意多个参数,可以使用eval()解析生成的字符串,也可以用new Function([arg1[,arg2[,...argN]],]functionBody)结合读取arguments数组读取任意多个参数,也是通过解析生成的字符串。

使用不重复的属性。ES6提供了symbol()用于标志唯一的事物,它可以以作为对象的属性,我们仍以apply作为例子。

Function.prototype.myApply = function(){
  let [thisValue,parameters] = arguments
  if(!thisValue)thisValue = window
  if(!parameters)parameters = []
  let _func = Symbol()
  thisValue[_func] = this
  let result = thisValue[_func](...parameters)
  delete thisValue[_func]
  return result
}

此外还可以用Math.random()生成一个不太可能重复的属性名。

作用域 Scope

作用域即变量和函数的可访问范围和生命周期。

变量作用域

可以分为全局作用域、函数内作用域(包括嵌套函数)、块级作用域。局部变量可以在局部覆盖同名全局变量。

作用域链 Scope Chain

作用域链决定了函数能访问变量的范围,组织了访问变量的顺序,在解析标识符时一级一级地按顺序查找函数和变量。需要注意的是,JS根据函数定义的位置查找,而非执行的位置。

执行环境/执行上下文 Execution Context

执行环境定义了变量与函数能访问的数据,包括全局、函数内、eval、块级作用域等。

执行环境包括至少3个重要的属性:作用域链、变量对象和this(按创建顺序排序)。这里关注前两个。

变量对象 Variable Object

每个执行环境都有与之关联的变量对象,它包括了环境中所有的变量和函数。全剧环境下的变量对象称为VO。

所有的变量和函数即函数声明、变量声明、函数形参。不包括匿名函数。

活动对象 Activation Object

当环境是函数时,活动对象就是变量对象。活动对象至少包含arguments对象(即函数参数)。有很多资料提到函数中不能直接访问VO,而以AO替代,不过反正这两个都是不能用JS代码打印出来的。

Arguments对象至少包括callee(对当前函数的引用)、length(真正传递参数的个数)、properties-indexs(函数的参数值),注意Arguments并不包含this。

作用域链

从外层到函数乃至嵌套函数内,多个执行环境形成了一个程序执行栈,同时也形成了一个指向多个环境的链表,即作用域链。

作用域链包括VO与所有上级的作用域。

数组 Array

数组属性与方法

length

数组长度/元素个数。

constructor

构造函数。

prototype

通过在原型上定义方法,让所有数组都可以使用自定义的方法。

变异方法与非变异方法 mutation method and non-mutation method

有些方法改变原数组,称为变异方法;也有些方法返回一个新数组,称为非变异方法;剩下的那些方法不改变数组也不返回数组。下面用M和N标记变异与非变异方法。

map N

Array.prototype.map(function(currentValue,index,arr),thisValue)

map的作用是按回调函数的规则从原数组映射出一个新数组(作为返回值)。

从函数原型可以看出,回调函数可以得到当前元素的值、索引、原数组,处理后的值应作为返回值,还能传入this对象。

currentValue和index以及arr是只读的,但arr是对象,currentValue也可能是对象,此时可以修改属性或执行方法。

forEach

Array.prototype.forEach(function(currentValue, index, arr), thisValue)

参数 描述
function(currentValue, index, arr) 必需。currentValue即当前元素(必需),index即索引值,arr即数组对象。
thisValue 传入的this值,默认undefined

无返回值,不能改变数组。如果数组成员是对象,当然可以更改其属性,因为数组只存了对象的指针。

reduce

Array.prototype.reduce(function(total, currentValue, index, arr), initialValue)

迭代器,reduce从左到右遍历数组并接受一个函数作为累积器。这个函数接受上次迭代的返回值(total,而初始值可以用initialValue指定),并最终得到一个返回值作为reduce的返回值。

使用reduce可以组织多个函数作为函数链,称为compose。

reduceRight

与reduce相似,但从右向左迭代。

filter N

Array.prototype.filter(function(currentValue, index, arr), thisValue)

过滤器,返回所有符合条件的项(回调函数中返回true)。不能改变数组。

push、pop M

Array.prototype.push(arg1, arg2, ...)

Array.prototype.pop()

栈方法。进栈和出栈。进栈返回新数组长度,出栈返回出栈元素的值。

shift、unshift M

Array.prototype.shift()

Array.prototype.unshift(arg1, arg2 , ...)

shift即出队列,删除数组第一项并将其作为返回值,unshift则是将item塞回队列(成为新数组的第0项、第1项等等)。push和shift方法结合使用则数组可以作为队列使用。

slice N

Array.prototype.slice(start, end)

切片的意思,即返回数组的一部分。start和end指定了开始和结束的数组下标,他们都接受负数值,-n代表从末尾向前数的第n个元素。

find ES6

Array.prototype.find(function(currentValue, index, arr), thisValue)

接受函数作为条件,返回第一个函数返回true的数组项。

findIndex ES6

和find用法一致,返回第一个函数返回true的数组项的索引。如果找不到返回-1。

indexOf

Array.prototype.indexOf(item, start)

indexOf接受一个值直接与数组项进行比较,返回第一次匹配的项的索引,如果找不到返回-1。start可以指定从哪一项开始搜索。

lastIndexOf

Array.prototype.lastIndexOf(item,start)

与indexOf相似,但返回最后一个匹配项。

sort M

Array.prototype.sort([compareFunction(firstEl,secondEl)])

排序。修改原数组,返回对原数组的引用(即修改后的数组)。

比较函数是可选的,接受比较的第一个元素和第二个元素,如果返回结果小于0,a将被排在b前面,如果大于0,b将被排在a前面,如果等于0,顺序不变(遗憾的是浏览器不一定遵守)。

默认的sort将元素转为字符串并比较编码值大小。

经测试在chrome下传入自定义函数时有问题。

reverse M

Array.prototype.reverse()

翻转数组。改变原数组,返回其引用。

splice M

Array.prototype.slice(index[, howmany, item1, item2, ...])

Splice的中文意为铰接、粘接,该方法可以自由地从数组添加、删除元素。

第一个参数index用于确定位置,从哪里开始操作;第二个参数决定从下标为index的元素起,删除几个元素;后面的参数将会添加到数组中从index开始的位置上。

concat N

Array.prototype.concat([arr1, arr2, ...])

concat意为串联,即将两个或多个数组连接在一起并返回。参数为空时返回原数组拷贝,常用于复制数组。

join

Array.prototype.join([separator=","])

join返回所有数组元素转换成的一个字符串,默认用半角逗号分隔,和toString表现一致,也可以自己指定分隔符。

fill M ES6

Array.prototype.fill(value[, start=0[, end=array.length]])

以固定值填充数组。start和end指定填充区间,array[end]不会被填充。

copyWithin M

Array.prototype.copyWithin(targetIndex[, start[, end=array.length]])

targetIndex确定了要复制到的位置,而start与end决定了被复制元素的范围。这种复制不是插入,而是覆盖原有元素,并且不改变数组长度。

includes ES6

Array.prototype.includes(searchElement[, fromIndex])

第一个参数传入值,第二个参数确定从哪开始搜索,-n代表倒数第n项。如果数组包括该值则返回true,否则返回false。

from ES6

Array.prototype.from(object[, mapFunction[, thisValue]])

object如果是拥有length属性或可迭代的对象,通过此方法能够返回一个数组。不符合要求的对象会导致from方法返回空数组。

every

Array.prototype.every(function(currentValue,index,arr),thisValue)

返回Boolean值,当且仅当所有元素传入回调函数的返回结果为true时every的结果为true。

some

和every用法一致,有一个元素传入回调的结果为true即返回true并不再检验之后的元素。

entries ES6

Array.prototype.entries()

从数组返回一个可迭代对象(Array Iterator)。如果不明白可以先了解function generator。

keys ES6

Array.prototype.keys()

创建key的可迭代对象,而数组的key是0、1、2、3...

isArray

Array.prototype.isArray(obj)

顾名思义,判断是否数组,返回Boolean值。

toString

其实和join类似,返回字符串,不过不带参数,只能以逗号分隔。

valueOf

数组对象的默认方法,返回数组原始值。不必手动调用。

对象 ✏️

可枚举属性 Enumerable Properties

Object.keys()、for...in、JSON.stringily()都只处理可枚举属性,可以用Object.propertyIsEnumerable()方法判断属性是否可枚举。

定义一个不可枚举属性:

let obj = {}
obj.defineProperty(obj,"attr",{
	value:"bread",
	enumerable:false
})

待填坑

如果属性存在于原型

认识对象

虽然{a:1,b:2}看起来简单,但其后深藏功与名。首先,Object对象具有Object.prototype方法,同时,对象的属性背后还有属性描述符,属性也分可否枚举,属性也可以是对象,甚至可以是自身{ obj: [Circular] }

对象拷贝

除了接下来介绍的Object.assign(),还可以用扩展运算符复制对象,以及JSON.parse(JSON.stringify())深拷贝对象(不支持循环引用与方法),对于数组可以用concat()。

Object.assign()

Object.assign(target, ...source)

(浅)拷贝数组,将所有可枚举属性从一个或多个源对象复制到目标对象,并将其作为返回值。需要注意它调用源对象的Setter和目标对象的Getter,如果合并源包括Getter或目标包括Setter,那么应该使用Object.getOwnPropertyDescriptor()与Object.defineProperty()准确复制属性定义。

原型 Prototype ✏️

JavaScript中的继承。原型法设计的思想是从类A扩展出类B,B以A为原型,具有A的属性与方法。JavaScript中每一个对象都有prototype属性。

String 字符串

Unicode表示法

JS允许以\uXXXX的形式显示一个字符,但不支持大于0xFFFF的数值,ES6中支持以\u{XX...}的形式显示码点更大的字符。

Iterator接口 ES6

对于字符串来说,遍历的结果是每一个字符。

模板字符串 ES6

基于模板字符串的模板引擎

待填坑

字符串方法

字符串方法均返回新值,不改变原字符串。

扩展字符串的方法

concat()

String.prototype.concat([str1[, str2[, ...]]])

连接多个字符串并作为结果返回。

repeat() ES6

String.prototype.repeat(count=0)

将字符串复制指定次数合并成一个字符串返回,默认count=0即返回空字符串。

获取子串的方法

substring()

String.prototype.substring(from[, to=array.length])

给定两个下标,截取部分字符串作为返回值,arr[to]不会被截取。

substr()

String.prototype.substr(start[, length=array.length-start])

截取从arr[start]开始的length个字符作为返回值,不指定length则截取到结尾。

slice()

String.prototype.slice(start[, end=array.length])

截取字符串片段,不包括arr[end],end可以为负(-n代表倒数第n个)。

charAt()

String.prototype.charAt(index=0)

接受index并返回str[index]。

与正则相关的方法

replace()

String.prototype.replace(searchValue, newValue)

接受字符串或RegExp进行匹配和替换。如果是字符串只能替换第一次匹配的子字符串,正则表达式可以替换所有的项(/str/g)。

match()

String.prototype.match(RegExp)

接受一个正则表达式,返回结果数组或null。

matchAll() ES6

接受一个正则表达式,返回所有匹配。返回的结果具有Iterator接口,但不是数组(可以转为数组)。

String.prototype.search(searchValue)

接受字符串或正则表达式,返回第一次匹配的子字符串起始位置的下标,找不到返回-1。

split()

String.prototype.split([separator[, limit]])

将字符串按分隔符拆成数组,separator可以是字符串或RegExp,默认不会拆分,传入""空字符串可拆分每一个字符,limit限制返回子字符串的最大数量(达到limit个子字符串后split即返回)。

搜索、检验字符串的方法

indexOf()

String.prototype.indexOf(searchValue[, start])

与search相似,但只接受字符串,可以给一个开始搜索的位置。注意它不会来回找,如果在start到end区间都没有匹配项就返回-1。

lastIndexOf()

String.prototype.lastIndexOf(searchValue[, start])

与indexOf的区别在于,该方法从后往前找,能找到最后一次出现的位置。

includes() ES6

String.prototype.includes(searchValue[, start])

与indexOf类似,但不返回子字符串位置,只返回Boolean值。

startsWith() ES6

String.prototype.startsWith(value[, start=0])

用于判断字符串是否以value开头。不过start可以自定义,也就是判断字符串start处开始的子串是否匹配字符串value。返回结果为Boolean值。

endsWith() ES6

与startsWith相似,判断字符串尾部是否与参数匹配。但endsWith的第二个参数表示以此处作为结束的位置,例如Apple Computer可以匹配Apple,只需要endsWith('Apple',5)

修饰字符串的方法

trim()

ES6新增了trimStart() trimEnd() trimLeft() trimRight()方法。

去除字符串两边的空格作为返回值。trimStart和trimLeft只去除左边的空格,trimEnd和trimRight相反。

toLowerCase()

将大写转换成小写字母并返回。

toUpperCase()

将小写转换成大写字母并返回。

padStart() ES6

补全字符串。第一个参数是新的字符串长度,第二个参数是用于补全的字符串,该字符串将被填充到新字符串头部。

padEnd() ES6

与padStart相似,但填充到尾部。

与码点相关的方法

charCodeAt()

String.prototype.charCodeAt(index=0)

接受index并返回str[index]的Unicode编码。

codePointAt() ES6

取回字符码点,用于弥补charCodeAt不支持大于0xFFFF码点的不足。

normalize() ES6

例如欧洲语言有时会用两个码点组合成一个字符,也可以直接表示一个字符,但两者对js而言并不相等,可以通过对两者normalize化使两者相等。

String对象的方法

String.raw() ES6

返回转义前的字符串,比如下划线\会被转成两个下划线\\

String.fromCharCode()

String.fromCharCode([code1[, code2[, ...]]])

接受一个或多个字符编码(0xFFFF内),返回创建的字符串。

String.fromCodePoint() ES6

弥补fromCharCode的不足,可以接受大于0xFFFF的码点。

HTML包装方法

包括anchor、big、blink、bold、fixed、fontcolor、fontsize、italics、link、small、strike、sub、sup。这些方法从字符串返回一个HTML标签用于网页中展示。

错误处理

try...catch

try {
  //insert code here
  a.b=c
}
catch(err) {
  console.error("Error catched: "+err)
}

throw

抛出自定义错误。

try {
  throw 'Number is too big'
}
catch(err) {
  console.error(err)
}

finally

无论是否发生错误都会执行的语句。

try {
}
catch(err) {
}
finally {
}

Error对象

如果是内置错误(非throw抛出),抛出的是一个Error对象。包括name和message属性。

错误名name通常包括ReferenceError(引用尚未声明的变量)、SyntaxError(语法错误)、TypeError(类型错误,如xxx is not a function)、URIError(由URI函数抛出)、rangeError(由Number的方法抛出)。

参考

模板字符串

`我买了${a}箱苹果``

变量作用域

JS中变量的作用域可以是全局,也可以是局部(函数参数和局部变量),同名局部变量会

函数作用域

JS的函数作用域是指在函数内声明的变量始终可见(变量提升)。

作用域链

JS查找上级作用域时,根据定义的位置查找,与执行环境无关。

参考目录

this

https://www.cnblogs.com/xiaohuochai/p/5735901.html

JavaScript

https://www.liaoxuefeng.com/wiki/1022910821149312

ECMA Script 6

http://es6.ruanyifeng.com/

Execution Context

https://juejin.im/entry/58edde2761ff4b00581b93ff

Symbol

https://www.cnblogs.com/diligenceday/p/5462733.html

MDN

runoob

最后更新: 9/6/2019, 5:47:07 AM