каталог
Чем отличается передок без закидывания от соленой рыбы?
2 Предисловие
Сначала запомните одно предложение:
thisвсегда указывает на объект, который последним вызвал его
Запомните еще 2 предложения:
- в нормальном режиме
thisуказывая, являетсяthisпри исполненииконтекст - в стрелочной функции
thisуказывая, являетсяthisкогда определеноконтекст
цепочка объемов иthisЭто две разные системы, между которыми мало связи.
thisОн привязан к контексту выполнения, то есть каждый контекст выполнения имеетthis.
Существует 3 типа контекстов выполнения:
- глобальный контекст выполнения
- контекст выполнения функции
-
evalконтекст выполнения
Обратите внимание, что это браузер
this, а в узлеthisразные.
Три this в глобальном контексте выполнения
В консоли Chrome введите:this, вы увидите ответ:
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
в глобальном контексте выполненияthisуказывает наWindowиз.
function foo() {
console.log(this);
}
foo();
Этот код также выводитWindow,Зачем?
запомнитьthisТо есть тот, кто его называет, указывает на.
Мы вызываем глобальный объектfoo, что на самом деле эквивалентноwindow.foo(), то он указывает наWindow.
Фактически, после выполнения приведенного выше кода небольшие партнеры могут войти в консоль Chrome.
window, вы увидите, что естьfoo()метод.
Обратите внимание, что это нестрогий режим. Глобальный объект в строгом режиме
undefined, он сообщит об ошибкеUncaught TypeError: Cannot read property 'name' of undefined
Четыре this в контексте выполнения функции
Выше мы знаем, что общий метод вызова состоит в том, чтобы вызватьwindowспособ выше.
Как получить текущую функциюthisШерстяная ткань?
4.1 Измените это через call/bind/apply
this.myName = 'jsliang';
let foo = function() {
this.myName = 'zhazhaliang';
}
foo();
console.log(window.myName); // 输出啥?
console.log(foo.myName); // 输出啥?
В настоящее времяthisнаправлениеwindow, поэтому выходной результат:
- zhazhaliang
- undefined
пройти черезcallПосле привязки:
this.myName = 'jsliang';
let foo = function() {
this.myName = 'zhazhaliang';
}
foo.call(foo);
console.log(window.myName); // 输出啥?
console.log(foo.myName); // 输出啥?
Результат:
- jsliang
- zhazhaliang
Конечно, вы также можете заменитьapplyа такжеbind, чтобы не быть исчерпаны здесь.
4.2 Вызов настроек метода через объекты
Используйте объект для вызова метода внутри него, методthisотносится к самому объекту.
- Дело 1
let myObj = {
name: 'jsliang',
showThis: function() {
console.log(this.name);
},
};
myObj.showThis(); // 输出啥?
Ответ: выводjsliang.
Мы всегда должны помнить:кто кому звонит. вот черезmyObjделает вызов, поэтому в данный моментthisнаправлениеmyObj. а такжеmyObjестьname: jsliang, поэтому выводjsliang.
Конечно, мы должны осознавать себя:
- Случай 2
let myObj = {
myName: 'jsliang',
showThis: function() {
console.log(this.myName);
},
};
let foo = myObj.showThis;
foo(); // 输出啥?
Тогда это становитсяwindowуказал, на данный моментlet foo = myObj.showThisПросто определение, реальная реализация находится вfoo(). тогда в этот моментfoo()Что он делает?window.foo()какие! вывод без сомненийundefined.
- Случай 3
let myObj = {
name: 'jsliang',
showThis: function() {
console.log(this.name);
},
};
let foo = myObj.showThis;
foo(); // 输出啥?
В общем, вывод этого кода должен бытьundefined.
Однако здесь следует отметить, что,window.nameактуаленwindowимя, этоwindow.open()Значение второго параметра этого метода для открытия новой веб-страницы.
Итак, вот результатwindow.nameявляется нулевым значением''или существующий в настоящее времяwindowИмя.
jsliangДавайте посмотрим, как это произошло, на примере:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>jsliang</title>
</head>
<body>
<button class="btn">打开新网页</button>
<script>
(function() {
const btn = document.querySelector('.btn');
btn.onclick = function() {
window.open('index.html', 'jsliang 的网页');
}
})()
</script>
</body>
</html>
В консоли только что открытой веб-страницы введитеwindow.name,Получатьjsliang 的网页.
В заключение:
- Чтобы вызвать функцию в глобальной среде, внутри функции
thisуказывает на глобальную переменнуюwindow. - Чтобы вызвать метод внутри объекта, контекст выполнения метода
thisуказывает на сам объект.
4.3 Установка в конструкторе
this.name = 'jsliang';
let Foo = function() {
this.name = 'zhazhaliang';
}
let foo = new Foo();
console.log(foo.name); // 输出啥?
console.log(window.name); // 输出啥?
ответ:
- zhazhaliang
- jsliang
Прежде чем поставить этот ответ, давайте посмотрим наnew Foo()Что вы сделали в движке JavaScript:
- Сначала создайте пустой объект
tempObj = {}. - Тогда позвони
Foo.applyметод, будетtempObjтак какapplyпараметры метода, чтобы приFooКогда создается контекст выполнения, егоthisпросто укажите наtempObjобъект. - затем выполнить
Fooфункция, в это времяFooв контексте выполнения функцииthisуказал наtempObjобъект. - последнее возвращение
tempObjобъект.
function myNew(func, ...args) {
const tempObj = {};
func.apply(tempObj, args);
return tempObj;
}
this.name = 'jsliang';
let Foo = function(name, age) {
this.name = name;
this.age = age;
}
let foo = myNew(Foo, 'zhazhaliang', 25);
console.log(foo.name); // 输出啥?
console.log(foo.age); // 输出啥?
console.log(window.name); // 输出啥?
Как и выше, мы можем видеть, чтоthisпринадлежатьtempObj, привязанный кfooПоднимитесь, чтобы получить:
- zhazhaliang
- 25
- jsliang
Конечно, зная это, нам еще нужно улучшитьnewЭтот метод рукописного ввода, чтобы не вводить друзей в заблуждениеnewПросто сделал что-то вроде этого:
function myNew(func, ...args) {
// 1. 判断方法体
if (typeof func !== 'function') {
throw '第一个参数必须是方法体';
}
// 2. 创建新对象
const obj = {};
// 3. 这个对象的 __proto__ 指向 func 这个类的原型对象
// 即实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
obj.__proto__ = Object.create(func.prototype);
// 为了兼容 IE 可以让步骤 2 和 步骤 3 合并
// const obj = Object.create(func.prototype);
// 4. 通过 apply 绑定 this 执行并且获取运行后的结果
let result = func.apply(obj, args);
// 5. 如果构造函数返回的结果是引用数据类型,则返回运行后的结果
// 否则返回新创建的 obj
const isObject = typeof result === 'object' && result !== null;
const isFunction = typeof result === 'function';
return isObject || isFunction ? result : obj;
}
// 测试
function Person(name) {
this.name = name;
return function() { // 用来测试第 5 点
console.log('返回引用数据类型');
};
}
// 用来测试第 2 点和第 3 点
Person.prototype.sayName = function() {
console.log(`My name is ${this.name}`);
}
const me = myNew(Person, 'jsliang'); // 用来测试第 4 点
me.sayName(); // My name is jsliang
console.log(me); // Person {name: 'jsliang'}
// 用来测试第 1 点
// const you = myNew({ name: 'jsliang' }, 'jsliang'); // 报错:第一个参数必须是方法体
Таким образом, мы знаем, что в конструктореnewВ чем дело.
Пять недостатков этой конструкции и решения
5.1 это во вложенной функции не будет унаследовано от внешней функции
var myObj = {
myName: "jsliang",
showThis: function(){
console.log(this.myName); // 输出啥?
function bar(){
console.log(this.myName); // 输出啥?
}
bar();
},
};
myObj.showThis();
ответ:
- jsliang
- undefined
Решение 1: пройтиthatконтрольthisнаправление
var myObj = {
myName: "jsliang",
showThis: function(){
console.log(this.myName); // 输出啥?
let that = this;
function bar(){
console.log(that.myName); // 输出啥?
}
bar();
},
};
myObj.showThis();
Это выводитjsliang.
Решение 2. Решите проблему со стрелочными функциями ES6.
var myObj = {
myName: "jsliang",
showThis: function(){
console.log(this.myName); // 输出啥?
const bar = () => {
console.log(this.myName); // 输出啥?
}
bar();
},
};
myObj.showThis();
Это связано с тем, что стрелочные функции в ES6 не создают собственного контекста выполнения, поэтомуthisзависит от его внешней функции, т. е. от того, кто его называетthisот кого.
5.2 В обычных функциях это указывает на окно глобального объекта
На практике нам не нужен контекст выполнения функции.thisПо умолчанию указывается на глобальный объект, потому что это нарушит границу данных и вызовет некоторые неверные операции.
Если вы хотите, чтобы функция выполняла контекстthisуказать на объект, лучший способ - передатьcallспособ отображения вызова.
Эту проблему можно решить, установив JavaScriptстрогий режимрешать. В строгом режиме выполняет функцию по умолчанию, чей контекст выполнения функцииthisзначениеundefinedЭто решает вышеуказанную проблему.
6. Это указывает на в React
Источник:this.handleClik = this.handleClick.bind(this);
почему ты хочешь сделать это?
Давайте сначала посмотрим на сравнение кода:
Код 1: это в поле вызова объекта
const test = {
myName: 'jsliang',
getName: function() {
console.log(this.myName); // 输出 jsliang
}
};
test.getName();
Код 2: хранится в глобальной переменной
const test = {
myName: 'jsliang',
getName: function() {
console.log(this.myName); // 输出 undefined
}
};
const func = test.getName;
func();
Поэтому метод в React если и не обязывающий, тоthisбудет испорчено указание на глобальный объектwindow.
Итак, как решить эту проблему?
Примечание: пользователи сети отметили:Сравнение следующих четырех методов на самом деле неверно, а обычные объекты нельзя сравнивать с классами.
Конечно,jsliangЯ все еще хочу перечислить здесь
конкретные выводы, поверьте
202XКогда мы будем проводить повторный обзор в 2019 году, мы постепенно ответим на наши внутренние сомнения, когда у нас будет возможность.
Поэтому во избежание конфликтов друзья могут пропустить эту главу.
6.1 Решение 1: привяжите это заранее
const test = {
myName: 'jsliang',
getName: function() {
console.log(this.myName); // 输出 jsliang
}
};
test.getName = test.getName.bind(test);
const func = test.getName;
func();
Соответствует React в:
constructor (props) {
this.handleClick = this.handleClick.bind(this);
}
<button onClick={this.handleClick}>btn 1</button>
6.2 Решение 2: привязать привязать это при вызове
const test = {
myName: 'jsliang',
getName: function() {
console.log(this.myName); // 输出 jsliang
}
};
const func = test.getName.bind(test);
func();
Соответствует React в:
<button onClick={this.handleClick.bind(this)}>btn 2</button>
6.3 Решение 3. Возврат стрелочной функции
const test = {
myName: 'jsliang',
getName: function() {
console.log(this.myName); // 输出 jsliang
}
};
const func = () => test.getName();
func();
Соответствует React в:
<button onClick={() => this.handleClick()}>btn 3</button>
6.4 Решение 4. Превратите вызывающий метод в стрелочную функцию (сбой)
const test = {
myName: 'jsliang',
getName: () => {
console.log(this.myName);
}
};
const func = test.getName;
func();
Соответствует React в:
handleClick2 = () => {
console.log('jsliang 2021');
}
<button onClick={this.handleClick2}>btn 4</button>
Однако этот метод терпит неудачу, возвращаяundefined, какова причина?
Пользователи сети отметили:
- Сравнение этих четырех методов на самом деле неверно, а обычные объекты нельзя сравнивать с классами.
Конечно,jsliangЯ все еще хочу перечислить их, с конкретными выводами.Я верю, что у меня будет возможность шаг за шагом ответить на мои внутренние сомнения.
6.5 React this указывает на фактическое содержимое
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
// this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
Зная вышеуказанный метод, после компиляции он становится:
"use strict";
// ...代码省略
var Toggle = /*#__PURE__*/function (_React$Component) {
_inherits(Toggle, _React$Component);
var _super = _createSuper(Toggle);
function Toggle(props) {
var _this;
_classCallCheck(this, Toggle);
_this = _super.call(this, props);
_this.state = {
isToggleOn: true
}; // 为了在回调中使用 `this`,这个绑定是必不可少的
// this.handleClick = this.handleClick.bind(this);
return _this;
}
_createClass(Toggle, [{
key: "handleClick",
value: function handleClick() {
console.log(this); // 输出是 undefined
}
}, {
key: "render",
value: function render() {
return /*#__PURE__*/React.createElement("button", {
onClick: this.handleClick
}, this.state.isToggleOn ? 'ON' : 'OFF');
}
}]);
return Toggle;
}(React.Component);
В основном см._createClassметод, первый параметр — созданный класс, второй параметр — массив, массив — метод в созданном классе.
Очевидно, в кодеhandleClickвыходthis, Определенно естьundefined.
а такжеrenderметод возвращаетReact.createElement, в этом методеthisуказал на_createClassПервый параметр метода, которыйToggle.
На данный момент, если этот метод становится функцией стрелки:
handleClick = () => {
console.log(this);
}
Теперь функция стрелкиthisКонтекст, в котором он был определен.
Когда мы нажмем кнопку, она будет вызванаhandleClickметод для обработки события, в то время какhandleClickвToggleметод, поэтомуthisОтносится кToggleэтот класс.
6.6 React это указывает на решение
import React, { Component } from 'react'
class App extends Component {
constructor (props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
console.log('jsliang 2020');
}
handleClick2 = () => {
console.log('jsliang 2021');
}
render () {
// 四种绑定方法
return (
<div className='App'>
{/* 方法一:通过 constructor 中进行 bind 绑定 */}
<button onClick={this.handleClick}>btn 1</button>
{/* 方法二:在里边绑定 this */}
<button onClick={this.handleClick.bind(this)}>btn 2</button>
{/* 方法三:通过箭头函数返回事件 */}
<button onClick={() => this.handleClick()}>btn 3</button>
{/* 方法四:让方法变成箭头函数 */}
<button onClick={this.handleClick2}>btn 4</button>
{/* 额外:直接调用不需要绑定 this */}
{this.handleClick()}
</div>
)
}
}
export default App;
Семь Резюме
Сначала запомните одно предложение:
thisвсегда указывает на объект, который последним вызвал его
Запомните еще 2 предложения:
- в нормальном режиме
thisуказывая, являетсяthisпри исполненииконтекст - в стрелочной функции
thisуказывая, являетсяthisкогда определеноконтекст
Восемь тем
8.1 Анализ этой темы 5 шагов
- Первый вопрос
var name = 'window name';
function a() {
var name = 'jsliang';
console.log(this.name); // 输出啥?
console.log('inner:' + this); // 输出啥?
}
a();
console.log('outer:' + this); // 输出啥?
Что выводит приведенный выше код?
Отвечать:
window name
inner:window name
outer:window name
Анализ: здесьa()можно рассматривать какwindow.a(), так что оба указывают наwindowвнутри.
- вопрос 2
var name = 'window name';
var a = {
name: 'jsliang',
fn: function () {
console.log(this.name); // 输出啥?
}
}
a.fn();
Что выводит приведенный выше код?
Ответ: джслянг
Анализ: это сейчасa.fn(), так что это указывает наa, поэтому выводjsliang
- Вопрос 3
var name = 'window name';
var a = {
// name: 'jsliang',
fn: function () {
console.log(this.name); // 输出啥?
}
}
a.fn();
Что выводит приведенный выше код?
Ответ: неопределенный
Анализ: Очевидно,aздесь нетnameметод, поэтомуa.fn()не могу найтиaобъект имеетname, поэтому выводundefined
- Вопрос 4
var name = 'window name';
var a = {
name: 'jsliang',
fn: function () {
console.log(this.name); // 输出啥?
}
}
var f = a.fn;
f();
Что выводит приведенный выше код?
Отвечать:window name
Разбор: кодvar f = a.fnне звонилa.fn, а определение. существуетf()был вызван, когдаfn()даwindow.fn(), поэтому он указывает наwindow, поэтому выводwindow name
- Вопрос 5
var name = 'window name';
function fn() {
var name = 'jsliang';
function innerFn() {
console.log(this.name);
};
innerFn();
}
fn();
Отвечать:window name
Анализ: Маленькие друзья понимают и понимают
8.2 это let/const
let a = 10;
const b = 20;
function foo() {
console.log(this.a);
console.log(this.b);
}
foo();
console.log(window.a);
Что выводит приведенный выше код?
Отвечать:undefined,undefined,undefined
Анализ: еслиvarизменился наletилиconst, переменная не привязана кwindow, поэтому он распечатает триundefined.
8.3 это стрелочные функции
var name = 'window name';
var a = {
name: 'jsliang',
func1: function () {
console.log(this.name);
},
func2: function () {
setTimeout(() => {
this.func1();
}, 100);
},
};
a.func1(); // 输出啥?
a.func2(); // 输出啥?
Что выводит приведенный выше код?
Отвечать:
jsliang
jsliang
Анализ: стрелочные функцииthisуказывает на определение функцииthis, а не во время выполнения. Стрелочные функции не имеютthisпривязка, значение которой должно быть определено путем поиска по цепочке областей видимости, если стрелочная функция содержится в нестрелочной функции, тоthisПривязка — ближайший слой нестрелочных функцийthis,иначе,thisдляundefined.
8.4 Найдите выходной результат
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
var obj2 = { a: 3, foo2: obj.foo }
obj.foo(); // 输出啥?
foo2(); // 输出啥?
obj2.foo2(); // 输出啥?
Что выводит приведенный выше код?
отвечать:
1
2
3
Разобрать:
-
obj.foo():objпередачаfoo(), поэтому укажитеobj, выход1 -
foo2():в реальностиwindow.foo2(),направлениеwindow, выход2 -
obj2.foo2():obj2передачаfoo2(),направлениеobj2, выход3
8.5 Проблема потери неявной привязки
8.5.1 Найдите выходной результат
function foo() {
console.log(this.a);
}
function doFoo(fn) {
console.log(this);
fn();
}
var obj = { a: 1, foo };
var a = 2;
doFoo(obj.foo); // 输出啥?
Что выводит приведенный выше код?
Отвечать:Window {...},2
Разобрать:Проблема потери неявной привязки.deFooпередать параметрыobj.fooкогда, в этот моментfooне реализовано, поэтому вdoFooсерединаfn()эквивалентноwindow.fn(), поэтому указывает наwindowЛа!
Обратите внимание, что при звонке в этот раз искомый
fnдаwindowВверхfn, вместоdoFooвнутренний,doFooне установленfnСюда.
8.5.2 Найдите выходной результат
function foo() {
console.log(this.a);
}
function doFoo(fn) {
console.log(this);
fn();
}
var obj = { a: 1, foo };
var a = 2;
var obj2 = { a: 3, doFoo };
obj2.doFoo(obj.foo); // 输出啥?
Что выводит приведенный выше код?
Отвечать:{ a: 3, doFoo: f },2
Разобрать:
- В данный момент
fn()звоните, найденное место по-прежнемуwindow.foo(), поэтому при вызове он будет указывать наwindow. - здесь
fn()Он приходит путем передачи параметров, а неdoFooсуществует в нем, поэтому при выполненииthisнайденоfooОпределенное местоположение, или фактическиwindow.fn() - Как исправить эту проблему?
function foo() {
console.log(this.a);
}
function doFoo(fn) {
console.log(this);
fn.call(this);
}
var obj = { a: 1, foo };
var a = 2;
var obj2 = { a: 3, doFoo };
obj2.doFoo(obj.foo);
8.6 Проблемы с привязкой дисплея
8.6.1 Найдите выходной результат
function foo() {
console.log(this.a);
}
var obj = { a: 1 };
var a = 2;
foo(); // 输出啥?
foo.call(obj); // 输出啥?
foo().call(obj); // 输出啥?
Что выводит приведенный выше код?
Отвечать:
2
1
2
Uncaught TypeError: Cannot read property 'call' of undefined
Разобрать:
-
foo():направлениеwindow -
foo.call(obj):Будуfooизthisуказал наobj -
foo().call(obj): выполнить первымfoo(), выход2, то он безвозвратный, эквивалентныйundefined.call(obj), сообщить об ошибке напрямую
8.6.2 Найдите выходной результат
function foo() {
console.log(this.a);
return function () {
console.log(this.a);
};
}
var obj = { a: 1 };
var a = 2;
foo(); // 输出啥?
foo.call(obj); // 输出啥?
foo().call(obj); // 输出啥?
Что выводит приведенный выше код?
Отвечать:
2
1
2
1
Анализ: Излишне говорить, что первые три вопроса такие же, как и в предыдущем вопросе.
последнийreturn function { this.a }Поэтому этот метод статьcall(obj), поэтому выводobjсерединаa, то есть,1.
8.7 Найдите выходной результат
function Foo() {
'use strict'
console.log(this.location);
}
Foo();
пожалуйста, выберите:
- A: Текущее окно
Locationобъект - Б:
undefined - С:
null - Д:
TypeError
Ответ: Д
Разобрать: если нетuse strict, затем выберите A; если это строгий режим, это D, запрещенный в строгом режимеthisКлючевое слово указывает на глобальный объект.
8.8 Уточняющие вопросы
let userInfo = {
name: 'jsliang',
age: 25,
sex: 'male',
updateInfo: function(){
// 模拟 XMLHttpRequest 请求延时
setTimeout(function(){
this.name = "zhazhaliang"
this.age = 30;
this.sex = 'female';
}, 1000);
},
};
userInfo.updateInfo();
решить здесьthisУкажите на проблему и получите конечный результат:
{
name: "zhazhaliang",
age: 30,
sex: "female",
updateInfo: function(),
}
Отвечать:setTimeout(() => {})Вот и все.
9 ссылок
- Еще 40 вопросов к этому интервью, кислых и крутых, продолжение[Рекомендация по прочтению: 1 час]
- это, это, обсудите это еще раз в javascript, очень подробно【Рекомендация по прочтению: 10 минут】
- это в JavaScript【Рекомендация по прочтению: 10 минут】
- Углубленная интерпретация JavaScript из спецификации ECMAScript.【Рекомендация по прочтению: 20 минут】
- Продвинутые основы интерфейса (7): исчерпывающая интерпретация этого【Рекомендация по прочтению: 20 минут】
- Основной менталитет JavaScript - это【Рекомендация по прочтению: 20 минут】
- 11 | this: Сделайте это понятным с точки зрения контекста выполнения JavaScript.【Рекомендация по прочтению: 2 часа】
- Говоря об этом, указывая на реакцию【Рекомендация по прочтению: 10 минут】
- Оптимизация производительности реакции【Рекомендация по прочтению: 5 минут】
- Почему обработчики событий React должны использовать bind(this)【Рекомендация по прочтению: 10 минут】
- Это вызвано привязкой в конструкторе React для понимания (почему метод компонента React должен использовать привязку для привязки этого)【Рекомендация по прочтению: 20 минут】
- Это в this.handleClick = this.handleClick.bind(this) в React указывает на проблему【Рекомендация по прочтению: 10 минут】
репозиторий документации jsliang предоставляетсяЛян ЦзюньронгиспользоватьCreative Commons Attribution-NonCommercial-ShareAlike 4.0 Международная лицензияЛицензия.
на основеGitHub.com/l ian Jun Ron…Создание работ выше.
Права на использование, отличные от разрешенных в настоящем Лицензионном соглашении, могут быть получены отCreative Commons.org/licenses/не…получено в.