js面试,闭包等相关问题
其实这一部分我不是太想说的,不过面向基础,而且面试也是喜欢问,我就根据我的理解说说吧!
在这里先说一下变量提升
改一改风格,先问几个问题
a.说一下this几种不同的使用场景
b.创建10个< a >标签,点击的时候弹出对应的序号,请手写代码
c.如何理解作用域
d.闭包你在项目开发中有用过吗?怎么使用的?
一、执行上下文
- 范围:一段< script > 或者一个函数
- 全局:变量定义、函数声明
- 函数:变量定义、函数声明、this、argument(函数参数集合,类数组,值得注意的是es6箭头函数语法是没有这个东西的)
了解:函数声明和函数表达式的区别,其实没啥大的区别就是函数声明存在变量提升,而表达式不会
二、this
this这个东西,要在执行时才能确认,定义时无法确认,理解下面代码:
window.name = "window"
let a = {
name:'A',
fn:function(){
console.log(this.name)
}
}
a.fn() //这里this指向a
a.fn.call({name:'B'}) //这里this指向{name:"B"}
let fn1 = a.fn
fn1() //这里this又是指向了window
上面代码 a.fn()打印结果是A,a.fn.call({name:‘B’})打印结果是B,最后一个打印结果是window
想必最后一个结果挺出乎意料的吧,为什么会这样呢?其实this的作用机制就是谁调用他this就指向谁
a.fn这个没什么好说的,很直白,就是a对象调用了fn,因此this指向了a
a.fn.call({name:‘B’}),这个call的作用就是改变了执行上下文,什么是执行上下文,前面已经列出来了,因此call将本来在a对象执行上下文,改换成了{name:‘B’}的执行上下文,因此就相当于{name:‘B’}.fn(),this指向调用他的{name:‘B’}
而let fn1 = a.fn 相当于
fn1 = function (){
console.log(this.name)
}
在浏览器中,所有的全局变量和函数其实可以认为是window上的属性和方法也就相当于
window.fn1 = function(){
console.log(this.name)
}
因此是window调用了这个方法,this指向了window
扩展:this的使用场景
1.作为构造函数执行
2.作为对象属性执行
3.作为普通函数执行
4.call apply bind
三、作用域
a.js没有块级作用域,(es6有了let,我看了一本书好像把这个称为模拟块级作用域,是块级声明,这个感觉不用太纠结,哈哈哈)
b.js只有全局和函数作用域
四、闭包
//概念不多说
function F1(){
var a = 100
return function(){
console.log(a)
}
}
let f1 = F1()
var a = 200
let f1 = F1()
f1()
这条代码,let f1 = F1()其实就相当于
let f1 = function(){
console.log(a)
}
按通常情况,这个a会打印出全局a=200;但实际上由于它作为一个函数结果返回的,首先访问的是父级函数层的a,因此是100
闭包的使用场景:
a.函数作为返回值
b.函数作为参数传递
扩展:创建10个< a >标签,点击的时候弹出对应的序号,请手写代码
//这个就是上述的问题,es5中就需要用到闭包的特性
var i = null;
for(i = 0;i<10;i++){
(function(i){
var a = document.createElement('a');
a.innerHTML = i + '<br>';
a.addEventListener('click',function(e){
e.preventDefault();
alert(i)
})
document.body.appendChild(a)
})(i)
}
es6实现就方便多了,所以在项目中尽量使用let,const等es6语法
for(let i = 0;i<10;i++){
let a = document.createElement('a');
a.innerHTML = `${i}<br>`;
a.addEventListener('click',(e)=>{
e.preventDefault();
alert(i)
})
document.body.appendChild(a)
}