寻找一个清楚的解释为什么这不起作用

时间:2018-03-31 02:11:40

标签: javascript arrays function typeerror

所以我正在学习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不是函数

对我而言,工作似乎合乎逻辑,但事实并非如此 为什么呢?

4 个答案:

答案 0 :(得分:1)

JavaScript是一种面向对象的语言,具有原型继承。

面向对象意味着您拥有包含成员(在JavaScript中称为属性)的对象,这些对象包含值。在JavaScript中,函数是“一等公民”,这意味着您可以将它们分配给变量,就像您将其他值一样。

当你写一个函数,比如'function x(y){return y +1; }',真正发生的事情是1)你声明一个名为“x”的变量,2)你正在创建一个函数“value”,然后将其赋值给该变量。如果您可以访问该变量(它在范围内),则可以调用“x(5)”之类的函数。这将计算为可以分配给另一个变量的新值,依此类推。

好的,现在我们遇到了问题。如果函数是,并且值占用空间(内存),那么当你需要一堆具有相同功能的对象时会发生什么?这就是原型继承的用武之地。当我们尝试访问对象的值时,通过成员访问运算符'。',如'myObj.someValue',或通过索引运算符'[]',如'myObj [“someValue “](两者在JavaScript中大部分都相同),会发生以下情况:

  1. 运行时检查当前作用域中是否存在“myObj”变量。如果没有?例外!
  2. 运行时查看变量引用的对象,并检查它是否具有键为“someValue”的属性。
  3. 如果对象具有该属性,则成员访问表达式('myObj.someValue')将计算该属性的值,并且我们已完成。
  4. 如果对象没有该属性,我们就开始做原型继承。在JavaScript中,所有这些意味着当我们尝试访问不存在的属性时,运行时会说“嘿,如果这个对象* prototype有一个带有该键的属性怎么办?”如果有,我们使用原型的属性值。如果没有,我们返回'undefined'。
  5. 请注意,因为原型只是对象,因此它们本身可以有原型,所以步骤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。在它之前。

&#13;
&#13;
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;
&#13;
&#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)

由于函数forEach2Array.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来获取当前数组。