【问题标题】:JavaScript functions and the new operatorJavaScript 函数和 new 运算符
【发布时间】:2017-06-24 06:00:19
【问题描述】:

向函数对象添加属性与向对象原型添加属性有什么区别。请参阅下面的代码。

var foo = function() { this.name = alert("test") };

foo.newProp = function() { ... };
var value = new foo();

// vs

foo.prototype.newProp = function() { ... }
var value = new foo();

我的困惑是当为每个代码运行此代码时值会发生什么,以及“this”如何受到影响。

【问题讨论】:

  • foofoo.prototype不同的 对象。一个是创建实例的[[prototype]],另一个不是。这就是区别。 (提示:在每种情况下都尝试访问value.newProp。为避免被污染的结果,请为两个测试创建一个 / 不同名称的函数。)

标签: javascript prototype


【解决方案1】:

首先,我们知道函数只不过是对象,因为我们可以为其附加属性。这就是 case1 中发生的情况。 我们只是简单地将属性附加到 function Foo

案例 1

var Foo = function(){
    this.name = "myname";
}

Foo.newProp = function(){}; 

附加到函数的属性可以像访问对象的属性一样简单地访问。

案例 2

在这种情况下,相同的属性位于该函数的prototype。当使用此function Foo 创建任何对象时,默认情况下将访问此函数。

当使用该对象调用函数时,上下文this 将知道调用该函数的对象并且this 指向调用它的对象。

Foo.prototype.newProp = function(){ console.log("function on prototype");}

var value = new Foo();

console.log(value);

同样在案例 2 中,由于我们正在向函数的原型添加属性,它们不是位于使用该函数创建的对象上,而是位于该对象的原型上。检查如下所示的屏幕截图:

这意味着访问该属性的每个对象都将指向一个公共内存位置,因此它也具有内存效率。

您始终可以继续修改该属性,方法是直接在对象上将其定义为:

value.newProp = function(){ console.log("modified property") };

现在,如果你调用这个函数,它会直接在对象上找到它,并且不会沿着原型链从公共内存位置访问它,即function Foo的原型

【讨论】:

  • 我刚刚将您的示例输入到控制台中,并且添加到原始构造函数中的Foo.newProp() 出现在value.__proto__.constructor.newProp 我的问题是,newProp() 不应该在value.__proto__.newProp()既然你说它应该在 Foo() 的构造函数上
  • 是的,这是因为 value.constructor 返回了对创建实例对象的 Object 构造函数的引用,在本例中为 function Foo。由于我们已经为该函数附加了一个属性,因此它会显示在控制台中。
【解决方案2】:

简介

由于函数是对象,所以情况 1 只不过是向对象添加属性。案例 2 更有趣。考虑以下代码:

function f (name) {
  this.name = name;
}

f.prototype.sayHello = function () {
  console.log("Hello I'm " + this.name + " !");
};

var x = new f("X");
var y = new f("Y");
<button type="button" onclick="x.sayHello()">x.sayHello()</button>
<button type="button" onclick="y.sayHello()">y.sayHello()</button>

请注意,此代码中没有类似 x.sayHello = function () {...} 的内容。三思而后行,问问自己:

由于从未明确定义x.sayHello,防止x.sayHello() 使程序崩溃的底层机制是什么?

这就是问题。

揭开魔法的面纱

幸运的是,没有什么神奇的,只有 JavaScript 内部处理。事实上,当您使用 new 创建对象时,或调用函数时,幕后会发生一些事情,其中​​涉及到 prototype 属性。

当您编写 new f() 时,JavaScript 引擎:

  1. 创建一个新对象。
  2. this 设置为新对象。
  3. 执行f
  4. 将名为__proto__ 的属性添加到新对象。
  5. __proto__ 绑定到f.prototype
  6. 返回新对象。

当您编写 x.sayHello() 时,JavaScript 引擎:

  1. 查看x
  2. 如果找到 sayHello,则转到第 6 步。
  3. 否则查看x.__proto__
  4. 如果找到 sayHello,则转到第 6 步。
  5. 引发异常并退出。
  6. this 设置为x
  7. 执行sayHello

要强调的要点:

  • 当您使用 new 关键字时,JavaScript 会创建一个原型链,即引擎执行以下任务:x.__proto__ = f.prototype
  • 当您尝试调用函数时,JavaScript 会通过此原型链执行查找。例如,当您编写x.sayHello() 时,如果JavaScript 无法将sayHello 查找到x,它会查找x.__proto__,即f.prototype
  • this 的值取决于上下文。例如,当你写x.sayHello()时,thisx,当你写y.sayHello()时,thisy,当你写new f()时,this是一个新对象。 More about this

为了忍者

自定义实施:

function instanciate (f, args) {
  var object = {};
  f.apply(object, args);
  object.__proto__ = f.prototype;
  return object;
}

function execute (object, fName, args) {
  var f = lookup(object, fName);
  if (typeof f !== "function") {
    throw "not a function";
  } else {
    f.apply(object, args);
  }
}

function lookup (object, key) {
  do {
    if (object.hasOwnProperty(key)) {
      return object[key];
    } else {
      object = object.__proto__;
    }
  }
  while (object !== null);
}

function f (name) {
  this.name = name;
}

f.prototype.sayHello = function () {
  console.log("Hello I'm " + this.name + " !");
};

var x = instanciate(f, ["X"]);
var y = instanciate(f, ["Y"]);
<button 
  type="button"
  onclick="execute(x, &quot;sayHello&quot;)"
>execute(x, "sayHello")</button>
<button
  type="button"
  onclick="execute(y, &quot;sayHello&quot;)"
>execute(y, "sayHello")</button>

旁注

全局对象并不特殊:

f.prototype.__proto__ = window.__proto__;
window.__proto__ = f.prototype;
name = "John Doe";
sayHello(); // "Hello I'm John Doe !"

相关链接

【讨论】:

    【解决方案3】:

    foo.newProp = function() { ... };?

    这将在函数(类)级别定义函数。

    你可以不使用对象直接访问它。

    foo.newProp() // call it.
    

    foo.prototype.newProp = function() { ... };?

    它在实例级别定义newProp (object or class instance)。

    您需要使用new 定义对象,然后只有您可以访问它。

    var value = new foo();
    value.newProp() // call it
    

    通过使用原型定义函数,您可以将它提供给所有已经 + 新定义的给定函数的对象

    【讨论】:

      猜你喜欢
      • 2011-06-11
      • 1970-01-01
      • 2014-01-22
      • 1970-01-01
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 2013-06-05
      • 1970-01-01
      相关资源
      最近更新 更多