闭包
什么是闭包?
函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包。
特点:可以重复利用变量,并且这个变量不会污染全局的一种机制(可以实现变量的私有化);这个变量是一只保存在内存中,不会被垃圾回收机制回收.
缺点:闭包较多的时候,会消耗内存,导致页面性能下降。
使用场景: 防抖、截流,函数嵌套函数避免全局污染的时候。
<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() // 阻止事件冒泡到obj1alert('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引擎会尝试在当前作用域下去寻找改变量,如果没有找到,再到它的上层作用域中去寻找,一次类型知道找到该变量或者已经到了全局作用域。