首頁  >  文章  >  web前端  >  JS 中的 OOP -

JS 中的 OOP -

WBOY
WBOY原創
2024-09-03 21:08:26818瀏覽

OOP in JS -

  • JS 類別就像語法糖,與其他強類型語言的類別不同。
  • 僅添加語法包裝以使來自其他語言的開發人員熟悉。
  • 類別是一種特殊類型的幕後函數,因此可以寫成類別表達式和類別宣告。
## class expression:
const Person = class {
}

## class declaration:
class Person {
  constructor(fName, bYear){
   this.fName = fName;
   this.bYear = bYear;
  }
  calcAge(){
   console.log(2024 - this.bYear);
  }
}

- constructor is a method of this class. Pass values for properties to have in objects created using this fn.
- then set the properties of the object using this.xxx = xxx;
- On using 'new' operator, this constructor will be called automatically and return a new object which will be stored in LHS variable as shown below.
Ex. const ronald = new Person('ronald',1975); // Person { fName: 'ronald', bYear: 1975 }
- Methods are written outside the constructor fn and will be added to the prototype property of the object which can be verified using devConsole.
Ex. ronald.calcAge(); // 49

ronald.__proto__ === Person.prototype; // true

- No commas need to be added while adding multiple methods below the constructor fn inside the class.

## Hence, the above syntax works same as constructor fn syntax but with a familiar syntax of strongly typed class based languages.

## Adding a fn explicitly to the prototype:
Person.prototype.greet = function(){
console.log(`Hey ${this.fName}`);
}
ronald.greet(); // 'Hey ronald'

導入點:

  • Fn 聲明被提升,而 Class 聲明不被提升。
  • 也是一等公民,就像 Fns 一樣,即可以傳遞給 fns 並從 fns 返回。
  • 無論我們是否啟動嚴格模式,類別的主體總是在嚴格模式下執行。
  • 類別使程式碼看起來更乾淨,並且減少了字元噪音,前提是您知道它是如何在幕後實現的。 ** 要成為 JS 專家,您需要了解複雜的語言實作細節,例如類別。

存取器屬性:取得器和設定器,即取得和設定值的 fns。但從外觀上看,它們仍然像普通房產一樣。

普通屬性稱為資料屬性。

  • Getter 和 Settter 是 JS 中所有物件所共有的,即每個物件都可以有 getter 和 setter 屬性。這些 getter-setter 稱為存取器屬性,而普通屬性稱為資料屬性。

- Getter 和 setter 是獲取和設定值的 fns,從外部看起來就像普通屬性。

const account = {
  owner: 'jonas',
  movements: [200,300,100,500],
  get latest(){
    // will return an array with last value. Hence, use pop to get the value.
    return this.movements.slice(-1).pop();
  },
  set latest(mov){
    this.movements.push(mov);
  }
}

account.latest; // 500
account.latest = 50; 
account.latest; // 50

Just like above, classes also support the getter-setter methods but acccessed like using a property syntax.

These are very useful for data validation.

靜態方法

例如。 Array.from() = 將類似陣列的結構轉換為陣列。
Array.from(document.querySelector('h1'));
Array.from(document.querySelectorAll('h1'));

例如。 .from 附加到陣列建構函數,而不是建構函數的原型屬性。因此,並非所有數組都繼承此 fn。
[1,2,3].from(); // .from 不是函數

例如。 Number.parseFloat(12) 是 Number 建構子上的靜態方法,不適用於數字變數。

建立靜態方法。

// Static methods are not inherited. They are not added to prototype.
className.fnName = function(){
  console.log(this); // Entire constructor() which is calling the method
  console.log("JS is awesome")
};
className.fnName();

// Rule =  whatever object is calling the method, 'this' points to that object inside the fn. Hence its simply the entire constructor() above.

//Inside class, we need to use static keyword for adding a static method.
static fnName = function(){
  console.log(this); // can point to the entire class defn
  console.log("JS is awesome")
};

// Static methods and instance methods will be different from each other.
// instance methods will be prototype, hence all instances can have access to them

對象.create():

手動用於將物件的原型設定為我們想要的任何物件。
將用於實作繼承黑白類別。
使用此 fn 實作原型繼承。
Object.create 傳回一個空物件。
與建構函數 fns 和類別的工作方式不同。
即使不涉及「prototype」、「constructor()」、「new」運算符,仍然存在原型繼承的想法。

const PersonProto = {
  // This method will be looked up using __proto__ link
  calcAge(){
    console.log(2024 - this.bYear);
  }
};

// baba will be created, with its prototype set to PersonProto object.
const baba = Object.create(PersonProto);
baba;

