Получите закрытие и это сразу

внешний интерфейс JavaScript

Закрытие и это — два довольно часто встречающихся тестовых сайта, но задумывались ли вы когда-нибудь, что они на самом деле связаны с одним и тем же пунктом знаний?

Пожалуйста, наш главный герой этой статьи, контекст исполнения

контекст выполнения

каков контекст выполнения

Нетрудно понять, что контекст выполнения — это среда, в которой выполняется js-код, его состав следующий


executionContextObj = {
	this: 对的就是你关注的那个this,
	VO:变量对象,
    scopeChain: 作用域链,跟闭包相关
}

Поскольку JS является однопоточным, одновременно может происходить только одно действие, остальные будут поставлены в очередь в указанном стеке контекста. Когда интерпретатор js инициализирует исполняемый код, он создает в стеке глобальный контекст выполнения, а затем при каждом вызове функции создает и помещает в стек новый контекст выполнения. После выполнения функции контекст выполнения извлекается.

Пять ключевых моментов:

  1. один поток
  2. Синхронное выполнение
  3. глобальный контекст
  4. Неограниченный контекст функции
  5. Создает новый контекст для каждого вызова функции, включая сам вызов
Скорость установления контекста выполнения

создать сцену

  1. Инициализировать цепочку областей видимости
  2. Создать переменный объект
    1. создавать аргументы
    2. Декларация функции сканирования
    3. Сканировать объявления переменных
  3. спроси это

этап выполнения

  1. Инициализировать ссылки на переменные и функции
  2. выполнить код

this

Когда функция выполняется, это всегда относится к объекту, вызвавшему функцию. Чтобы определить смысл этого, на самом деле нужно определить, кому принадлежит эта функция.

указать на вызывающий объект
function foo() {
	console.log( this.a );
}
var obj = {
	a: 2,
	foo: foo
};
obj.foo(); // 2
указывает на глобальный объект
function foo() {
	console.log( this.a );
}
var a = 2;
foo(); // 2

Уведомление


//接上
var bar = foo
a = 3
bar() // 3不是2

На этом примере вы можете лучше понять, что это определяется только при вызове функции

пройтись еще немного


function foo() {
	console.log( this.a );
}
function doFoo(fn) {
	this.a = 4
	fn();
}
var obj = {
	a: 2,
	foo: foo
};
var a =3
doFoo( obj.foo ); // 4

а также


function foo() {
	this.a = 1
	console.log( this.a );
}
function doFoo(fn) {
	this.a = 4
	fn();
}
var obj = {
	a: 2,
	foo: foo
};
var a =3
doFoo( obj.foo ); // 1

Почему это? Это потому, что набор a в foo читается первым, подобно принципу области действия?

Напечатав this для foo и doFoo, вы можете узнать, что их this указывает на окно, и их операции изменят значение a в окне. Дело не в том, что набор a в foo читается первым

Итак, если вы измените код на


function foo() {
	setTimeout(() => this.a = 1,0)
	console.log( this.a );
}
function doFoo(fn) {
	this.a = 4
	fn();
}
var obj = {
	a: 2,
	foo: foo
};
var a =3
doFoo( obj.foo ); // 4
setTimeout(obj.foo,0) // 1

Приведенные выше результаты кода могут подтвердить наше предположение.

Построение с новыми точками к новому объекту

a = 4
function A() {
	this.a = 3
	this.callA = function() {
		console.log(this.a)
	}
}
A() // 返回undefined, A().callA会报错。callA被保存在window上
var a = new A()
a.callA() // 3,callA在new A返回的对象里
apply/call/bind
function foo() {
	console.log( this.a );
}
var obj = {
	a: 2
};
foo.call( obj ); // 2
//bind返回一个新的函数
function foo(something) {
	console.log( this.a, something );
	return this.a + something;
}
var obj =
	a: 2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
стрелочная функция

Стрелочные функции особенные, у них нет своего this, они используют this значение включающего контекста выполнения (функции или глобального).

var x=11;
var obj={
 x:22,
 say:()=>{
   console.log(this.x); //this指向window
 }
}
obj.say();// 11
obj.say.call({x:13}) // 11
x = 14
obj.say() // 14
//对比一下
var obj2={
 x:22,
 say() {
   console.log(this.x); //this指向window
 }
}
obj2.say();// 22
obj2.say.call({x:13}) // 13
функция прослушивания событий

указывает на связанный элемент dom

document.body.addEventListener('click',function(){
	console.log(this)
}
)
// 点击网页
// <body>...</body>
HTML

В атрибутах тегов HTML можно написать JS, в данном случае это относится к элементу HTML.

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>

переменный объект

Объект переменной — это область данных, связанная с контекстом выполнения, в котором хранятся объявления переменных и функций, определенные в контексте.

Переменный объект — это абстрактное понятие, которое представляет разные объекты в разных контекстах.

Переменный объект глобального контекста выполнения

В глобальном контексте выполнения переменный объект является глобальным объектом.
В коде js верхнего уровня this указывает на глобальный объект, а глобальные переменные запрашиваются как свойства объекта. В браузерах окно является глобальным объектом.

var a = 1
console.log(window.a) // 1
console.log(this.1) // 1
Переменный объект контекста выполнения функции

В контексте функции переменный объект VO является активным объектом AO.

При инициализации со свойством arguments.
Код функции выполняется в два этапа

  1. При входе в контекст выполнения
    На данный момент переменный объект включает

    1. формальный параметр
    2. Объявление функции, которая заменит существующий объект переменной
    3. Объявления переменных, без подстановки параметров и функций
  2. выполнение функции

