Kyle's Notebook

JavaScript Cheat Sheet

Word count: 4.1kReading time: 19 min
2019/06/28

JavaScript Cheat Sheet

变量类型:根据存储方式区分

值类型(变量存放值)

1
2
3
4
let a = 100
let b = a
a = 200
console.log(b) // 100

引用类型(变量存放地址):

对象,数组,函数

1
2
3
4
let a = {age: 20}
let b = a // b 和 a 都指向同一个地址,因此支持动态添加属性
b.age = 21
console.log(a.age) // 21

typeof 运算符

规律:只能区分值类型和函数(引用类型数组和函数不能判断)。

判断值 输出结果
undefined undefined
ywh string
41 number
true boolean
{}, [], null object
console.log function

强制类型转换

字符串拼接

1
2
let a = 100 + 10      // 110
let b = 100 + '10' // '10010'

== 运算符

1
2
3
4
5
6
7
8
9
10
11
// 以下判断返回结果都为 true
100 == '100'
0 == ''
null == undefined

// 除了这个以外其余都用 “===”
if (obj.a == null) {
// ...
}

// 建议都使用`===`或参考 jQuery 源码推荐写法

if 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 注意当a未定义时,if (a == null) 或 if (a == undefined) 会报错 

let a = true
let b = 100
let c = ''
if (a) {
// true
}
if (b) {
// true
}
if (c) {
// false
}

逻辑运算符

1
2
3
4
5
6
7
10 && 0    // 0
'' || 'abc' // 'abc'
!window.abc // true

// 判断一个变量会被当做 true 还是 false
let a = 100
!!a // 反反

安全赋值(避免覆盖)

1
var a = a || {}    // a 的值存在则不允许定义

函数

内置函数

  1. Object
  2. Array
  3. Boolean
  4. Number
  5. String
  6. Function
  7. Date
  8. RegExp
  9. Error
  10. Math

构造函数

  • var a = {}var a = new Object() 的语法糖;

  • var a = []var a = new Array() 的语法糖;

  • function Foo(){...} var Foo = new Function(...) 语法糖;

  • 可使用 a instanceof Array 判断一个函数是否是一个变量的构造函数(如区分数组和对象)。

1
2
3
4
5
6
7
8
// 注意构造函数首字母要用大写
function Foo(name, age) {
this.name = name
this.age = age
// return this // 默认返回this
}

let f = new Foo('ywh', 18) // 创建一个实例

原型规则

  • 所有引用类型(数组,对象,函数)都具有共有的对象特性,即可自由扩展属性(null除外);

  • 所有的引用类型都有一个 __proto__ 属性(对象),即隐式原型,存放该类型共有属性的地址(指向构造函数的 prototype,即 (new Object).__proto__ === Object.prototype );

  • 所有的函数都有一个 prototype 属性(对象),即显式原型;

  • 当试图访问对象的某个属性,会先从对象本身查找,不存在则会从 __proto__ 沿着原型链依次查找;

  • f instanceof Foo 的原理也是沿着原型链一层层往上判断,看可否达到 Foo.protype,再试着判断 f instance of Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Foo(name, age) {
this.name = name
this.age = age
}
Foo.prototype.alertName = function () {
alert(this.name)
}

let f = new Foo('ywh')
f.printName = function () {
console.log(this.name)
}

f.printName() // 自身属性
f.alertName() // 原型属性
f.toString() // 原型的原型的属性

// new 创建对象的过程
// 1. 创建一个新对象
// 2. this指向这个新对象
// 3. 执行代码,对this赋值
// 4. 返回this

循环对象自身的属性:

1
2
3
4
5
6
var item
for (item in f) {
if (f.hasOwnProperty(item))
console.log(item)
}
// 高级浏览器已经在“for...in...”中屏蔽来自原型的属性,但还是建议加入判断

原型链继承的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Animal() {
this.eat = function () {
congole.log("animal eat")
}
}

function Dog() {
this.bark = function () {
console.log("dog bark")
}
}

Dog.prototype = new Animal()
var dog = new Dog()

实例:封装 DOM 查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Elem(id) {
this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val) {
let elem = this.elem
if (val) {
elem.innerHTML = val
return this
}
else {
return elem.innerHTML
}
}
Elem.prototype.on = function (type, fn) {
let elem = this.elem
elem.addEventListener(type, fn)
}

