предисловие
Это еще одна распространенная тема в JavaScript, а также классический экзаменационный вопрос, которого нельзя избежать на собеседовании.«Фронтальный пакет»На эту тему будет рассказано в третьей части цикла. Надпись очень белая, если что-то не так, пожалуйста, поправьте меня~
Прежде чем говорить об этом, давайте поговорим о环境это понятие. Когда язык работает, ему нужна среда, называемая宿主环境. Для JavaScript наиболее распространенной хост-средой является веб-браузер, а другой наиболее распространенной — Node.Также как хост-среда, node также имеет свой собственный движок JavaScript:V8(В настоящее время самый быстрый движок JavaScript, созданный Google). Эта статья в узле не будет расширяться. Обновите позже~
Первоначальный замысел этого
Первоначальный замысел этого дизайнаИспользуется внутри функции для ссылки на текущую среду выполнения.. Почему ты это сказал?
Поведение присваивания объектов в JavaScript заключается в назначении адреса переменной Когда движок считывает переменную, он на самом деле запрашивает адрес, а затем считывает объект с исходного адреса. А JavaScript позволяет телу функции ссылаться на другие переменные текущей среды, и эта переменная предоставляется средой выполнения. Поскольку функции могут выполняться в разных средах выполнения (например, в глобальной области видимости, в объекте...), необходим механизм, указывающий, где выполняется код! Так оно и появилось, и цель его разработки — ссылаться на текущую рабочую среду функции внутри тела функции.
global this
В браузере глобально:
- это эквивалентно объекту окна;
- Объявление переменной с помощью var эквивалентно добавлению свойств к this или окну;
- Если вы объявляете переменную без использования var, let или const (es6), вы добавляете или изменяете свойства глобального this.
// 1
console.log(this === window); //true
//2
var name = "Jake";
console.log(this.name ); // "Jake"
console.log(window.name ); // "Jake"
//3
age = 23;
function testThis() {
age = 18;
}
console.log(this.age ); // 23
testThis();
console.log(this.age ); // 18
Подводя итог: это старший брат в глобальной области видимости, он эквивалентен объекту окна (то есть указывает на окно), если вы объявите какие-то глобальные переменные (неважно где), эти переменные будут использоваться как свойства этого.
function this
Для задачи этой проблемы в функции есть предложение, которое очень полезно:运行时это всегда указывает на объект, который вызвал его последним.
взять каштан
var name = "windowsName";
function sayName() {
var name = "Jake";
console.log(this.name); // windowsName
console.log(this); // Window
}
sayName();
console.log(this) // Window
Давайте посмотрим на последний вызов sayNamesayName(); объект, не вызываемый ранее, является глобальным окном объекта, что эквивалентноwindow.sayName().
Следует отметить, что для строгого режима привязка глобального объекта по умолчанию недопустима, и для него установлено значение undefined. сообщит об ошибке
Uncaught TypeError: Cannot read property 'name' of undefined。
Посмотрите на каштан ниже
function foo() {
console.log( this.age );
}
var obj1 = {
age : 23,
foo: foo
};
var obj2 = {
age : 18,
obj1: obj1
};
obj2.obj1.foo(); // 23
Или предложение в начале, последний звонокfoo()даobj1, так что это указывает наobj1, выход 23.
это в конструкторе
Так называемый конструктор должен генерировать новый объект (object) через эту функцию. Когда функция используется в качестве конструктора (через ключевое слово new), ее значение this привязывается к вновь созданному объекту. Если ключевое слово new не используется, то это обычная функция, которая будет указывать на объект окна.
Вот еще одна классическая тема:новый процесс
var a = new Foo("zhang","jake");
new Foo{
var obj = {};
obj.__proto__ = Foo.prototype;
var result = Foo.call(obj,"zhang","jake");
return typeof result === 'obj'? result : obj;
}
Если выполняется new Foo(), процесс выглядит следующим образом: 1) Создать новый объект obj;
2) Присвоить значения внутренним свойствам нового объекта и построить цепочку прототипов (указать неявный прототип нового объекта на явный прототип его конструктора);
3) Выполнить функцию Foo, и внутренне this указывает на вновь созданный объект obj во время выполнения (вызов здесь используется для изменения этой точки);
4) Если Foo явно возвращает данные типа объекта, данные возвращаются и выполнение завершается, в противном случае возвращается только что созданный объект obj.
var name = "Jake";jiuzhixiang
function testThis(){
this.name = 'jakezhang';
this.sayName = function () {
return this.name;
}
}
console.log(this.name ); // Jake
new testThis();
console.log(this.name ); // Jake
var result = new testThis();
console.log(result.name ); // jakezhang
console.log(result.sayName()); // jakezhang
testThis();
console.log(this.name ); // jakezhang
Очевидно, кто новичок, на это указывает.
это в классе
Этот раздел взят из статьи Али Года Ниляна ---->[ПОСЛЕДНИЙ РАЗ] это: позвонить, подать заявку, связать
В es6 классы являются очень важной частью приложения JavaScript. Классы обычно содержат конструктор, и он может указывать на любой вновь созданный объект. Однако при использовании в качестве метода это также может указывать на любое другое значение, если метод вызывается как обычная функция. Как и методы, классы могут терять своих получателей.
class Hero {
constructor(heroName) {
this.heroName = heroName;
}
dialogue() {
console.log(`I am ${this.heroName}`)
}
}
const batman = new Hero("Batman");
batman.dialogue();
Это в конструкторе относится к вновь созданному экземпляру класса. Когда мы вызываем batman.dialogue(), dialog() вызывается как метод, а batman является его получателем. Но если мы сохраним ссылку на метод dialog() и вызовем его позже как функцию, мы потеряем получателя метода, а параметр this теперь указывает на undefined .
const say = batman.dialogue;
say();
Причина ошибки в том, что классы JavaScript неявно выполняются в строгом режиме. Мы вызываем функцию say() без какой-либо автоматической привязки. Чтобы исправить это, нам нужно вручную связать функцию dialog() с batman, используя bind().
const say = batman.dialogue.bind(batman);
say();
это в вызове, применить и связать
call, apply и bind называются сильными привязками this, которые используются для изменения указания this при выполнении функции.В настоящее время все их приложения основаны на этом.
var name = 'zjk';
function fun() {
console.log (this.name);
}
var obj= {
name: 'jake'
};
fun(); // zjk
fun.call(obj); //Jake
надfun.ccall(obj)Эквивалентноfun.capply(obj)а такжеfun.cbind(obj)()
это в стрелочных функциях
В es5 это зависит от того, где вызывается функция (то есть зависит от времени выполнения), и определяется тем, кто является объектом, вызвавшим функцию последним. но эс6В стрелочной функции нет этой привязки, ее значение должно быть определено путем поиска по цепочке областей видимости., если стрелочная функция содержится в нестрелочной функции, она привязывается к this ближайшей нестрелочной функции, в противном случае this не определено. this в стрелочной функции всегда ссылается на this, когда функция определена, а не когда она выполняется.
let name = "zjk";
let o = {
name : "Jake",
sayName: function () {
console.log(this.name)
},
func: function () {
setTimeout( () => {
this.sayName()
},100);
}
};
o.func() // Jake
Используйте такие методы, как call, apply или bind, чтобы передать значение this, которое игнорируется стрелочными функциями. Стрелочные функции относятся к этому значению, установленному стрелочной функцией при ее создании.
let obj = {
name: "Jake",
func: (a,b) => {
console.log(this.name,a,b);
}
};
func.call(obj,1,2);// 1 2
func.apply(obj,[1,2]);// 1 2
最后放一道常见的this面试题
var number = 1;
var obj = {
number:2,
showNumber:function(){
this.number = 3;
(function(){
console.log(this.number);
})();
console.log(this.number);
}
};
obj.showNumber();// 答案就欢迎留在评论区囖~
call & apply
Каждая функция содержит два ненаследуемых метода: apply() и call(). Целью этих двух методов является вызов функции в определенной области видимости, что фактически эквивалентно установке значения объекта this в теле функции.
apply()
Метод apply() принимает два аргумента: область, в которой выполняется функция, и массив аргументов. Среди них вторым параметром может быть экземпляр Array или объект arguments.
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 传入 arguments 对象
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 传入数组
}
console.log(callSum1(10,10)); //20
console.log(callSum2(10,10)); //20
В строгом режиме, если функция вызывается без указания объекта среды, это значение не будет преобразовано в окно. Если функция явно не добавлена к объекту или не вызывается apply() или call(), это значение будет неопределенным.
call()
Метод call() работает так же, как и метод apply(), единственная разница между ними заключается в том, как они получают параметры. При использовании метода call() параметры, передаваемые функции, должны быть перечислены один за другим.
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
console.log(callSum(10,10)); //20
Результаты, возвращаемые методом call() и методом apply(), абсолютно одинаковы.Используете ли вы apply() или call(), всецело зависит от того, какой метод вам наиболее удобен для передачи параметров в функцию.
- Если количество/порядок параметров определено, используйте call, а если количество/порядок параметров неясно, используйте apply.
- Позаботьтесь о читабельности: используйте call, если количество параметров невелико.Если количество параметров велико, интегрируйте параметры в массив и используйте apply.
bind()
Метод bind() создает экземпляр функции, чье значение this привязано к значению, переданному в функцию bind(). Это означает, что bind() вернет новую функцию. Например:
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
Разница между call/apply и bind
воплощать в жизнь:
- Выполнить функцию сразу после того, как call/apply изменит этот контекст функции
- bind возвращает функцию после изменения контекста и не выполняет функцию
function add (a, b) {
return a + b;
}
function sub (a, b) {
return a - b;
}
add.bind(sub, 5, 3); // 这时,并不会返回 8
add.bind(sub, 5, 3)(); // 调用后,返回 8
возвращаемое значение:
- call/apply возвращает результат выполнения fun
- bind возвращает копию fun и указывает эту точку fun, сохраняя параметры fun.
Основная идея call/apply/bind
Из приведенных выше нескольких простых примеров видно, чтоcall/apply/bindЭто заимствование методов у других объектов, что также соответствует нашему обычному мышлению, для простого каштана.
Я прекрасно провел время с одним из моих одноклассников. Мы все вместе носили одежду и обувь. Когда я ходил по магазинам, он покупал одежду, а я покупал обувь. После того, как я вернулся, я хотел носить одежду, которую он купил, но я Так что я одолжил его одежду. Таким образом, я не только достигаю цели ношения новой одежды, но и экономлю деньги~
У объекта А есть метод, и объекту Б тоже по какой-то причине нужно использовать тот же метод.В это время Б может заимствовать метод объекта А, что не только достигает цели, но и экономит память.
Это основная идея call/apply/bind: заимствование.
Сценарии применения call/apply/bind
Использование call/apply/bind не будет расширяться из-за ограниченного места.Вы можете взглянуть на следующую статью.Лично я думаю, что она очень хорошо написана!
Рукописная реализация apply, call, bind
apply
1. Сначала расширить метод на прототипе функции и получить 2 параметра,
Function.prototype.myApply = function (context, args) {}
2. Потому что если контекст не передан, this будет указывать на окно, поэтому контекст и аргументы здесь будут отказоустойчивыми.
Function.prototype.myApply = function (context, args) {
// 处理容错
context = (typeof context === 'object' ? context : window)
args = args ? args : []
}
3. Используйте неявную привязку для реализации явной привязки
Function.prototype.myApply = function (context, args) {
// 处理容错
context = (typeof context === 'object' ? context : window)
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol()
context[key] = this
//通过隐式绑定的方式调用函数
context[key](...args)
}
4. Последний шаг — вернуть возвращаемое значение вызова функции и удалить атрибуты в контексте, чтобы это не повлияло
Function.prototype.myApply = function (context, args) {
// 处理容错
context = (typeof context === 'object' ? context : window)
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol();
context[key] = this;
//通过隐式绑定的方式调用函数
const result = context[key](...args);
//删除添加的属性
delete context[key]
//返回函数调用的返回值
return result
}
Реализована такая нищенская версия apply.Что касается оптимизации, то в интернете есть много отличных писателей, которые хорошо их написали.Можете поискать их.Мы не будем продолжать их оптимизацию здесь.
Проверьте это~
function fun(...args) {
console.log(this.name,...args)
}
const result = {
name: 'Jake'
}
// 参数为数组;方法立即执行
fun.myApply (result, [1, 2])
Результат выглядит следующим образом, указывая на то, что метод применения был реализован.
call
Реализация call почти такая же, как и у apply, только переходим непосредственно к коду.
//传递参数从一个数组变成逐个传参了,不用...扩展运算符的也可以用arguments代替
Function.prototype.NealCall = function (context, ...args) {
//这里默认不传就是给window,也可以用es6给参数设置默认参数
context = (typeof context === 'object' ? context : window)
args = args ? args : []
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol();
context[key] = this;
//通过隐式绑定的方式调用函数
const result = context[key](...args);
//删除添加的属性
delete context[key];
//返回函数调用的返回值
return result;
}
bind
Реализация bind чуть более хлопотная, т. к. bind возвращает связанную функцию, а apply — прямой вызов, а по факту — просто возвращает функцию, выполняющую вышеперечисленные операции применения, однако есть момент, который необходимо быть судимым, потому что возвращая новую функцию, необходимо учитывать использование новой для вызова, а приоритет новой относительно высок, поэтому необходимо судить о вызове новой, и еще одна особенность заключается в том, что при вызове связывания , параметры могут быть переданы, и новая функция, сгенерированная после вызова, также может передавать параметры, эффект тот же, поэтому с этой частью также следует разобраться.
Function.prototype.myBind = function (objThis, ...params) {
const thisFn = this; // 存储源函数以及上方的params(函数参数)
// 对返回的函数 secondParams 二次传参
let fToBind = function (...secondParams) {
const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
return thisFn.call(context, ...params, ...secondParams); // 用call调用源函数绑定this的指向并传递参数,返回执行结果
};
if (thisFn.prototype) {
// 复制源函数的prototype给fToBind 一些情况下函数没有prototype,比如箭头函数
fToBind.prototype = Object.create(thisFn.prototype);
}
return fToBind; // 返回拷贝的函数
};
Суммировать
- В браузере это указывает на объект окна в глобальной области видимости;
- В функции это всегда указывает на объект, вызвавший его последним;
- В конструкторе this указывает на новый объект из new;
- Это в вызове, применении и привязке строго привязано к указанному объекту;
- Это особенность стрелочных функций. Это this родительской области, а не this во время вызова. Вы должны знать, что все первые четыре метода определяются во время вызова, то есть являются динамическими, а this стрелочной функции является статической. , она определяется при выполнении оператора;
- apply, call и bind — это все API, встроенные в функции с помощью js. Вызов их может указать выполнение this для функции, а также может передавать параметры.
Поставьте картинку в конце, чтобы помочь памяти
позже
Стыдно сказать, когда я только изучал JS, я написал на эту тему учебную заметку. Казалось, она была закончена за 2 часа. Я думал, что смогу закончить это за день. Я также прочитал несколько статей, написанных разными большие ребята раньше.Я разместил ссылки ниже.Я могу только вздохнуть, что письмо действительно хорошо! Однако в последние несколько дней у меня все еще есть новое понимание этой точки знаний. Кроме того, Сяошэн - новичок в области переднего плана. Первоначальная цель написания статьи - получить более глубокое впечатление и понимание этого пункта знаний. То, что он написал, также очень белое. Если в тексте есть что-то не так, пожалуйста, поправьте меня~ и тогда есть надежда Друзья, которые прочитали это, могут поставить лайк или подписаться на волну~ Я буду продолжать выводить!
Персональная домашняя страница CSDN
Персональная домашняя страница Nuggets
Справочная статья
红宝书第五章