JavaScript
数据类型
基本数据类型
Number String Boolean Undefined Symbol
引用数据类型
Object Array Function Map Set Null
如何判断数据类型
typeof 能判断所有的值类型,能识别引用类型
let a =>undefined
let b = 1 =>number
let c = 'c' =>string
let d = true => boolean
let e = Symbol('e') =>symbol
typeof console.log =>function
typeof null =>object
typeof [] =>object
instanceof能判断引用类型
[] instanceof Array
{} instanceof Object
如何判断两个数据是否相等
使用==/===判断
==对于基础类型,是判断值是否相等,并且有一定的隐式转换。如
4 == '4' //true
0 == '' //true
0 == false //true
null == undefined //true
除了判断 是否等于null其他判断都应该使用 ===
==对于引用类型,是判断址是否相等
变量提升
在声明变量前就使用该变量。
var存在变量提升,输出的值是undefined
let、const不允许变量提升,会报错。
实现浅拷贝和深拷贝
浅拷贝:这个一般用来克隆一维数组
let a = [1,2,3]
let b = []
for(let i=0;i<a.length;i++){
b.push(a[i])
}
深拷贝:使用递归。
首先考虑一下克隆的对象会是什么:基本类型、Array、Object。
基本类型:使用=赋值
Array:使用push
Object:使用obj[k]=…
function deepClone(o){
if(Array.isArray(o)){
var result = []
for(let i=0;i<o.length;i++){
result.push(deepClone(o[i]))
}
}else if(typeof o == 'object'){
var result = {}
for(let k in o){
result[k] = deepClone(o[k])
}
}else{
var result = o
}
return result
}
稍微检查一下,如果
var t = deepClone(null)
期望的结果是null,而不是{}
修改下
else if(typeof o == 'object' && o != null)
再考虑到原型链,Array不好使用Push的方法了,可以这么改
function deepClone(o){
if(typeof o!='object'|| o==null){
return o
}
let result
if(Array.isArray(o)){
result = []
}else{
result = {}
}
for(let k in o){
if(o.hasOwnProperty(k)){
result[k] = deepClone(o[k])
}
}
return result
}
变量计算
小心隐式转换:+号
let a = 100 +'10'=>'10010'
作用域
全局、函数、块级
自由变量
一个变量被使用了,但是在当前作用域内没有找到该变量的定义,便会向外层的作用域寻找,直到找到全局作用域都没有,便会报错。
闭包
当函数执行完毕后,函数作用域销毁,在函数内定义的变量也应该销毁。
如果函数的返回值不是数值而是一个函数,则形成闭包。
函数作为参数,也同理。
function f(){
let a = 200
return function(){
console.log(a)
}
}
let a = 100
const fn = f()
fn() //200
function fn(){
console.log(a)
}
function f(fn){
let a = 200
fn()
}
let a = 100
f(fn) //100
自由变量会向外层查找变量定义,而且会从函数定义的地方向外查找而不是函数执行的地方查找。
第一个例子中,函数的外层是f,有变量a的定义,所以输出200
第二个例子中,函数的外层是全局作用域,有变量a的定义,输出100
使用闭包封装数据
function f(){
const data={}
return {
set:(k,v)=>{
data[k] = v
},
get:(k)=>{
return data[k]
}
}
}
原型链
再提一下instanceof
class People{}
class Student extend People{}
const xiaomin = new Student()
xiaomin instanceof Student //true
xiaomin instanceof People //true
//people继承于object
每个class都有原型,即
People.prototype
所有class都继承于Object,即
People.prototype.__proto__ === Object.prototype
//没继承之前
Student.prototype.__proto__=== Object.prototype
原型链的终点
Object.prototype.__proto__ =>null
不使用class怎么继承?改变Student的原型指向
function People(){}
function Student(){}
Student.prototype.__proto__=People.prototype
Student.prototype = new Person()
上下文
this的值是在执行的时候决定的。
obj.fn() //this === obj
const fn = obj.fn
fn() //this === window 严格模式下是undefined
箭头函数里的this取其上级作用域的this
fn(){
setTimeout(function(){
//this === window
})
}
fn(){
setTimeout(()=>{
//this === fn()
})
}
事件委托
function handleClick(e){
if(e.target.tagName.toLowerCase() === 'a'){
console.log(e.target.innerHTML)
}
}
for(let i = 0;i<10;i++){
const dom = document.createElement('a')
dom.innerHTML = i
document.body.appendChild(dom)
}
document.body.addEventListener('click',handleClick)
通用事件委托函数
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
let target
elem.addEventListener(type, (e) => {
if (selector) {
target = e.target
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
异步
通过下面三种方法可以实现异步
定时器
Promise
状态
pending
resolve->resolved(fulfilled)
reject->(reject)
单向不可逆。
首先,声明promise时,会进入pending状态。使用该实例时,会根据实例的函数确认promise状态。
const p = new Promise(fn) //pending
p() //根据fn确认promise状态
返回值
resolve()或reject()里的参数会传到then/catch里
默认返回resolved状态的promise,不使用默认值的话,需要在return处返回自己的promise实例
p.then(()=>{return 123})
//相当于
p.then(()=>{
return new Promise((resolve,reject)=>{
resolve(123)
})
})
then和catch
它们本质上也是一个promise实例。如果上一个promise实例是resolved状态,则会执行then(),是rejected状态则执行catch()
举一个回调地狱的例子
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
el.style.transform = `translate3d(${x}px,${y}px,0)`
el.addEventListener("transitionend", () => {
end()
})
}
const box = document.getElementById("box")
box.addEventListener("click", () => {
move(box, { x: 150 }, () => {
move(box, { x: 150, y: 150 }, () => {
move(box, { x: 0, y: 150 }, () => {
move(box)
})
})
})
})
用Promise解决回调地狱
const movePromise = (el, point) => {
return new Promise((resolve) => {
move(el, point, () => {
resolve()
})
})
}
box.addEventListener("click", () => {
movePromise(box, { x: 150 })
.then(() => {
return movePromise(box, { x: 150, y: 150 })
})
.then(() => {
return movePromise(box, { x: 0, y: 150 })
})
.then(() => {
return movePromise(box)
})
})
为什么能解决
回调地狱的本质是函数返回时再次调用该函数,且环环相扣,在回调中添加代码或更改回调的顺序很麻烦。
promise使每一次回调后都返回一个promise实例,修改它不需要考虑上一个回调和下一个回调,它们之间也互不影响。
使用Promise加载图片
let src = "xxx.jpg"
const loadImg = (src)=>{
new Promise((resolve,reject)=>{
const img = document.createElement('img')
img.src = src
img.onload = ()=>{
resolve(img)
}
img.onerror = ()=>{
reject('图片加载失败')
}
})
}
loadImg(src).then((img)=>{document.body.appendChild(img)}).catch(err=>{ console.log(err)})
Ajax
怎么实现
通过XMLHttpRequest实现
const url = "xxx"
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return
if (
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304
) {
console.log(xhr.responseText)
}
}
xhr.open("GET", url, true)
xhr.send(null)
可以使用promise
const url = "xxx"
function getAjaxResponse(url){
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) reject(err)
if (
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304
) {
resolve(xhr.responseText)
}else{
reject(err)
}
}
xhr.open("GET", url, true)
xhr.send(null)
})
}
状态码
xhr.readystate === 4
xhr.status >=200&& xhr.status<300 || xhr.status === 304
JSON数据转换
JSON->js
JSON.parse(xxx)
js->JSON
JSON.stringify(xxx)
跨域问题
https(协议)😕/www.xxx.com(域名):8800(端口号)
三个有一个不一样就是跨域
由于同源策略,跨域请求会被阻止。
解决:
CORS,服务器端在响应头允许跨域(IE10)
JSONP,script标签的跨域请求不会被阻止,可以使用它加载跨域文件。
优化
节流
每N秒执行一次
一直按着键盘,也会每N秒触发一次事件
function throttle(fn, delay = 500, lock) {
return function () {
if (!lock) return
lock = false
setTimeout(() => {
fn.apply(this, arguments)
lock = true
}, delay)
}
}
input.addEventListener(
"keyup",
throttle(
function () {
console.log(this.value)
},
100,
true
)
)
防抖
在N秒内,该事件只执行一次,如果被重复触发,就重新计时。
下例中,如果一直按着键盘,就永远不会执行该事件
const input = document.getElementById("input")
function debounce(fn, delay = 500) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
/* console.log(this)
fn.apply(this, arguments)
console.log(this) */
fn()
timer = null
}, delay)
}
}
input.addEventListener(
"keyup",
debounce(function () {
/* console.log(this.value) */
console.log(input.value)
})
)
fn.apply(this, arguments)将this指向调用该函数的实例