let div1 = new Elem('detail-page')
div1.html("<p>Hello World</p>").on("click", function(){
alert("click")
})

执行上下文

  • 范围:一段 <script> 或者一个函数;

  • 全局:变量定义、函数声明

  • 函数:变量定义、函数声明、this、arguments

1
2
3
4
5
6
7
8
9
console.log(a)      // undefined
var a = 100 // 把var a提升到最上面,此处剩余a = 100,所以console.log时a还未被赋值

fn("ywh") // "ywh" 20
function fn(name) { // 被提升到“fn("ywh")”之前
age = 20
console.log(name, age)
var age // 被提升到“age = 20”之前
}

this

  1. thiscall 的第一个参数,在执行时才确定值,定义时无法确认;
    • 作为构造函数执行;
    • 作为普通函数执行;
    • call,apply,bind
1
2
3
4
5
6
7
8
9
10
11
let a = {
name: "A",
fn: function() {
console.log(this.name)
}
}

a.fn() // this === a
a.fn.call({name: 'B'}) // this === {name: 'B'}
var fn1 = a.fn
fn1() // this === window

作用域与闭包

ES6 之前只有函数作用域与全局作用域,没有块级作用域;

1
2
3
4
5
6
7
8
9
10
11
12
if (true) {    			// 此处没有隔离作用域,是全局声明
var name = "ywh"
}
console.log(name)

var a = 100
funtion fn() {
var a = 200
console.log("fn", a)
}
console.log("global", a)
fn()

作用域链(树)

1
2
3
4
5
6
7
8
9
10
11
12
var a = 100
function fn1() {
var b = 200
function fn2() {
var c = 300
console.log(a) // 当前作用域没有定义的变量,即“自由变量”,会从当前作用域向上查找
console.log(b)
console.log(c)
}
fn2()
}
fn1()

闭包的使用场景:

  • 函数作为返回值

  • 函数作为参数传递

主要用于封装变量,收敛权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function fn1() {
var a = 100
return function() {
console.log(a) // a是自由变量,向父级作用域查找,因此是100
}
}
// 函数作为返回值
var f1 = fn1()
var a = 200
f1() // 100

// 函数作为参数传递
function fn2(fn) {
var a = 200
fn().call()
}
fn2(fn1)

实例:利用闭包保护变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 错误
var i, a
for (i = 0; i < 10; i ++ ) {
a = document.createElement("a")
a.innerHTML = i + "<br>"
a.addEventListener("click", function(e) {
e.preventDefault()
alert(i)
}) // 循环执行完才点击,i是自由变量,会从父作用域查找,此时 i === 9,因此点击任何一个都会输出 9
document.body.appendChild(a)
}

// 正确
var i
for (i = 0; i < 10; i ++ ) {
(function(i) {
var a = document.createElement("a")
a.innerHTML = i + "<br>"
a.addEventListener("click", function(e) {
e.preventDefault()
alert(i) // 此处的 i 使用的是外部变量,因此构成闭包,值会一直保存在函数作用域不会被修改
})
document.body.appendChild(a)
})(i) // 封装成一个立即执行闭包函数
}

实例:使用闭包收敛权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function isFirstLoad() {
var _list = [] // 一直保存在内存中
return funciton(id) {
if (_list.indexOf(id) >= 0) {
return false
}
else {
_list.push(id)
return true
}
}
}
var firstload = isFirstLoad()
firstload(10) // true
firstload(10) // false
firstload(20) // true

异步和单线程

JS 单线程执行可避免 DOM 渲染冲突:

  • 浏览器需要渲染 DOM;

  • JS 可以修改 DOM,修改时浏览器会暂停渲染;

  • 两段 JS 同时执行会发生冲突;

  • webworker 支持多线程,但不能访问 DOM。

同步:代码顺序执行,中间可能会阻塞

1
2
3
console.log(100)
alert(200) // 同步执行,点确认前会阻塞
console.log(300)

异步:不考虑执行结果(或通过回调获取执行结果),调用立即返回;

使用异步的场景:在可能发生等待、且等待过程中不能阻塞(像alert一样):

  • setTimeoutsetInterval
  • ajax请求,动态<img>加载;
  • 事件绑定。
1
2
3
4
5
6
7
8
9
10
11
12
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
console.log(4)
}, 1000)
console.log(5)
// 1, 3, 5, 2, 4

// $.get("https://www.baidu.com", function(data){console.log(data)})

