Javascript 面向對象還是基於對象

寫在前面

  • 昨天 Mentor 說以後要開始用 React Hooks(我還沒有涉足 )寫點東西,進而談論到 React 生態一直想推展開來的的函數式編程思想,我不由得想到一個問題:都說 Javascript 不是典型的面向對象編程語言,它並不具備完整的 OOP 該有的特性,它雖引進了 class 語法糖,但只是讓對象原型寫法更加像面向對象編程語言的寫法,那面向對象到底可以理解爲什麼 ? 🤔 以及函數式編程思想在推什麼?Javascript 定位究竟是什麼?
  • 簡單記錄一下自己關於這個問題的思考

OOP Wikipedia

  • 探討的是面向對象
  • 我們首先可以想想,我們爲什麼需要封裝成對象?
    • 我的理解是:我們需要減少我們的操作粒度,每個操作都去落實到 bit 數據是非常龐大的,減少問題求解複雜度
    • wiki 上關於 object 特性也給到了支持
  • A feature of objects is an object’s procedures that can access and often modify the data fields of the object with which they are associated (objects have a notion of “this” or “self”).
    • 可以和麪向過程 (Procedure Oriented) 放在一起說。

    • 首先 OOP 是一個很自然的思想,在C語言中也能寫出符合面向對象思想的代碼

      // C語言例子
      struct Student{
          char *name;  //姓名
          int num;  //學號
          int age;  //年齡
          char group;  //所在學習小組
          float score;  //成績
      };
      
      char* GetStudentName(struct Student* stu)
      {
          // 略
      }
      
      void SetStudentName(struct Student* stu, char* newName)
      {
          // 略
      }
      
      int main()
      {
          struct Student s1, s2, s3, s4; // 創建了多個學生
          SetStudentName(&s1, "小明");
          SetStudentName(&s2, "小紅");
          return 0;
      }
      
  • 其次 OOP 面向對象編程,在做一件什麼事情?
    • 在面對複雜性業務需求中,面向對象思想可以將業務先進行分析,如果業務需求全新無關聯,那我們可以新建一個對象,在裏面封裝對應的方法;如果業務需求只是一條延展線(比如特定節假日打折),那我們可以繼承現有對象,並對現有對象的某些方法(discount),進行特定操作,即多態:用統一的方法對不同的對象進行同樣的操作。

      override fun discount(price: Double): Double {
          if (!isCouple()) return price
          if (price > 99) {
              val lucky = Random().nextInt(gifts.size)
              println("Congratulations on getting ${gifts[lucky]}!")
          }
          return price * 0.77
      }
      
      • 而面對這種頻繁操作數據單元的使用面向過程編程思想,可能會在現有對象加上判斷,萬一節假日還要做其他的業務,判斷只會越來越多。這就與我們 Nicklaus Wirth 提出的:程序 = 數據結構 + 算法,越來越割裂。
    • 在我現在的淺薄思考看來:面向對象編程思想是想先讓不同對象以儘可能的統一特性進行歸組,形成“大對象”,然後各個對象變成了這個大對象中衍生出來的基類,並在父類派生出來的對象中,去實現各自解決問題的具體方法。這樣當我們在拿到一個問題,我們可以不用去管它的內部實現,我們根據類型就可以知道它能做什麼事,這比我們手動去一步一步執行要先進點。讓對象有多態性,把不同對象以同一特性來歸組,統一處理。至於所謂繼承等概念,是實現的細節

prototype-based programming

  • Languages with abstract data type support which may be used to resemble OO programming, but without all features of object-orientation. This includes object-based and prototype-based languages. Examples: JavaScript, Lua, Modula-2, CLU.

  • The Document Object Model of HTML, XHTML, and XML documents on the Internet has bindings to the popular JavaScript/ECMAScript language. JavaScript is perhaps the best known prototype-based programming language, which employs cloning from prototypes rather than inheriting from a class (contrast to class-based programming).

  • 我們在大概瞭解了 OOP 思想後,我們可以繼續看看“類OOP”–基於原型編程 Javascript 實現

  • 我們可以先從 ECMA-262 規範中找到關於 Object 的定義

    • “Objects are created by using constructors in new expressions.”
    • “Each constructor is a function that has a property named ‘prototype’ that is used to implement prototype-based inheritance and shared properties.”
    • “Every object created by a constructor has an implicit reference (called the object’s prototype) to the value of its constructor’s ‘prototype’ property. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain. When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name. In other words, first the object mentioned directly is examined for such a property; if that object contains the named property, that is the property to which the reference refers; if that object does not contain the named property, the prototype for that object is examined next; and so on.”
    • ECMA原型鏈
  • 同時 Douglas Crockford 關於 prototypal inheritance

    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
    
    • “The object function untangles JavaScript’s constructor pattern, achieving true prototypal inheritance. It takes an old object as a parameter and returns an empty new object that inherits from the old one. If we attempt to obtain a member from the new object, and it lacks that key, then the old object will supply the member. Objects inherit from objects.”
    • “What could be more object oriented than that?” 😝
  • 也有新的對 Object 的思考

    • “In JavaScript, an object is an associative array, augmented with a prototype (see below); each string key provides the name for an object property, and there are two syntactical ways to specify such a name: dot notation (obj.x = 10) and bracket notation (obj[‘x’] = 10). A property may be added, rebound, or deleted at run-time. Most properties of an object (and any property that belongs to an object’s prototype inheritance chain) can be enumerated using a for…in loop.”

Functional programming

  • Functional programming has its origins in lambda calculus.It is a programming paradigm —- a style of building the structure and elements of computer programs – that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.
  • One of the key motivations for the development of functional programming is making a program easier to understand by eliminating changes in state that do not depend on function inputs which are called side effects.
  • side effects include modifying a non-local variable, modifying a static local variable, modifying a mutable argument passed by reference, performing I/O or calling other side-effect functions.
  • referential transparency
    • the same language expression can result in different values at different times depending on the state of the executing program.
    • Consider C assignment statement x = x * 10, this changes the value assigned to the variable x. Let us say that the initial value of x was 1, then two consecutive evaluations of the variable x yields 10 and 100 respectively. Clearly, replacing x = x * 10 with either 10 or 100 gives a program with different meaning, and so the expression is not referentially transparent. In fact, assignment statements are never referentially transparent.
    • Absence of side effects is a necessary, but not sufficient, condition for referential transparency. Referential transparency means that an expression (such as a function call) can be replaced with its value. This requires that the expression is pure, that is to say the expression must be deterministic (always give the same value for the same input) and side-effect free.

個人總結

  • Javasript 說它是基於面向對象的編程語言是不嚴謹的,準確的說他是類面向對象的編程語言,基於原型的編程語言。

參考文獻

寫在後面

  • 有對 Object 以及對 reference 產生的 side effects
    新的認識
  • 閉包 closure
    • A nested function is a function defined within another function. It is created each time the outer function is invoked. In addition, each nested function forms a lexical closure: The lexical scope of the outer function (including any constant, local variable, or argument value) becomes part of the internal state of each inner function object, even after execution of the outer function concludes.
  • 祝大家多多發財
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章