Измените значение объекта переменной в соответствии с кодом

Например


function test (a,c) {
  console.log(a, b, c, d) // 5 undefined [Function: c] undefined
  var b = 3;
  a = 4
  function c () {
  }
  var d = function () {
  }
  console.log(a, b, c, d) // 4 3 [Function: c] [Function: d]
  var c = 5
  console.log(a, b, c, d) // 4 3 5 [Function: d]
}
test(5,6)

анализировать процесс

1. При создании контекста выполнения

VO = {
arguments: {0:5},
a: 5,
b: undefined,
c: [Function], //Функция C переопределяет параметр c, но объявление переменной c не может переопределять объявление функции c
d: undefined, // выражение функции не поднимается, оно не определено до выполнения соответствующего оператора
}

  1. при выполнении кода

Через финальную консоль можно узнать, что объявление функции может быть перезаписано

цепочка прицелов

Сначала узнайте о роли

объем

Доступная область переменных и функций управляет видимостью и временем жизни переменных и функций. Делится на глобальную область действия и локальную область действия.

Глобальный охват:

Объекты, к которым можно получить доступ в любом месте кода, имеют глобальную область действия и включают следующее:

  1. переменные, определенные на самом внешнем уровне;

  2. Свойства глобального объекта

  3. Переменные, которые неявно определены где-либо (переменные, которые не присваиваются напрямую), и переменные, которые неявно определены где-либо, определяются в глобальной области видимости, то есть переменные, которые присваиваются напрямую без объявления var.

Локальная область:

Область действия JavaScript определяется функциями. Переменные, определенные в функции, видны только внутри функции, которая называется функциональной (локальной) областью видимости.

цепочка прицелов

Цепочка областей действия — это список объектов, используемых для получения идентификаторов, которые появляются в коде контекста.
Под идентификаторами можно понимать имена переменных, параметры, объявления функций.

Когда функция определена, набор объектов переменного объекта родителей AO / VO хранится во внутреннем свойстве [[Scope]], который называется цепью области.
Свободные переменные — это переменные, которые не объявлены внутри функции.
Когда функции требуется доступ к свободной переменной, она следует по цепочке областей видимости, чтобы найти данные.


 
function foo() {
    function bar() {
        ...
    }
}
foo.[[scope]] = [
  globalContext.VO
];
bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO
];

Когда функция активирована, она сначала скопирует атрибут [[scope]] для создания цепочки областей видимости, затем создаст переменный объект VO, а затем добавит его в цепочку областей видимости.


executionContextObj: {
	VO:{},
	scopeChain: [VO, [[scope]]]
}

Закрытие

Что такое закрытие

Замыкание определяется mdn как функция, которая может обращаться к свободным переменным. Свободные переменные, как упоминалось ранее, относятся к переменным, которые не объявлены внутри функции.

форма закрытия
function a() {
	var num = 1
	function b() {
		console.log(num++)
	}
	return b
}
var c1 = a()
c1() // '1'
c1() // '2'
var c2 = a()
c2() // '1'
c2() // '2'
закрытый процесс

Не очень строго написано. Некоторые процессы могут быть опущены

  1. запустить функцию а
    1. Создайте VO функции a, включая переменную num и функцию b
    2. При определении функции b переменный объект VO и глобальный переменный объект a будут сохранены в [[scope]]
    3. Функция возврата b, сохранение в c1
  2. запустить с1
    1. Создайте цепочку областей действия c1, которая содержит переменный объект VO объекта a.
    2. Создать озвучку для с1
    3. Запустив c1, обнаруживается, что требуется доступ к переменной num, которая не существует в текущем VO, поэтому доступ к ней осуществляется через цепочку областей видимости, и найдено num, сохраненное в VO a, и значение num устанавливается на 2.
  3. Снова запустите c1, повторите операцию второго шага и установите значение num равным 3.
некоторые проблемы

Из приведенных выше результатов выполнения мы можем заметить, что переменная num, к которой обращается c2, не является той же самой переменной, что и переменная num, к которой обращается c1. Мы можем изменить код, чтобы подтвердить нашу гипотезу

function a() {
	var x = {y : 4}
	function b() {
		return x
	}
	return b
}
var c1 = a()
var c2 = a()
c1 === c2()  // false

Поэтому мы можем быть уверены, что переменные, к которым обращается замыкание, воссоздаются каждый раз при запуске родительской функции независимо друг от друга.
Обратите внимание, что свободные переменные, созданные в одной и той же функции, могут быть общими для разных замыканий.

function a() {
	var x = 0
	function b() {
		console.log(x++)
	}
	function c() {
		console.log(x++)
	}
	
	return {
		b,
		c
	}
}
var r =  a()
r.b() // 0
r.c() // 1

наконец

Статья относительно длинная и охватывает широкий спектр.Ошибок может быть много.Надеюсь вы меня поправите.

Эта статьяПередовая продвинутая сериячасть,
Добро пожаловать, чтобы следовать иstarэтот блог или следуйте за мнойgithub

Ссылаться на

  1. Глубокое понимание этого в стрелочных функциях ES6
  2. Сводка JS, о которой вы не знали
  3. Углубленный стек контекста выполнения JavaScript
  4. Понимание цепочки областей видимости JavaScript
  5. Углубленный объект переменной JavaScript
  6. Глубокое понимание серии JavaScript (12): переменные объекты (переменная объект)
  7. Понимание контекста выполнения JavaScript