通过异步解决执行效率低仍存在问题:

  • 可读性差(不是按顺序执行)。

  • callback 不容易做模块化。

1
2
3
4
5
6
7
8
9
10
11
12
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
console.log(3)
setTimeout(function() {
console.log(4)
}, 1000)
console.log(5)
// 1, 3, 5, 2, 4

// $.get("https://www.baidu.com", function(data){console.log(data)})

Event-Loop(事件轮询):

  • 顺序执行所有同步函数;

  • 需要等待的异步函数(延时、AJAX)会在等待完成后再暂存在异步队列(单线程);

  • 轮询异步队列,依次取出函数执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
url: "xxx",
success: function(result) {
console.log("a")
}
})
setTimeout(function() {
console.log("b")
}, 100)
setTimeout(function() {
console.log("c")
})
console.log("d")

// 执行顺序:dcab或dcab(取决于AJAX请求时长)

jQuery Deferred

1.5 版本前的 AJAX:

1
2
3
4
5
6
7
8
9
10
var ajax = $.ajax({
url: "data.json",
success: function() {
// ...
},
error: function() {
// ...
}
})
console.log(ajax) // XHR对象

1.5 版本后的 AJAX(类似 Promise):

Deferred 两类 API:

  • dtd.resolvedtd.reject
  • dtd.thendtd.donedtd.fail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var ajax = $.ajax("data.json")
ajax.done(function() {
// ...
}).fail(function() {
// ...
}).done(function() {

})
console.log(ajax) // deferred对象


var ajax = $.ajax("data.json")
ajax.then(function() {
// ...
}, function() {
// ...
}).then(function() {
// ...
}, function() {
// ...
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var wait = function() {
var task = function() {
console.log("")
}
setTimeout(task, 2000)
}
wait()

// 可修改为:
function waitHandle() {
var dtd = $.Deferred()
var wait = function(dtd) {
var task = function() {
console.log("")
dtd.resolve() // 异步任务已完成
}
setTimeout(task, 2000)
return dtd // 返回dtd.promise(),则最后返回promise对象
}
return wait(dtd) // 必须有返回值
}

var w = waitHandle()
w.then...

Promise

Callback Hell:难以阅读的异步函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function loadImg(src, callback, fail) {
var img = document.createElement("img")
img.onload = function() {
callback(img)
}
img.onerror = function() {
fail()
}
img.src = src
}

var src = ""
loadImg(src, function(img) {
console.log(img.width)
}, function() {
console.log("failed")
})

Promise

三种状态(不可逆):

  • pending(初始状态)

  • fulfilled(执行成功回调)

  • rejected(执行失败回调)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function loadImg(src) {
const promise = new Promise(function(resolve, reject) {
var img = document.createElement("img")
img.onload = function() {
resolve(img)
}
img.onerror = function() {
reject()
}
img.src = src
})
return promise
}
var src = ""
var result = loadImg(src)
result.then(function(img) { // promise实例,必须实现then方法(成功和失败的回调),且返回的必须是
console.log(img.width)
}, function() {
console.log("failed")
})

result.then(function(img) {
console.log(img.height)
})


// 异常捕获
var result = loadImg(src)
result.then(function() { // 使用异常捕获,则不需要传失败的回调函数(catch不会捕获reject)
// throw new Error("Error!")
}).catch(fucntion(ex) {
// 统一捕获异常
})

Promise 串联

1
2
3
4
5
6
7
8
9
10
11
var result1 = loadImg("x")
var result2 = loadImg("y")

result1.then(function(img) { // 执行第一个promise
console.log("load image x")
return result2
}).then(function(img) { // 执行第二个promise
console.log("load image y")
}).catch(function(ex) {
// ...
})

Promise all 与 race

1
2
3
4
5
6
7
8
9
10
11
12
// 接收一个promise对象数组

// 待全部完成后统一执行success
Promise.all([result1, result2]).then(data => {
console.log(data[0]) // data是数组,包含依次的多个promise返回的内容
console.log(data[1])
})

// 只要一个完成就执行success
Promise.race([result1, result2]).then(data => {
console.log(data) // data为最先执行完成的promise返回的内容
})

async/await

待续

Web API

DOM

便于描述树形结构的文档对象的模型,使浏览器可识别、JS 可操作。

DOM 属性操作

获取DOM节点:

1
2
3
var div1 = document.getElementById("div1")
var divList = document.getelementsByTagName("div")
var containerList = document.getElementsByClassName(".container")

property:JS 对象属性(获取/修改)

1
2
3
4
5
var pList = document.querySelectorAll('p')
var p = pList[0]
p.style.width
p.className
p.nodeName

attribute:HTML 标签属性(获取/修改)

1
2
3
4
5
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute("data-name")
p.setAttribute("data-name", "ywh")
p.getAttribute("style")

DOM 结构操作

1
2
3
4
5
6
7
8
9
10
11
12
13
// 新增节点
var div1 = document.getElementById("div1")
var p1 = document.createElement("p")
p1.innerHTML = "p1"
div1.appendChild(p1)
var p2 = document.getElementById("p2")
div1.appendChild(p2)

// 获取父子元素
var div1 = document.getElementById("div1")
var parent = div1.parentElement
var child = div1.childNodes
div1.removeChild(child[0])

BOM

浏览器对象模型

  • navigator

  • screen

  • location

  • history

1
2
3
4
5
6
7
8
9
10
11
12
13
navigator.userAgent

screen.width
screen.height

location.href
location.protocol
location.pathname
location.search
location.hash

history.back()
history.forward()

事件

通用事件绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var btn = document.getElementById("btn1")
btn.addEventListener("click", function(event) {
console.log("clicked")
})

function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}

var a = document.getElementById("link1")
bindEvent(a, "click", function(e) {
e.prevntDefault()
alert("clicked")
})

事件冒泡

节点事件触发时,如果其父节点都绑定相同的事件,则事件会从节点依次向外传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
</div>
<script>
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}

