Повторно выучить это ключевое слово (основные дополнительные знания)

внешний интерфейс JavaScript
Повторно выучить это ключевое слово (основные дополнительные знания)

Зачем изучать это ключевое слово

  1. Интервью спросят! Всегда найдутся интервьюеры, которым нравится спрашивать вас о фрагменте кода, который невозможно написать. Посмотрите на классический и старый вопрос для интервью (после того, как вы закончите эту статью, в конце вас будет ждать более сложный вопрос для интервью!)

    код показывает, как показано ниже:

    var a = 5;
    var obj = {
      a : 10,
      foo: function(){
        console.log(this.a)
      }
    }
    
    var bar = obj.foo
    obj.foo() 
    bar()
    
  2. Когда я читал исходный код lib/events событий, я обнаружил, что ключевое слово call используется много раз, кажется, что необходимо понимать все, что связано с этим и вызовом.

    Часть кода написана так

    // 场景1:
    function EventEmitter() {
      EventEmitter.init.call(this);
    }
    
    // 场景2:
    return this.listener.call(this.target);
    
    // 场景3:
    return listenerCount.call(emitter, type);
    
  3. Неправильное использование стрелочных функций сообщает об ошибке. При инкапсуляции Sequelize, инфраструктуры отображения ORM Node.js, инкапсулируется отношение ассоциации таблиц. Из-за использования стрелочных функций контекст чтения изменяется. Вместо желаемой информации о модели он указывает на глобальное.

  4. Ключевое слово call по-прежнему широко используется в процессе написания кода.Иногда мы часто используем ключевое слово call для указания контекста выполнения функции, а иногда также используем ключевое слово call для реализации наследования.

Пример кода выглядит следующим образом:

var person = {
    "name": "koala"
};
function changeJob(company, work) {
    this.company = company;
    this.work    = work;
};

changeJob.call(person, '百度', '程序员');
console.log(person.work); // '程序员'

Обзорная карта статьи

图片描述
Примечание. Если в этой статье не указано иное, выходные результаты относятся к результатам вывода в браузере.

Об авторе: koala, сосредоточив внимание на совместном использовании полного стека технологий Node.js, от JavaScript до Node.js, до серверной базы данных, я желаю вам стать отличным старшим инженером Node.js. [Руководство по развитию программиста] Автор, блог Github с открытым исходным кодомGitHub.com/koala-co Nth…

вызов функции

В JS (ES5) есть три формы вызова функций:

func(p1, p2) 
obj.child.method(p1, p2)
func.call(context, p1, p2) // 这里先不讲 apply

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

func.call(context,p1,p2)

Два других являются синтаксическим сахаром и могут быть эквивалентно изменены наcallформа.func(p1, p2)等价于 func.call(undefined, p1, p2);

obj.child.method(p1, p2) 等价于 obj.child.method.call(obj.child, p1, p2);Глядя на это таким образом, наш вызов функции имеет только одну форму:

func.call(context,p1,p2)

В это время, знаете ли вы, что это такое, контекст выше. Вернемся к вопросу интервью, который я упомянул в начале.

var a = 5;
var obj = {
  a : 10,
  foo: function(){
    console.log(this.a)
  }
}

var bar = obj.foo
obj.foo() 
bar()
  • Форма obj.foo(), преобразованная в вызов, такова: obj.foo.call(obj)

так что это указывает на obj

  • Форма bar(), преобразованная в вызов, bar.call() Поскольку контекст не передается, это не определено.Если он находится в браузере, он, наконец, даст вам объект этого окна по умолчанию. При работе в среде Node.js это -- объект глобуса. Результат равен 5 в браузере и не определен в среде Node.js.

Описание этого ключевого слова, указывающего на глобальную среду Node.js (вы можете этого не знать)

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

(function(exports, require, module, __filename, __dirname) {
    {
    // 模块的代码
    // 所以那整个代码应该在这里吧
    var a = 10;
    function A(){
        a = 5;
        console.log(a);
        console.log(this.a);
    }
    // const haha = new A();
    A();
    }
});

Давайте поговорим о том, что происходит, когда код модуля js запускается в среде Node.js.Node.js будет использовать оболочку кода для инкапсуляции кода перед его выполнением, например, как показано ниже:

(function(exports, require, module, __filename, __dirname) {
    {
    // 模块的代码
    // 所以那整个代码应该在这里吧
    }
});

Вывод этого кода в среде Node.js:5,undefinedЭто понятно. Это здесь является привязкой по умолчанию к глобалу.Когда this.a выводится, глобал должен указывать на самый внешний слой замыкания. Таким образом, результат вывода не определен.

это ключевое слово в синтаксисе []

function fn (){ console.log(this) }
var arr = [fn, fn2]
arr[0]() // 这里面的 this 又是什么呢? 

мы можем поставить обр0Представьте его как arr.0(), хотя синтаксис последнего неверен, но форма соответствует obj.child.method(p1, p2) в коде преобразования, поэтому его можно успешно преобразовать:

        arr[0]() 
