【问题标题】:javascript: what's the difference between a function and a classjavascript:函数和类有什么区别
【发布时间】:2012-08-11 19:08:47
【问题描述】:

我想知道函数和类之间有什么区别。 都使用关键字function,这两者有明显区别吗?

【问题讨论】:

    标签: javascript


    【解决方案1】:

    构造函数functionclass之间的区别

    javascript 中的class 关键字非常类似于构造函数,方式如下:

    • 它们都在使用 javascript 的原型继承系统
    • 它们都用于创建具有以下语法的对象:new myObj(arg1, arg2)

    构造函数和类非常相似,通常可以根据偏好互换使用。但是,javascript 的类私有字段是构造函数无法实现的功能(没有范围黑客)

    示例:

    class PersonClass {
      constructor(name) {
        this.name = name;
      }
      
      speak () { console.log('hi'); }
    }
    
    console.log(typeof PersonClass); 
    // logs function, a class is a constructor function under the hood.
    
    console.log(PersonClass.prototype.speak);
    // The class's methods are placed on the prototype of the PersonClass constructor function
    
    const me = new PersonClass('Willem');
    
    console.log(me.name); 
    // logs Willem, properties assinged in the constructor are placed on the newly created object
    
    
    // The constructor function equivalent would be the following:
    function PersonFunction (name) {
      this.name = name;
    }
    
    PersonFunction.prototype.speak = function () { console.log('hi'); }

    【讨论】:

    • 这正是我问自己的问题。谢谢!
    • 现在 private fieldsprivate methods 等正在取得进展,class 不再像您最初发布答案时那样只是语法糖。
    • 感谢 T. J. Crowder 指出这一点,我已经更新了答案。
    【解决方案2】:

    talk 强调了函数和类之间的一个关键区别,这表明函数是可以携带数据的行为,而相反,类是可以携带行为的数据。

    【讨论】:

      【解决方案3】:

      一个类声明的作用域是它的包含块。这是一个错误 在同一个块中声明一个类名两次。类定义是 没有吊起来。

      函数

      函数声明是严格模式下的块作用域。

      下表总结了类和函数的区别

      【讨论】:

        【解决方案4】:

        JavaScript 是 ECMAScript 标准最流行的实现。 Javascript 的核心功能基于 ECMAScript 标准,但 Javascript 还具有其他不在 ECMA 规范/标准中的附加功能。每个浏览器都有一个 JavaScript 解释器。


        Overview

        « ECMAScript 最初被设计为一种 Web 脚本语言,提供一种机制来活跃浏览器中的网页,并作为基于 Web 的客户端-服务器架构的一部分执行服务器计算.脚本语言是一种用于操作、定制和自动化现有系统设施的编程语言。

        ECMAScript 是一种面向对象的编程语言,用于在主机环境中执行计算和操作计算对象。 Web 浏览器为客户端计算提供了 ECMAScript 宿主环境,包括例如表示窗口、菜单、弹出窗口、对话框、文本区域、锚点、框架、历史记录、cookie 和输入/输出的对象。 em>

        ECMAScript 是基于对象的:基本语言和宿主工具由对象提供,ECMAScript 程序是通信对象的集群

        对象 «

        • 每个构造函数都是一个具有名为 “prototype” 的属性的函数,用于实现基于原型的继承和共享属性。

        • 构造函数创建的每个对象都有一个对其 constructor’s “prototype” 属性值的隐式引用(称为对象的原型)。此外,原型可能具有对其原型的非空隐式引用,依此类推;这称为prototype chain


        功能

        JavaScript 将函数视为一等对象,因此作为对象,您可以将属性分配给函数。

        提升是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作。 Function Hoisting, declarations & expressions

        FunctionDeclaration : function BindingIdentifier (FormalParameters) { FunctionBody } FunctionExpression : function BindingIdentifier (FormalParameters) { FunctionBody }

        ES5 功能:

        function Shape(id) { // Function Declaration
            this.id = id;
        };
        // prototype was created automatically when we declared the function
        Shape.hasOwnProperty('prototype'); // true
        
        // Adding a prototyped method to a function.
        Shape.prototype.getID = function () {
            return this.id;
        };
        
        var expFn = Shape; // Function Expression
        console.dir( expFn () ); // Function Executes and return default return type - 'undefined'
        

        如果函数没有指定返回值,则返回undefined 如果函数以new调用并且返回值不是对象,然后返回 this(新对象)。

        注意:每个函数都会自动创建一个原型属性,以允许该函数被用作构造函数。

        • constructor « 创建和初始化对象的函数对象。
        • prototype « 为其他对象提供共享属性的对象。
        • __proto__ « proto 属性指向其超级对象的原型。如果你打开它,你会看到 proto 指向它的超级对象变量和函数。

        要访问上述 crated 函数的原型方法,我们需要使用 new 关键字和 constructor function 创建对象。如果您使用new 关键字创建Shape - Object,那么它有一个internal (or) private link 到函数的原型Shape

        ES5 构造函数类:使用 Function.prototype.bind 创建的函数对象

        Shape.prototype.setID = function ( id ) {
            this.id = id;
        };
        
        var funObj = new Shape( );
        funObj.hasOwnProperty('prototype'); // false
        funObj.setID( 10 )
        console.dir( funObj );
        
        console.log( funObj.getID() );
        /*
        expFun                            funObj
            name: "Shape"                   id: 10
            prototype:Object
                constructor: function Shape(id)
                getID: function()
                setID: function( id )
            __proto__: function ()          __proto__: Object
                                                constructor: function Shape(id)
                                                getID: function()
                                                setID: function( id )
            <function scope>
        */
        

        ES6 introduced Arrow function: 箭头函数表达式的语法比函数表达式短,并且不绑定自己的 this、arguments、super 或 new.target。这些函数表达式最适合非方法函数,它们不能用作构造函数。 ArrowFunction 语法产生没有原型属性。

        ArrowFunction : ArrowParameters => ConciseBody

          a => (a < 10) ? 'valid' : 'invalid'
        
          const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
            console.log( fn(2) ); // Even
            console.log( fn(3) ); // Odd
        

        在基于类的面向对象语言中,一般情况下,状态由实例承载,方法由类承载,继承只是结构和行为。在ECMAScript中,状态和方法都是由对象携带的,结构、行为、状态都是继承的。

        Babel 是一个 JavaScript 编译器。使用它将ES6 转换为ES5 格式BABEL JS(或)ES6Console

        ES6 类ES2015 classes 是基于原型的 OO 模式的简单糖。拥有一个方便的声明形式使类模式更易于使用,并鼓励互操作性。类支持基于原型的继承、超级调用、实例和静态方法以及构造函数。

        class Shape {
          constructor(id) {
            this.id = id
          }
        
          get uniqueID() {
            return this.id;
          }
          set uniqueID(changeVal) {
            this.id = changeVal;
          }
        }
        Shape.parent_S_V = 777;
        
        // Class Inheritance
        class Rectangle extends Shape {
        
          constructor(id, width, height) {
            super(id)
            this.width = width
            this.height = height
          }
          // Duplicate constructor in the same class are not allowed.
          /*constructor (width, height) { this._width  = width; this._height = height; }*/
        
          get area() {
            console.log('Area : ', this.width * this.height);
            return this.width * this.height
          }
          get globalValue() {
            console.log('GET ID : ', Rectangle._staticVar);
            return Rectangle._staticVar;
          }
          set globalValue(value) {
            Rectangle._staticVar = value;
            console.log('SET ID : ', Rectangle._staticVar);
          }
        
          static println() {
            console.log('Static Method');
          }
        
          // this.constructor.parent_S_V - Static property can be accessed by it's instances
          setStaticVar(staticVal) { // https://sckoverflow.com/a/42853205/5081877
            Rectangle.parent_S_V = staticVal;
            console.log('SET Instance Method Parent Class Static Value : ', Rectangle.parent_S_V);
          }
        
          getStaticVar() {
            console.log('GET Instance Method Parent Class Static Value : ', Rectangle.parent_S_V);
            return Rectangle.parent_S_V;
          }
        }
        Rectangle._staticVar = 77777;
        
        var objTest = new Rectangle('Yash_777', 8, 7);
        console.dir( objTest );
        

        ES5 函数类:使用Object.defineProperty ( O, P, Attributes )

        Object.defineProperty() 方法直接在对象上定义新属性,或修改对象上现有属性,并返回该对象。

        可用作构造函数的函数实例具有prototype property

        此属性具有属性 { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }。

            'use strict';
        var Shape = function ( superClass ) {
            var currentClass = Shape;
            _inherits(currentClass, superClass); // Prototype Chain - Extends
        
            function Shape(id) { superClass.call(this); // Linking with SuperClass Constructor.
                // Instance Variables list.
                this.id = id;   return this;
            }
            var staticVariablesJOSN = { "parent_S_V" : 777 };
            staticVariable( currentClass, staticVariablesJOSN );
        
            // Setters, Getters, instanceMethods. [{}, {}];
            var instanceFunctions = [
                {
                    key: 'uniqueID',
                    get: function get() { return this.id; },
                    set: function set(changeVal) { this.id = changeVal; }
                }
            ];
            instanceMethods( currentClass, instanceFunctions );
        
            return currentClass;
        }(Object);
        
        var Rectangle = function ( superClass ) {
            var currentClass = Rectangle;
        
            _inherits(currentClass, superClass); // Prototype Chain - Extends
        
            function Rectangle(id, width, height) { superClass.call(this, id); // Linking with SuperClass Constructor.
        
                this.width = width;
                this.height = height;   return this;
            }
        
            var staticVariablesJOSN = { "_staticVar" : 77777 };
            staticVariable( currentClass, staticVariablesJOSN );
        
            var staticFunctions = [
                {
                    key: 'println',
                    value: function println() { console.log('Static Method'); }
                }
            ];
            staticMethods(currentClass, staticFunctions);
        
            var instanceFunctions = [
                {
                    key: 'setStaticVar',
                    value: function setStaticVar(staticVal) {
                        currentClass.parent_S_V = staticVal;
                        console.log('SET Instance Method Parent Class Static Value : ', currentClass.parent_S_V);
                    }
                }, {
                    key: 'getStaticVar',
                    value: function getStaticVar() {
                        console.log('GET Instance Method Parent Class Static Value : ', currentClass.parent_S_V);
                        return currentClass.parent_S_V;
                    }
                }, {
                    key: 'area',
                    get: function get() {
                        console.log('Area : ', this.width * this.height);
                        return this.width * this.height;
                        }
                }, {
                    key: 'globalValue',
                    get: function get() {
                        console.log('GET ID : ', currentClass._staticVar);
                        return currentClass._staticVar;
                    },
                    set: function set(value) {
                        currentClass._staticVar = value;
                        console.log('SET ID : ', currentClass._staticVar);
                    }
                }
            ];
            instanceMethods( currentClass, instanceFunctions );
        
            return currentClass;
        }(Shape);
        
        // ===== ES5 Class Conversion Supported Functions =====
        function defineProperties(target, props) {
            console.log(target, ' : ', props);
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);
            }
        }
        function staticMethods( currentClass, staticProps ) {
            defineProperties(currentClass, staticProps);
        };
        function instanceMethods( currentClass, protoProps ) {
            defineProperties(currentClass.prototype, protoProps);
        };
        function staticVariable( currentClass, staticVariales ) {
            // Get Key Set and get its corresponding value.
            // currentClass.key = value;
            for( var prop in staticVariales ) {
                console.log('Keys : Values');
                if( staticVariales.hasOwnProperty( prop ) ) {
                    console.log(prop, ' : ', staticVariales[ prop ] );
                    currentClass[ prop ] = staticVariales[ prop ];
                }
            }
        };
        function _inherits(subClass, superClass) {
            console.log( subClass, ' : extends : ', superClass );
            if (typeof superClass !== "function" && superClass !== null) {
                throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
            }
            subClass.prototype = Object.create(superClass && superClass.prototype, 
                    { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
            if (superClass)
                Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
        }
        
        var objTest = new Rectangle('Yash_777', 8, 7);
        console.dir(objTest);
        

        下面的代码sn-p是为了测试一下每个实例都有自己的实例成员副本和普通静态成员。

        var obj1 = new Rectangle('R_1', 50, 20);
        Rectangle.println(); // Static Method
        console.log( obj1 );    // Rectangle {id: "R_1", width: 50, height: 20}
        obj1.area;              // Area :  1000
        obj1.globalValue;       // GET ID :  77777
        obj1.globalValue = 88;  // SET ID :  88
        obj1.globalValue;       // GET ID :  88  
        
        var obj2 = new Rectangle('R_2', 5, 70);
        console.log( obj2 );    // Rectangle {id: "R_2", width: 5, height: 70}
        obj2.area;              // Area :  350    
        obj2.globalValue;       // GET ID :  88
        obj2.globalValue = 999; // SET ID :  999
        obj2.globalValue;       // GET ID :  999
        
        console.log('Static Variable Actions.');
        obj1.globalValue;        // GET ID :  999
        
        console.log('Parent Class Static variables');
        obj1.getStaticVar();    // GET Instance Method Parent Class Static Value :  777
        obj1.setStaticVar(7);   // SET Instance Method Parent Class Static Value :  7
        obj1.getStaticVar();    // GET Instance Method Parent Class Static Value :  7

        函数和类的主要区别是:

        • 函数声明将 Hoisted 置于上下文的顶部,其中不提升类声明和函数表达式。
        • 函数声明,表达式可以是 Overridden,因为它们就像一个变量 - var 如果有多个声明可用,那么它会覆盖其父范围。由于类没有被覆盖,它们就像let | constlet 不允许在其作用域内使用同名的多个声明。
        • 函数/类只允许其对象范围使用单个构造函数。
        • Computed method names 允许使用 ES6 类具有 class 关键字,但 function 关键字不允许

          function myFoo() {
           this.['my'+'Method'] = function () { console.log('Computed Function Method'); };
          }
          class Foo {
              ['my'+'Method']() { console.log('Computed Method'); }
          }
          

        【讨论】:

        【解决方案5】:

        技术上没有类,它们都只是函数。任何函数都可以使用关键字new 作为构造函数调用,并且该函数的原型属性用于对象继承方法。

        “类”只是在概念上用来描述上述做法。

        所以当有人对你说“创建一个颜色类”或其他什么时,你会这样做:

        function Color(r, g, b) {
            this.r = r;
            this.g = g;
            this.b = b;
        }
        
        Color.prototype.method1 = function() {
        
        };
        
        Color.prototype.method2 = function() {
        
        };
        

        当你分解它时,只有一个函数和一些对该函数名为prototype 的属性的赋值,所有 通用的 javascript 语法,没什么特别的。

        当你说var black = new Color(0,0,0) 时,这一切都变得有点神奇。然后您将获得一个具有属性.r.g.b 的对象。那个物体 还将有一个隐藏的 [[prototype]] 链接到Color.prototype。这意味着即使.method1() 不存在于black 对象中,您也可以说black.method1()

        【讨论】:

        • 如果是这样,为什么 React 使用关键字 class?这里:tutorialspoint.com/reactjs/reactjs_jsx.htm
        • @Harsha ES6 中添加了类语法。但它们仍然是底层的函数。
        • 全部:请注意,此答案现已过时。发布三年后,ES2015 将 class 语法添加到 JavaScript。查看此后发布的其他答案。
        • @T.J.Crowder 它仍然适用。 ES6“类”在底层仍然以完全相同的方式工作。 ES6 只是添加了一个新语法。这是一篇关于这个主题的精彩文章:toptal.com/javascript/es6-class-chaos-keeps-js-developer-up
        • @JoelMellon - 是的,它仍然是原型继承,旧语法并没有消失(当然)。 :-) 但是class 语法不仅仅是语法糖,而且在底层工作方式也不尽相同。但是当我说“过时”时,我的意思是class 语法提供了一种更清洁、更方便并且(现在)功能更丰富(例如私有字段和方法)的方法。有关详细信息,请参阅my answer to another question(如果您愿意,请参阅我最近的著作 JavaScript: The New Toys 的第 4 章和第 18 章)。
        【解决方案6】:

        在 javascript 中,没有类。 javascript 使用原型继承而不是基于类的继承。很多人会提到javascript中的类,因为它更容易理解,但这纯粹是一个类比。

        在基于类的继承中,您创建一个类(如果您愿意的话,是一个“蓝图”),然后从该类实例化对象。

        在原型继承中,一个对象直接从另一个父对象实例化,不需要任何“蓝图”。

        有关类与原型继承的更多信息,请参阅the wikipedia page

        【讨论】:

        • 全部:请注意,此答案现已过时。发布三年后,ES2015 将 class 语法添加到 JavaScript。查看此后发布的其他答案。
        【解决方案7】:

        这个词通常用在面向对象的编程语言环境中。类是将在实例化时创建的对象的模板。 JavaScript 是一种基于原型的编程语言,因此使用术语类来描述 JavaScript 原型有点奇怪。在 JavaScript 中,原型被创建为函数

        【讨论】:

        • 如果几乎​​没有人像这样使用“类”这个词会很奇怪,但显然情况正好相反,所以我一点也不觉得奇怪。我们将对象基于原型,就像我们基于类/模板一样。
        猜你喜欢
        • 2016-07-06
        • 2010-12-14
        • 1970-01-01
        • 2021-04-29
        • 2020-06-20
        • 2013-04-24
        相关资源
        最近更新 更多