предисловие
Здравствуйте, яВакагава. Это вторая статья интервьюера из серии, которая призвана помочь читателям улучшить
JS
базовые знания, в том числеnew、call、apply、this、继承
связанная информация.
面试官问系列
Статья выглядит следующим образом: Заинтересованные читатели могут нажать, чтобы прочитать.
1.Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?
2.Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
3.Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
4.Интервьюер спросил: этот пункт JS
5.Интервьюер спросил: наследование JS
использовалReact
Все студенты знают, что они часто используютbind
связыватьthis
.
import React, { Component } from 'react';
class TodoItem extends Component{
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
console.log('handleClick');
}
render(){
return (
<div onClick={this.handleClick}>点击</div>
);
};
}
export default TodoItem;
Затем интервьюер может спросить,bind
Что именно он делал и как это имитировать.
Прикреплен абзац из предыдущей статьи: уже существует множество реализаций симуляции.
bind
Почему ты снова должен писать это сам? Обучение похоже на большую гору: люди взбираются на гору по разным дорогам и делятся пейзажами, которые видят. Возможно, вы не сможете увидеть пейзаж, который видят другие, и почувствовать настроение других. Только когда вы отправляетесь в горы самостоятельно, вы можете увидеть разные пейзажи и испытать более глубокие переживания.
Первый взглядbind
что. сверхуReact
В коде видно, чтоbind
За выполнением следует функция, и каждая функция может выполняться, вызывая ее.
Видеть значит верить, слышать ложно. Читатели могут нажимать на консоль шаг за шагомПример 1серединаobj
:
var obj = {};
console.log(obj);
console.log(typeof Function.prototype.bind); // function
console.log(typeof Function.prototype.bind()); // function
console.log(Function.prototype.bind.name); // bind
console.log(Function.prototype.bind().name); // bound
Отсюда можно сделать вывод 1:
1,bind
даFunctoin
в цепочке прототиповFunction.prototype
Атрибут , который может вызывать каждая функция.
2,bind
сама является функцией с именемbind
Возвращаемое значение также является функцией, и имя функцииbound
. (Введите этоbound加上一个空格
).
понялbind
это функция, вы можете передавать параметры и возвращать значение'bound '
Это тоже функция, и она тоже может передавать параметры, поэтому ее легко написатьПример 2:
объединениеbound
относится к исходной функцииoriginal
bind
Функция, которая возвращается после этого, легко объяснить.
var obj = {
name: '若川',
};
function original(a, b){
console.log(this.name);
console.log([a, b]);
return false;
}
var bound = original.bind(obj, 1);
var boundResult = bound(2); // '若川', [1, 2]
console.log(boundResult); // false
console.log(original.bind.name); // 'bind'
console.log(original.bind.length); // 1
console.log(original.bind().length); // 2 返回original函数的形参个数
console.log(bound.name); // 'bound original'
console.log((function(){}).bind().name); // 'bound '
console.log((function(){}).bind().length); // 0
Отсюда можно сделать вывод 2:
1. звонокbind
в функцииthis
направлениеbind()
Первый параметр функции.
2. Перейти кbind()
Остальные параметры принимаются и обрабатываются,bind()
После этого параметры возвращаемой функции также принимаются и обрабатываются, то есть объединяются и обрабатываются.
3, иbind()
Послеname
дляbound + 空格 + 调用bind的函数名
. Если это анонимная функция, то даbound + 空格
.
4.bind
После функции возвращаемого значения возвращаемое значение после выполнения является исходной функцией (original
) Возвращаемое значение.
5.bind
параметры функции (то естьlength
)Да1
.bind
вернулся послеbound
Параметры функции являются неопределенными, в соответствии с исходной функцией связанной функции (original
) Определяется количество формальных параметров.
Согласно выводу 2: мы можем просто смоделировать упрощенный вариантbindFn
// 第一版 修改this指向,合并参数
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + 'must be a function');
}
// 存储函数本身
var self = this;
// 去除thisArg的其他参数 转成数组
var args = [].slice.call(arguments, 1);
var bound = function(){
// bind返回的函数 的参数转成数组
var boundArgs = [].slice.call(arguments);
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
return self.apply(thisArg, args.concat(boundArgs));
}
return bound;
}
// 测试
var obj = {
name: '若川',
};
function original(a, b){
console.log(this.name);
console.log([a, b]);
}
var bound = original.bindFn(obj, 1);
bound(2); // '若川', [1, 2]
Если интервьюер увидит здесь ваш ответ, предполагается, что ваше впечатление должно быть на 60 или 70 баллов.
Но мы знаем, что функции можно использовать сnew
создать экземпляр. Такbind()
Как выглядит функция возвращаемого значения?
см. далееПример 3:
var obj = {
name: '若川',
};
function original(a, b){
console.log('this', this); // original {}
console.log('typeof this', typeof this); // object
this.name = b;
console.log('name', this.name); // 2
console.log('this', this); // original {name: 2}
console.log([a, b]); // 1, 2
}
var bound = original.bind(obj, 1);
var newBoundResult = new bound(2);
console.log(newBoundResult, 'newBoundResult'); // original {name: 2}
отПример 3виды можно увидетьthis
указал наnew bound()
Создайте новый объект.
Можно сделать вывод, что 3:
1,bind
изначально указывал наobj
недействителен, другие параметры действительны.
2,new bound
Возвращаемое значениеoriginal
Новый объект, созданный исходным конструктором функции.original
исходная функцияthis
Указывает на этот новый объект.
Еще одна статья, которую я написал некоторое время назад:Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?. Простое резюме:что нового делает:
1. Создается новый объект.
2. Этот объект будет выполнен[[Prototype]]
(то есть,__proto__
)Связь.
3. Новый сгенерированный объект будет привязан к этому вызову функции.
4. Пройтиnew
Каждый созданный объект в конечном итоге будет[[Prototype]]
ссылка на эту функциюprototype
на объекте.
5. Если функция не возвращает тип объектаObject
(ВключатьFunctoin
,Array
,Date
,RegExg
,Error
),Такnew
Вызовы функций в выражениях автоматически возвращают этот новый объект.
Таким образом, это эквивалентноnew
Когда звонили,bind
функция возвращаемого значенияbound
Чтобы смоделировать реализацию внутриnew
реализованная операция.
Нечего сказать, сразу к коду.
// 第三版 实现new调用
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + ' must be a function');
}
// 存储调用bind的函数本身
var self = this;
// 去除thisArg的其他参数 转成数组
var args = [].slice.call(arguments, 1);
var bound = function(){
// bind返回的函数 的参数转成数组
var boundArgs = [].slice.call(arguments);
var finalArgs = args.concat(boundArgs);
// new 调用时,其实this instanceof bound判断也不是很准确。es6 new.target就是解决这一问题的。
if(this instanceof bound){
// 这里是实现上文描述的 new 的第 1, 2, 4 步
// 1.创建一个全新的对象
// 2.并且执行[[Prototype]]链接
// 4.通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。
// self可能是ES6的箭头函数,没有prototype,所以就没必要再指向做prototype操作。
if(self.prototype){
// ES5 提供的方案 Object.create()
// bound.prototype = Object.create(self.prototype);
// 但 既然是模拟ES5的bind,那浏览器也基本没有实现Object.create()
// 所以采用 MDN ployfill方案 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
function Empty(){}
Empty.prototype = self.prototype;
bound.prototype = new Empty();
}
// 这里是实现上文描述的 new 的第 3 步
// 3.生成的新对象会绑定到函数调用的`this`。
var result = self.apply(this, finalArgs);
// 这里是实现上文描述的 new 的第 5 步
// 5.如果函数没有返回对象类型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),
// 那么`new`表达式中的函数调用会自动返回这个新的对象。
var isObject = typeof result === 'object' && result !== null;
var isFunction = typeof result === 'function';
if(isObject || isFunction){
return result;
}
return this;
}
else{
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
return self.apply(thisArg, finalArgs);
}
};
return bound;
}
Когда интервьюер видит такой код реализации, это, по сути, полная оценка, а в душе монолог: Этот парень/девушка неплохи. Но, может быть, спроситьthis instanceof bound
некорректный вопрос.
упоминалось в комментариях вышеthis instanceof bound
Тоже не очень точноES6 new.target
Хорошее решение этой проблемы, давайте возьмемПример 4:
instanceof
Неточный,ES6 new.target
Очень хорошее решение этой проблемы
function Student(name){
if(this instanceof Student){
this.name = name;
console.log('name', name);
}
else{
throw new Error('必须通过new关键字来调用Student。');
}
}
var student = new Student('若');
var notAStudent = Student.call(student, '川'); // 不抛出错误,且执行了。
console.log(student, 'student', notAStudent, 'notAStudent');
function Student2(name){
if(typeof new.target !== 'undefined'){
this.name = name;
console.log('name', name);
}
else{
throw new Error('必须通过new关键字来调用Student2。');
}
}
var student2 = new Student2('若');
var notAStudent2 = Student2.call(student2, '川');
console.log(student2, 'student2', notAStudent2, 'notAStudent2'); // 抛出错误
Внимательные студенты могут обнаружить, что эта версия кода не реализована.bind
Послеbound
функциональныйname
MDN Function.nameа такжеlength
MDN Function.length. Интервьюер также может найти это и продолжить спрашивать, как этого добиться, или спросить, видели ли вы это.es5-shim
реализация исходного кодаL201-L335
. если не ограниченоES
Версия.其实可以用ES5
изObject.defineProperties
реализовать.
Object.defineProperties(bound, {
'length': {
value: self.length,
},
'name': {
value: 'bound ' + self.name,
}
});
es5-shim
реализация исходного кодаbind
Прикрепите исходный код напрямую (с удаленными комментариями и частичными изменениями и т.д.)
var $Array = Array;
var ArrayPrototype = $Array.prototype;
var $Object = Object;
var array_push = ArrayPrototype.push;
var array_slice = ArrayPrototype.slice;
var array_join = ArrayPrototype.join;
var array_concat = ArrayPrototype.concat;
var $Function = Function;
var FunctionPrototype = $Function.prototype;
var apply = FunctionPrototype.apply;
var max = Math.max;
// 简版 源码更复杂些。
var isCallable = function isCallable(value){
if(typeof value !== 'function'){
return false;
}
return true;
};
var Empty = function Empty() {};
// 源码是 defineProperties
// 源码是bind笔者改成bindFn便于测试
FunctionPrototype.bindFn = function bind(that) {
var target = this;
if (!isCallable(target)) {
throw new TypeError('Function.prototype.bind called on incompatible ' + target);
}
var args = array_slice.call(arguments, 1);
var bound;
var binder = function () {
if (this instanceof bound) {
var result = apply.call(
target,
this,
array_concat.call(args, array_slice.call(arguments))
);
if ($Object(result) === result) {
return result;
}
return this;
} else {
return apply.call(
target,
that,
array_concat.call(args, array_slice.call(arguments))
);
}
};
var boundLength = max(0, target.length - args.length);
var boundArgs = [];
for (var i = 0; i < boundLength; i++) {
array_push.call(boundArgs, '$' + i);
}
// 这里是Function构造方式生成形参length $1, $2, $3...
bound = $Function('binder', 'return function (' + array_join.call(boundArgs, ',') + '){ return binder.apply(this, arguments); }')(binder);
if (target.prototype) {
Empty.prototype = target.prototype;
bound.prototype = new Empty();
Empty.prototype = null;
}
return bound;
};
ты говоришьes5-shim
исходный кодbind
Реализация, этот код действительно эффективен и строгим. Интервьюер монологически, вы можете быть: вы люди, которых я хочу найти, платить за оплату, вы можетеHR
Говорите о следующем.
Наконец, чтобы подвести итог
1,bind
даFunction
в цепочке прототиповFunction.prototype
Атрибут , который является функцией, которая изменяетthis
Укажите, объедините параметры, переданные исходной функции, и возвращаемое значение будет новой функцией.
2,bind
Доступ к возвращенной функции можно получить черезnew
звоните, когда будет предоставленоthis
Аргумент игнорируется и указывает наnew
Получился совершенно новый объект. Реализовано внутреннее моделированиеnew
оператор.
3.es5-shim
Реализация имитации исходного кодаbind
время использоватьFunction
Достигнутоlength
.
На самом деле редко требуется использовать ваши собственные реализованные входные данные в среде сборки. Но интервьюер может проверить много знаний с помощью этого вопроса интервью. Напримерthis
Знание указателей, цепочек прототипов, замыканий, функций и т. д. можно значительно расширить.
Читатели могут указать, что не так или что можно улучшить. Кроме того, если вы считаете, что текст хороший, вы можете поставить лайк, что также является своеобразной поддержкой автора.
Примеры и тестовый код в статье размещены вgithub
серединапривязать фиктивную реализацию github.Реализация моделирования привязки Адрес для предварительного просмотра F12
Глядя на вывод консоли, объединитеsource
Вид на панели лучше.
// 最终版 删除注释 详细注释版请看上文
Function.prototype.bind = Function.prototype.bind || function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + ' must be a function');
}
var self = this;
var args = [].slice.call(arguments, 1);
var bound = function(){
var boundArgs = [].slice.call(arguments);
var finalArgs = args.concat(boundArgs);
if(this instanceof bound){
if(self.prototype){
function Empty(){}
Empty.prototype = self.prototype;
bound.prototype = new Empty();
}
var result = self.apply(this, finalArgs);
var isObject = typeof result === 'object' && result !== null;
var isFunction = typeof result === 'function';
if(isObject || isFunction){
return result;
}
return this;
}
else{
return self.apply(thisArg, finalArgs);
}
};
return bound;
}
Ссылаться на
Глубокое понимание перевода OShotokillES6
Упрощенная китайская версия — функции главы 3(Хотя я читаю бумажные книги, я рекомендую эту онлайн-книгу)
MDN Function.prototype.bind
Ю Ю: имитационная реализация глубокого связывания JavaScript
Хоу Се в «React State Management and Isomorphism»: от вопроса на собеседовании до «Возможно, я читал поддельный исходный код»
Избранные статьи автора
Изучите общую архитектуру исходного кода sentry и создайте собственный SDK для мониторинга исключений переднего плана.
Изучите общую архитектуру исходного кода lodash и создайте собственную библиотеку классов функционального программирования.
Изучите общую архитектуру исходного кода подчеркивания и создайте собственную библиотеку классов функционального программирования.
Изучите общую архитектуру исходного кода jQuery и создайте собственную библиотеку js.
Интервьюер спросил: наследование JS
Интервьюер спросил: этот пункт JS
Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?
Внешний интерфейс использует сканер puppeteer для создания PDF-файла «React.js Book» и его слияния.
о
Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. По дороге на фронт | Энтузиасты РРТ | Знаю очень мало, только хорошо учусь.
личный блог
segmentfault
Передняя колонка обзора, открылПередний планКолонка, добро пожаловать на внимание ~
Колонка самородков, добро пожаловать, обратите внимание~
Знайте переднюю колонку видения, открылПередний планКолонка, добро пожаловать на внимание ~
github blog, спроситьstar
^_^~
Публичный аккаунт WeChat Ruochuan Vision
Может быть более интересным общедоступный аккаунт WeChat, нажмите и удерживайте, чтобы отсканировать код, чтобы следовать. Вы также можете добавить WeChatruochuan12
, укажите источник и втяните вас в [Front-end Vision Exchange Group].