JavaScript中声明函数时使用的原型是什么?

4

我有一个关于JavaScript函数的问题。考虑以下两个函数定义:

    // run time definition 
    var foo_1 = function () {
        console.log("I ma foo 1"); 
    }
    // parse time definition 
    function foo_2 ()  {
        console.log("I ma foo 2"); 
    }

在JavaScript中,每个东西都是一个对象,那么在上面的代码中,是否创建了名为foo_1foo_2的两个新对象?如果是,它们的原型对象是什么,这些对象何时创建?

我也很难理解var obj={}var foo=function(){}之间的真正区别。两者都是对象,但第一个具有 "type" Object,而第二个具有类型function。这是正确的吗?

根据我的书《JavaScript高级程序设计》,每个新的字面量对象都链接到默认为空对象的Object.prototype。为了验证这种行为,我尝试运行以下脚本:

Object.prototype
//Object {}
Object.prototype = { name: "This is an experiment"}; 
//Object {name: "This is an experiment"}
function test() { conosle.log("Test"); }
test.prototype;
//Object {}

为什么test.prototype的结果是一个空对象?

最后,我想知道这些函数定义是否等同于以下代码:

   this.foo =  function() {
         console.log("I ma foo");
    }

如果是这样,那么“this”指的是什么?

请参考以下链接:https://dev59.com/a3RB5IYBdhLWcg3wn4UL?rq=1。 - Sarath
3个回答

4
为什么test.prototype的结果是一个空对象?
所有函数都有一个原型属性,最初引用一个空对象。
定义:(resig)
原型在JavaScript中被广泛用作一种方便的手段,用于定义将自动应用于对象实例的属性和功能。
您设置了Object的原型值,而不是test函数的原型值。
在JS中,每个对象都继承Object.prototype。
如果您将函数附加到Object.prototype上 - 如您所需:
Object.prototype.name = function (){alert(1);}; 
function test() { console.log("Test"); }
var a = new test();
a.name();

这将会触发警报1

编辑

关于你的评论:

我们真正想要实现的是一个原型链,使得Player可以成为Person,Person可以成为Mammal,Mammal可以成为Animal等等,一直到Object。创建这样一个原型链的最佳技术是使用一个对象实例作为另一个对象的原型:

SubClass.prototype = new SuperClass();

考虑以下代码:

function Person(){}

   Person.prototype.dance = function(){};

function Player(){}

   Player.prototype = new Person();     <------

var player = new Player();

assert(player instanceof Player,"player receives functionality from the Player prototype");
assert(player instanceof Person, "... and the Person prototype");
assert(player instanceof Object, "... and the Object prototype");
assert(typeof player.dance == "function", "... and can play!")

注意下面的代码行可以使你从一个人对象创建一个播放器(你会发现,播放器实际上是一个人的实例!):

enter image description here

http://jsbin.com/oNixIMo/8/edit


你设置了Object的原型值,而不是测试函数的。但是当函数被创建时,这应该从Object继承下来,不是吗?Object.prototype.name = ...Object.prototype = ...之间有什么区别? - Giuseppe Pes
test.prototype 也是一个对象,因此它将继承在 Object.prototype 中定义的属性。 - ZER0
@RoyiNamir就像我所说的那样,这是不正确的:http://jsbin.com/oNixIMo/5/edit test的原型(prototype)本身就是一个对象,所以它当然也会继承自Object.prototype;除非你按照我的答案中所解释的那样打破了链条,并将test.prototype设置为一个完全没有原型链的新对象。 - ZER0
@ZER0 我明白你的意思。是的,你是对的。我已经移除了那一部分。因为所有对象都会继承Object.prototype,所以它将使用它。感谢指出这一点。 - Royi Namir

1

我认为你对函数和对象原型有些混淆,我希望能为你澄清一下。

首先:

function foo_2 ()  {
    console.log("I ma foo 2"); 
}

这被称为“函数声明”,其中:
var foo_1 = function () {
    console.log("I ma foo 1"); 
}

被称为“函数表达式”。您可以在此处查看详细信息:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions_and_function_scope#Function_constructor_vs._function_declaration_vs._function_expression,还有 Function 构造函数示例。

由于在JavaScript中每个东西都是对象,因此在上面的代码中创建了名为foo_1和foo_2的两个新对象吗?

是的,它们是对象,如果修改 prototype 链,则会影响它们:

Object.prototype.testName = "this is an experiment";

