什么是闭包?
闭包是指能在函数外部获取到声明在函数内部变量的函数
闭包的作用
例如:
function A() {
let num = 10
return function () {
return num
}
}
作用域链保证了执行环境里有权访问的变量和函数时有序的,作用域链中的变量只能向上访问,直到访问到 window
对象
每个对象中都有一个内部属性,就是 prototype
(原型),每个对象声明的实例中也都含有一个属性,就是 __proto__
(原型)
关系: instance.__proto__ == instance.constructor.prototype
特点: 当我们访问一个对象的属性时,如果在对象上找不到,就会沿着原型链向上层寻找,直到 Object
父类(构造函数):
function Animal(name) {
this.name = name
}
Animal.prototype.eat = function () {
console.log('吃肉..')
}
function Dog() {}
Dog.prototype = new Animal()
let dog = new Dog()
console.log(dog.name) // undefined
dog.eat() // 吃肉..
重点: 让子构造函数的原型指向父构造函数的实例
优点: 可以继承父构造函数原型中的属性和方法
缺点: 新实例无法向父类构造函数传参,无法继承构造函数实例中的属性和方法
function Cat() {
Animal.call(this, '猫')
}
let cat = new Cat()
console.log(cat.name) // 猫
cat.eat() // cat.eat is not a function
重点: 子构造函数中调用父构造函数并改变 this
指向
优点: 可以继承父构造函数中的属性和方法
缺点: 无法继承构造函数原型中的属性和方法
function Cat(name) {
Animal.call(this, name)
}
Cat.prototype = new Animal()
let cat = new Cat('猫')
console.log(cat.name)
cat.eat()
重点: 结合构造函数继承和原型链继承的特点
优点: 可以继承父构造函数及其原型中的属性和方法
缺点: 代码复杂
父类:
class Animal {
constructor(name) {
this.name = name
}
eat() {
console.log(this.name + '吃吃吃')
}
}
子类:
class Cat extends Animal {
constructor(name) {
super(name)
}
}
let cat = new Cat('猫')
console.log(cat.name) // 猫
cat.eat() // 猫吃吃吃
重点: 子类的 constructor
方法中要调用 super
方法,表示调用父类的 constructor
方法
优点: 可以继承父类中的属性和方法,代码简单
事件代理又叫事件委托,是把原本要监听的事件绑定给父元素,原理是利用了事件冒泡,使用事件委托的优点是:
this
指向 window
this
,所以 this
会像未声明的变量一样,验证作用域链向上层寻找,即箭头函数中 this
的指向与箭头函数外部环境的 this
指向一致call
、apply
、bind
会改变 this
的指向,指向改为指向方法调用时传入的第一个参数this
指向函数调用者
new
关键字会生成一个新的空对象this
指向这个空对象
创建 Ajax 过程:
let xhr = new XMLHttpRequest()
xhr.open(method, url, asynchronous)
xhr.send()
xhr.onreadystatechange = function () {
if (readyState == 4) {
if (xhr.status == 200) {
success(xhr.responseText)
} else {
fail(xhr.status)
}
}
}
readyState:
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
同源策略: 协议名、域名、端口号都相同的两个URL地址叫做同源,非同源的地址间交互会产生跨域。
解决跨域:
JSONP
解决跨域CORS
解决跨域(跨域资源共享)node.js
中间件代理跨域WebSocket
协议跨域详细情况:https://segmentfault.com/a/1190000011145364
传参方式: get
传参会拼接到URL地址后,形式是 ?
后面以 &
分割,post
传参是将参数放到请求体里
数据大小: get
传参有数据大小的限制,一般参照浏览器地址栏支持的最大字节长度,post
传参没有参数数据大小要求
安全性: post
的安全性相对较高。
使用: get
和 post
都可以用来获取或更新数据,只是一般用 get 来获取数据,post 来存储数据
立即执行函数,不暴露私有成员
let module = (() => {
let num = 0
let f1 = () => {}
let f2 = () => {}
return { f1, f2 }
})()
defer
(只支持 IE):并行加载 js 文件,会按照页面上 script
标签的位置执行脚本async
:并行加载 js 文件,加载完成后立即执行脚本script
共同点
document.write
元素视图属性
offsetWidth
:元素 content
宽度 + 左右 border
+ 左右 padding
offsetHeight
:元素 content
高度 + 上下 border
+ 上下 padding
clientWidth
:元素 content
宽度 + 左右 padding
clientHeight
:元素 content
高度 + 上下 padding
scrollWidth
:元素内容真实宽度,内容不超出盒子宽度时为 clientWidth
scrollHeight
:元素内容真实高度,内容不超出盒子宽度时为 clientHeight
window 视图属性
innerWidth
:浏览器窗口可视区宽度innerHeight
:浏览器窗口可视区高度
Promise
主要用于异步操作,可以将异步操作队列化。新建实例时,传入一个无名函数并且具有两个参数;参数一是成功后调用的函数名,函数体当做参数传入 then()
中,参数二是失败后调用的函数名,函数体当做参数传入 catch()
中
let promise = new Promise((resolve, reject) => {
if (true) {
resolve('成功')
} else {
reject('失败')
}
})
promise.then(res => console.log(res)).catch(err => console.log(err))
基本数据类型: Number
、String
、Boolean
、Null
、Undefined
----- 不能拥有属性和方法
引用数据类型: Function
、Array
、Object
----- 拥有属性和方法(对象)
基本包装类型: Number
、String
、Boolean
、Null
、Undefined
----- 属于特殊的引用类型,与基本类型对应
每当声明一个基本数据类型(Number
、String
、Boolean
)的变量,后台都会声明一个同名的与之对应的引用类型(基本包装类型)的变量,从而使基本类型的变量可以调用一些属性和方法
如果声明的是基本类型(Number
、String
、Boolean
),此时在实例上新增的属性或方法只存在一瞬间(一行代码内);
如果声明的是引用类型(Number
、String
、Boolean
),此时在实例上新增的属性或方法长久存在;
例:
// 声明基本类型,代码 // 后台对应代码
const str1 = 'hello world' // const str1 = new String('hello world')
str1.color = 'red' // str1.color = 'red'
// str1.color = null (属性消除)
console.log(str1.color) // undefined
// 声明引用类型,代码 // 后台对应代码
const str2 = new String('hello world') // const str2 = new String('hello world')
str2.color = 'red' // str2.color = 'red'
console.log(str2.color) // red
===
/ !==
来比较 true
/ false
或者数值new Array
这种形式let
/ var
限定作用域
undefined
是声明了变量但没有赋值,null
是声明了变量并且赋值为 null
判断 undefined
和 null
时,必须用 ===
/ !==
,因为 ==
和 !=
无法区分 undefined
和 null
在 js 脚本开头使用 use strict
严格模式的限制:
with
this
指向全局变量
attribute
是 DOM 元素在 html 文档中作为标签拥有的属性
property
是 DOM 元素在 JS 中作为对象拥有的属性
对于标准属性来说,attribute
和 property
是同步的,但是自定义属性不同步
var
声明的变量会挂载到 window
对象上,而 let
和 const
不会let
和 const
会生成块级作用域let
和 const
不允许变量提升,必须先声明,后使用let
和 const
不允许重复声明,同意作用域下,只能声明一次const
声明后必须赋值,且不能改变
Vue 是采用数据劫持结合订阅者-发布者模式,通过 Object.defineProperty()
来劫持各个属性的 setter
和 getter
,数据变动时发布消息给订阅者,触发响应的监听回调
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.sort(() => Math.random() - 0.5)
console.log(arr)
不能一次性将几万条数据都渲染出来,可以分批渲染,每次规定渲染条数
利用 window.requestAnimationFrame
来进行页面刷新
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
head>
<body>
<ol id="olObj">ol>
body>
<script>
let total = 10000
let each = 23
let loopCount = Math.ceil(total / each)
let count = 0
const olObj = document.getElementById('olObj')
function appendObj() {
let fragment = document.createDocumentFragment()
for (let i = 0; i < each; i++) {
let totalObj = count * each + i + 1
if (totalObj > total) {
break
}
const liObj = document.createElement('li')
liObj.innerHTML = 'item' + totalObj
fragment.appendChild(liObj)
}
olObj.appendChild(fragment)
count++
loop()
}
function loop() {
if (count < loopCount) {
window.requestAnimationFrame(appendObj)
}
}
loop()
script>
html>
window.requestAnimationFrame
window.requestAnimationFrame()
告诉浏览器,希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
document.createDocumentFragment
createDocumentFragment()
方法,是用来创建一个虚拟的节点对象,或者说,是用来创建文档碎片节点。它可以包含各种类型的节点,在创建之初是空的。
DocumentFragment
节点不属于文档树,继承的 parentNode
属性总是 null
。它有一个很实用的特点,当请求把一个 DocumentFragment
节点插入文档树时,插入的不是 DocumentFragment
自身,而是它的所有子孙节点,即插入的是括号里的节点。这个特性使得 DocumentFragment
成了占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作。 另外,当需要添加多个 dom
元素时,如果先将这些元素添加到 DocumentFragment
中,再统一将 DocumentFragment
添加到页面,会减少页面渲染 dom
的次数,效率会明显提升。
如果使用 appendChid
方法将原 dom
树中的节点添加到 DocumentFragment
中时,会删除原来的节点。
创建新节点
createElement()
传入标签名称,创建元素节点createTextNode()
创建文本节点createDocumentFragment()
创建一个空的 DOM 片段添加、移除、替换、插入
appendChild()
末尾添加节点removeChild()
移除节点,传入要被移除的节点,返回被移除的节点insertBefore()
插入节点,传入新插入的节点和在哪个节点之前插入replaceChild()
替换节点,传入要被替换的节点查找
getElementById()
getElementsByClssName()
getElementsByTagName()
getElementsByName()
querySelector()
querySelectorAll()