Интервьюер ByteDance - придумайте, пожалуйста, метод 🌈, чтобы следующая программа наконец смогла выдать успех

JavaScript

предисловие

В этой статье подробно объясняется Байтовые вопросы на собеседовании Кстати, шаг за шагом полностью решить проблему этого указания на приоритет в js.⛹️‍♂️⛹️‍♂️
Проблема этого в js должна быть темой, которая обсуждается давно, в Nuggets тоже много статей на эту тему. Однако, может быть, статьи, которые я видел раньше, мне не очень подходят, и каждый раз, когда я их читаю, я все еще не понимаю и не имею особых зацепок. Мне повезло, что несколько дней назад мой старый старший…Мэн братПосле обмена кажется, что у меня появилось более глубокое понимание этого вопроса, поэтому я написал статью, чтобы обобщить его. 🌈

只要你仔细认真看完这篇文章,不管你是 js 新手、还是大佬,我保证你一定会有收获的!如果一丢丢收获都没有,你可以揍我!️👀

Без лишних слов, давайте сначала поговорим о теме

  • Пожалуйста, реализуйте это вручную в ES5Functionпрототипbind(т.е. написанный от руки myBind в следующем блоке кода), чтобы следующая программа наконец смогла вывести'success'.
function Animal(name, color) {
  this.name = name;
  this.color = color;
}
Animal.prototype.say = function () {
  return `I'm a ${this.color} ${this.name}`;
};
const Cat = Animal.myBind(null, 'cat');
const cat = new Cat('white');
if (cat.say() === 'I\'m a white cat' &&
  cat instanceof Cat && cat instanceof Animal) {
  console.log('success');
}
先来看看解题需要了解的一些问题 慢慢来 不要慌张🌈

связанная информация

Чтобы разобраться в этом вопросе, необходимо иметь некоторые знания

Во-первых, смысл ЭТОГО в JS

  • В ES5 указание this всегда является принципом: указание this не определяется при его создании.это всегда указывает на объект, который последний раз вызывал его.

Вот несколько примеров, демонстрирующих вышеизложенное

one

var name = "globalName";
function a() {
  var name = "jingjing";
  console.log(this.name)
}
a();    //globalName

По принципу чуть выше:это всегда указывает на объект, который последний раз вызывал егоможет получить ответ. Где мы видим последний звонок к? В последней строке кодаa(); объект, который не вызывается перед ним, то он является глобальным объектом по умолчаниюwindow,такconsole.log(this.name)это становитсяconsole.log(window.name), вывод результатаglobalName(👉В нестрогом режиме 👈).

two

var name = "globalName";
var a = {
  name: "jingjing",
  jing: function () {
    console.log(this.name);  // jingjing
  }
}
window.a.jing();

Я собираюсь повторить вышеприведенную фразу снова 😁.это всегда указывает на объект, который последний раз вызывал егоможно получить ответ. Давайте посмотрим на последний звонокjing()Где место для функции? или функцияjing()этот слева.Какой предмет находится слева от ? очевидно, объект a, поэтомуconsole.log(this.name)это становитсяconsole.log(a.name), вывод результатаjingjing.

three

var name = "globalName";
var a = {
  name: "jingjing",
  jing: function () {
    console.log(this.name);  // globalName
  }
}
var hao = a.jing
hao();

🎈 Здесь мы присваиваем метод jing объекта a переменной hao, но будьте внимательны! ! ! 🔍 Этот шаг не вызывает метод исполнения цзин! . последняя строка кодаhao()только бытьwindowвызов выполненjing()метод. такconsole.log(this.name)это становитсяconsole.log(window.name), вывод результатаglobalName. Возьмем еще раз этот принцип:это всегда указывает на объект, который последний раз вызывал его. 🌈🌈🌈

2. Четыре обязательных правила для этого

В JavaScript есть четыре правила привязки, на которые указывает this:

  • Привязка по умолчанию (в нестрогом режиме это указывает наwindow, в строгом режиме это указывает наundefined. )
  • Неявная привязка (если есть объект, вызывающий ее при вызове функции, то это будет неявно привязано к этому объекту)
  • Явная привязка (функция черезcall(),apply(),bind()Вызывается, это указывает на связанный объект. )
  • новая привязка (функцияnewпередача,thisрежиссерnewНовый построенный объект. )

Первые два были поняты выше, а теперь давайте сосредоточимся на последних двух.

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

Этот метод привязки заключается в использованииFunction.prototypeтри метода вcall(),apply(),а такжеbind().这三个函数,都可以改变函数的 this 指向到指定的对象,不同之处在于:

  • call()а такжеapply()обевыполнить функцию немедленно, но они принимают параметры в разных формах, например:
call(this, arg1, arg2, ...)
apply(this, [arg1, arg2, ...])
  • а такжеbind()являетсявозвращает новую функцию-оболочкуНе сразу.bind()Будет создана новая функция. Когда эта новая функция вызывается,bind()Первый аргумент будет использоваться в качестве времени выполнения.this, следующая последовательность аргументов будет передана в качестве его аргументов перед переданными аргументами.
bind(this, arg1, arg2, ...)

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

Когда функция вызывается с new или происходит вызов конструктора, автоматически выполняются следующие операции.

  • 1. Создайте (или сконструируйте) совершенно новый объект.
  • 2. Этот новый объект будет связан с [[Prototype]].
  • 3. Этот новый объект будет привязан к этому вызову функции.
  • 4. Если функция не возвращает другой объект, то вызов функции в новом выражении автоматически возвращает новый объект.

В JavaScript оператор new не похож на другие объектно-ориентированные языки, а представляет собой эмулируемый механизм. В JavaScript все функции могут вызываться с помощью new. В настоящее время эта функция обычно называется «конструктором». На самом деле так называемого «конструктора» не существует. Более точно следует понимать «конструктор» функция "режим вызова".

Вышеприведенное о новой привязке исходит из неизвестных вам правил привязки js rollup 2.2.

3. Приоритет обязательных правил

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

Вывод такой же, как и выше, и ниже приведены некоторые примеры, подтверждающие этот вывод.

毫无疑问,默认绑定的优先级是四条规则中最低的,所以我们可以先不考虑它。

one

function jing() {
  console.log(this.a);
}

var obj1 = {
  a: 10,
  foo: jing
};

var obj2 = {
  a: 20,
  foo: jing
};

obj1.foo();   //10
obj2.foo();   //20

obj1.foo.call(obj2);   //20
obj2.foo.call(obj1);   //10

Из этого текущего результата видно, что последние две строки приведенного выше блока кода изменили точку этого с помощью метода call(). Таким образом, вы можете получитьЯвное связывание > Неявное связываниеэтот вывод.

two

function jing() {
  this.a = 'hao';
}

let obj = {
  a: 'jing'
};
// 1、bind
const Bar = jing.bind(obj);
// 2、new
const bar = new Bar();
console.log(obj.a, '--', bar.a) //jing -- hao

Третья-последняя строка вышеуказанного блока кода проходитbind()метод измененthisуказывает наobj, из вышеперечисленногоbar.aРезультат распечаткиhaoВидно, что предпоследняя строка кода измениласьthisнаправлениеjing(), так что вы можете получитьновая привязка > явная привязка.

Итак, в итоге можно сделать вывод, что:новая привязка > явная привязка > неявная привязка > привязка по умолчанию


В-четвертых, напишите упрощенную версию bind().

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

Function.prototype.myBind = function (thisObj, ...arg1) {
  // 1、这里 ...arg1 是第一次传的参数
  let fn = this;
  // 这里用 fn 记下调用对象
  function jingbind(...arg2) {
    // 2、这里 ...arg2 是下一次传的参数
    const args = arg1.concat(arg2);
    // 谁调用 bind,最终拼好的参数就传给谁
    return fn.apply(thisObj, args);
  }
  return jingbind;
  // bind() 返回一个未执行的函数
}

с рукописнымmyBind(), давайте напишем еще один тестовый код, чтобы попробовать эффект:

function sum(a, b, c) {
  return a + b + c;
}

const sum10 = sum.myBind(null, 10);
let jing = sum10(20, 30);
console.log(jing)   //60

🙌🙌После тестирования успешно выводится правильный результат. Теперь мы можем попробовать то, что мы только что реализовали.myBind()Может ли метод решить вопрос интервью, поставленный в верхней части статьи! Интересно думать об этом! ! 🙋‍♂️🙋‍♂️

процесс решения проблем

Поместите вышеуказанный метод вместе с данной темой и попробуйте его.

Function.prototype.myBind = function (thisObj, ...arg1) {
  if (typeof this !== "function") {
    throw new TypeError("not a function");
  }
  // 1、这里 ...arg1 是第一次传的参数
  let fn = this;
  // 这里用 fn 记下调用对象
  function jingbind(...arg2) {
    // 2、这里 ...arg2 是下一次传的参数
    const args = arg1.concat(arg2);
    // 谁调用 bind,最终拼好的参数就传给谁
    return fn.apply(thisObj, args);
  }
  return jingbind;
  // bind() 返回一个未执行的函数
}

