测试你对作用域的掌握程度
执行上下文与作用域
执行上下文(Execution Context)
执行上下文包括三个重要的组成部分:
- 变量对象(Variable Object):用于存储变量、函数声明和函数参数。在全局上下文中,它被称为全局对象(Global Object),在函数上下文中,它被称为活动对象(Activation Object)。
- 作用域链(Scope Chain):用于查找变量的链条,它由当前执行上下文的变量对象和所有包含(父级)执行上下文的变量对象组成。
- this 值:指向函数执行的当前对象,在全局上下文中通常指向全局对象,在函数上下文中取决于函数的调用方式。
this是什么
- 当函数作为普通函数调用时,
this
指向全局对象(在浏览器中通常是window
对象)。 - 当函数作为对象的方法调用时,
this
指向调用该方法的对象。 - 当函数作为构造函数调用时(使用
new
关键字),this
指向新创建的实例对象。 - 当函数作为事件处理函数绑定到 DOM 元素上时,
this
指向触发事件的 DOM 元素。
全局作用域
// 全局作用域
console.log(this); // 指向全局对象(window)
对象方法中的this
const obj = {
name: "Alice",
greet() {
console.log(this.name);
},
};
obj.greet();
构造函数方法的this
function Person(name) {
this.name = name;
}
const person1 = new Person("Bob");
console.log(person1.name);
上下文栈
闭包怎么产生的
function outerFunction() {
let outerVar = 'I am from outer function';
function innerFunction() {
console.log(outerVar);
}
return innerFunction;
}
const innerFunc = outerFunction();
innerFunc(); // 输出:I am from outer function
-
上面的代码输出:
I am from outer function
。因为innerFunction
是在outerFunction
内部定义的,可以访问outerFunction
的变量outerVar
。 -
闭包的优点包括可以实现数据封装、延长变量的生命周期、实现模块化等;缺点包括可能导致内存泄漏(如果不注意释放闭包)、影响性能(因为变量未被及时释放)等。
作用域(Scope)
- 全局作用域(Global Scope):在整个代码中都可访问的作用域,任何在全局作用域中声明的变量或函数都可以被任何地方的代码访问。
- 局部作用域(Local Scope):在特定代码块(通常是函数)中可访问的作用域,局部作用域可以嵌套,内部作用域可以访问外部作用域的变量,但外部作用域不能访问内部作用域的变量。
- 块级作用域(Block Scope):块级作用域指的是由一对花括号
{}
包裹起来的代码块内部所创建的作用域。在 JavaScript 中,使用let
和const
关键字声明的变量具有块级作用域,即只在声明它们的代码块内部可见。块级作用域可以帮助我们避免变量污染和提供更好的封装性。
全局作用域
局部/函数作用域
作用域链
作用域链的作用
作用域查找示例
function outer() {
var outerVar = "Outer variable";
function inner() {
var innerVar = "Inner variable";
console.log(innerVar); // 内部作用域的变量
console.log(outerVar); // 外部作用域的变量
console.log(globalVar); // 全局作用域的变量
}
inner();
}
var globalVar = "Global variable";
outer();
不要忽略作用域内部的变量提升
函数声明、函数声明提升
通过function声明的函数会被提升
console.log(sum(10, 20));
function sum(num1, num2) {
return num1 + num2;
}
函数表达式不能提升
console.log(sum(10, 20));
var sum = function (num1, num2) {
return num1 + num2;
};
变量声明 、变量提升
未声明的变量
能力自测:下面代码输出结果
function add(num1, num2) {
Asum = num1 + num2;
return Asum;
}
let res = add(10, 20);
console.log(Asum);
var声明变量
函数声明和var声明重复了如何处理?
能力自测1:下面代码的输出结果
function foo() {
console.log(a);
var a = 5;
console.log(a);
}
foo();
能力自测2:下面代码的输出结果
var myVar = "global";
function func() {
console.log(myVar); // 输出什么?
var myVar = "local";
console.log(myVar); // 输出什么?
}
func();
能力自测3:下面代码的输出结果
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
console.log(a);
自测题答案
能力自测1
function foo() {
console.log(a);
var a = 5;
console.log(a);
}
foo();
能力自测2
var myVar = "global";
function func() {
console.log(myVar); // 输出什么?
var myVar = "local";
console.log(myVar); // 输出什么?
}
func();
能力自测3
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
console.log(a);
let声明变量、形成块级作用域
块级作用域怎么产生的?
let声明的变量有以下特性:
能力自测1:下面代码的输出结果
function fun() {
if (true) {
var a;
}
console.log(a);
}
function fun2() {
if (true) {
let a;
}
console.log(a);
}
fun();
fun2();
能力自测2:下面代码的输出结果
function fun() {
if (true) {
console.log(a);
var a = 1;
}
}
function fun2() {
if (true) {
console.log(a);
let a = 1;
}
}
fun();
fun2();
const声明常量、形成块级作用域
通过debugger看下const形成的Block块级作用域
function fun() {
if (true) {
const b = {};
b.a = 1; //可以更改对象里的属性
// b = { 2: 3 };//不能切换b的引用,会报错
console.log(b);
}
}
fun();
挑战作用域的笔试题
考察var的理解
console.log(a);
var a = 100;
fn("zhangsan");
function fn(name) {
age = 20;
console.log(name, age);
var age;
}
console.log(b);
b = 100;
考察var、作用域的理解
let res = new Array();
for (var i = 0; i < 10; i++) {
res.push(function () {
return console.log(i);
});
}
res[0]();
res[1]();
res[2]();
考察var作用域理解2
var name = "World!";
(function () {
if (typeof name === "undefined") {
var name = "Jack";
console.log("Goodbye " + name);
} else {
console.log("Hello " + name);
}
})();
考察作用域
var a = 10
function foo(){
console.log(a)
}
function bar() {
var a = 20
foo()
}
bar()
考察let块级作用域
var a = 1;
function fun() {
if (false) {
var a = 10;
let b = 20;
}
console.log(a);
console.log(b);
}
fun();
let暂时性死区
function foo() {
var a = 1;
if (true) {
console.log(a);
let a = 2;
}
}
foo();
考察this1
var a = 2;
const foo = {
bar: function () {
console.log(this.a);
},
bar1: function () {
return function () {
console.log(this.a);
};
},
a: 1,
};
foo.bar();
var bar1 = foo.bar1();
bar1();
考察this2
var name = "Global";
var person = {
name: "Alice",
sayHello: function () {
console.log("Hello, " + this.name);
function innerFunction() {
console.log("Hi, " + this.name);
}
innerFunction();
},
};
person.sayHello();
考察this3
var counter = {
count: 0,
increase: function () {
this.count++;
console.log(this.count);
},
};
var increaseFunc = counter.increase;
increaseFunc();
counter.increase();
考察异步方法中的this
function outerFunction() {
var name = "Global";
function innerSayHello() {
console.log("Hi," + this.name);
let _this = this;
setTimeout(function () {
console.log("Hello, " + this.name);
console.log("Hello," + _this.name);
}, 1000);
}
innerSayHello.call({ name: "Inner" });
}
outerFunction();
考察闭包、函数作用域
function createCounter() {
var count = 0;
return function () {
count++;
console.log(count);
};
}
var counter1 = createCounter();
counter1();
counter1();
var counter2 = createCounter();
counter2();
counter2();
立即执行函数IIFE
var b = 10;
(function b() {
b = 20;
console.log(b);
})();
考察var+this+作用域链
var b = "boy";
console.log(b);
function fighting() {
console.log(a);
console.log(c);
if (a === "apple") {
a = "Alice";
} else {
a = "Ada";
}
console.log(a);
var a = "Andy";
middle();
function middle() {
console.log(c++);
var c = 100;
console.log(++c);
small();
function small() {
console.log(a);
}
}
var c = (a = 88);
function bottom() {
console.log(this.b);
b = "baby";
console.log(b);
}
bottom();
}
fighting();
console.log(b);