var p1 = document.getElementById("p1")
var body = document.body

bindEvent(p1, "click", function(e) {
e.stopPropagation() // 阻止点击p1时点击事件传递到body而产生alert("取消")
alert("激活")
})
bindEvent(body, "click", function(e) {
alert("取消")
})
</script>
</body>

代理:在父节点绑定事件(并加入对子节点的判断),而不需要对每个子节点都绑定事件(且支持动态增删)

  • 代码简洁
  • 减少浏览器内存占用

使用代理完善通用绑定事件函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div id="div1">
<a id="a1">a1</a>
<a id="a2">a2</a>
<a id="a3">a3</a>
<a id="a4">a4</a>
</div>
<script>
function bindEvent(elem, type, selector, fn) {

if (fn === undefined) {
fn = selector
selector = null
}

elem.addEventListener(type, function (e) {
if (selector && e.target && e.target.matches(selector))
fn.call(e.target, e)
else
fn(e)
})
}

// 使用代理
var div1 = document.getElementById("div1")
bindEvent(div1, "click", "a", function (e) {
console.log(this.innerHTML)
})

// 不使用代理
var a = document.getElementById("a1")
bindEvent(div1, "click", function (e) {
console.log(a.innerHTML)
})
</script>

存在兼容性问题:

  • IE 低版本使用 attachEvent 绑定事件,和 W3C 标准不一样;
  • IE 低版本使用量非常少,很多网站早已不支持(忽略)。

HTTP 请求

XMLHttpRequest

readyState:

  • 0 未初始化:还没有调用 send() 方法;

  • 1 载入:已经调用 send() 方法,正在发送请求;

  • 2 载入完成:send() 方法执行完成,已经接受到全部响应内容;

  • 3 交互:正在解析响应内容;

  • 4 完成:响应内容解析完成,可以在客户端调用。

status:

  • 2xx:成功处理成球

  • 3xx:需要重定向,浏览器直接跳转

  • 4xx:客户端错误

  • 5xx:服务端错误

1
2
3
4
5
6
7
8
9
10
let xhr = new XMLHttpRequest()
xhr.open("GET", "/api", false)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
alert(xhr.responseText)
}
}
}
xhr.send(null)

兼容性问题:

  • IE 低版本使用 ActiveXObject,和 W3C 标准不一样。
  • 本身用于客户端和服务端之间通信;

  • 具有本地存储功能,document.cookie 可以修改/获取;

  • 存储量小(4KB);

  • 所有HTTP请求都会带上,影响获取资源的效率。

LocalStorage 和 SessionStorage

  • HTML5 专门为存储设计,存储量大(5M);

  • API 简单易用,LocalStorage.setItem(key, value)LocalStorage.getItem(key)

  • SessionStorage 在关闭浏览器时会自动清理,LocalStorage 则会一直保留。

