【问题标题】:Javascript Prototypes,objects,constructor??i am confusedJavascript原型,对象,构造函数??我很困惑
【发布时间】:2013-05-25 23:40:08
【问题描述】:

我已经经历了很多 Stack Overflow 问题的描述,但我发现它们非常令人困惑。我想要的是一个简单的解释,请不要引用链接。

我完全糊涂了,完全混淆了:

  • 原型,

  • 对象,

  • 构造函数

我做了很多研究,但发现它们非常复杂。

有什么简单的解释吗??

【问题讨论】:

  • 你用谷歌搜索过它们吗?阅读this
  • 是的!!我经历了很多,我熟悉了语法,但我没有完全理解这个概念,我的脑海里仍然很模糊
  • 您需要更具体地说明您不了解的具体内容。否则,除了重复其他地方所说的话之外,很难做任何事情。
  • 这对于 StackOverflow 格式来说问题太大了。你需要把你的问题分解成更小的问题。 javascript 中的 OOP 需要大约一本书来解释,而仅使用 Prototype 至少需要一整章。原型中是否存在一些没有意义的具体内容?
  • 我想是时候阅读一本好的 JS 书了。请参阅 stackoverflow 的 JS 页面 [stackoverflow.com/tags/javascript/info] 以获取参考

标签: javascript class oop methods constructor


【解决方案1】:

JavaScript 是 Web 应用程序中用于使网页动态化的脚本语言。该语言允许通过处理页面上显示的元素上的事件来处理用户操作....还用于根据条件修改网页上的内容...

方法(或)函数是 javascript 中用于指定逻辑的编程构造。您将把所需的逻辑放入一个方法中,然后在任何事件时从网页调用它...

javascript 中没有构造函数...

javascript中会有隐式和用户定义的对象...隐式对象包括Date、Array等...

这只是一个基本的摘要.. 您需要关注网站(或)书籍以了解更多关于 javascript 的信息

