/**
* Run a callback after a click or tap, without running duplicate callbacks for the same event
* Hem dokunma hemde click eventlerinin aynı anda tetiklenmemesi için bir Callback ile geri besleme
* Works in all modern browsers, and at least back to IE9.
* @public
* @polyfill @required None
* @components @required @TODO 'util.queryElement()', 'util.on()'
* @param {Node} elem The element to listen for clicks and taps on
* @param {Function} callback The callback function to run on a click or tap
*/
onClickOrTap: function (elem, callback) {
elem = util.queryElement(elem);
// Make sure a callback is provided
if ( !callback || typeof(callback) !== 'function' ) return;
// Variables
var isTouch, startX, startY, distX, distY;
/**
* touchstart handler
* @param {event} event The touchstart event
*/
var onTouchStartEvent = function (event) {
// Disable click event
isTouch = true;
// Get the starting location and time when finger first touches surface
startX = event.changedTouches[0].pageX;
startY = event.changedTouches[0].pageY;
};
/**
* touchend handler
* @param {event} event The touchend event
*/
var onTouchEndEvent = function (event) {
// Get the distance travelled and how long it took
distX = event.changedTouches[0].pageX - startX;
distY = event.changedTouches[0].pageY - startY;
// If a swipe happened, do nothing
if ( Math.abs(distX) >= 7 || Math.abs(distY) >= 10 ) return;
// Run callback
callback(event);
};
/**
* click handler
* @param {event} event The click event
*/
var onClickEvent = function (event) {
// If touch is active, reset and bail
if ( isTouch ) {
isTouch = false;
return;
}
// Run our callback
callback(event);
};
// Event listeners
util.on('touchstart', elem, onTouchStartEvent);
util.on('touchend', elem, onTouchEndEvent);
util.on('click', elem, onClickEvent);
}
Function Components
on()
/**
* Add events or an event
* Works in all modern browsers, and at least back to IE9.
* @public
* @polyfill @required None
* @components @required @TODO 'eventListenerHandler()'
* @param {String|Node} types The event type or types (space separated)
* @param {String} selector The selector to run the event on (, seperated)
* @param {Function} callback The function to run when the event fires
*/
on: function (types, selector, callback) {
// Make sure there's a selector and callback
if (!selector || !callback) return;
// Loop through each event type
types.split(',').forEach((function (type) {
// Remove whitespace
type = type.trim();
// If no event of this type yet, setup
if (!activeEvents[type]) {
activeEvents[type] = [];
WIN.addEventListener(type, eventListenerHandler, true);
}
// Push to active events
activeEvents[type].push({
selector: selector,
callback: callback
});
}));
}
off()
/**
* Remove events or an event
* Works in all modern browsers, and at least back to IE9.
* @public
* @polyfill @required None
* @components @required @TODO 'getListenerIndex()', 'eventListenerHandler()'
* @param {String|Node} types The event type or types (space separated)
* @param {String} selector The selector to remove the event from (, seperated)
* @param {Function} callback The function to remove
*/
off: function (types, selector, callback) {
// Loop through each event type
types.split(',').forEach((function (type) {
// Remove whitespace
type = type.trim();
// if event type doesn't exist, bail
if (!activeEvents[type]) return;
// If it's the last event of it's type, remove entirely
if (activeEvents[type].length < 2 || !selector) {
delete activeEvents[type];
WIN.removeEventListener(type, eventListenerHandler, true);
return;
}
// Otherwise, remove event
var index = getListenerIndex(activeEvents[type], selector, callback);
if (index < 0) return;
activeEvents[type].splice(index, 1);
}));
}
one()
/**
* Add events or an event
* The handler is executed at most once per element per event type.
* İşleyici, etkinlik türü başına öğe başına en fazla bir kez yürütülür ve sonra otomatik olarak silinir.
* Works in all modern browsers, and at least back to IE9.
* @public
* @polyfill @required None
* @components @required @TODO 'util.on()', 'util.off()'
* @param {String|Node} types The event type or types (space separated)
* @param {String} selector The selector to remove the event from (, seperated)
* @param {Function} callback The function to remove
*/
one: function (types, selector, callback) {
util.on(types, selector, function callbackWrapper(e){
callback(e);
util.off(types, selector, callbackWrapper);
});
}
queryElement()
/**
* Return Query Element
* Works in all modern browsers, and at least back to IE9/8.
* @public
* @polyfill @required None
* @components @required None
* @param {String|Node} selector The selector to query element
* @param {Node} elem Parent Element
* @returns {Node} elem If has class return true
*/
queryElement: function (selector, parent) {
var lookUp = parent ? parent : DOC;
return typeof selector === 'object' ? selector : lookUp.querySelector(selector);
}
Bir eklenti (Plug-in) yazarken nelere dikkat etmeliyiz?
Bir javaScript eklentisi yazarken dikkat edilmesi gereken en önemli hususlardan birisi de o eklentisinin ANATOMİsidir.
Anatomi, Yunancada çıkarmak anlamına gelen ana ve kesmek anlamına gelen tomeden türetilmiş bir kelimedir.
wikipedia.org
The Anatomy
İşte tüm projelerime başladığım şablon. Merak etmeyin, adım adım ve inceleyerek ilerleyeceğiz.
(function (root, factory) {
if ( typeof define === 'function' && define.amd ) {
define(['boz'], factory(root));
} else if ( typeof exports === 'object' ) {
module.exports = factory(require('boz'));
} else {
root.myPlugin = factory(root, root.boz);
}
})(typeof global !== "undefined" ? global : this.window || this.global, function (root) {
'use strict';
//
// Variables
//
var myPlugin = {}; // Object for public APIs
var supports = !!document.querySelector && !!root.addEventListener; // Feature test
var settings; // Placeholder variables
// Default settings
var defaults = {
someVar: 123,
initClass: 'js-myplugin',
callbackBefore: function () {},
callbackAfter: function () {}
};
//
// Methods
//
// @todo add plugin methods here
/**
* Handle events
* @private
*/
var eventHandler = function (event) {
// @todo Do something on event
};
/**
* Destroy the current initialization.
* @public
*/
myPlugin.destroy = function () {
// If plugin isn't already initialized, stop
if ( !settings ) return;
// Remove init class for conditional CSS
document.documentElement.classList.remove( settings.initClass );
// @todo Undo any other init functions...
// Remove event listeners
document.removeEventListener('click', eventHandler, false);
// Reset variables
settings = null;
};
/**
* Initialize Plugin
* @public
* @param {Object} options User settings
*/
myPlugin.init = function ( options ) {
// feature test
if ( !supports ) return;
// Destroy any existing initializations
myPlugin.destroy();
// Merge user options with defaults
settings = boz.extend( defaults, options || {} );
// Add class to HTML element to activate conditional CSS
document.documentElement.classList.add( settings.initClass );
// @todo Do stuff...
// Listen for click events
document.addEventListener('click', eventHandler, false);
};
//
// Public APIs
//
return myPlugin;
});
Bağımlılıklar “Dependencies”
Söylemekte fayda var: Bu isteğe bağlıdır
Projelerimde genellikle iki ek dosya kullanırım. Bunlardan birincisi: ClassList.js[eklenecek]polyfill(destek)’i (desteğidir). Peki bu destek ne işe yarar? Diğer yazılarımızda da bahsettiğimiz gibi bu destek Internet Explorer için geliştirilmiştir. ClassList desteğini IE10+ dan IE8+ ‘ya yükseltmenize olanak sağlar. Tabi bu durum kişiye özeldir. Projelerinizin en düşük hangi Explorer sürümünü çalıştırması gerektiği ile doğru orantılıdır. O nedenle kullanmak pek tabi size (kodlayıcıya) kalmış bir durumdur.
Bir diğer ek dosyamız ise, boz.js[eklenecek] dir. Boz.js benim tarafımdan kodlanmış olan ufak bir kütüphanedir. Böylelikle her seferinde aynı kodları yazmaktan kurtuluyor ve bu kütüphanedeki hazır fonksiyonları çağırıp işin içinden çıkıyorum. Şunu belirtmeliyim ki bunu veya benzerlerini de kullanıp kullanmamak gene kodlayıcının arzusuna kalmış bir istisnai durumdur.
Biz yazımıza her iki durumu da dahil ederek devam edeceğiz. Böylelikle kullanmak istemediğiniz hususları es geçebilirsiniz.
Hadi başlayalım!
UMD Wrapper
Namı değer UMD Sarıcı. Bunu şöyle izah edelim, projelerinize (eklenti vs. fark etmez) UMD sarıcı – Universal Module Definition (UMD) sarmalayıcı– kullanarak yola çıkarsanız komut dosyalarınızın hem AMD hem de Common JS ile uyumlu olduğunu bildirmiş olursunuz. Bununla birlikte yazdığınız kodlamaların [önemli]“Hem değişken hem de fonksiyonların” geleneksel (GLOBAL) bir modül deseni olarak çalıştığı anlamına gelir. Yani yazdığınız bir eklenti bir başka eklenti veya kod’un etkisi altında kalmaz.
İşte sizin yazdığınız kod içerisinde bir değişkenin değeri “10” iken bir başkasının veya sizin yazdığınız herhangi bir başka eklenti vb. (etc.) kodlamalarda aynı isimdeki başka bir değişkenin değeri “0” kabul edildiğini düşünün. Mantıksal bir çelişki açığa çıkacaktır. Fakat MAKİNE DİLİ‘n de çelişkiye yer yoktur. O değişkenlerden hangisi Üniversite Mezunu ise veya referansı oraya dayanıyorsa onun dediği olur.
Pek tabi eşitlik olması durumunda akış şeması devreye girer ve SON SÖZÜ son konuşan söyler. Yani kısacası son gülen iyi güler ve kodlama sırasında en son olanın dediği olur.
Tabi biz şimdiye dek hep değişkenlerden bahsettik. Bu durumun aynısını fonksiyonlar içinde düşünebilirsiniz.
Neyse biz konumuza dönelim, biliyorum fazlaca saçmaladım 😛
1myPlugin burada yazacağınız eklentinin adını tanımlar. Bunu kendi isteğinize ve arzunuza göre değiştirebilirsiniz.
2boz yukarıda da bahsettiğim üzere yardımcı bir eklenti ve bu eklentinin çalışması için gereken ön koşul. Eğer boz tanımlanmamışsa eklenti çalışmayacaktır.
3 Bir tarayıcıda her zaman için root = window kabul edilmeyebilir. Yani her şeyin kökü pencere olmalıdır. Ama böyle bir takım hatalar (bug) gerçekleşebilir. Bu hatayı engellemek için aşağıdaki satır kodlamayı ekiyoruz.
typeof global !== 'undefined' ? global : this.window
Use Strict use strict
use strict aslında tarayıcıya bir takım kurallar açısından daha katı olmasını söyler. Yani yapılan kural dışı kodlamalar, senaryolar ve geliştirilen fonksiyonlar açısından oluşabilecek hoş görü (istisnai durum) dışında davran ve kendi kurallarının (dil) dışına çıkma. Daha da kısaltacak olursak kabul edilebilir hata payını azaltıyor.
Kulağa saçma geliyor. Bunu neden kullanılır ki? Aslında bu durum sizi daha iyi, daha kaliteli, daha hatasız bir kod yazmaya zorlayacak. Eğer amacınız bu ise use-strict kullanmanızda fayda var diyelim ve devam edelim.
Variables
Namı değer değişkenler. Ama şunu ifade etmeliyim ki bu çeviri bana biraz saçma geliyor. Onun yerine yazılabilirler denilse daha iyi olurmuş. Tabi bu durum zaten güçlükle anlaşılan ifadeleri daha da karmaşıklaştırabilir. En iyisi bırakalım değişkenler olarak kalsın 🙂
//
// Variables
//
var myPlugin = {}; // Object for public APIs
var supports = !!document.querySelector && !!root.addEventListener; // Feature test
var settings; // Placeholder variables
// Default settings
var defaults = {
someVar: 123,
initClass: 'js-myplugin',
callbackBefore: function () {},
callbackAfter: function () {}
};
Tekrar tekrar söylemekte fayda var. Unutmayalım ki buradaki, myPlugin yazdığımız eklentinin adıdır. Bunu kendinize göre değiştirmeniz gerekir.
[önemli] myPlugin ‘i burada bir değişken olarak tanımladık fark ettiyseniz. Bunun sebebi yazdığımız eklenti deki toplanan tüm verilerin RETURN değeri ile SARMALAYICI yani UMD ‘nin dışına çıkarmasıdır.
Yani lafın özü, günün sonunda (sembolik olarak) yazdığımız { } bu süslü parantezlerin dışına hangi isimde ve hangi verilerin çıktığıdır ki, biz de o satırda tam olarak bunu yapıyoruz.
[önemli] bu tanımlamayı yapmamış olsaydık. Dışarıdan hiç bir değişkene veya fonksiyona ulaşamazdık.
[örnek]myPlugin.init(); fonksiyonunu dışarıdan erişilebilecek (PUBLIC) olarak kodladık. Fakat yukarıdaki tanımlamayı yapmamış olsaydık, bu fonksiyonunun dışarıdan kullanılması mümkün olmayacaktı.
Herhangi bir olay dinleyicisini –tıklamak, kaydırmak, pencereyi yeniden boyutlandırmak, zoom yapmak vb. etc.- eventHandler yönteminden geçiririz ki içinde bir takım mantıksal sorgulamalar yapabilelim.
Her türlü girişi buraya yazabilirsiniz. Örneğin, ben click olayı dinleyicilerimi document ögesine yerleştirmeyi seviyorum ve ardından tıklanan ögenin önemsediğim ögelerden biri olup olmadığını kontrol ediyorum. (Yani tıklanan öge benim aradığım öge mi onu sorguluyorum)
Örnek 1.0
var eventHandler = function (event) {
var toggle = boz.getClosest(event.target, '[data-ornek]');
if ( toggle ) {
// Prevent default click event
if ( toggle.tagName.toLowerCase() === 'a') {
event.preventDefault();
}
// Eklenti deki diğer fonksiyonlar.
myPlugin.someMethod();
}
};
Burada mantıksal olarak şunları sağladık: document yani sayfadaki herhangi bir yere tıklandığı zaman eventHandler fonksiyonumuzu çalıştırdık. Bu fonksiyonun içinde ise, önce tıklanan elemanın ‘[data-ornek]’ etiketine sahip olup olmadığını kontrol ettik. Eğer bu etikete sahip bir eleman var ise deyip, o elemanın bir LİNK “a” olup olmadığını kontrol ettik. Eğer bir “LINK” ise preventDefault(); fonksiyonu ile normal tarayıcı davranışını reddetmesini emrettik. Kısacası link’e ait yeni sayfa açma işlemi iptal olacak. Ve hemen ardından someMethod(); isimli kendi fonksiyonumuzu çalıştırdık.
Eğer bu fonksiyon içerisine, bir sayfadan veri çekmeyi işlersek. Browser’da sayfa yenilemeden içerikleri değiştirebileceğimiz basit bir yol olacaktır 🙂
İsterseniz tüm olay türlerini tek bir işleyiciye aktarabilir ve olay türüne göre eylemin seyrini belirlemek için bazı mantıkları kullanabilirsiniz.
Örnek 1.1
var eventHandler = function (event) {
if ( event.type === 'scroll' ) {
myPlugin.scrollMethod();
}
if ( event.type === 'click' ) {
myPlugin.clickMethod();
}
};
Burada, gelen tüm eylemleri bir fonksiyonda topladık. Ve gelen olay türüne göre ayrıştırıp ona göre bir fonksiyon çalıştırdık. Örneğin, event.type === ‘scroll’ olayın bir scroll yani kaydırma olduğunu belirtiyor.
Destroy Method
Yani az çok anlaşılmıştır. Eklentiyi yok etmek. Çalışmaz hale getirmek.
Bu, aslında herhangi bir nedenle yeniden başlatmanız gerektiğinde veya başka bir komut dosyasından her şeyi durdurmanız gerektiğinde yararlıdır.
/**
* Destroy the current initialization.
* @public
*/
myPlugin.destroy = function () {
// If plugin isn't already initialized, stop
if ( !settings ) return;
// Remove init class for conditional CSS
document.documentElement.classList.remove( settings.initClass );
// @todo Undo any other init functions...
// Remove event listeners
document.removeEventListener('click', eventHandler, false);
// Reset variables
settings = null;
};
Initialize
Namı değer başlatmak. “Yazdığımız eklentinin çalışmasını başlatmak” olarak tercüme edebiliriz.
Bir diğer tanım: Manuel (EL ile) başlatmak.
Bazen yazdığınız bir algoritmanın (eklenti vs. etc.) kendi kendine sayfanın yüklenmesiyle birlikte otomatikman çalışmasını istemeyebilirsiniz. Bunun kontrolünün elinizde olmasını isteyebilirsiniz. Sizin istediğiniz zamanda ve/veya istediğiniz şartlar sağlandığında çalışması veya en azından bunu destekliyor olabilme-si-niz, istendik bir durumdur. Böylesi durumlarda “manuel initialize” etmek gerekmektedir.
Bu durum projelerinizde (örneğin bir web sayfası hazırlamak) size çok büyük esneklikler sağlar. Çünkü her eklentinin her sayfada çalışması gerekmez. [önemli] *Hatta bazı sayfalarda çalışması gerekli iken bazı sayfalarda ise çalışmaması gerekebilir.
/**
* Initialize Plugin
* @public
* @param {Object} options User settings
*/
myPlugin.init = function ( options ) {
// feature test
if ( !supports ) return;
// Destroy any existing initializations
myPlugin.destroy();
// Merge user options with defaults
settings = boz.extend( defaults, options || {} );
// Add class to HTML element to activate conditional CSS
document.documentElement.classList.add( settings.initClass );
// @todo Do stuff...
// Listen for click events
document.addEventListener('click', eventHandler, false);
};
Burada dikkat edilmesi gereken öncelikle, gerekli web ve JavaScript API’lerinin desteklendiğinden emin olmak için bir kontrol yazılmış olması. Buradaki durumumda document.querySelectorve window.addEventListenerbizim için gerekli olan API’ler. Bunlar yukarıda (değişken olarak) yazdığımız, supports betiğinin başlangıcındaki değişkende tanımlanır.
Örnek 2.0
var supports = !!document.querySelector && !!root.addEventListener;
Eklentimizin başında yazdığımız bu değişkeni başlatma fonksiyonumuzun içinde kontrol ediyoruz.
Örnek 2.1
if ( !supports ) return;
(Çok basit bir tabirle hızlı geçmek istiyorum burayı) Eğer o ve o ‘ndan herhangi birisi yok ise, fonksiyondan boş çık! Doğal olarak eklentimizden de boş bir veri ile çıkış sağlanacaktır.
Ardından, iç –içe– çakışmaları önlemek veya olay dinleyicilerini çoğaltmak için betiğin /VARSA/ mevcut tüm başlatmalarını imha ediyoruz.
Örnek 2.2
myPlugin.destroy();
Sonra herhangi bir kullanıcı ayarını (eğer girilmiş ise) varsayılan değerler ile değiştiriyoruz: <abkz.> extend
Bu kısımda artık ana başlıklar altında değilde, kısa kısa püf noktaları vererek ilerleyeceğiz. Bu nedenle hepsini birleştirip, Diğer Adımlar adı altında sergiliyoruz.
1 Eklenti çalıştırıldıktan sonra DOM ögelerinden istediklerimize mesela (BODY) bir CSS sınıfı ekliyoruz. Bunun amacı ola ki eklentimiz herhangi bir nedenle hata verir ve/veya çalışmaz ise, bu durumda eklenecek olan sınıf o elemente eklenmaz ve bizde anlarız ki eklentimiz çalışmıyor. Buna bağlı olarak esnek bir CSS yazabiliriz. Böylelikle istenmedik CSS hatalarının da önüne geçmiş oluruz.
3 [önemli] Eklenti başlatılır başlatılmaz çalışması gereken (Bizim bu örneğimizde yer almayan) diğer tüm yöntemler de burada çağrılmalıdırlar.
Örnek 3.2
myPlugin.herhangiIslev();
myPlugin.moreFunc();
// ... daha fazla
4 [önemli] Eklenti dışında kullanılacak olan yöntemlerinizi dışarı çıkartın ( Return your public methods )!
Bu eklentideki dışarı çıkartacağımız son ve tek şey myPlugin‘ dir. Ki bu durumdan daha önce de bahsetmiştik. Bu değişkeni bu isimle dışarı çıkartmak aslında bize o isimi bir ÖN-EK olarak kullanacağımızı ifade ediyor. Bu ön-ek’i kullanarak eklentimizin içindeki –PUBLIC-, metodları dışarıdan çalıştırabiliriz.
return myPlugin;
Evet arkadaşlar böylelikle eklentimizin de yazımızın da sonuna geldik.
Yazım hatalarının ve (kimi yerde İngilizce, kimi yerde Türkçe) kullanmamın kusuruna bakmayın. Naçizane bazı terimleri tercüme etmek imkansız veya bazılarının da anlaşılabilmesi için orijinal hali ile kalması gerekir…
Sayfa başında eklediğimiz full sürümü bir de sonuna ekleyerek kapanışı yapalım:
Full Code: Anatomy of The JavaScript Plugin’s
(function (root, factory) {
if ( typeof define === 'function' && define.amd ) {
define(['boz'], factory(root));
} else if ( typeof exports === 'object' ) {
module.exports = factory(require('boz'));
} else {
root.myPlugin = factory(root, root.boz);
}
})(typeof global !== "undefined" ? global : this.window || this.global, function (root) {
'use strict';
//
// Variables
//
var myPlugin = {}; // Object for public APIs
var supports = !!document.querySelector && !!root.addEventListener; // Feature test
var settings; // Placeholder variables
// Default settings
var defaults = {
someVar: 123,
initClass: 'js-myplugin',
callbackBefore: function () {},
callbackAfter: function () {}
};
//
// Methods
//
// @todo add plugin methods here
/**
* Handle events
* @private
*/
var eventHandler = function (event) {
// @todo Do something on event
};
/**
* Destroy the current initialization.
* @public
*/
myPlugin.destroy = function () {
// If plugin isn't already initialized, stop
if ( !settings ) return;
// Remove init class for conditional CSS
document.documentElement.classList.remove( settings.initClass );
// @todo Undo any other init functions...
// Remove event listeners
document.removeEventListener('click', eventHandler, false);
// Reset variables
settings = null;
};
/**
* Initialize Plugin
* @public
* @param {Object} options User settings
*/
myPlugin.init = function ( options ) {
// feature test
if ( !supports ) return;
// Destroy any existing initializations
myPlugin.destroy();
// Merge user options with defaults
settings = boz.extend( defaults, options || {} );
// Add class to HTML element to activate conditional CSS
document.documentElement.classList.add( settings.initClass );
// @todo Do stuff...
// Listen for click events
document.addEventListener('click', eventHandler, false);
};
//
// Public APIs
//
return myPlugin;
});
Bu durumdan daha önce bahsetmiştik, ama genede bir köşeye ekleyelim: – Pure JS, – Vanilla JS, – Plain JS üçlemelerinin anlamları aynı kapıya dayanır. Her üçü de saf javaScript kullanarak bir takım kod blokları elde etme yöntemidir. Kısacası seçicileri içeren ek kütüphaneler bulunmaz. Buna bağlı olarak sadece kullandığınız yardımcı fonksiyonları kullanabilirsiniz.
Adım 1
Eşitliği kontrol etmek için önce dizilerin aynı uzunlukta olduğundan emin olmamız gerekir. Olmazsa, eşit değiller ve return false yapabiliriz (döndürebiliriz).
var arraysMatch = function (arr1, arr2) {
// İlk önce dizilerin öge sayıları eşit mi onu kontrol et
if (arr1.length !== arr2.length) return false;
};
İlk dizideki her öge arasında döngü kuracağız ve dizinin (ideğişkeni sayesinde) ikinci dizideki aynı ögenin diziniyle aynı olup olmadığını kontrol edeceğiz. Değilse (ya da öge hiç mevcut değilse), geri göndereceğimiz değer: return false.
var arraysMatch = function (arr1, arr2) {
// İlk önce dizilerin öge sayıları eşit mi onu kontrol et
if (arr1.length !== arr2.length) return false;
// Tüm ögelerin aynı olup olmadığını kontrol et
for (var i = 0; arr1.length < i; i++) {
if (arr1[i] !== arr2[i]) return false;
}
};
Her şey kontrol edildiğinde, eşitlik devam ediyorsa geri göndereceğimiz değer: return true.
Not: Burada dikkat edilmesi gereken durum EĞER(IF) koşulu gerçekleşirse fonksiyondan dışarı FALSE değeri ile döndüğünden ötürü, bunların hiç biri gerçekleşmezse (Koşulsuz + şartsız) direkt olarak TRUE değeri döndüre bilmemizdir.
Getting Full Function Helper
var arraysMatch = function (arr1, arr2) {
// İlk önce dizilerin öge sayıları eşit mi onu kontrol et
if (arr1.length !== arr2.length) return false;
// Tüm ögelerin aynı olup olmadığını kontrol et
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
// True döndür
return true;
};