添加/删除元素
push()
向尾部添加元素pop()
从尾部提取一个元素shift()
从首端提取元素unshift()
从首端添加元素splice(start, deleteCount, item1...itemN)
start
表示开始计算的索引,deleteCount
表示从start开始计算的元素的个数,item1...itemN
从start开始要加入到数组的元素。slice(start, end)
返回一个新的对象,该对象由start
和end
决定的原数组的浅拷贝arr.concat(item)
向arr中添加item搜索元素
indexOf/lastIndexOf(item, start)
从索引start
开始(不填从开头或者末尾开始)搜索item,搜索到则返回item的下标,否则返回-1includes(item)
如果数组有item
,则返回true
,否则返回false
find/filter(func)
通过func
返回符合条件的第一个值/所有值findIndex
和find
雷类似,但返回的是索引而不是值操作数组
forEach(func)
遍历数组,对每个元素都调用func
map(func)
表里数组,每个元素经过func
处理之后,返回新的数组sort()
对数组进行排序,然后返回reverse()
反转数组,然后返回split/join
将字符串转换为数组并返回reduce/reduceRight(func, initial)
方法对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。reduceRight
方向相反(从右到左)。还有一些其他的方法参见 MDN
AJAX 指的是JavaScript的异步通讯,是通过XMLHttpRequest()
实现网络通讯。
const url = "/server"
let xml = new XMLHttpRequest()
// 监听状态码
xml.onreadystatechange = function() {
if(this.readyState !== 4) return;
// 请求成功
if(this.status === 200) {
console.log("请求成功了")
console.log(this.response)
} else {
console.log("请求失败")
console.log(this.status,this.statusText)
}
}
// 监听请求失败
xml.onerror = function() {
console.log(this.statusText)
}
// 创建Http请求
xml.open("GET", url, true)
// 设置返回的的类型
xml.responseType = "json"
// 设置请求头
xml.setRequestHeader("Accept", "application/json")
// 发送请求
xml.send(null)
要搞清楚这个问题,首先要知道 实际中 new
到底做了什么,来看个例子:
第一种情况
function Animal(name) {
this.name = name
this.run = function(){}
}
let cat = new Animal('猫咪')
console.log(cat)
运行结果如下:
可以得出以下结论:
第二种情况
function Animal(name) {
this.name = name
this.run = function(){}
return {type: "all"} // or return function() {...}
}
let cat = new Animal('猫咪')
console.log(cat)
那么总结一下new的过程需要干什么:
(1).让cat 获取到Animal的所有属性和方法,并绑定this到cat上
(2).将cat 的原型指向Animal
(3).判断是否Animal上有object类型的返回值
代码实现如下:
function myNew() {
// 创建一个空对象
const newObj = {}
// 从参数中获取构造函数(第一个参数)
// arguments是类数组,call能让arguments可以用shift的方法
// 此时arguments已经被取走第一个参数
console.log(arguments)
const constructor = Array.prototype.shift.call(arguments)
// 让创建的对象的__proto__指向constructor的原型
newObj.__proto__ = constructor.prototype
// 执行constructor方法,并绑定this到newObj上
const result = constructor.apply(newObj, arguments)
// 判断返回值
return (result instanceof Object) ? result : newObj
}
for…of 是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构,并返回各项的值,与ES3中的for…in的区别如下:
总结 for…in 循环主要是为了遍历对象而生,不适合遍历遍历数组。for…of适合遍历数组、类数组、字符串、Set、Map以及Generator对象。
for…of 遍历类数组,可以结合Array.from来转换成数组:
const obj = {
0: 11,
1: 22,
length:2
}
for(const b of Array.from(obj)) {
console.log(b)
}
(1)typeof
console.log(typeof true) // boolean
console.log(typeof 2) // number
console.log(typeof "hello") // string
console.log(typeof undefined) // undefined
console.log(typeof null) // object
console.log(typeof {}) // object
console.log(typeof []) // object
console.log(typeof Symbol) // function
console.log(typeof BigInt) // function
其中null、数组、对象都判断为object
(2)instanceof
instanceof
可以正确的判断对象类型,其内部的运行机制是在其原型链中是否能找到该类型的原型。
console.log(1 instanceof Number) // false
console.log(true instanceof Boolean) // false
console.log('hello' instanceof String) // false
console.log([1,2,3] instanceof Array) // true
console.log({name:"tom"} instanceof Object) // true
console.log(function(){} instanceof Function) // true
可见instanceof不能判断 number、string、boolean基础类型,只能用来判断引用类型
(3)constructor
console.log((1).constructor) // true
console.log((1).constructor === Number) // true
console.log(('hello').constructor === String) // true
console.log((true).constructor === Boolean) // true
console.log(([1,2,3]).constructor === Array) // true
console.log(({}).constructor === Object) // true
console.log((function(){}).constructor === Function)// true
console.log((()=>{}).constructor === Function) // true
constructor
可以用来判断数据结构,也可以通过constructor来访问它的构造函数。需要注意的是如果改变了它的constructor的话,判断会不准确
(4)Object.prototype.toString.cell()
var a = Object.prototype.toString
console.log(a.call(1)) // [object Number]
console.log(a.call(true)) // [object Boolean]
console.log(a.call('hello')) // [object String]
console.log(a.call([])) // [object Array]
console.log(a.call({})) // [object Object]
console.log(a.call(function(){})) // [object Function]
console.log(a.call(undefined)) // [object Undefined]
console.log(a.call(null)) // [object Null]
可见该方法能正确判断常见的类型
因为在第一个JavaScript第一个版本中,所有值都存储在一个32位的单元中,用低位的1或者3个bit来表示类型。其他区域存储真实的数据,例如:
000:object // 当前存储的是一个对象
1:int // 当前存储的数据是一个31位的有符号整数
010:double // 当前存储的数据指向一个双精度的浮点数
100:string // 当前存储的数据指向一个字符串
110:boolean // 当前存储的的数据是布尔值
null的值是机器码NULL指针(null指针的值全是0),和object的类型标签一样全是000,所以typeof null被识别为object
首先undefined和null都是基本数据类型,undefined代表的含义是未定义
,null代表的含义是空值
,一般变量声明了但还未定义的时候会返回undefined
如何得到一个安全的undefined?
可通过void 0
来得到一个安全的undefined,因为在JavaScript中undefined不是一个保留字,就意味着他可以作为一个变量来使用,会影响对undefined的判断,例如:
function fn() {
var undefined = 1
console.log(undefined)
}
fn() // 1
instanceof 用来判断构造函数的prototype属性是否出现在原型链上的任一位置
function myInstanceof(left, right) {
// 获取原型
let proto = Object.getPrototypeOf(left)
// 获取构造函数的prototype
const prototype = right.prototype
// 循环遍历
while(true) {
if(!proto) return false;
if(proto === prototype) return true;
// 沿着原型链往上找
proto = Object.getPrototypeOf(proto)
}
}
闭包是指有权访问另一个函数作用域的函数,最常用创建闭包的方式就是在一个函数中创建另一个函数,创建的函数可以访问到当前函数的局部变量。
闭包的两个常用的用途
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this的指向可以用以下四种调用模式来判断
这四种方式的优先级:构造器调用模式 > call、apply和bind调用模式 > 方法调用模式 > 函数调用模式
它们的作用一模一样,区别在于传入的参数形式不同
这个函数几乎与 apply() 相同,只是函数的参数以列表的形式逐个传递给 call(),而在 apply() 中它们被组合在一个对象中,通常是一个数组——例如,func.call(this, “eat”, “bananas”) 与 func.apply(this, [“eat”, “bananas”])。
call函数
call 的语法为call(this, arg1...argN)
this
:表示指向的this,如果函数不在严格模式下,null 和 undefined 将被替换为全局对象,并且原始值将被转换为对象。arg1...argN
:表示函数参数 Function.prototype.myCall = function(context) {
// 判断调用对象
if(typeof this !== 'function') {
console.log("type error")
}
// 获取参数除了第一个之后的参数
let args = [...arguments].slice(1)
// 判断 context 是否传入, 未传入则设置为window
context = context || window
// 函数调用的结果
let result = null
// 将调用函数设为对象的方法
context.fn = this // this是原函数
// 调用原函数(相当于使用了对象方法调用的方式,this绑定在对象上)
result = context.fn(...args)
// 将属性删除
delete context.fn
return result
}
apply函数
apply 的语法为call(this, [arg1...argN])
this
:表示指向的this,如果函数不在严格模式下,null 和 undefined 将被替换为全局对象,并且原始值将被转换为对象。[arg1...argN]
:表示函数参数,是一个数组 Function.prototype.myApply = function(context) {
// 判断调用对象
if(typeof this !== 'function') {
console.log("type error")
}
// 判断是否存在context
context = context || window
// 函数调用的结果
let result = null
// 将调用函数设为对象的方法
context.fn = this // this是原函数
// 判断是否存在参数
if(arguments[1]){
result = context.fn(...arguments[1]) // arg1...argN
} else {
result = context.fn()
}
// 将属性删除
delete context.fn
return result
}
bind函数
bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。
bind()的语法为bind(this, arg1, arg2, / …, / argN)
在调用绑定函数时,作为 this 参数传入目标函数 func 的值。如果函数不在严格模式下,null 和 undefined 会被替换为全局对象,并且原始值会被转换为对象。如果使用 new 运算符构造绑定函数,则忽略该值。
Function.prototype.myBind = function(context) {
// 判断调用对象
if(typeof this !== 'function') {
console.log("type error")
}
// 获取参数
let args = [...arguments].slice(1)
let fn = this
return function Fn() {
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
)
}
}
Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,能够避免回调地域的问题。
(1)Promise有三种状态
(2)Promise的实例有两个过程
注意:一旦从进行状态变成其他状态就永远不能改变状态了
Promise 构造函数接收一个函数作为参数,该函数的两个参数分别是resolve
和reject
const promise = new Promise((resolve, reject) => {
if(true) {
resolve(1)
} else {
reject(2)
}
})
promise.then((e) => {
console.log('success:' + e)
}).catch((err) => {
console.log('err:' + err)
})
Promise 的方法
Promise有五个常用的方法:then()、catch()、all()、race()、finally()
const promise = new Promise((resolve, reject) => {
resolve(1)
})
promise.then((e) => {
console.log('success:' + e)
return new Promise((resolve, reject) => {
resolve(3)
})
}).then((e) => {
console.log("成功:", e)
})
rejected
的时候会进入到catch函数中,二是在Promise执行的过程中如果发生异常,则也会进入到catch中。 const promise = new Promise((resolve, reject) => {
// throw("123") 抛出异常
reject(1)
})
promise.then((e) => {
console.log('success:' + e)
}).catch((err) => {
console.log("error:", err)
})
all
方法可以完成并行任务,它接收一个数组,数组的每一项都是Promise实例,数组中的Promise的状态都变成resolved时,all方法的状态就会变成resolved,否则只要其中一个变成了rejected,那么all的状态也会变成rejected const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 3000)
})
Promise.all([p1,p2,p3]).then((e) => {
console.log(e)
})
结果是[1,2,3]
可见all方式的结果是按顺序的
race()
race() 方法和all方法一样的用法,区别在于,当数组中的Promise有一个执行完成,那么就立即返回第一个执行完成的Promise的值,如果第一个Promise的状态是resolved,那么race的状态也会变成resolved,反之第一个是rejected的话,race也会变成rejected。
finally()
finally 方法不管Promise的状态如何,都会执行finally方法。
async/await 其实是generator和Promise的语法糖,他能实现的效果都能用then链式来实现,它是用来优化then的。Promise是用来解决回调地狱的问题,而async/await 被发明用来进一步优化Promise。
await 表达式的运行结果取决于它等的是什么
async / await 可以像同步代码一样运行
async function doIt() {
const time1 = 300;
const time2 = await promise1(time1);
const time3 = await promise2(time2);
const result = await promise3(time3);
console.log("result is " + result)
}
解决了Promise代码中的then链式调用带来额外的阅读负担,上例中的代码如果用then来调用的话,会变成下面这样子:
async function doIt() {
const time1 = 300;
promise1(time1)
.then(time2 => step2(time2))
.then(time3 => step2(time3))
.then(result => {
console.log("result is " + result)
})
}
await 只能得到resolve的结果, 异常的结果可以使用try/catch语法捕捉异常
减少垃圾回收
虽然浏览器可以进行垃圾自动回收,但是当代码比较复杂的时候,垃圾回收带来的代价也是比较大的,所以应该尽量减少垃圾回收