baba.name = 'Roger';
baba.bYear = '2000';
baba.calcAge();

建構子 Fn --(.prototype)--> Person.prototype
物件實例 --(proto)--> Person.prototype

工作方式就像在 fn 建構子或類別中一樣
不需要 constructor() 或 .prototype 屬性來實現此目標。

const PersonProto = {
  // This method will be looked up using __proto__ link
  calcAge(){
    console.log(2024 - this.bYear);
  },
  // Noting special with init name, its a normal fn here.
  // This has nothing to with ES6 constructor()
  // Manual way of initialzing an object.
  init(fName, bYear){
    this.fName = fName;
    this.bYear = bYear;
  }
};

// baba will be created, with its prototype set to PersonProto object.
const baba = Object.create(PersonProto);
baba;

baba.name = 'Roger';
baba.bYear = '2000';
baba.calcAge();

baba.__proto__;    // { calcAge: [Function: calcAge] }
baba.__proto__ === PersonProto; //true


const alice = Object.create(PersonProto);
alice.init("alice", 2000);
alice;   // { fName: 'alice', bYear: 2000 }  

創造原型繼承的方法:
建構子 Fn
ES6 類
對象.create

使用constructor()在類別之間進行繼承:

所有這些技術都允許物件在其原型上尋找方法。
JS 中不存在真正的類別。

const Person = function(firstName, bYear){
  this.firstName = firstName;
  this.bYear = bYear;
};

Person.prototype.calcAge = function(){
  console.log(2024 - this.bYear);
};

const Student = function(firstName, bYear, course){
  // This is the duplicate code, any change in Person won't be reflected here.
  this.firstName = firstName;
  this.bYear = bYear;
  this.course = course;
};

Student.prototype.introduce = function(){
  console.log(`My name is ${this.firstName} and I study ${this.course}`);
}

const matt = new Student("Matt", 2000, "CSE");
matt.introduce(); //  'My name is Matt and I study CSE'

刪除上面範例中的冗餘程式碼:

const Person = function(firstName, bYear){
  this.firstName = firstName;
  this.bYear = bYear;
};

Person.prototype.calcAge = function(){
  console.log(2024 - this.bYear);
};

const Student = function(firstName, bYear, course){
  // Person(firstName, bYear); -> This doesn't work because we are calling it as a regular fn call. 'new' has to be used to call this fn constructor. This fn call is simply a regular fn call, in which 'this' is set 'undefined'. Hence, an error as it cannot set firstName on undefined.
  // We want to set the 'this' inside this fn to be same as inside Person above.
  Person.call(this, firstName, bYear);
  this.course = course;
};

Student.prototype.introduce = function(){
  console.log(`My name is ${this.firstName} and I study ${this.course}`);
}

const matt = new Student("Matt", 2000, "CSE");
matt.introduce(); //  'My name is Matt and I study CSE'

'new' 透過 proto
在物件實例與其原型之間自動建立鏈接 繼承的整體思想是子類別可以在原型鏈上共享父類別的行為。
原型[Object.prototype] = null; // 位於原型鏈的頂部。

const Person = function(firstName, bYear){
  this.firstName = firstName;
  this.bYear = bYear;
};

Person.prototype.calcAge = function(){
  console.log(2024 - this.bYear);
};

const Student = function(firstName, bYear, course){
  Person.call(this, firstName, bYear);
  this.course = course;
};

// Student.prototype = Person.prototype; => This doesn't work because we won't get the prototype chain, rather we will get 
// Constructor fn[i.e Person()]    --------------> Person.prototype
// Constructor fn[i.e Student()]   --------------> Person.prototype
// Object [Matt] __proto__: Student.prototype ---> Person.prototype

// Student.prototype manually linked for lookup to Person.prototype.
// This has to be done here and not after else Object.create will overwrite any of the existing methods like introduce() on it.
Student.prototype = Object.create(Person.prototype);

Student.prototype.introduce = function(){
  console.log(`My name is ${this.firstName} and I study ${this.course}`);
}

const matt = new Student("Matt", 2000, "CSE");
matt.introduce(); //  'My name is Matt and I study CSE'
matt.calcAge();    // 24

matt.__proto__;                   // Person { introduce: [Function (anonymous)] }
matt.__proto__.__proto__;        // { calcAge: [Function (anonymous)] }
matt.__proto__.__proto__.__proto__;   // [Object: null prototype] {}

Student.prototype.constructor = Student;   // [Function: Student]

matt instanceof Student; // true
matt instanceof Person; // true
matt instanceof Object; // true

以上是JS 中的 OOP -的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn