js面试必备知识,关注作者查看系列js知识体系
导语:作为一个程序员,要是作用域和闭包都没弄清楚,那将会是一个项目成员里面的核弹。
本文的目录
1,作用域
我们先来看一段代码,你觉得它们会输出什么?
var a = "123";
function test(){
console.log(a);
var a = "456"; //var a会预解析在函数开头,执行到这行才进行赋值
console.log(a);
}
test();
console.log(a); //"123" 找全局环境下的声明,找到了var a="123"
/*答案
undefined,
456.
123
函数执行后,在函数环境内发现有a,但是它在后面,就先对它初始化undefined。
接着就输出
*/
var a = "123";
function test(){
console.log(a);
a = "456";
}
test();
console.log(a);
/*答案
123
456
函数执行后,在函数环境内,没有找到本层环境关于a的声明,所以开始向上一层环境查找。
然后改变了全局变量a的内容。
*/
function test(){
b();
var a = 1;
function b(){
console.log(1);
console.log(a);
var a=2;
}
}
test();
/*答案
1
undefined
*/
这些都是有迹可循的,先找本层环境有无声明,有的话,看是否进行了赋值;只有声明没有执行赋值,就是undefined。没有声明也没有赋值的话,就再向上一层查找,直到找到为止。如果所有的执行环境都没有找到,那么控制台就会报错变量找不到。
2,闭包
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
主要有两种表现:函数作为参数被传递,函数作为返回值被返回。
function f1(){
let a = 123
function f2(){
console.log(a)
}
return f2;
}
var f = f1();
let a = 456
f(); //123
let a = 100
function printf(fn) {
let a = 200
fn()
console.log(a)
}
function fn() {
console.log(a)
}
printf(fn) // 100 200
要记住一点,变量的查找要在函数定义的地方查找,查找不到才向上查找。
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,0,0,0
var b = fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,0,1,1
3,this
this的值是什么,是在函数执行的时候才知道的,而不是在定义的时候。
function fn() {
console.log(this)
}
fn() //window
fn1.call({ a: 1 }) //{a:1}
fn1.call(fn) //fn
fn1.bind({ a: 1 })() //{a:1}
4,call,bind,apply
它们都是劫持另外一个对象的方法,其中bind是返回一个方法(未执行),而call和apply都是返回一个执行的方法。不同的是apply传递的是一个数组。
/*定义一个人类*/
function Person(name,age)
{
this.name=name;
this.age=age;
}
/*定义一个学生类*/
function Student(name,age,grade)
{
Person.apply(this,arguments); //劫持person的方法,将参数传递过去
console.log(this)
this.grade=grade;
}
//创建一个学生类
var student=new Student("mi",21,"高一");
console.log("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);