假想为    arr.0()
然后转换为 arr.0.call(arr)
那么里面的 this 就是 arr 了

этот обязательный принцип

привязка по умолчанию

Привязка по умолчанию заключается в том, что при независимом вызове функции ссылка на функцию вызывается без каких-либо изменений, в нестрогом режиме это указывает на глобальный объект (в браузере он указывает на Window, а среда Node.js Global).В строгом режиме это Binding to undefined, строгий режим не позволяет this указывать на глобальный объект.

var a = 'hello'

var obj = {
    a: 'koala',
    foo: function() {
        console.log(this.a)
    }
}

var bar = obj.foo

bar()              // 浏览器中输出: "hello"

этот код,bar()Это привязка по умолчанию, при вызове функции перед ней нет вызова модификации, и можно использовать и предыдущий.callФорма вызова функции понятна, поэтому результат выводаhello.

Другой случай привязки по умолчанию

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

пример:

var name = 'koala';
var person = {
    name: '程序员成长指北',
    sayHi: sayHi
}
function sayHi(){
    setTimeout(function(){
        console.log('Hello,', this.name);
    })
}
person.sayHi();
setTimeout(function(){
    person.sayHi();
},200);
// 输出结果 Hello,koala
// 输出结果 Hello,koala

неявное связывание

Основные критерии для оценки этой неявной привязки: вызывается ли функция в контексте, когда функция вызывается, или объект вызывает функцию.

пример:

var a = 'koala'

var obj = {
    a: '程序员成长指北',
    foo: function() {
        console.log(this.a)
    }
}
obj.foo()       // 浏览器中输出: "程序员成长指北"

Метод foo вызывается как свойство объекта, поэтому при выполнении метода foo это указывает на объект obj.

Другой случай неявного связывания

Когда есть несколько слоев объектов, вложенных для вызова функции, например对象.对象.函数, это указывает на последний слой объектов.

пример:

function sayHi(){
    console.log('Hello,', this.name);
}
var person2 = {
    name: '程序员成长指北',
    sayHi: sayHi
}
var person1 = {
    name: 'koala',
    friend: person2
}
person1.friend.sayHi();

// 输出结果为 Hello, 程序员成长指北

Прочитав этот пример, вы также понимаете ситуацию с неявным вызовом.

явная привязка

Явная привязка, указатель функции this может быть изменен через вызов функции apply bind. Методы вызова и применения являются методами, смонтированными в прототипе функции, и могут использоваться всеми функциями.

Разница между вызовом и применением

  1. Первый параметр вызова и применения привязан к этому телу функции, если不传参数,Напримерfun.call(), нестрогий режим, по умолчанию привязан к глобальному объекту
  2. Функция вызова получает список параметров, а функция применения — массив параметров.
unc.call(thisArg, arg1, arg2, ...)        // call 用法
func.apply(thisArg, [arg1, arg2, ...])     // apply 用法

См. пример кода:

var person = {
    "name": "koala"
};
function changeJob(company, work) {
    this.company = company;
    this.work    = work;
};

changeJob.call(person, '百度', '程序员');
console.log(person.work); // '程序员'

changeJob.apply(person, ['百度', '测试']);
console.log(person.work); // '测试'

Примечания о вызове и применении

Когда эти два метода вызываются, если мы передаем числа или строки, эти два метода преобразуют входящие параметры в типы объектов.

пример:

var number = 1, string = '程序员成长指北';
function getThisType () {
    var number = 3;
    console.log('this指向内容',this);
    console.log(typeof this);
}
getThisType.call(number);
getThisType.apply(string); 
// 输出结果
// this指向内容 [Number: 1]
// object
// this指向内容 [String: '程序员成长指北']
// object

функция привязки

метод привязки Будет создана новая функция. Когда эта новая функция вызывается, первым аргументом bind() будет this среды выполнения, а следующая последовательность аргументов будет передана в качестве ее аргументов перед переданными аргументами. (Содержание определения взято из MDN)

func.bind(thisArg[, arg1[, arg2[, ...]]])    // bind 用法

пример:

var publicAccounts = {
    name: '程序员成长指北',
    author: 'koala',
    subscribe: function(subscriber) {
        console.log(subscriber + this.name)
    }
}

publicAccounts.subscribe('小红')   // 输出结果: "小红 程序员成长指北"

var subscribe1 = publicAccounts.subscribe.bind({ name: 'Node成长指北', author: '考拉' }, '小明 ')
subscribe1()       // 输出结果: "小明 Node成长指北"

новая привязка

Что происходит, когда функция вызывается с помощью new:

  1. Создать пустой объект
  2. пустой объектprotoуказатель на прототип исходного объекта
  3. 执行构造函数中的代码
  4. вернуть этот новый объект

пример:

