前言
题目来自ConardLi的blog
写的是自己的题解,水平有限,所以仅供参考
代码会整合在github,觉得有帮助就给个star吧~
正文
一、Javascript基础
作用域和闭包
三、作用域和闭包
1、理解词法作用域和动态作用域
只有词法作用域,没有真正的动态作用域
2、理解JavaScript的作用域和作用域链
- 作用域是某个变量,函数,对象的可访问性。
- 作用域就是一个地盘。
- 作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6的到来,为我们提供了‘块级作用域’,可通过新增命令let和const来体现。
沿着作用域一层一层往上查找的过程就叫做作用域链
3、理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题
一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
4、this的原理以及几种不同使用场景的取值
this 的取值是函数执行上下文(context)的一部分
严格模式下,禁止this指向全局对象
1、全局&调用普通函数
全局
console.log(this === window) //true
调用普通函数
var x = 10
function foo(){
console.log(this === window) //true
console.log(this.x); //10
}
2、构造函数
如果函数作为构造函数使用,那么其中的 this 就代表它即将 new 出来的对象
function foo(){
this.x = 10
console.log(this) //Foo {x:10}
}
const demo = new foo()
console.log(foo.x); //10
3、对象的方法
如果函数作为对象的方法时,方法中的 this 指向该对象。
const obj = {
x: 10,
foo: function(){
console.log(this) //obj
console.log(this.x) //10
}
}
obj.foo();
有一个比较特殊的情况,就是在对象的方法里面定义方法,就像这样
const obj = {
x: 10,
foo: function(){
console.log(this)
function f(){
console.log(this); //Window
console.log(this.x); //undefined
}
f()
}
}
函数 f 虽然是在 obj.foo 内部定义的,但它仍然属于一个普通函数,this 仍指向 window
4、构造函数 prototype 属性
在 Foo.prototype.getX 函数中,this 指向的 foo 对象。不仅仅如此,即便是在整个原型链中,this 代表的也是当前对象的值。
function Foo(){
this.x = 10;
}
Foo.prototype.getX = function () {
console.log(this); //Foo {x: 10, getX: function}
console.log(this.x); //10
}
const foo = new Foo();
foo.getX();
5、函数用 call、apply或者 bind 调用。
var obj = {
x: 10
}
function foo(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
foo.call(obj);
foo.apply(obj);
foo.bind(obj)();
6、DOM event this
function Listener(){
document.getElementById('foo').addEventListener('click', this.handleClick); //这里的 this 指向 Listener 这个对象。不是强调的是这里的 this
}
Listener.prototype.handleClick = function (event) {
console.log(this); //<div id="foo"></div>
}
const listener = new Listener();
document.getElementById('foo').click();
7、箭头函数,this穿透
5、闭包的实现原理和作用,可以列举几个开发中闭包的实际应用
闭包的概念:指有权访问另一个函数作用域中的变量的函数,一般情况就是在一个函数中包含另一个函数。
闭包的作用:访问函数内部变量、保持函数在环境中一直存在,不会被垃圾回收机制处理。
在实际开发中,闭包主要是用来封装变量,收敛权限。
6、理解堆栈溢出和内存泄漏的原理,如何防止
内存泄露:是指申请的内存执行完后没有及时的清理或者销毁,占用空闲内存,内存泄露过多的话,就会导致后面的程序申请不到内存。因此内存泄露会导致内部内存溢出
堆栈溢出:是指内存空间已经被申请完,没有足够的内存提供了
常见的手段是将一个变量置为null,该变量就会被下一轮垃圾回收机制回收。
常见的内存泄露的原因:
- 全局变量引起的内存泄露
- 闭包
- 没有被清除的计时器
解决方法:
- 减少不必要的全局变量
- 严格使用闭包(因为闭包会导致内存泄露)
- 避免死循环的发生
7、如何处理循环的异步操作
async await
递归
设置标记位
8、理解模块化解决的实际问题,可列举几个模块化方案并理解其中原理
解决的问题:
Common.js
引入模块化编程AMD
1、多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器;
2、js加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长。CMD
模块定义方式和模块加载(可以说运行、解析)时机上有所不同。ES6 import
尽量的静态化、使得编译时就能确定模块的依赖关系,以及输入和输出的变量。(CommonJS和AMD模块,都只能在运行时确定这些东西)。