- 值类型
(1)值类型赋值的时候不会相互影响
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 值类型
let a = 100
let b = a
a = 200
console.log(b) // 100
复制代码</pre>
(2)常见值类型
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let a // Undefinded
const s = 'abc' // String
const n = 100 // Number
const b = true // Boolean
const s = Symbol('s') // Symbol
复制代码</pre>
- 引用类型
(1)引用类型赋值的时候,赋值的是引用地址,会相互影响。引用类型赋值地址主要是考虑计算机性能问题
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 引用类型
let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21
复制代码</pre>
栈从上往下添加数据,堆从下往上添加数据
(2)常见引用类型
<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const obj = { x: 100 } // 对象
const arr = [1, 2, 3] // 数组
// 特殊引用类型,指针指向空地址(《JS高级程序设计》中将null归为值类型)
const n = null
// 特殊引用类型,但不用于存储数据,所以没有‘拷贝、复制函数’这一说
function fn() {}
复制代码</pre>
2.1.2 typeof运算符
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 判断所有值类型
let a; typeof a // 'undefinded'
const s = 'abc'; typeof s // 'string'
const n = 100; typeof n // 'number'
const b = true; typeof b // 'boolean'
const s = Symbol('s'); typeof s // 'symbol'
// 能判断函数
typeof console.log // 'function'
typeof function () {} // 'function'
// 能识别引用类型(不能再识别的更细)
typeof null // 'object'
typeof ['a', 'b'] // 'object'
typeof { x: 100 } // 'object'
复制代码</pre>
2.1.3 深拷贝
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- 深拷贝
*/
const obj1 = {
name: 'Mary',
age: 21,
address: {
city: 'Beijing'
},
arr: [1, 2, 3]
}
const obj2 = deepClone(obj1)
obj2.address.city = 'Shanghai'
obj2.arr[0] = 0
console.log(obj1, obj2)
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for(let key in obj) {
if (obj.hasOwnProperty(key)) { // 保证key不是原型的属性
result[key] = deepClone(obj[key]) // 递归调用
}
}
return result
}
复制代码</pre>
2.2 变量计算-类型转换
2.2.1 字符串拼接
<pre class="prettyprint hljs go" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const a = 100 + 10 // 110
const b = 100 + '10' // '10010'
const c = true + '10' // 'true10'
复制代码</pre>
2.2.2 == 运算符
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true
复制代码</pre>
<pre class="prettyprint hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 除了 == null之外,其他一律都用 ===
const obj = { x: 100 }
if (obj.a == null) {}
// 相当于:
// if (obj.a === null || obj.a === undefined)
复制代码</pre>
2.2.3 if语句和逻辑运算
- truly变量:!!a === true的变量
- falsely变量:!!a === false的变量
<pre class="prettyprint hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 以下是falsely变量。除此之外都是truly变量
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false
复制代码</pre>
- if语句
<pre class="prettyprint hljs xquery" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// if语句判断的不是true还是false,而是truly变量或falsely变量
// truly变量
const a = true
if (a) {}
const b = 100
if (b) {}
// falsely变量
const c = ''
if (c) {}
const d = null
if (d) {}
let e
if (e) {}
复制代码</pre>
- 逻辑判断
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 逻辑判断也是判断truly变量和falsely变量
console.log(10 && 0) // 0
console.log('' || 'abc') // 'abc'
console.log(!window.abc) // true
复制代码</pre>
三、题目解析
3.1 typeof 能判断哪些类型
- 识别所有值类型
- 识别函数
- 判断是否是引用类型(不可再细分)
3.2 何时使用=== 何时使用==
==
会转换数据类型,尝试使两端相等,因此最好使用 ===
<pre class="prettyprint hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 除了 == null 之外,其他一律都用 ===
const obj = { x: 100 }
if (obj.a == null) {}
// 相当于:
// if (obj.a === null || obj.a === undefined)
复制代码</pre>
3.3 值类型和引用类型的区别
面试题:
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const obj1 = { x: 100, y: 200 }
const obj2 = obj1
let x1 = obj1.x
obj2.x = 101
x1 = 102
console.log(obj1) // { x: 101, y: 200 }
复制代码</pre>
- 存储位置不一样
- 值类型存放在栈内存中
- 引用类型在堆内存中开辟一个空间,存储具体的值,而在栈内存中存储的是指向该值的地址
3.4 手写深拷贝
- 注意判断值类型和引用类型
- 注意判断是数组还是对象
- 递归
具体代码见2.1.3 深拷贝
原型和原型链
一、题目
1.1 如何准确判断一个变量是不是数组?
1.2 手写一个简易的jQuery,考虑插件和扩展性
1.3 class的原型本质,怎么理解?
二、知识点
2.1 class和继承
2.1.1 class
- constructor
- 属性
- 方法
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 类
class Student {
constructor(name, number) {
this.name = name
this.number = number
}
sayHi () {
console.log(姓名:${this.name} 学号:${this.number}
)
}
}
// 通过类 new 对象/实例
const tom = new Student('Tom', 21)
console.log(tom.name)
console.log(tom.number)
tom.sayHi()
复制代码</pre>
2.1.2 继承
- extends
- super
- 扩展和重写方法
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// Student继承了People,People继承了Object
// 父类
class People {
constructor(name) {
this.name = name
}
eat () {
console.log(${this.name} eat something
)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name);
this.number = number
}
sayHi () {
console.log(姓名:${this.name} 学号:${this.number}
)
}
}
// 子类
class Teacher extends People {
constructor(name, major) {
super(name)
this.major = major
}
teach () {
console.log(${this.name} 教授:${this.major}
)
}
}
// 通过类 new 对象/实例
const tom = new Student('Tom', 21)
console.log(tom.name)
console.log(tom.number)
tom.sayHi()
tom.eat()
// 通过类 new 对象/实例
const teacherWang = new Teacher('Wang', 'Math')
console.log(teacherWang.name)
console.log(teacherWang.major)
teacherWang.teach()
teacherWang.eat()
复制代码</pre>
2.2 类型判断 instanceof
instanceof 可以判断变量属于哪个class或构造函数
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">tom instanceof Student // true
tom instanceof People // true
tom instanceof Object // true
[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
复制代码</pre>
2.3 原型和原型链
2.3.1 原型
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// class实际上是函数,可见是语法糖
typeof People // 'function'
typeof Student // 'function'
// 隐式原型和显示原型
console.log(tom.proto)
console.log(Student.prototype)
console.log(tom.proto === Student.prototype)
复制代码</pre>
原型关系:
- 每个class都有显示原型prototype
- 每个实例都有隐式原型proto
- 实例的proto指向对应class的prototype
基于原型的执行规则
获取属性tom.name或执行tom.sayHi()时,
- 先在自身属性和方法寻找
- 如果找不到则自动去proto中寻找
2.3.2 原型链
<pre class="prettyprint hljs elm" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">console.log(Student.prototype.proto)
console.log(People.prototype)
console.log(People.prototype === Student.prototype.proto)
复制代码</pre>
再看instanceof:
tom instanceof People
tom的隐式原型沿着原型链,一路往上查找,能否找到People的显示原型,若能找到,则返回 true
重要提示!
- class是ES6语法规范,由ECMA委员会发布
- ECMA只规定语法规则,即我们代码的书写规范,不规定如何实现
- 以上实现方式否是V8引擎的实现方式,也是主流的
三、题目解答
3.1 如何准确判断一个变量是不是数组?
a instanceof Array
3.2 手写一个简易的jQuery,考虑插件和扩展性
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length
for(let i = 0;i < length; i++) {
this[i] = result[i]
}
this.length = length
this.selector = selector
}
get(index) {
return this[index]
}
each(fn) {
for(let i = 0; i < this.length; i++) {
const elem = this[i]
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
}
// 扩展方式1:插件
jQuery.prototype.dialog = function (info) {
alert(info)
}
// 扩展方式2:'造轮子'
class myJQuery extends jQuery {
constructor(selector) {
super(selector);
}
// 扩展自己的方法
addClass(className) {
}
style (data) {
}
}
const p.get(0)
p.on('click', () => alert('clicked'))
$p.dialog('sth')
复制代码</pre>
3.3 class的原型本质,怎么理解?
- 原型和原型链的图示
- 每个class都有显示原型prototype
- 每个实例都有隐式原型proto
- 实例的proto指向对应class的prototype
- 属性和方法的执行规则
- 先在自身属性和方法寻找
- 如果找不到则自动去proto中寻找
作用域和闭包
一、题目
1.1 this的不同应用场景,如何取值?
1.2 手写bind函数
1.3 实际开发中闭包的应用场景,举例说明
1.4 创建10个a标签,点击的时候弹出来对应的序号
二、知识点
2.1 作用域和自由变量
2.1.1作用域
作用域代表了一个变量的合法使用范围,如上图红框所示,若变量在所在红框之外是无法使用的。 作用域分为:
- 全局作用域:代码中定义一个变量,不受函数约束,在全局均可使用,如window对象,document对象
- 函数作用域:任何定义在函数体内的变量或者函数都将处于函数作用域中,跳出该作用域外,变量无法使用
- 块级作用域(ES6新增)
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 块级作用域
if (true) {
let a = 1
}
console.log(a) // 报错
复制代码</pre>
2.1.2 自由变量
- 一个变量在当前作用域没有定义,但被使用了
- 向上级作用域,一层一层依次寻找,直至找到为止
- 如果到了全局作用域都没找到,则报错 xx is not defined
如对于function fn3() {}中,a,a1,a2均属于自由变量
2.2 闭包
作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递
<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 函数作为参数
function print(fn) {
const a = 200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn) // 100
复制代码</pre>
- 函数作为返回值被返回
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 函数作为返回值
function create() {
const a = 100
return function () {
console.log(a)
}
}
const fn = create()
const a = 200
fn() // 100
复制代码</pre>
所有的自由变量的查找,是在函数【定义】的地方,向上级作用域查找,不是在执行的地址!!!
2.3 this
2.3.1 this使用场景有:
- 作为普通函数:this指向全局对象(浏览器是window),严格模式中,表现为undefined
- 使用call apply bind:this指向绑定的对象上
- 作为对象方法被调用:this指向调用函数的对象
- 在class方法中调用:this指向对应的实例
- 箭头函数:所有的箭头函数都没有自己的this,都指向上级作用域(箭头函数本身不支持this)
2.3.2 this取什么值,是在函数【执行】的时候确定的,不是在函数定义的时候
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function fn1() {
console.log(this)
}
fn1() // window
fn1.call({ x: 100 }) // { x: 100 }
const fn2 = fn1.bind({ x: 200 })
fn2() // { x: 200 }
const zhangsan = {
name: '张三',
sayHi() {
console.log(this) // this即当前对象
},
wait() {
setTimeout(function() {
console.log(this) // function() {}是由setTimeout触发了执行(作为一个普通函数被执行),而非作为zhangsan的对象方法被执行,因此this === window
}, 1000)
},
waitAgain() {
setTimeout(() => {
console.log(this) // this即当前对象
}, 1000)
}
}
zhangsan.sayHi()
zhangsan.wait()
zhangsan.waitAgain()
class People {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi() {
console.log(this)
}
}
const zhangsan = new People('张三', 20)
zhangsan.sayHi() // zhangsan 对象
复制代码</pre>
三、题目解答
3.1 this的不同应用场景,如何取值?
见2.3.1
3.2 手写bind函数
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Function.prototype.myBind = function () {
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
// 获取this(args数组第一项)
const t = args.shift()
// fn1.bind(...)中的fn1
const self = this
// 返回一个函数
return function () {
return self.apply(t, args)
}
}
function fn1(a, b, c) {
console.log('this ', this)
console.log(a, b, c)
console.log('this is fn1')
}
const fn2 = fn1.myBind({ x: 100 }, 1, 2, 3)
fn2()
复制代码</pre>
3.3 实际开发中闭包的应用场景,举例说明
- 隐藏数据
<pre class="prettyprint hljs sql" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 闭包隐藏数据,做一个简单的cache工具,只提供API
function createCache() {
const data = {}
return {
set: function (key ,val) {
data[key] = val
},
get: function (key, val) {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
复制代码</pre>
3.4 创建10个a标签,点击的时候弹出来对应的序号
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// i是全局变量,a的点击事件发生时,i的值已经变为10
// let i, a
// for (i = 0; i < 10; i++) {
// a = document.createElement('a')
// a.innerHTML = i + '
'
// a.addEventListener('click', function(e){
// e.preventDefault()
// alert(i) // 10
// })
// document.body.appendChild(a)
// }
// 修改定义i的位置,构成块级作用域
let a
for (let i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '
'
a.addEventListener('click', function(e){
e.preventDefault()
alert(i) // 10
})
document.body.appendChild(a)
}
复制代码</pre>
异步
一、题目
1.1 同步和异步的区别是什么?
1.2 手写用Promise加载一张图片
1.3 前端使用异步的场景有哪些?
1.4 说出以下的打印顺序
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// setTimeout 笔试题
console.log(1)
setTimeout(function() {
console.log(2)
}, 1000)
console.log(3)
setTimeout(function() {
console.log(4)
}, 0)
console.log(5)
复制代码</pre>
二、知识点
2.1 单线程和异步
- JS是单线程语言,只能同时做一件事
- 浏览器和nodejs已支持JS启动进程,如Web Worker
- JS和DOM渲染共用同一个线程,因为JS可修改DOM结构
- 遇到等待(网络请求,定时任务)不能卡住
- 因此,需要异步(为了解决单线程可能发生等待的问题)
- 异步基于回调callback函数形式
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 异步 (callback 回调函数)
console.log(100)
setTimeout(() => {
console.log(200)
}, 1000)
console.log(300)
// 同步
console.log(100)
alert(200)
console.log(300)
复制代码</pre>
异步和同步
- 基于JS是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
2.2 异步的应用场景
- 网络请求,如ajax,图片加载
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// ajax
console.log('start')
$.get('./data1.json', function(data1) {
console.log(data1)
})
console.log('end')
// 图片加载
console.log('start')
let img = document.createElement('img')
img.onload = function () {
console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
复制代码</pre>
- 定时任务,如setTimeout
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// setTimeout
console.log(100)
setTimeout(() => {
console.log(200)
}, 1000)
console.log(300)
// setIntelval
console.log(100)
setIntelval(() => {
console.log(200)
}, 1000)
console.log(300)
复制代码</pre>
2.3 callback hell和Promise
callback hell 回调地狱
<pre class="prettyprint hljs typescript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 获取第一份数据
$.get(url1, (data1) => {
console.log(data1)
// 获取第二份数据
$.get(url2, (data2) => {
console.log(data2)
// 获取第三份数据
$.get(url3, (data3) => {
console.log(data3)
// 还可能获取更多数据
})
})
})
复制代码</pre>
Promise解决回调地狱
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function getData(url) {
return new Promise((resolve, reject) => {
$.ajax({
url,
success(data) {
resolve(data)
},
error(err) {
reject(err)
}
})
})
}
getData(url1).then(data1 => {
console.log(data1)
return getData(url2)
}).then(data2 => {
console.log(data2)
return getData(url3)
}).then(data3 => {
console.log(data3)
}).catch(err => console.error(err))
复制代码</pre>
三、题目解答
3.1 同步和异步的区别是什么?
- 基于JS是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
3.2 手写用Promise加载一张图片
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function loadImg(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => {
resolve(image);
};
image.onerror = () => {
reject(new Error('图片加载失败:' + url));
};
image.src = url;
});
}
const url = '*'
loadImg(url).then(img => {
console.log(img.width)
}).catch(e => {
console.log(e)
})
const url1 = ''
const url2 = '*'
loadImg(url1).then(img1 => {
console.log(img1.width)
return img1 // 普通对象
}).then(img1 => {
console.log(img1.height)
return loadImg(url2) // Promise 实例
}).then(img2 => {
console.log(img2.width)
return img2
}).then(img2 => {
console.log(img2.height)
}).catch(e => {
console.log(e)
})
复制代码</pre>
3.3 前端使用异步的场景有哪些?
- 网络请求,如ajax,图片加载
- 定时任务,如setTimeout
3.4 说出以下的打印顺序
1 3 5 4 2
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// setTimeout 笔试题
console.log(1)
setTimeout(function() {
console.log(2)
}, 1000)
console.log(3)
setTimeout(function() {
console.log(4)
}, 0)
console.log(5)
复制代码</pre>
JS Web API:DOM操作
DOM(Document Object Model)文档对象模型
一、题目
1.1 DOM是哪种数据结构
1.2 DOM操作的常用API
1.3 attr和property的区别
1.4 一次性插入多个DOM节点,考虑性能
二、知识点
2.1 DOM本质
本质上是一棵树
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<p>this is p</p>
</div>
</body>
</html>
复制代码</pre>
2.2 DOM节点操作
2.2.1 获取DOM节点
<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const div1 = document.getElementById('div1') // 元素
const divList = document.getElementsByTagName('div') // 集合
const containerList = document.getElementsByClassName('container') // 集合
const pList = document.querySelectorAll('p') // 集合
复制代码</pre>
2.2.2 attribute
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const pList = document.querySelectorAll('p')
const p = pList[0]
p.setAttribute('data-name', 'type')
console.log(p.getAttribute('data-name'))
p.setAttribute('style', 'color: red;')
console.log(p.getAttribute('style'))
复制代码</pre>
2.2.3 property
JS通过property这种形式,修改页面的DOM渲染结构
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const pList = document.querySelectorAll('p')
const p = pList[0]
p.style.width = '100px'
p.className = 'red-text'
console.log(p.nodeName) // P
console.log(p.nodeType) // 1
复制代码</pre>
- attribute:
- 是HTML标签上的某个属性,如id、class、value等以及自定义属性。
- 它的值只能是字符串
- 关于这个属性一共有三个相关的方法,setAttribute、getAttribute、removeAttribute;
- 修改HTML属性,会改变HTML结构
- property:
- 是js获取的DOM对象上的属性值,比如a,你可以将它看作为一个基本的js对象。
- 这个节点包括很多property,比如value,className以及一些方法onclik等方法。
- 修改对象属性,不会体现到HTML结构中
以上两者都有可能引起DOM重新渲染
2.3 DOM结构操作
2.3.1 新增/插入节点
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="div1" style="border-bottom: 1px solid red;">
<p id="p1">this is p1</p>
<p>this is p2</p>
<p>this is p3</p>
</div>
<div id="div2">
<p>this is p in div2</p>
</div>
<script> const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
// 新建节点
const newP = document.createElement('p')
newP.innerHTML = 'this is new p'
// 插入节点
div1.appendChild(newP)
// 移动节点
const p1 = document.getElementById('p1')
div2.appendChild(p1) </script>
</body>
</html>
复制代码</pre>
2.3.2 获取子元素列表,获取父元素
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 获取父元素
console.log(p1.parentNode)
// 获取子元素列表
const div1ChildeNodes = div1.childNodes
console.log('div1ChildeNodes: ', div1ChildeNodes)
const div1ChildeNodesP = Array.prototype.slice.call(div1ChildeNodes).filter(item => item.nodeType === 1) // 文本节点的nodeType === 3
console.log('div1ChildNodesP: ', div1ChildeNodesP)
复制代码</pre>
2.3.3 删除子元素
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 删除子元素节点
div1.removeChild(div1ChildeNodesP[0])
复制代码</pre>
2.4 DOM性能
DOM操作非常‘昂贵’,避免频繁的DOM操作:
2.4.1 对DOM查询做缓存
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 不缓存DOM查询结果
for(let i = 0; i < document.getElementsByTagName('p').length; i++) {
// 每次循环,都会计算length,频繁进行DOM操作
}
// 缓存DOM查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length
for(let i = 0; i < length; i++) {
// 缓存length,只进行一次DOM查询
}
复制代码</pre>
2.4.2 将频繁操作改为一次性操作
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const list = document.getElementById('list')
// 方法1:频繁操作
for(let i = 0; i < 10; i++) {
const li = document.createElement('li')
li.innerHTML = list item ${i}
list.appendChild(li)
}
// 方法2:一次性操作
// 创建一个文档片段,此时还没有插入到DOM树中
const frag = document.createDocumentFragment()
// li元素插入到文档片段
for(let i = 10; i < 20; i++) {
const li = document.createElement('li')
li.innerHTML = list item ${i}
frag.appendChild(li)
}
// 都完成之后,再将文档片段插入到DOM树中
list.appendChild(frag)
复制代码</pre>
三、题目解答
3.1 DOM是哪种数据结构
树(DOM树)
3.2 DOM操作的常用API
- DOM节点操作
- DOM结构操作
- attribute和property
3.3 attr和property的区别
- property:修改对象属性,不会体现到HTML结构中
- attribute:修改HTML属性,会改变HTML结构
- 两者都有可能引起DOM重新渲染
(优先使用property)
3.4 一次性插入多个DOM节点,考虑性能
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const list = document.getElementById('list')
// 创建一个文档片段,此时还没有插入到DOM树中
const frag = document.createDocumentFragment()
// li元素插入到文档片段
for(let i = 10; i < 20; i++) {
const li = document.createElement('li')
li.innerHTML = list item ${i}
frag.appendChild(li)
}
// 都完成之后,再将文档片段插入到DOM树中
list.appendChild(frag)
复制代码</pre>
JS-Web-API-BOM
一、题目
1.1 如何识别浏览器的类型
1.2 分析拆解URL各部分
二、知识点
2.1 navigator
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome)
复制代码</pre>
2.2 screen
<pre class="prettyprint hljs css" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">console.log(screen.width)
console.log(screen.height)
复制代码</pre>
2.3 location
<pre class="prettyprint hljs awk" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">location.href
location.protocol // 'http:' 'https:'
location.host // location.hostname + location.port
location.pathname
location.search
location.hash
复制代码</pre>
2.4 history
<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">history.back()
history.forward()
复制代码</pre>
JS-Web-API-事件
一、题目
1.1 编写一个通用的事件监听函数
1.2 描述事件冒泡的流程
1.3 无限下拉的图片列表,如何监听每个图片的点击?
二、知识点
2.1 事件绑定
<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 点击事件
const btn = document.getElementById('btn1')
btn.addEventListener('click', event => {
console.log('clicked')
})
复制代码</pre>
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 通用的绑定函数(简版
<body>
<a id="link1" href="https://www.baidu.com">link</a>
<script>)
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
const a = document.getElementById('link1')
bindEvent(a, 'click', e => {
console.log(event.target) // 获取触发的元素
e.preventDefault() // 阻止默认行为
alert('clicked')
}) </script>
</body>
复制代码</pre>
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 通用的绑定函数(支持普通绑定和事件代理)
<body>
<a id="link1" href="https://www.baidu.com">link</a>
<div id="div3">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<button>加载更多</button>
</div>
<script> function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type , event => {
const target = event.target
if (selector) {
// 代理绑定
if (target.matches(selector)) {
fn.call(target, event)
}
} else {
// 普通绑定
fn.call(target, event)
}
})
}
const link1 = document.getElementById('link1')
bindEvent(link1, 'click', function(e) {
e.preventDefault() // 阻止默认行为
console.log(this.innerHTML)
})
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function(event) {
event.preventDefault()
console.log(this.innerHTML)
}) </script>
</body>
复制代码</pre>
2.2 事件冒泡
子元素身上的事件,会冒泡到父元素身上,可用event.stopPropagation() 阻止冒泡行为
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><body>
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div2" style="border-top: 1px solid red;">
<p id="p5">取消</p>
<p id="p6">取消</p>
</div>
<script> function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
const p1 = document.getElementById('p1')
const body = document.body
bindEvent(p1, 'click', event => {
event.stopPropagation() // 阻止冒泡
console.log('激活')
})
bindEvent(body, 'click', event => {
console.log('取消')
}) </script>
复制代码</pre>
2.3 事件代理(事件委托)
针对瀑布流这种子元素个数很多,或者内部结构比较复杂的情况下,将本来加在子元素身上的事件,加在其父元素身上。事件代理的好处有:
- 代码简洁,比如不用for循环为子元素添加事件,js新生成的子元素不用新为其添加事件
- 减少浏览器内存占用(相对于只为一个父元素绑定监听事件,为每个子元素都绑定监听事件更耗费内存)
但是,不要滥用
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><body>
<div id="div3">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<button>加载更多</button>
</div>
<script> function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', event => {
event.preventDefault()
const target = event.target
if (target.nodeName === 'A') {
console.log(target.innerHTML)
}
}) </script>
</body>
复制代码</pre>
三、题目解答
3.1 编写一个通用的事件监听函数
见2.1
3.2 描述事件冒泡的流程
基于DOM树形结构,事件会顺着触发元素往上冒泡
应用场景:代理
3.3 无限下拉的图片列表,如何监听每个图片的点击?
- 利用事件代理,
- 用e.target获取触发元素,
- 用matches来判断触发元素是否在限制的触发条件内
JS-Web-API-Ajax
一、题目
1.1 手写一个简易的ajax
1.2 跨域的常用实现方式
二、知识点
2.1 XMLHttpRequest
2.1.1 get请求
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// get 请求
const xhr = new XMLHttpRequest()
xhr.open('GET', '/data/test.json', true) // true 异步请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
} else {
console.log('其他情况:', xhr.status)
}
}
}
xhr.send(null)
复制代码</pre>
2.1.2 POST请求
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// POST 请求
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText))
} else {
console.log('其他情况:', xhr.status)
}
}
}
const postData = {
username: 'zhangsan',
password: 'xxx'
}
xhr.send(JSON.stringify(postData))
复制代码</pre>
2.1.3 xhr.readyState
- 0-(未初始化)还没有调用send()方法
- 1-(载入)已调用send()方法,正在发送请求
- 2-(载入完成)send()方法执行完成,已经接收到全部响应内容
- 3-(交互)正在解析响应内容
- 4-(完成)响应内容解析完成,可以在客户端调用
2.1.4 xhr.status
- 2xx - 表示成功处理请求,如200
- 200 请求成功,对GET和POST请求的应答文档跟在后面
- 3xx - 需要重定向,浏览器直接跳转,如301,302,304
- 301 永久重定向,被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。
- 302 临时重定向,请求的资源被暂时的移动到了由Location 头部指定的 URL上。浏览器会重定向到这个URL
- 304 未改变,说明无需再次传输请求的内容,也就是说可以使用缓存的内容
- 4xx - 客户端请求错误,如400,403,404
- 400 Bad Request,响应状态码表示由于语法无效,服务器无法理解该请求
- 403 Forbidden, 资源不可用,服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。
- 404 Not Found,请求失败,请求所希望得到的资源未被在服务器上发现
- 5xx - 服务器端错误
- 500 Internal Server Error,服务器遇到了不知道如何处理的情况。
2.2 跨域:同源策略,跨域解决方案
2.2.1 同源策略
- ajax请求
- ajax请求时, 浏览器 要求当前网页和server必须同源(安全)
- 同源:协议、域名、端口,三者必须一致。如下就不属于同源:前端(http:///a.com:8080/),server( a.com/api/xxxx )
- 加载图片、css、js
-
可无视同源策略:
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;"><img src=跨域的图片地址 />
<link src=跨域的css地址 />
<script src=跨域的js地址></script>
复制代码</pre> -
加载图片、css、js可无视同源策略,实现跨域,它们可实现的功能有:
<pre class="hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;"><img />
<link /> <script> <script> </pre>
2.2.2 跨域
- 所有的跨域,都必须经过server端的允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// CORS - 服务器设置http header
//第二个参数填写允许跨域的域名称,不建议直接写'*'
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:8011');
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
response.setHeader('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
// 接收跨域的cookie
response.setHeader('Access-Control-Allow-Credentials', 'true');
复制代码</pre>
2.2.3 JSONP
访问https://imooc.com,服务端一定返回一个html文件吗?- 不是的,服务器可以任意动态拼接数据返回,只要符合html格式要求。同理于 <script src="https://imooc.com/getData.js"></script>
<script>
可绕过跨域限制,服务器可以任意动态拼接数据返回。所以, <script>
就可以获得跨域的数据,只要服务端愿意返回
JSONP的实现原理:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 前端jsonp.html
<script> window.callback = function(data) {
console.log(data)
} </script>
<script src='http://127.0.0.1:5501/jsonp.js'></script>
// 服务jsonp.js
callback(
{ name: 'zhangsan' }
)
复制代码</pre>
三、题目解答
3.1 手写一个简易的ajax
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
} else {
reject('其他情况' + xhr.status)
}
}
}
xhr.send(null)
})
return p
}
ajax('/data/test.json').then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
复制代码</pre>
3.2 跨域的常用实现方式
- JSONP
- CORS
JS-Web-API 存储
一、题目
1.1 描述cookie、localStorage、sessionStorage的区别
二、知识点
2.1 cookie
<pre class="hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">document.cookie = 'val=xxxxx'
</pre>
cookie的缺点:
<pre class="hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">document.cookie = '***'
</pre>
2.2 localStorage、sessionStorage
- HTML5专门为存储而设计,最大可存5M(针对每个域名)
- API简单易用 setItem getItem
- 不会随着http请求被发送出去
localStorage、sessionStorage的区别:
- localStorage数据会永久存储,除非代码或手动删除
- sessionStorage数据只存在于当前会话,浏览器关闭则清空
- 一般用localStorage会更多一些
三、题目解答
3.1 描述cookie、localStorage、sessionStorage的区别
- 容量
- API易用性
- 是否会跟随http请求发送出去
开发环境
一、Git
- 最常用的代码版本管理工具
- 大型项目需要多人协作开发,必须熟用git
- Mac OS自带git命令,windows可去官网下载安装
- git服务端常见的有:github,coding.net等
1.1 常用git命令
git命令 | 作用 |
---|---|
git init | 在目录中创建新的 Git 仓库 |
git clone [url] | 拷贝一个 Git |
git add index.html hello.js | 将文件添加到缓存 |
git add . | 将所有文件添加到缓存 |
git status | 查看在上次提交之后是否有修改 |
git diff | 查看执行 git status 的结果的详细信息 |
git commit -m "提交注释" | 将缓存区内容添加到仓库中 |
git branch [branchname] | 创建分支 |
git checkout [branchname] | 切换到需要修改的分支 |
git checkout -b [branchname] | 创建新分支并立即切换到该分支下 |
git log | 查看提交历史 |
git push origin master | 上传代码至远程仓库 |
git pull origin master | 从远程仓库下载代码病快速合并 |
git fetch | 从远程仓库下载新分支与数据,但不会自动merge |
git merge | 远端仓库提取数据并尝试合并到当前分支 |
二、Chrome调试工具
2.1 Elements
dom结构
2.2 Console
打印日志窗口
2.3 debugger
代码中在需设置断点处的地址加上 debugger
,或在Sources对应的js文件添加断点(点击左侧代码行序号),则可以触发断点调试
2.4 Network
所有资源加载
2.5 Application
查看和操作本地存储信息
三、抓包
移动端H5页,查看网络请求,需要用工具抓包:
- windows一般用fiddler
- Mac OS一般用charles
抓包过程:
- 手机和电脑连同一个局域网
- 将手机代理到电脑上
- 手机浏览网页,即可抓包
四、webpack babel
- ES6模块化,浏览器暂不支持
- ES6语法,浏览器并不完全支持
- 压缩代码,整合代码,以让网页加载更快
五、Linux常用命令
linux命令 | 作用 |
---|---|
ls | 查看所有文件 |
ls -a | 查看所有文件,包括隐藏文件(-a 是all的意思) |
ll | 查看所有文件列表 |
clear | 清屏 |
mkdir [name] | 创建文件夹 |
rm -rf [name] | 删除文件夹及文件夹内部所有内容(-r递归,-f强制) |
rm [name] | 删除文件 |
cd [name] | 进入文件夹 |
mv [oldName] [newName] | 修改文件名称 |
mv index.html ../index.html | 移动文件存放路径 |
cp index.html index1.html | 复制文件 |
cat [name] | 将该文件的所有内容显示在控制台中 |
grep "关键字" [name] | 在文件中查找关键字 |
touch [name] | 新建文件 |
vi [name] | 若无该文件则新建文件并进入vim编辑器模式,若有该文件则直接打开进入vim |
进入vim编辑器模式,输入i,即可编辑输入 | - |
点击esc退出编辑模式,再输入:wq,即可保存并退出vim模式 | - |
若输入后,不想保存,则输入:q!,即可强制退出 | - |
运行环境
- 运行环境即浏览器(server端有nodejs)
- 下载网页代码,渲染出页面,期间会执行若干JS
- 要保证代码在浏览器中稳定且高效
一、网页加载过程
1.1 题目
1.1.1 从输入URL到渲染出页面的整个过程
1.1.2 window.onload和DOMContentLoaded的区别
1.2 知识点
1.2.1 加载资源的形式
- HTML代码
- 媒体文件,如图片、视频等
- javascript css
1.2.2 加载资源的过程
- DNS解析:域名 -> IP地址
- 浏览器根据IP地址向服务器发起http请求
- 服务器处理http请求,并返回给浏览器
1.2.3 渲染页面的过程
根据HTML代码生成 DOM Tree
根据CSS代码生成CSSOM(object model)
将DOM Tree和CSSOM整合形成Render Tree
根据Render Tree渲染页面
遇到
<script>
则暂停渲染,优先加载并执行JS代码,完成再继续直至把Render Tree渲染完成
思考
为什么把CSS放在head标签中?
- css放在body标签尾部时, DOMTree构建完成之后便开始构建RenderTree, 并计算布局渲染网页, 等加载解析完css之后, 开始构建CSSOMTree, 并和DOMTree重新构建RenderTree, 重新计算布局渲染网页
- css放在head标签中时, 先加载并解析css,构建CSSOMTree, 与此同时构建DOMTree, CSSOMTree和DOMTree都构建完毕之后开始构建RenderTree, 计算布局渲染网页
对比两者, css放在head标签中比css放在body标签尾部少了一次构建RenderTree, 一次计算布局和一次渲染网页, 因此性能会更好; 并且css放在body标签尾部时会在网页中短暂出现"裸奔"的HTML, 这不利于用户体验
为何建议把js放在body最后?
- 避免JavaScript操作DOM失效
- 在解析JavaScript代码之前,将页面的内容完全呈现在浏览器中,用户会因为浏览器显示空白页面的时间缩短而感到打开页面的速度变快了
<pre class="hljs xquery" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">window.onload和DOMContentLoaded
</pre>
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">window.addEventListener('load', function() {
// 页面的全部资源加载完才会执行,包括图片、视频等
})
window.addEventListener('DOMContentLoaded', function() {
// DOM渲染完即可执行,此时图片、视频还可能没加载完
})
复制代码</pre>
1.3 题目
1.3.1 从输入URL到渲染出页面的整个过程
- 下载资源:各个资源类型,下载过程
- 渲染页面:结合html css javascript 图片等
1.3.2 window.onload和DOMContentLoaded的区别
- window.onload资源全部加载完成后才能执行,包括图片
- DOMContentLoaded DOM渲染完成即可,图片可能尚未下载
二、性能优化
2.1 性能优化原则
- 多使用内存、缓存或其他方法
- 减少CPU计算量,减少网络加载耗时
2.2 从何入手
2.2.1 让加载更快
-
减少资源体积:
- 压缩代码
-
减少访问次数:
- 合并代码(webpack合并js css文件、雪碧图)
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;"><script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script><script src="abc.js"></script>
复制代码</pre>- SSR服务端渲染
- 服务端渲染:将网页和数据一起加载,一起渲染
- 非SSR:先加载网页,再加载数据,再渲染数据
- 缓存
- 静态资源加hash后缀,根据文件内容计算hash
- 文件内容不变,则hash不变,则URL不变
- URL和文件不变,则会自动触发http缓存机制,返回304
使用更快的网络:CDN(根据地域做静态服务)
2.2.2 让渲染更快
- CSS 放在head,JS放在body最下面
- 尽早开始执行JS,用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多)
- 对DOM查询进行缓存
- 频繁DOM操作,合并到一起再插入到DOM结构
- 节流 防抖
2.2.3 防抖 debounce
含义:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function debounce (fn, delay = 500) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
const input1 = document.getElementById('input1')
input1.addEventListener('keyup', debounce(function(e) {
console.log(e.target)
console.log(this.value)
}, 1000))
复制代码</pre>
2.2.4 节流
含义:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内一滴一滴的往下滴。
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置
- 直接用drag事件,则会频繁触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每隔100ms触发一次
*/
<body>
<div id="div1" draggable="true" style="width: 100px;height: 100px;border: 1px solid red;">可拖拽</div>
<script> // 节流
function throttle(fn, delay = 100) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
const div1 = document.getElementById('div1')
div1.addEventListener('drag' , throttle(function(e) {
console.log(e.offsetX, e.offsetY)
}, 500)) </script>
</body>
复制代码</pre>
三、安全
3.1 常见的web前端攻击方式有哪些
3.1.1 XSS跨站请求攻击
攻击情景模拟:
<pre class="hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><script> </pre>
XSS攻击预防:
- 替换特殊字符,如
<
变为<
>
变为>
-
<script>
变为<script>
,将会作为字符串直接显示,而不会作为脚本执行(npm 有一个xss工具包可帮忙替换: www.npmjs.com/package/xss… - 前端要替换,后端也要替换
3.1.2 CSRF跨站请求伪造
攻击情景模拟:
- 银行网站A,它以GET请求来完成银行转账的操作,如: www.mybank.com/Transfer.ph…
- 危险网站B,它里面有一段HTML的代码如下:
<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"> <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
复制代码</pre>
- 首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块......
为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的 <img>
以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源“ www.mybank.com/Transfer.ph…,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作.
CSRF攻击预防:
- 使用POST接口
- 增加验证,例如密码、短信验证码、指纹等
需要更多学习资料的同学可以加Q群:1093606290