function study(name){
    this.name = name;
	
}
var studyDay = new study('koala');
console.log(studyDay);
console.log('Hello,', studyDay.name);
// 输出结果
// study { name: 'koala' }
// hello,koala

существуетnew study('koala')Когда он изменит указатель this, будетthis指向指定到了studyDay对象. Примечание. Если вы создаете новый объект, а конструктор не передает значение, свойство в новом объекте не будет иметь значения, но новый объект будет иметь это свойство.

Вручную реализовать код создания нового объекта (несколько реализаций)

function New(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype;
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);

этот обязательный приоритет

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

новая привязка > явная привязка > неявная привязка > привязка по умолчанию

это в стрелочных функциях

стрелочная функция

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

определение

MDN: выражения функции стрелки имеют более короткий синтаксис, чем выражения функции, и не связывают свои собственные this, arguments, super или new.target. Эти функциональные выражения лучше всего использовать для функций, не являющихся методами, и их нельзя использовать в качестве конструкторов.

  • В стрелочных функциях нет аргументов

Обычные функции могут напрямую получать свойство arguments, но если вы используете свойство arguments в стрелочной функции, вы получаете свойство arguments внешней функции стрелочной функции.

пример:

function constant() {
    return () => arguments[0]
}

let result = constant(1);
console.log(result()); // 1

Что, если мы просто хотим получить доступ к параметрам стрелочной функции?

Вы можете получить доступ к параметрам как к именованным параметрам ES6 или остальным параметрам.

let nums = (...nums) => nums;
  • Стрелочные функции не имеют конструктора

Стрелочные функции отличаются от обычных функций.Стрелочные функции не имеют конструктора.Поскольку конструктора нет, их нельзя вызвать с помощью new.Если мы вызовем стрелочную функцию напрямую с помощью new, будет сообщено об ошибке.

пример:

let fun = ()=>{}
let funNew = new fun(); 
// 报错内容 TypeError: fun is not a constructor
  • Стрелочные функции не имеют прототипа

Прототип-прототип является свойством функций, но не стрелочных функций.

пример:

let fun = ()=>{}
console.loh(fun.prototype); // undefined
  • В стрелочных функциях нет super

Как было сказано выше, прототипа нет, даже прототипа.Естественно, к свойствам прототипа нельзя обратиться через super, поэтому у стрелочной функции тоже нет super, а вот так, аргументы, и new.target, эти значения определяются ближайшей нестрелкой во внешнем слое, решает функция.

  • 箭头函数中没有自己的this

У стрелочной функции нет собственного this.Это в стрелочной функции нельзя изменить с помощью call(), apply(), bind() и этих методов.Это в стрелочной функции напрямую указывает на调用函数的 上一层运行时.

let a = 'kaola'

let obj = {
    a: '程序员成长指北',
    foo: () => {
        console.log(this.a)
    }
}

obj.foo()             // 输出结果: "koala"

Прочитав результаты вывода, боюсь, что у всех возникнут сомнения или анализ, в стрелочной функции, о которой я упоминал ранее, это прямо указывает на调用函数的上一层运行时, этот кодobj.fooПри вызове, если функция стрелки не используется, это должно указывать на OBJ, но если используется функция стрелки, она будет указывать на весь мир, поэтому результат выводаkoala.

самовыполняющаяся функция

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

(function(){
    console.log('程序员成长指北')
})()

или

(function(){
    console.log('程序员成长指北')
}())

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

(() => {
    console.log('程序员成长指北')
})()

этот сценарий приложения

Сценарий применения, собственно, то, почему я написал эту статью в начале, и повторю еще раз.

  1. Интервьюер прошел тест!
  2. Глядя на исходный код, я всегда его вижу, и иногда хочется подтвердить текущий контекст. Почему так много используется исходный код, вы можете подумать над этим вопросом.
  3. Мы также используем его при написании кода, и часто есть контекст, в котором вызов используется для указания на объект или для реализации наследования и т. д.

После школьной практики

Узнав об этом, вы обнаружили, что вопрос интервью в начале был немного простым, и он больше не может удовлетворить ваш текущий запас знаний по этому ключевому слову. Хорошо, давайте перейдем к более сложному вопросу интервью.

код показывает, как показано ниже:

var length = 10;
function fn() {
    console.log(this.length);
}
 
var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};
 
obj.method(fn, 1);//输出是什么?

Вывод этого кода:10,2

Если вы внимательно читали статью, то должны быть в состоянии ответить правильно. Каждая деталь описана в статье. Я не буду разбирать это здесь. Если вы не поняли, вы можете прочитать статью еще раз, или добавить в друзья прямо и давайте обсудим это вместе.Каола-это люди, которые готовы поделиться, с нетерпением ждем прогресса вместе с вами.

Отказ от ответственности: Пожалуйста, свяжитесь со мной, если вы перепечатаете в любой форме, и спасибо за ваши указания и предложения, если у вас есть какие-либо вопросы.

Справочная статья

Присоединяйтесь к нам, чтобы учиться вместе!