function Animal(name, color) {
  this.name = name;
  this.color = color;
}
Animal.prototype.say = function () {
  return `I'm a ${this.color} ${this.name}`;
};
const Cat = Animal.myBind(null, 'cat');
const cat = new Cat('white');
console.log(cat.say())  //这句代码是作者解题调试加上去的
if (cat.say() === 'I\'m a white cat' &&
  cat instanceof Cat && cat instanceof Animal) {
  console.log('success');
}

Жаль, это нормально, это неправильно. Скриншот сообщения об ошибке выглядит следующим образом:

  • Сообщение об ошибке показывает, чтоcat.say() is not a function, то почему это не метод?
  • Найдем это в кодеcat.say(), шаг за шагом, чтобы наверстать упущенное.
  • этоcatОткуда это? ОтCatначальствоnewПример, который выходит, то этоCatОткуда это? КAnimal.myBindсгенерировано, вы позвонилиmyBind()Я вернул его вам. ЭтоmyBind()Что оно вернуло? то, что он возвращаетjingbind(). Так что в итоге мы можем получитьCat === jingbind(), который можно получитьcat === new jingbind().
  • Затем мы обнаружили, что написали этоjingbind()В функции нет определенияsay()кстати, этоsay()Метод определенAnimal.prototypeнад.
  • То есть изначальноAnimal.prototypeEстьsay()метод, но после того, как мы напишемmyBind()После обработки метода поместитеAnimal.prototypeпотерянный.
  • Итак, мы должны добавить к этому, так как? ИзмененоmyBind()код показывает, как показано ниже:
Function.prototype.myBind = function (thisObj, ...arg1) {
  if (typeof this !== "function") {
    throw new TypeError("not a function");
  }
  // 1、这里 ...arg1 是第一次传的参数
  let fn = this;
  // 这里用 fn 记下调用对象
  function jingbind(...arg2) {
    // 2、这里 ...arg2 是下一次传的参数
    const args = arg1.concat(arg2);
    // 谁调用 bind,最终拼好的参数就传给谁
    return fn.apply(thisObj, args);
  }
  jingbind.prototype = fn.prototype;    //只添加了这一行代码
  return jingbind;
  // bind() 返回一个未执行的函数
}

Попробуйте снова

😎😎😎

  • Код не сообщает об ошибке, указывающей на добавление прототипа.
  • Но этот вывод показывает, что мы только что успешно добавили прототип и обнаружили, что не передаем параметры нормально. 🤕🤕🤕
  • потому что это не связано с кошкой

Это включает в себя приоритет этого правила привязки в js, упомянутого ранее. мы написалиmyBind()В функции нет определения приоритета(Другими словами, нет соответствующей привязки this для разных правил привязки this).


Сделать myBind() идеальным

МыmyBind()Внесите некоторые изменения в метод следующим образом:

Function.prototype.myBind = function (thisObj, ...arg1) {
  if (typeof this !== "function") {
    throw new TypeError("not a function");
  }
  // 1、这里 ...arg1 是第一次传的参数
  let fn = this;
  // 这里用 fn 记下调用对象
  function jingbind(...arg2) {
    // 2、这里 ...arg2 是下一次传的参数
    const args = arg1.concat(arg2);
    // 谁调用 bind,最终拼好的参数就传给谁
    let isjing = this instanceof jingbind;  //判断是否是 new 调用
    return fn.apply(isjing ? this : thisObj, args);
  }
  jingbind.prototype = fn.prototype;
  return jingbind;
  // bind() 返回一个未执行的函数
}
  • instanceofразумеется
  • мы используемinstanceofЧтобы определить, вызывается ли он новым, если он вызывается новым, мы должны привязать это к экземпляру.
  • Если он не был вызван new , мы просто привязываем его кmyBind()Первый параметр функции -thisObj, мы должны получить желаемый результат, если поступим с этим так 🤐🤐
再来测试一下🥱

В итоге мы придумали этот метод. 💦💦💦

наконец

  • Этот вопрос исходит от моего старого старшегоМэн братВопросы для интервью для тех, кто не является автором (все еще 🥦💪).
  • Мой брат Мэн сказал: «Я тщательно разобрался с этой проблемой, и связанные с этим вопросы js почти понятны.
  • Все вышеперечисленные точки зрения написаны автором, и если вы этого не понимаете, вы также можете прочитать краткое содержание книги.
  • Автор - студент, который только начал заниматься фронтендом, поэтому в статье могут быть какие-то ошибки, если начальник это увидит и укажет на исправление, чтобы у Дабая была еще одна возможность научиться, то ему настоящий босс😜

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

Путь роста Jingjing

последний из последних

Если у вас есть какие-либо вопросы, добро пожаловать, чтобы добавить автора WeChat для обмена и обучения (большой брат игнорирует 👀)