其他 API

日期

1
2
3
4
5
6
7
8
9
Date.now()
var dt = new Date()
dt.getTime()
dt.getFullYear()
dt.getMonth()
dt.getDate()
dt.getHours()
dt.getMinutes()
dt.getSeconds()

数学

1
Math.random()    // 获取随机数

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 遍历所有元素
var arr = [1, 2, 3]
arr.forEach(function(item, index) {
console.log(index, item)
})

// 判断是否所有元素都符合条件,some判断是否至少一个元素符合条件
var arr = [1, 2, 3]
var result = arr.every(function(item, index) {
if (item < 4) {
return true
}
})
console.log(result)

// 排序
var arr = [1, 4, 2, 3]
var arr2 = arr.sort(function(a, b) {
return a - b
})
console.log(arr2)


map // 对元素重新组装生成新数组
filter // 过滤符合条件的元素
// ...

对象

1
2
3
4
5
6
7
8
9
var obj = {
x: 100, y: 200, z: 300
}

for (var key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key])
}
}

常见问题

  1. JS中使用 typeof 能得到的哪些类型?
  2. 何时使用 ==,何时使用 ===
  3. JS 中有哪些内置函数?
  4. JS 变量按照存储方式区分为哪些类型,并描述其特点。
  5. 如何理解 JSON?
  6. 如何准确判断一个变量是数组类型?
  7. 写一个原型链继承的例子。
  8. 描述 new 一个对象的过程。
  9. zepto(值得阅读,或其他框架)源码中如何使用原型链?
  10. 如何理解变量提升?
  11. 说明 this 几种不同的使用场景。
  12. 创建 10个 <a> 标签,点击时弹出来对应的序号。
  13. 如何理解作用域?
  14. 实际开发中闭包的作用?
  15. 同步和异步的区别是什么?分别举一个同步和异步的例子。
  16. setTimeout 函数的用法。
  17. 前端使用异步的场景有哪些?
  18. 获取 2017-06-10 格式的日期。
  19. 获取随机数,要求是长度一致的字符串格式。
  20. 写一个能遍历对象和数组的通用 forEach 函数。
  21. DOM 是哪种基本的数据结构?
  22. DOM 操作的常用 API 有哪些?
  23. DOM节点的 attr 和 property 有何区别?
  24. 如何检测浏览器类型?
  25. 拆解 url 各部分。
  26. 编写一个通用的事件监听函数。
  27. 描述事件冒泡流程。
  28. 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件?
  29. 手动编写一个 ajax,不依赖第三方库。
  30. 跨域的几种实现方式。
  31. Cookie,SessionStorage,LocalStorage 的区别。
CATALOG
  1. 1. JavaScript Cheat Sheet
    1. 1.1. 变量类型:根据存储方式区分
      1. 1.1.1. 值类型(变量存放值)
      2. 1.1.2. 引用类型(变量存放地址):
    2. 1.2. typeof 运算符
      1. 1.2.1. 强制类型转换
        1. 1.2.1.1. 字符串拼接
      2. 1.2.2. == 运算符
      3. 1.2.3. if 语句
      4. 1.2.4. 逻辑运算符
      5. 1.2.5. 安全赋值(避免覆盖)
    3. 1.3. 函数
      1. 1.3.1. 内置函数
      2. 1.3.2. 构造函数
      3. 1.3.3. 原型规则
      4. 1.3.4. 执行上下文
      5. 1.3.5. this
      6. 1.3.6. 作用域与闭包
    4. 1.4. 异步和单线程
      1. 1.4.1. jQuery Deferred
      2. 1.4.2. Promise
      3. 1.4.3. async/await
    5. 1.5. Web API
      1. 1.5.1. DOM
        1. 1.5.1.1. DOM 属性操作
        2. 1.5.1.2. DOM 结构操作
      2. 1.5.2. BOM
      3. 1.5.3. 事件
        1. 1.5.3.1. 事件冒泡
      4. 1.5.4. HTTP 请求
        1. 1.5.4.1. XMLHttpRequest
      5. 1.5.5. Cookie
      6. 1.5.6. LocalStorage 和 SessionStorage
    6. 1.6. 其他 API
      1. 1.6.1. 日期
      2. 1.6.2. 数学
      3. 1.6.3. 数组
      4. 1.6.4. 对象
    7. 1.7. 常见问题