所以我正在学习Javascript并在控制台中测试它。 谁能想出一个很好的解释为什么这样的东西不起作用?
var students = ['foo', 'bar', 'baz'];
function forEach2(arr1) {
for (i = 0; i < arr1.length; i++) {
console.log(arr1[i]);
}
}
students.forEach2(students)
我得到:未捕获的TypeError:students.forEach2不是函数
对我而言,工作似乎合乎逻辑,但事实并非如此 为什么呢?
答案 0 :(得分:1)
JavaScript是一种面向对象的语言,具有原型继承。
面向对象意味着您拥有包含成员(在JavaScript中称为属性)的对象,这些对象包含值。在JavaScript中,函数是“一等公民”,这意味着您可以将它们分配给变量,就像您将其他值一样。
当你写一个函数,比如'function x(y){return y +1; }',真正发生的事情是1)你声明一个名为“x”的变量,2)你正在创建一个函数“value”,然后将其赋值给该变量。如果您可以访问该变量(它在范围内),则可以调用“x(5)”之类的函数。这将计算为可以分配给另一个变量的新值,依此类推。
好的,现在我们遇到了问题。如果函数是值,并且值占用空间(内存),那么当你需要一堆具有相同功能的对象时会发生什么?这就是原型继承的用武之地。当我们尝试访问对象的值时,通过成员访问运算符'。',如'myObj.someValue',或通过索引运算符'[]',如'myObj [“someValue “](两者在JavaScript中大部分都相同),会发生以下情况:
请注意,因为原型只是对象,因此它们本身可以有原型,所以步骤4是递归的,直到我们用完原型来尝试成员访问。
好的,所以在这一点上你可能会想“什么是原型,它与我的问题有什么关系?”它只是一个对象。如果任何对象具有键为“prototype”的属性,则该属性的值为原型。具体来说,就是那个对象的原型。所以这就是你的问题所在。
对象上没有使用“forEach2”键的属性。为什么?因为你没有把它放在那里,你没有把它放在对象的原型上(或任何原型“上游”。
Array的'forEach'函数作为Array原型的属性存在:'Array.prototype.forEach = function(...){...}'。你的函数没有,因此你不能在数组上使用成员访问来获取该值(函数),这就是你的代码被borked的原因。
幸运的是,您当前的范围中存在变量'forEach2',您只需使用它而无需进行任何成员访问!你只需写'forEach2(学生);'就是这样。
但是如果你想在任何有阵列的地方访问该功能怎么办?您有两种选择:将它放在数组的每个实例上,或者将它放在Array的原型上。 'Array.prototype.forEach2 = forEach2;'但是,如果你这样做,你需要稍微改变你的功能。现在它期望数组作为它的第一个参数('arr1'),但是写入'students.forEach2(学生)'是多余的,因为当成员访问后立即调用一个函数时,该函数将提供一个特殊变量'this',它将具有您要访问其成员的对象的值。所以,在这种情况下,你会省略'arr1'参数,而只是使用特殊的'this'变量,这个变量在你的函数范围内是神奇的。
Array.prototype.forEach2 = function ()
{
for (var i = 0; i < this.length; i++)
{
console.log(this[i]);
}
}
我希望这能为你澄清一些事情,我希望它会引发更多问题。
PS:在原型中添加东西既强大又被认为是有害的,除非你知道自己在做什么,并且有充分的理由去做(比如写填充)...所以这样做是为了你自己的危险并负责任地使用。答案 1 :(得分:0)
forEach2不是学生的功能。 students是一个包含3个字符串值的数组。只使用没有学生的forEach2。在它之前。
var students = ['foo', 'bar', 'baz'];
function forEach2(arr1) {
for (i = 0; i < arr1.length; i++) {
console.log(`arr1[${i}]:`, arr1[i]);
}
}
console.log("students:", students);
console.log("students has .forEach2 function ?", typeof students.forEach2 == "function");
console.log("forEach2 is a function?", typeof forEach2 == "function");
console.log("forEach2(arr1)...");
forEach2(students);
console.log("students.forEach(student)...");
//forEach already has a native implementation
students.forEach((student)=> {
return console.log("student:", student);
});
&#13;
答案 2 :(得分:0)
要使您的示例正常工作,只需在定义forEach2
之后添加以下语句:
// Here you're redefining the built-in forEach function with your
// custom function
students.forEach = forEach2;
// Now this must work
students.forEach(students);
这就解释了为什么:
数组是Object的特定实现。对象是property:value
对的集合,例如
{'some_property': '1',
'some_function': function(),
'forEach': function() {<function logic>},
...
}
您的错误&#39; Uncaught TypeError: students.forEach2 is not a function
&#39;告诉我,在Object的属性之间没有属性forEach2
(它应该是一个函数)。因此,有两种方法可以纠正这种情况: - 向对象添加属性(方法函数),或者更改具有类似功能的现有属性(不一定) 。
[1]第一种方法假设您使用Array.prototype.forEach2 = function() {...}
添加新的属性函数。
[2]第二个 - 您可以通过为该属性分配新值来重新定义或更改对象中的任何属性:Object.forEach = forEach2
答案 3 :(得分:-1)
由于函数forEach2
在Array.prototype
内不可用或者不是该数组的直接属性,因此,您有两种方法可以使用该自定义forEach
:
使用装饰器将该功能添加到特定阵列
var students = ['foo', 'bar', 'baz'];
function forEach2() {
for (i = 0; i < this.length; i++) {
console.log(this[i]);
}
}
function decorate(arr) {
arr['forEach2'] = forEach2;
return arr;
}
decorate(students).forEach2()
将该功能添加到
Array.prototype
var students = ['foo', 'bar', 'baz'];
Array.prototype.forEach2 = function() {
for (i = 0; i < this.length; i++) {
console.log(this[i]);
}
}
students.forEach2();
两种选择都使用上下文this
来获取当前数组。