function test() {}

console.log(test.testName) // "this is an experiment"

但是test.prototype也是一个对象,所以如果你执行以下操作:

console.log(test.prototype.testName) // "this is an experiment"

你无法正确获取属性的原因可能是因为继承了它,而你使用的控制台(node、浏览器)仅显示对象自身的属性(如Object.keys),而不会遍历整个原型链。
这就是为什么你不应该更改Object.prototype的主要原因:你将影响大部分内容,而且你无法预测后果。
我说“大部分”是因为你可以创建实际上不继承Object原型的对象。你可以使用Object.create——或者在支持的非标准方式中使用_proto_
var foo = {};
console.log(foo.testName) // "this is an experiment"

var bar = Object.create(null);
console.log(bar.testName) // undefined

最后,我想知道这些函数定义是否等同于以下代码:
this.foo =  function() {
  console.log("I ma foo");
 }

当你执行这段代码时,取决于this是什么。如果在全局范围内执行代码,则它们大多是等效的,因此this将是全局对象(在浏览器中为window)。但是,存在差异。使用此语法,您将能够删除foo属性,而在声明时无法这样做。所以:

this.foo = function() {};
var bar = function() {};

delete foo // can be deleted;
delete bar // shouldn't be deleted;

请参见: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/delete


1

是的,var fn = function(){};属于函数类型

为什么test.prototype是空的:因为它是Function.prototype,而不是Object.prototype

这是什么:在末尾发布的链接中回答了

您可以使用函数作为构造函数创建对象:

var Person = function(name){
  this.name = name;
};
Person.prototype.walk=function(){
  this.step().step().step();
};

在这个例子中,Person 被称为构造函数,因为它是一个对象,你可以给它添加属性,比如:Person.static="something",这对于与 Person 相关的静态成员非常有用,比如:
 Person.HOMETOWN=22;
 var ben = new Person("Ben");
 ben.set(Person.HOMETOWN,"NY");
 ben.get(Person.HOMETOWN);//generic get function what do you thing it'll get for ben?
 ben.get(22);//maybe gets the same thing but difficult to guess

当你使用Person创建一个实例时,必须使用new关键字:
var bob = new Person("Bob");console.log(bob.name);//=Bob
var ben = new Person("Ben");console.log(bob.name);//=Ben

属性/成员name是实例特定的,对于Bob和Ben来说是不同的。
成员walk可以在所有实例上使用(Bob和Ben都是Person类的实例)。
bob.walk();ben.walk();

因为bob(后来是ben)上找不到walk(),JavaScript会在Person.prototype上查找它。虽然bob和ben都共享一个walk函数,但函数的表现方式会有所不同,因为在walk函数中使用了this。
如果ben等待红灯,而你调用了walk,并且bob处于绿灯,则显然对ben和bob会发生不同的事情,即使对于bob和ben,walk的作用完全相同,但this将分别指向当前对象(bob对于bob和ben对于ben)。
当我执行像ben.walk=22这样的操作时,成员的遮蔽就会发生,即使bob和ben共享walk,将22赋值给ben.walk也不会影响bob.walk。这是因为该语句将在ben上创建一个名为walk的成员,并将其赋值为22。当请求bob.walk时,您将获得Person.prototype.walk函数,因为在bob上找不到walk。但是,请求ben.walk将获得值22,因为在ben上找到了walk,JavaScript不会查找Person.prototype。
当实例中找不到成员时,JavaScript引擎会查找构造函数的原型。那么ben.hasOwnProperty从哪里来?实例ben没有它,Person.prototype也没有它。答案是存在原型链,Person的构造函数是Function,因此当ben或Person.prototype上找不到hasOwnProperty时,它将在Person的构造函数的原型(即Function)上查找。如果在那里找不到hasOwnProperty(但它可以),则会在Function的构造函数的原型(即Object)中查找。

除非你使用Object.create(null,{}),否则所有内容都应该继承自Object.prototype,我认为这是个坏主意。您可以在Object.prototype上添加东西(这也不是一个好主意),JS中的任何对象都会“继承”它。

Object.prototype.yipee=function(){
  console.log("yipeee");
};

22..yipee();//logs yipeee
"hello".yipee();//logs yipee

现在你已经掌握了基本知识,可以查看以下内容:

https://dev59.com/J2Qo5IYBdhLWcg3wbe5K#16063711(从末尾的“this变量”开始)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接