【讨论】:

    【解决方案2】:

    好的,旋风之旅:

    对象

    对象是具有属性的事物。属性具有名称和值。名称始终是字符串(尽管大多数时候我们可以不带引号将它们写成),值可以是 JavaScript 支持的任何内容:数字、字符串、布尔值、null、未定义或对对象的引用。

    所以:

    var a = {
        propertyName: "property value"
    };
    

    a是一个引用对象的变量,该对象有一个名为propertyName的属性,其值为字符串"property value"

    原型

    一个对象(比如,obj)可以有另一个对象(比如,p)“在”它后面,p 借给obj 的属性,除非obj 有自己的属性和给定的名称. pobjprototype 对象。举个例子最容易理解:

    // Let's create an object `p` with a couple of properties, `x` and `y`
    // This syntax is called an "object initializer" (aka "object literal")
    var p = {
        x: "x on p",
        y: "y on p"
    };
    
    // Now, we'll create a new object, `obj`, using `p` as its prototype
    var obj = Object.create(p);
    
    // And that means if we ask `obj` for a property called `x`, since it doesn't
    // have its **own** `x`, it asks `p` for it. (And the same with `y`)
    console.log(obj.x); // "x on p"
    console.log(obj.y); // "y on p"
    
    // But we can give `obj` its *own* `x` instead if we want
    obj.x = "x on obj";
    console.log(obj.x); // "x on obj"
    console.log(obj.y); // "y on p"
    
    // Doing that to `obj` had no effect on `p`
    console.log(p.x); // "x on p"
    console.log(p.y); // "y on p"
    

    原型的一个非常非常重要的方面是对象之间的连接是实时的。因此,如果obj 没有y 属性,则每次我们向obj 询问y 时,它都会询问p。因此,如果我们将p 的值更改为y,如果我们向obj 询问,该更改就会出现:

    var p = {
        x: "x on p",
        y: "y on p"
    };
    
    var obj = Object.create(p);
    
    console.log(obj.y); // "y on p"
    
    p.y = "updated y on p";
    
    console.log(obj.y); // "updated y on p"
    

    这种实时连接是至关重要的。再说一遍,这样想:我们询问 obj 获取属性y,而obj 说“我没有自己的,所以我会去问@987654349 @ 给它,然后给你。”

    注意:我用来创建 objObject.create 函数是 ECMAScript5 的新功能(几年前的规范更新)。我们将回到另一种方式来为对象提供更进一步的原型。

    对象的原型目前始终在创建对象时设置,并且无法更改(在创建obj 后,我无法在上面的q 中替换p)。上面我使用Object.create 来完成它(下面我们将讨论构造函数)。在 ECMAScript5 (ES5) 之前,没有标准的方法来获取对象的原型。 ES5 现在为我们提供了一种方法,称为Object.getPrototypeOf,但仍然没有提供改变它的方法。下一个版本 ES6 会更进一步。

    功能

    函数是执行操作的代码单元。函数也是对象,因此它们可以具有属性,尽管实际上在函数上使用属性相对较少(callapply 除外,我们无需在这里讨论)。

    你可以声明一个函数:

    function foo() {
    }
    

    ...或者您可以使用 表达式创建一个:

    // An anonymous -- unnamed -- function assigned to variable `foo`
    var foo = function() {
    };
    
    // A function named `f` assigned to variable `foo`
    var foo = function f() {
    };
    

    声明和表达方式不同。在执行同一范围内的任何分步代码之前,对函数声明进行评估。与所有其他表达式一样,函数表达式在分步代码中遇到时进行评估。 (人们有时称其为“提升”,因为这意味着实际上,即使函数声明位于作用域的底部,它也会像被提升——提升——到顶部一样发生。)

    函数可以有参数:

    // `a` and `b` are arguments
    function sum(a, b) {
        console.log(a + b);
    }
    

    它们可以有返回值:

    function sum(a, b) {
        return a + b;
    }
    console.log(sum(1, 2)); // "3"
    

    如果函数没有返回其他值,则调用该函数的结果是值undefined

    方法

    JavaScript 没有方法。它只有功能——但这就是它真正需要的。但是如果你有一个分配给对象属性的函数,并且你调用该函数作为从对象中检索属性的表达式的一部分,那么就会发生一些事情使得 JavaScript 似乎 有方法:@987654364 @ 关键字指的是函数调用中的那个对象。再一次,一个例子创造了奇迹:

    // A blank object
    var obj = {};
    
    // Lets put a function on it as a property
    obj.foo = function() {
        console.log("this is obj? " + this === obj);
    };
    
    // Let's call that function
    obj.foo(); // "this is obj? true"
    

    更多关于我的博客:

    构造函数

    构造函数与new 关键字一起使用,它们是为对象提供原型的方法之一。当您通过new 调用函数时,会创建一个新对象,并从函数的prototype 属性中分配一个原型:

    function Foo() {
    }
    Foo.prototype.x = "x on Foo.prototype";
    
    var obj = new Foo();
    console.log(obj.x); // "x on Foo.prototype"
    

    每个函数都会自动拥有一个prototype 属性,尽管我们当然不会将绝大多数函数用作构造函数。

    这里需要注意的重要一点:函数的prototype 属性只是一个无聊的旧属性。它不是任何对象的原型,除非/除非通过 new 运算符调用该函数。当您通过new 调用函数时,new 运算符使用函数的prototype 属性来设置新对象的原型,但仅此而已。

    值得一提的是,在 ES5 之前,像上面这样的构造函数是您可以使用给定原型创建对象的唯一方法。但是在 ES5 中,我们得到了Object.create,它为如何使用 JavaScript 开辟了更多的模式。 (通过使用临时函数总是可以创建自己的Object.create,事实上这正是某些人所做的。)有些人不喜欢使用new 关键字和prototype 属性,他们更喜欢使用“构建器”模式,您只需调用一个函数并取回一个对象。 JavaScript 非常灵活,您可以做到这一点。


    更多探索

    【讨论】:

    • 非常高质量和好的答案。我认为这里唯一缺少的是对 MDN 和规范等地方的更多引用。
    • @jayeshjain:谢谢!我很高兴这有帮助!
    【解决方案3】:

    你很幸运。有一个很简单的解释:

    第一步:创建一个对象

    说你要圈:

    var circle = {};
    

    第二步:赋予它一些属性

    可以画一个圆,所以让我们创建一个名为draw的属性:

    circle.draw = function () {
        // drawing logic
    };
    
    1. 属性只是属于对象的变量。变量本身不是属性。
    2. 属性和变量可以保存任何类型的数据。 JavaScript 中的函数就是数据。
    3. 当一个属性包含一个函数时,它被称为一个方法。

    因此我们有一个名为draw 的方法属于对象circle

    第三步:扩展对象

    现在我想要一个球,而球有点像一个圆圈。所以让我们扩展circle 来创建一个球:

    var ball = Object.create(circle);
    
    1. 在这里,我们使用对象circle 并使用它来创建一个名为ball 的新对象。
    2. 对象ball 现在具有circle 的所有属性。所以我们可以拨打ball.draw
    3. 对象circleball的原型。

    第四步:赋予它一些属性

    每个球都有一个radius,所以让我们给我们一个:

    ball.radius = 5;
    

    第五步:创建构造函数

    这里有问题。每次我想创建一个新球时,我都会扩展circle 并手动定义球的radius。相反,我想要一个函数来创建球并给它一个半径。这个函数称为构造函数:

    function createBall(radius) {
        var ball = Object.create(circle);
        ball.radius = radius;
        return ball;
    }
    
    var baseball = createBall(5);
    var basketball = createBall(10);
    
    baseball.draw();
    basketball.draw();
    

    这就是您需要了解的关于原型、对象和构造函数的所有内容。

    当然还有很多解释,但对于一个 StackOverflow 答案来说太多了。我写了一篇关于它的博客文章,我不打算在这里重写同样的东西。你应该阅读我的博客。值得:http://aaditmshah.github.io/why-prototypal-inheritance-matters


    编辑:当然,我会解释这段代码中发生了什么:http://cssdeck.com/labs/4ksohwya

    首先,向下滚动到最后:

    window.addEventListener(
        'load',
        init(null),
        false);
    

    当页面加载时它执行init:

    function init(images) {
    
        canvas= document.getElementById('s');
        ctx= canvas.getContext('2d');
        canvas.width= window.innerWidth;
        canvas.height=window.innerHeight;
    
        garden= new Garden();
        garden.initialize(canvas.width, canvas.height, 300);
    
        lerp(0,2000);
    
        time= new Date().getTime();
        interval = setInterval(_doit, 30);
    }
    

    init 函数创建Garden (garden = new Garden();) 的实例并执行gardeninitialize 方法。它还以 30 毫秒的间隔调用 _doit 函数。

    initialize : function(width, height, size)  {
      this.width= width;
      this.height= height;
      this.grass= [];
    
      for(var i=0; i<size; i++ ) {
        var g= new Grass();
        g.initialize(
            width,
            height,
            50,      // min grass height 
            height*2/3, // max grass height
            20,     // grass max initial random angle 
            40      // max random angle for animation 
            );
        this.grass.push(g);
      }
    
      this.stars= [];
      for( i=0; i<this.num_stars; i++ )  {
        this.stars.push( Math.floor( Math.random()*(width-10)+5  ) );
        this.stars.push( Math.floor( Math.random()*(height-10)+5 ) );
      }
    },
    

    gardeninitialize 方法然后创建Grass 的一些实例,调用它们的initialize 方法并将它们存储在一个数组中。

    function _doit()    {
    
      ctx.fillStyle= gradient;
      ctx.fillRect(0,0,canvas.width,canvas.height);
      var ntime= new Date().getTime();
      var elapsed= ntime-time;
      garden.paint( ctx, elapsed );
    
      // lerp.
      if ( elapsed>nextLerpTime ) {
        lerpindex= Math.floor((elapsed-nextLerpTime)/nextLerpTime);
        if ( (elapsed-nextLerpTime)%nextLerpTime<lerpTime ) {
          lerp( (elapsed-nextLerpTime)%nextLerpTime, lerpTime );
        }
      }
    
    }
    

    _doit函数调用gardenpaint函数,gardenpaint函数调用每个草的paint函数。


    所以这里有两个构造函数:

    1. Grass
    2. Garden

    这是两个原型:

    1. Grass.prototype
    2. Garden.prototype

    init 函数中,我们创建Garden 的单个实例(这是一个对象):

    var garden= new Garden();
    

    gardeninitialize 方法中,我们创建了Grass 的多个实例:

    var g= new Grass();
    

    就是这样。

    【讨论】:

    • 非常好,我喜欢您使用术语构造函数来表示创建对象的函数而不是 使用 new 关键字的函数。
    • 在 JavaScript 中,如果您不通过 new 调用函数,最好不要将函数调用为构造函数。 “建造者”(或不太常见的“创造者”)是一个更好的术语,因为规范赋予术语“构造者”特定的含义。
    • @T.J.Crowder 我根本不会给它任何特殊标签。为了解释,我只是使用了术语“构造函数”。就个人而言,我更喜欢使用原型继承的原型模式,而不是大多数人使用的原型继承的构造函数模式。这是我不喜欢的关于 JavaScript 的事情之一:构造函数模式。这是有问题的,并且隐藏了原型模式。如果你还没有,你应该阅读我的博客文章。我试图尽可能地解释原型继承。显然,来自 Adob​​e 的 Eric Elliot 认为这是一篇很棒的文章。 =)
    • @AaditMShah:我理解你在做什么,我只是说最好不要通过称它为“构造函数”来混淆问题,因为语言(通过其规范)给出了这个术语特定(和不同的)含义。我对这种方法完全没有问题。我喜欢 JS 的一件事是它非常灵活,您可以采用更传统的原型方法来创建对象。该语言可以以多种方式使用,我喜欢它。
    • @AaditMShah:hii,我喜欢你的回答并浏览了你的博客。现在我可以说我对原型、构造函数和对象有点了解。下面的链接是原型方法的一个例子你提到了。我认为这是编写任何代码的正确标准。你能告诉我那个代码中的构造函数、原型和对象是什么,我保证我不会再麻烦你了。 cssdeck.com/labs/4ksohwya(需要注意的事情——函数绘制、初始化以及它们的调用方式——你能简单介绍一下)
    【解决方案4】:

    我想我来晚了,但这是我的 2 美分:

    原型:

    String.prototype.x = "yahoo"
    

    现在 String 的每个实例都将具有属性 x

    所以无论是(new String()).x 还是"".x 的值都等于yahoo

    因此它就像扩展一个预定义的类。


    对象

    JavaScript 中的所有东西,除了其他基本类型,都是一个对象。

    对象是名称-值对的集合,不多也不少。

    {"a": 0, "b" : 1}
    

    即使是 Array 也是 JS 中的对象,具有一些额外的属性和方法。


    函数和方法

    这是一个函数:

    function a() { };
    

    现在让我们给它一个 Array 的方法的状态:

    Array.prototype.a = a;
    

    构造函数:

    new String()
    

    String.constructor获取String的实现

    类似地,您在任何函数中编写的代码都会进入这个函数。

    【讨论】:

    • "从String.constructor获取String的实现"不,它没有,它从StringString.prototype获取。此外,我强烈建议不要使用扩展原生类型作为原型如何工作的示例。
    • @loxxy:不要小看你自己,你的解释肯定超过2美分,漂亮,简短,简单!!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多