闭包
什么是闭包?
函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包。
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制(可以实现变量的私有化);这个变量是一只保存在内存中,不会被垃圾回收机制回收.
缺点:闭包较多的时候,会消耗内存,导致页面性能下降。
使用场景: 防抖、截流,函数嵌套函数避免全局污染的时候。
<script>
function outer() {
let a = 1;
function inner() {
a++
console.log(a);
}
return inner;
}
let fn = outer()
fn() // 2
</script>
内存溢出
你要求分配的内存超出了系统能给你分配的,系统不能满足,产生溢出。
内存泄露
你向系统申请内存进行使用,使用完以后不归还,结果你申请的内存自己也不能使用了,系统也不会将你申请的内存分配给其它的需要的内存。
事件冒泡
<!DOCTYPE html>
<html>
<head>
<title>event</title>
</head>
<style>
#obj1 {
width: 100px;
height: 500px;
background-color: red;
}
#obj2 {
width: 100px;
height: 100px;
background-color: blue;
}
#obj3 {
width: 100px;
height: 100px;
background-color: green;
}
</style>
<body>
<div id="obj1">
welcome
<h5 id="obj2">hello</h5>
<h5 id="obj3">world</h5>
</div>
<script type="text/javascript">
var obj1 = document.getElementById('obj1');
var obj2 = document.getElementById('obj2');
obj1.addEventListener('click', function () {
alert('hello1');
}, false); // 冒泡
obj2.addEventListener('click', function (event) {
event.stopPropagation() // 阻止事件冒泡到obj1
alert('world2');
}, false) // 冒泡
</script>
</body>
</html>
事件委托
又叫事件代理,原理就是利用事件冒泡的机制来实现,也就说把子元素的事件绑定到父元素的身上。
如果字元素阻止了事件冒泡,那么委托也就不成立
阻止事件冒泡: event.stopPropagation
addEventListener(‘click’,函数名,true/false)
默认false—事件冒泡
true—事件捕获
优点:提高性能,减少事件的绑定
<body>
<ul id="my-ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
let ulD = document.getElementById('my-ul')
ulD.addEventListener('click', (e) => {
console.log('e: ', e.target.innerText);
}, true)
</script>
堆栈的理解
堆是动态分配内存,内存大小不一,也不会自动释放(堆是由自动的垃圾回收来负责的)
数据类型:引用类型 数组、function
栈是自动分配相对固定大小的内存空间,并由系统自动释放。
数据类型:基本数据类型 优势:读取速度快
事件循环
原型和原型链
原型 就是一个普通的对象,它是为构造函数的实例共享属性和方法。所有实例中引用的原型都是同一个对象。
<script>
function Person() {
this.say = function () {
console.log('say')
}
}
let p1 = new Person()
let p2 = new Person()
p1.say()
p2.say()
</script>
使用prototype可以把方法挂在原型上,内存值保存一份
Person.prototype.look = function () {
console.log('西游记');
}
let p1 = new Person()
let p2 = new Person()
p1.look() // 西游记
p2.look() // 西游记
</script>
__proto__
可以理解为指针,实例对象中的属性。指向了构造函数的原型(prototype)
console.log(p1.__proto__ === Person.prototype); // true
原型链
一个实例对象在调用属性和方法的时候,会依次从实例本身__proto__、构造函数原型prototype、原型的原型上去查找
new操作符的理解
- 先创建一个空对象
- 把空对象和和构造函数通过原型链链接
- 把构造函数的this绑定到新的空对象身上
- 根据构造函数返回类型判断,如果是值类型,则返回对象,如果是引用类型,则返回这个引用类型
<script>
function newFun(Fun, ...args) {
// 1. 先创建一个空对象
let newObj = {}
// 2. 把空对象和和构造函数通过原型链链接
newObj.__proto__ = Fun.prototype
// 3. 把构造函数的this绑定到新的空对象身上
const result = Fun.apply(newObj, args)
// 4. 根据构造函数返回类型判断,如果是值类型,则返回对象,如果是引用类型,则返回这个引用类型
return typeof result instanceof Object ? result : newObj
}
function Person(name) {
this.name = name
}
Person.prototype.say = function () {
console.log('say');
}
const p1 = newFun(Person, '李四')
p1.say()
console.log('对象', p1);
// say
// Person {name: '李四'}
</script>
js如何实现继承
- 原型链接 ----无法穿参数
- 借用构造函数 — 方法不能共享
- 组合式继承
- es6的class继承
js的设计原理
js引擎
运行上下文
调用栈
事件循环
回调
js中this指向问题
- 全局对象中this —window
- 全局作用域或者普通函数 —window
- this永远指向最后调用它的那个对象 (在不是箭头函数的情况下)
- new关键字改变了this的指向
- apply call bind可以改变this执行
- 箭头函数中的this,它的指向在定义时候已经确定了,箭头函数没有自己的this,要看外层是否有函数,有就是外层函数的this,没有就是window
- 匿名函数中this====永远指向了window,匿名函数的执行环境具有全局性,因此this指向window
setTimeout最小执行时间
setTimeout最小执行时间是4ms—html5规定的内容
setInterval最小执行时间
setInterval最小执行时间是10ms—html5规定的内容
用递归的时候有没有遇到
递归就是一个函数可以调用函数本身
----函数内部调用自己
注意:写递归必须要有return
深拷贝
就是完全拷贝一份新的对象,会在堆中开辟新的空间,拷贝的对象被修改后,原对象不受影响
- 扩展运算符 —只能一层
- JSON.parse(JSON.stringify()) — 函数不会复制
- 递归
let origin = {
name: '张三',
age: 18,
sex: '男',
arr: [1, 2, 3, 4],
// obj2: {
// name: 123123
// },
say() {
console.log(1111);
}
}
function extend(origin, deep) {
let obj = {}
if (origin instanceof Array) {
obj = []
}
for (let key in origin) {
let value = origin[key]
obj[key] = (!!deep && typeof value === 'object' && value !== null) ? extend(value, deep) : value
}
return obj
}
const copy = extend(origin, true)
copy.arr.unshift(999)
console.log('origin: ', origin);
console.log('copy: ', copy);
// {name: '张三', age: 18, sex: '男', arr: Array(5), say: ƒ}age: 18arr: (5) [999, 1, 2, 3, 4]name: "张三"say: ƒ say()sex: "男"[[Prototype]]: Object
作用域作用域链
作用域分为
-
全局作用域
-
局部作用域
-
函数作用域
-
块级作用域
作用域链
当在js中使用一个变量的时候,首先在js引擎会尝试在当前作用域下去寻找改变量,如果没有找到,再到它的上层作用域中去寻找,一次类型知道找到该变量或者已经到了全局作用域。