مقدمه
برای درک مفهوم factory functions و constructors، ابتدا باید توابع و اشیاء را درک کنید.
اگر تعداد زیادی از اشیاء با ویژگی ها و عملکردهای مشابه ایجاد می کنید، ممکن است خسته کننده و تکراری باشد. ما می خواهیم اصل حفظ کد D.R.Y را که مخفف “Don’t Repeat Yourself” است را در نظر داشته باشیم. الگوهای تابعی وجود دارد که می تواند به ما کمک کند کد کوتاه تر، تمیزتر و کارآمدتر بنویسیم. توابع کارخانه و توابع سازنده می توانند این کار را انجام دهند، اما از کدام یک باید استفاده کنید؟ بیایید کمی عمیق تر در مورد اینکه چه کارکردهای کارخانه و سازنده ها هستند، چه کاری می توانند و چه کاری نمی توانند انجام دهند و شباهت ها و تفاوت ها غوطه ور شویم.
Factory Function چیست؟
factory function را می توان به عنوان یک کارخانه واقعی در نظر گرفت که مواد اولیه را دریافت می کند و محصولات متعددی را به سرعت تولید می کند. از طرف دیگر، توابع کارخانه، ورودی های خاصی را دریافت می کنند و از آن ورودی ها برای ایجاد یک شی جدید استفاده می کنند. بنابراین، چگونه این واقعاً می تواند برای ما مفید باشد؟ ما به سادگی می توانیم هر شی را به صورت جداگانه ایجاد کنیم، اما ممکن است مدتی طول بکشد. اگر در حال ایجاد اشیایی با ویژگیهای یکسان و مقادیر متفاوت هستید، ایجاد یک تابع کارخانه میتواند این فرآیند را سریعتر کند.
const car1 = {
make: 'Toyota',
model: 'Tacoma',
year: 2018,
fuelType: 'gas',
bodyType: 'mid-size pick-up truck',
getFullName() {
return `${this.year} ${this.make} ${this.model}`;
}
}
console.log(car1.getFullName()); // => 2018 Toyota Tacoma
در مثال بالا، یک شی ایجاد می کنیم که یک ماشین خاص را توصیف می کند. حالا بیایید یک شی مشابه با این یکی بسازیم.
const car2 = {
make: 'Tesla',
model: 'Model S',
year: 2018,
fuelType: 'electric',
bodyType: 'sedan',
getFullName() {
return `${this.year} ${this.make} ${this.model}`;
}
}
console.log(car2.getFullName()); // => 2018 Tesla Model S
اکنون، من می توانم به ساخت اشیاء ماشین های بیشتری ادامه دهم، اما چه کسی برای آن وقت دارد، درست است؟ بیایید ببینیم که این به عنوان یک عملکرد کارخانه چگونه است.
function createCar (make, model, year, fuelType, bodyType) {
return {
make: make,
model: model,
year: year,
fuelType: fuelType,
bodyType: bodyType,
getFullName() {
return `${year} ${make} ${model}`;
}
}
}
ما اکنون یک تابع کارخانه داریم که اشیاء جدیدی را برای ما ایجاد می کند. اکنون، تنها کاری که باید انجام دهیم این است که داده ها را ارسال کنیم و به تابع کارخانه اجازه دهیم کار خود را انجام دهد.
function createCar (make, model, year, fuelType, bodyType) {
return {
make: make,
model: model,
year: year,
fuelType: fuelType,
bodyType: bodyType,
getFullName() {
return `${year} ${make} ${model}`;
}
}
}
const car1 = createCar('Toyota', 'Tacoma', 2018, 'gas', 'mid-size pick-up truck');
const car2 = createCar('Tesla', 'Model S', 2018, 'electric', 'sedan');
console.log(car1.getFullName()); // => 2018 Toyota Tacoma
console.log(car2.getFullName()); // => 2018 Tesla Model S
شما می توانید هر تعداد شی را با استفاده از تابع کارخانه به جای تکرار کد بسازید.
constructors چیست؟
تابع سازنده یکی دیگر از الگوهای جاوا اسکریپت است که بسیار شبیه توابع کارخانه است. اگرچه، بر خلاف توابع کارخانه، توابع سازنده در واقع یک شی را بر نمی گرداند. برای ایجاد اشیاء مختلف با خصوصیات یکسان، باید از کلیدواژه “new” و کلمه کلیدی “this” استفاده کنیم. با این گفته، بیایید ببینیم که چگونه توابع سازنده به صورت بصری کار می کنند.
function Car(make, model, year, fuelType, bodyType) {
this.make = make
this.model = model
this.year = year
this.fuelType = fuelType
this.bodyType = bodyType
this.getFullName = () => {
return `${this.year} ${this.make} ${this.model}`;
}
}
در مثال بالا، تابع سازنده بسیار شبیه یک تابع کارخانه است، به جز استفاده از کلمه کلیدی “this”.”this” به تابع سازنده ای اشاره دارد که شی نمونه را ایجاد کرده است. به عبارت دیگر، “this” در یک تابع سازنده ارزشی ندارد. شما می توانید آن را به عنوان یک پایه برای شی جدید در نظر بگیرید.
function Car(make, model, year, fuelType, bodyType) {
this.make = make
this.model = model
this.year = year
this.fuelType = fuelType
this.bodyType = bodyType
this.getFullName = () => {
return `${this.year} ${this.make} ${this.model}`;
}
}
const car1 = new car('Toyota', 'Tacoma', 2018, 'gas', 'mid-size pick-up truck');
const car2 = new car('Tesla', 'Model S', 2018, 'electric', 'sedan');
console.log(car1.getFullName()); // => 2018 Toyota Tacoma
console.log(car2.getFullName()); // => 2018 Tesla Model S
برای ایجاد اشیا با استفاده از توابع سازنده، از کلمه کلیدی دیگری به نام “جدید” استفاده می کنیم. وقتی از عبارت “جدید” در مقابل فراخوانی تابع استفاده می کنید، جاوا اسکریپت به طور خودکار دو کار را برای ما انجام می دهد: در داخل تابع، یک شی خالی با نام “this” ایجاد می کند. شی “this” را به عبارتی که ابتدا تابع را فراخوانی کرده بود برمی گرداند.
function Car(make, model, year, fuelType, bodyType) {
// const this = {};
this.make = make
this.model = model
this.year = year
this.fuelType = fuelType
this.bodyType = bodyType
this.getFullName = () => {
return `${this.year} ${this.make} ${this.model}`;
}
// return this;
}
const car1 = new car('Toyota', 'Tacoma', 2018, 'gas', 'mid-size pick-up truck');
const car2 = new car('Tesla', 'Model S', 2018, 'electric', 'sedan');
console.log(car1.getFullName()); // => 2018 Toyota Tacoma
console.log(car2.getFullName()); // => 2018 Tesla Model S
ارث
وراثت نقش مهمی در تفاوت عملکردهای کارخانه و سازنده دارد.
function createCar (make, model, year, fuelType, bodyType) {
return {
make: make,
model: model,
year: year,
fuelType: fuelType,
bodyType: bodyType,
getFullName() {
return `${year} ${make} ${model}`;
}
}
}
const car1 = createCar('Toyota', 'Tacoma', 2018, 'gas', 'mid-size pick-up truck');
const car2 = createCar('Tesla', 'Model S', 2018, 'electric', 'sedan');
car1.getFullName = function() {
return `My ${fuelType} ${bodyType} is a ${year} ${make} ${model}`;
}
console.log(car1.getFullName()); // => My gas mid-size pick-up truck is a 2018 Toyota Tacoma
console.log(car2.getFullName()); // => 2018 Tesla Model S
بیایید به اولین مثال از عملکرد کارخانه بازگردیم. اگر بخواهیم ()car1.getFullName را مجدداً اعلام کنیم، چه؟ خب، car1.getFullName() و car2.getFullName() تابع یکسانی در حافظه نیستند. هر شیء کپی خود را از تابع دریافت می کند. به این معنی که وقتی تابع یک شی ایجاد میکند و آن را برمیگرداند، ویژگیها و مقادیر را کپی کرده و به هر شیئی که تابع را فراخوانی میکند، متصل میکند.
function car(make, model, year, fuelType, bodyType) {
// const this = {};
this.make = make
this.model = model
this.year = year
this.fuelType = fuelType
this.bodyType = bodyType
this.getFullName = () => {
return `${this.year} ${this.make} ${this.model}`;
}
// return this;
}
const car1 = new car('Toyota', 'Tacoma', 2018, 'gas', 'mid-size pick-up truck');
const car2 = new car('Tesla', 'Model S', 2018, 'electric', 'sedan');
console.log(car1); // => car {make: 'Toyota', model: 'Tacoma' , etc.}
حالا بیایید نگاهی به تابع سازنده خود در بالا بیندازیم. وقتی سازنده ساخته می شود، نمونه اولیه خود را دارد. هنگامی که یک شیء جدید ماشین را با استفاده از کلمه کلیدی “new” ایجاد می کنیم، نمونه ای از نوع ماشین ایجاد می کند. به عبارت دیگر، نمونه اولیه car1 از نوع خودرو است. اکنون car1 از سازنده خودرو ارث می برد. این به ما اجازه می دهد تا ویژگی هایی را به نمونه اولیه خودرو اضافه کنیم.
Car.prototype.sentence = function() {
return `My ${this.fuelType} ${this.bodyType} is a ${this.year} ${this.make} ${this.model}`;
}
console.log(car1); // => Car {
// make: 'Toyota',
// model: 'Tacoma',
// year: 2018,
// fuelType: 'gas',
// bodyType: 'mid-size pick-up truck',
// getFullName: [Function (anonymous)]
// }
توجه داشته باشید که تابع جمله مستقیماً به خود سازنده خودرو اضافه نمی شود.
console.log(Car.prototype); // => { sentence: [Function (anonymous)] }
اما وقتی نمونه اولیه آن را بررسی می کنیم، آنجاست! اکنون میتوانیم به تابع جدید اضافه شده دسترسی پیدا کنیم.
console.log(car1.sentence()); // => My gas mid-size pick-up truck is a 2018 Toyota Tacoma
console.log(car2.sentence()); // => My electric sedan is a 2018 Tesla Model S
اکنون که می دانیم هر عملیات چگونه کار می کند، سؤال این است: کدام یک را باید استفاده کنیم؟ هر دو می توانند نتیجه یکسانی را برای شما به ارمغان بیاورند. سازنده ها زمانی عالی هستند که می خواهید یک ویژگی را از هر شی که از سازنده به ارث می برد اضافه یا حذف کنید. با این حال، درک عملکردهای کارخانه می تواند ساده تر باشد، زیرا در پایان روز، این فقط یک عملکرد است. برای عملکردهای کارخانه، لازم نیست کلمه کلیدی “جدید” را وارد کنیم. با قدرت بسته شدن می تواند انعطاف پذیرتر باشد. با آن، ما می توانیم چیزی به نام “حریم خصوصی داده ها” را انجام دهیم. بیایید به یک مثال دیگر نگاه کنیم که توضیح می دهد چگونه این کار می کند.
function createCar(make, model, year) {
return {
getFullName() {
return `${year} ${make} ${model}`;
}
}
}
const car1 = createCar('Toyota', 'Tacoma', 2018);
console.log(car1.getFullName()); // => 2018 Toyota Tacoma
console.log(car1.make); // => undefined
console.log(car1); // => { getFullName: [Function: getFullName] }
نتیجه
در مجموع، نکته مهم در مورد نوشتن کد خود این است که هیچ چیز لزوماً اشتباه نیست. روش خوبی برای تمیز و کوتاه نگه داشتن کد ما وجود دارد. از توابع کارخانه و توابع سازنده در جایی که فکر می کنید برای مورد شما بهترین است استفاده کنید.