作用域
含义:定义变量和访问变量的范围
- 全局作用域:全局作用域是在代码的顶层声明的变量和函数可被访问的范围。在任何地方都可以访问全局作用域中声明的变量和函数,包括其他作用域内部。全局作用域中声明的变量称为全局变量。
- 函数作用域:函数作用域是指在函数内部声明的变量的可访问范围。这意味着在函数外部无法直接访问在函数内部声明的变量。函数作用域允许在函数内部创建私有变量,这些变量只能在函数内部访问。
- 块级作用域:块级作用域是在代码块(例如 if 语句、for 循环或 {} 中的代码)内声明的变量的可访问范围。在 ES6(ECMAScript 2015)之前,JavaScript 中没有明确的块级作用域。但是,可以使用 var 关键字在函数内部创建具有函数作用域的变量。ES6 引入了 let 和 const 关键字,它们允许在块级作用域内声明变量,并限制变量的作用范围在当前代码块内。
作用域规定了标识符(变量、函数名等)在程序中的可见性和生命周期。当在代码中引用一个变量时,JavaScript 引擎会根据作用域去找到对应的变量。如果在当前作用域中找不到该变量,则会继续向上级作用域查找,直到找到或者达到全局作用域。这个过程被称为作用域链(Scope Chain)。
作用域链
含义:作用域链指的是变量和函数的可访问性和查找规则。
function outer() {
const outerVar = 'Outer variable';
function inner() {
const innerVar = 'Inner variable';
console.log(innerVar); // Inner variable
console.log(outerVar); // Outer variable
console.log(globalVar); // Global variable
}
inner();
}
const globalVar = 'Global variable';
outer();
this的上下文
this是在执行时动态读取上下文决定的,而不是创建时决定
函数直接调用–this指向window
function foo() {
console.log('函数内部', this) //window
}
foo()
隐式绑定–this指向调用堆栈的上一级=>对象、数组等引用关系逻辑
function fn() {
console.log('隐式绑定', this.a) //this指向obj
}
const obj = {
a: 1,
fn
}
obj.fn = fn;
obj.fn();
面试题
const foo={
bar:10,
fn:function(){
console.log(this.bar)
console.log(this)
}
}
//取出
const fn1 = foo.fn
//单独执行
fn1()
//结果:因为是单独取出,只是将function这个函数赋值给fn1,所以拿到的this上下文指向的是window,而不是foo这个对象
//undefined
//window
const o1 = {
txt:'o1',
fn:function(){
//直接使用上下文--传统派活
console.log('o1fn',this)
return this.txt
}
}
const o2 = {
txt:'o2',
fn:function(){
//呼叫领导执行--部门协作
return o1.fn()
}
}
const o3 = {
txt:'o3',
fn:function(){
// 直接内部构造 —— 公共人
let fn = o1.fn
return fn()
}
}
console.log('o1fn', o1.fn()); //this指向o1
console.log('o2fn', o2.fn()); // this指向o1
console.log('o3fn', o3.fn()); // this指向window
//追问:现在需要将console.log('o2fn', o2.fn())的结果是o2
//1.人为干涉:改变this指向(bind/call/apply)
o2.fn.call(o2)
//2.不人为干涉
const o1 = {
txt:'o1',
fn:function(){
// 直接使用上下文 - 传统派活
console.log('o1fn', this);
return this.txt
}
}
const o2 = {
txt:'o2',
fn: o1.fn
}
console.log('o2',o2.fn()) //this指向o2
//先打印 o1fn {txt:'o2',fn:f}
//后打印 o2 o2
显示绑定
function foo() {
console.log('函数内部', this);
}
foo();
// 使用
foo.call({
a: 1
});
foo.apply({
a: 1
});
const bindFoo = foo.bind({
a: 1
});
bindFoo();
面试题:call、apply、bind区别
- call vs apply 传参不同 依次传入/数组传入
- bind返回不同(即返回函数,需要手动执行)
bind的原理 / 手写bind
- 说明原理,写下注释
- 补全代码
//1.手写bind => bind挂在位置 => Function.prototype
Function.prototype.newBind = function(){
//2.bind是什么? 改变this
const _this = this
// 接受参数args,第一项参数是新的this,第二项到最后一项是函数传参
const args = Array.prototype.slice.call(arguments)
const newThis = args.shift()
//3.返回值
return function(){
return _this.newApply(newThis,args)
}
}
Function.prototype.newApply = function(context){
context = context || window
//挂载执行函数
context.fn = this
const result = argument[1] ? context.fn(...argument[1]) : context.fn()
delete context.fn
return result
}
闭包
含义:一个函数与它周围状态的引用捆绑在一起的组合
形态
- 函数作为返回值
- 函数作为参数
- 函数嵌套
//函数作为返回值
function mail(){
const content = '信'
return function(){
console.log(content)
}
}
const envelop = mail()
envelop()
//函数作为参数
let content
function envelop(fn){
content = 1
fn()
}
function mail(){
console.log(content)
}
envelop(mail)
//函数嵌套
let counter = 0
function outFn(){
function innerFn(){
counter++
console.log(counter)
}
return innerFn
}
outFn()()
//立即执行函数
let count = 0
(function immediate(args){
if(count === 0){
let count = 1
console.log(count)
}
})(args)
//实现私有变量
function createStack(){
return {
items:[],
push(item){
this.items.push(item)
}
}
}
function createStack(){
const items = []
return {
push(item){
items.push(item)
}
}
}