Когда вы извлекаете функцию из объекта, она теряет свой контекст, поэтому, когда вы вызываете ее и получаете доступ к this
, ее первоначальное значение теряется.
Чтобы исправить проблему, вам нужно использоватьFunction.prototype.bind()
для сохранения ссылки this
, указывающей на правильный объект.
Вы можете увидеть проблему и то, как bind
работает в этом примере:
const obj = {
prop: 'there',
print(prefix) {
console.log(`${ prefix }: Hello ${ this.prop }.`);
}
};
obj.print('obj.print');
// Context lost:
const extractedPrint = obj.print;
extractedPrint('extractedPrint');
// Context preserved to the original object:
const bindedPrint = obj.print.bind(obj);
bindedPrint('bindedPrint');
// Context replaced:
const alsoBindedPrint = obj.print.bind({ prop: 'bro' });
alsoBindedPrint('alsoBindedPrint');
Хотите знать, куда указывает this
, когда он «потерян»?Это указывает на window
:
const obj = {
prop: 'there',
print(prefix) {
console.log(`${ prefix }: Hello ${ this.prop }.`);
}
};
const extractedPrint = obj.print;
window.prop = 'window';
extractedPrint('extractedPrint');
В вашем случае вам необходимо убедиться, что когда push
вызывается forEach
, его контекст сохраняется, то есть значение this
должноссылаться на исходный массив:
links.forEach(array.push.bind(array));
В любом случае, это не будет работать должным образом, потому что NodeList.prototype.forEach()
вызывает свой обратный вызов с 3 аргументами: currentValue
, currentIndex
и listObj
и Array.prototype.push()
принимает несколько аргументов одновременно, поэтому вы можете сделать:
const array = [];
const links = document.querySelectorAll('a');
links.forEach(array.push.bind(array));
console.log(array.length);
<a>1</a>
<a>2</a>
<a>3</a>
Но для каждого Node
или вашего NodeList
вы будете вызывать push
с 3 аргументами вместо 1, что приведет к получению некоторых нежелательных элементовв списке.
Для преобразования NodeList
в Array
вы можете использовать Array.from()
вместо:
console.log(Array.from(document.querySelectorAll('a')).length);
<a>1</a>
<a>2</a>
<a>3</a>
Хотя есть и другие способы сделать это, например, нажать все элементы один за другим, определяя свой собственный обратный вызов:
const links = document.querySelectorAll('a');
const arr = [];
// Note we only define a single argument, so we ignore the other 2:
links.forEach((link) => {
arr.push(link);
});
console.log(arr);
<a>1</a>
<a>2</a>
<a>3</a>
Или то же самое с помощью цикла:
const links = document.querySelectorAll('a');
const arr = [];
for (const link of links) {
arr.push(link);
}
// Also valid:
// for (let i = 0; i < links.length; ++i) {
// arr.push(links[i]);
// }
console.log(arr);
// Note this won't work, as NodeList has some other iterable
// properties apart from the indexes:
const arr2 = [];
for(const i in links) {
arr2.push(links[i])
}
console.log(arr2);
<a>1</a>
<a>2</a>
<a>3</a>