整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          怎么選擇合適的JDK版本?

          年9月份的時候,JDK21發布了,作為又一里程碑的式的JDK,JDK21更新了非常多的內容,如果你在開發過程中依然使用著JDK8或者JDK6,想要升級但卻不知道升級到哪個版本,今天我就來總結一下各個里程碑式JDK的新增功能,分別從JDK8、JDK11、JDK17和JDK21這幾個版本介紹。

          JDK8

          目前使用最多最廣泛的JDK,它的升級內容有:

          • Lambda表達式:引入了一種簡潔的方式來實現只有一個方法的接口,極大地提高了代碼的可讀性和簡潔性。
          • StreamAPI:為集合(Collection)引入了一種新的抽象,稱為流(Stream),它允許用戶以聲明式的方式處理數據。
          • 新的日期時間API(java.time):基于Joda-Time庫,提供了更好的日期和時間處理。
          • 接口的默認方法和靜態方法:允許在接口中定義具有實現的方法,方便了接口的擴展。
          • Optional類:用于更優雅地處理空值情況。
          • 新的NashornJavaScript引擎:允許在JVM上更好地運行JavaScript代碼。

          代碼示例:

          JDK11

          升級內容有:

          • 新增HTTP客戶端API(java.net.http):支持HTTP/2協議和WebSocket,用于替代老舊的HttpURLConnection。
          • 本地變量類型推斷(var關鍵字):簡化了局部變量的聲明,提高代碼的可讀性。
          • 移除JavaEE和CORBA模塊:JDK更加輕量化,專注于標準Java應用程序開發。
          • 垃圾收集器改進:引入了低延遲的ZGC垃圾收集器和Epsilon垃圾收集器,后者主要用于性能測試。
          • FlightRecorder和MissionControl:提供了高級的診斷和分析工具。

          代碼示例:

          JDK17

          升級內容有:

          • 密封類(SealedClasses):允許類或接口限制其子類或實現類,增強類型安全性。
          • 模式匹配forinstanceof:簡化了對象類型檢查和強制類型轉換的代碼。
          • 新的垃圾收集器改進:G1垃圾收集器獲得了進一步的優化,提高了性能。
          • 強化封裝性:進一步隱藏了JDK內部API,以減少客戶端代碼對內部API的依賴。
          • 新的macOS渲染管道:改善了Java在macOS上的GUI性能。

          代碼示例:

          JDK21

          升級內容有:

          • 虛擬線程(預覽功能):引入了輕量級線程的概念,大幅降低線程創建和上下文切換的成本。
          • 模式匹配的增強:擴展了模式匹配的功能,進一步簡化了代碼。
          • 垃圾收集器的進一步改進:繼續優化和提高GC的性能和效率。
          • 泛型的改進:增加了更強大的類型推斷能力,簡化了泛型代碼的編寫。
          • 新的語言特性和API改進:包括對既有API的增強和新功能的引入,以提高開發效率和性能。

          代碼示例:

          總結

          JDK 8 引入了諸如 Lambda 表達式和 Stream API 等革命性特性,極大地豐富了 Java 的功能性和靈活性。

          JDK 11 通過引入 var 關鍵字和新的 HTTP 客戶端 API,進一步簡化了編碼并加強了網絡編程能力。

          JDK 17 引入的密封類和模式匹配增強了類型安全和代碼簡潔性。

          JDK 21 則通過虛擬線程和對泛型的改進,提升了并發處理能力和類型系統。

          大家可以通過上面的總結結合工作需求來選擇適合的JDK版本,畢竟適合的才是最好的!

          更多文章,推薦公眾號【程序員老J】

          階高級前端系列——JavaScript的"類"和繼承

          近期似乎總是能在各種場合聽到"程序員中年危機"這種言論,說是程序員到了35歲就會很難跳槽,很容易被優化等等。其實在今年疫情影響下各行各業經濟都不景氣,公司手里沒錢肯定要節省成本,35歲的人一般對工資的要求比較高,所以對這種比較高級的程序員需求就會降低。當然除此之外肯定也有其他大家都在說的原因,例如精力不如畢業生,沒有年輕人能加班能干活,不能走入管理崗的中年人就會被淘汰等等。

          其實個人感覺,雖然國內環境比較浮躁,但是渴望高級技術人才的公司還是大有人在,打開招聘軟件還是能找到不少高級前端的招聘信息。到了中年靠的應該是自己的經驗和技術,去做一些更加高級的事情。既然做了開發這一行,我想大家大部分都是想專心做一些技術工作,少一些應酬和虛與委蛇,但是不可否認的是,隨著vue、react等等這種非常強大的框架普及,大部分程序員做的都是一些"最沒技術含量的技術",簡單地復制粘貼,查找官方文檔照著寫,很少再去關心技術細節,時間久了自然會被淘汰。

          啰嗦了這么多,其實也是說給自己聽的,要做高級的事情就要離開自己的舒適區,去鉆研一些高級的事情。

          今天就一起討論一下JavaScript的"類"和繼承。

          如果你想用JavaScript做一些高級的事情,例如打造組件庫,封裝插件,一定離不開"類"和繼承,這是"封裝"里無法繞過的一環。

          為什么要給"類"加上引號呢?

          因為JavaScript的世界里根本沒有類,所有你見到的類,包括ES6里的新語法class,都不是真正的類,JavaScript是徹頭徹尾的,純粹到極致的面向對象語言,一切皆對象。

          我勸你最好暫時忘了之前接觸過的所有面向對象語言。因為他們可能早已深深地誤導了你。

          JavaScript的"類"和繼承實際上是利用原型鏈來實現的。例如下面的代碼:



          這是js一個最常用的利用構造函數聲明類的形式,里面有我們熟悉的new關鍵詞,表面上看確實是先有了Foo類然后用new實例化了一個對象。

          但實際上Foo只是一個函數,它和其它函數沒有什么不同,函數也是對象,直接像普通函數一樣直接調用Foo也不會出錯,加上new之后只是多了幾個操作:

          1. 創建一個新對象;
          2. 執行Foo函數,并將Foo的this綁定到新對象;
          3. 把新對象的_proto_指向Foo的prototype;
          4. 如果Foo方法沒有返回其它內容的話,返回這個新對象;

          這里我們看到a1并沒有say()方法,但是a1.say()卻正常運行了,這是原型鏈的作用,a1沒有say屬性,就去原型上查找,最終在Foo.prototype里找到。

          關于什么是原型鏈這里就不細說了,這屬于js基礎,不在高級討論范圍內。

          上面代碼本質上我們是利用一個函數對象(Foo)又創建了另一個對象(a1),根本沒有傳統意義上的類!

          嗯?等一下!利用對象生成對象?這不應該是Object.create()該干的事兒嗎?沒錯,上面的代碼完全可以利用Object.create()重構!

          這種寫法更符合JavaScript一切皆對象的說法!而且更加清楚易懂,原型鏈從上到下清晰可見。

          JavaScript的new真是個千古大忽悠!還有更忽悠的,ES6里的class,讓js的本質更加撲朔迷離:



          多么美麗的代碼,多么讓人沉浸無法自拔,當當當!給我清醒點!千萬別讓它美麗的外表迷惑!照妖鏡拿來!給我看清楚了,它的本質跟第一段代碼完全一樣!語法糖而已,實際上這里并沒有真正的類,class 仍然是通過 [[Prototype]]機制實現的。

          我們再來看看繼承。

          因為JavaScript沒有真正的類,所以所謂的繼承也都是一些掩人耳目的做法,通過各種惡心的手段達到復用和重寫的目的,來看看有多惡心:



          代碼里SubFoo繼承了Foo,并且SubFoo重寫了Foo的say方法,里面充滿了大量的xxx.prototype,為了讓SubFoo和Foo扯上關系,必須讓它們的原型鏈接起來:SubFoo.prototype = Object.create(Foo.prototype)。還有許多難以理解的借調(xxx.call()),特別是為了達到繼承Foo的say方法而寫的這一句:Foo.prototype.say.call(this),多么的丑陋。對于當初涉世未深的你能理解這幾句代碼里面的含義嗎?為了理解這些你花了多久?

          為了我腦袋上所剩無幾的頭發,呸!

          當然有了ES6后情況有所好轉:

          還是那句話,語法糖而已,本質上還是要讓Foo和SubFoo兩個小東西互相扯來扯去,如果再深入一點——加上二級、三級繼承——情況會無法想象地復雜。

          說到底這些其實都是強行使用類的思想來理解JavaScript的一切皆對象而出現的"變態"代碼。如果你放下屠刀,換一個思路,使用JavaScript語言最初的設計思路就會發現,一切其實非常簡單,我們用一切皆對象的思路再來實現一遍上面的邏輯:


          怎么樣?沒有了亂七八糟的prototype和構造函數,不用擔心原型鏈的走向,一切清新自然,而且最重要的,一切都是對象。

          這種實現方式的官方叫法(非人話叫法)就是"行為委托"。在行為委托模式中,Foo和 SubFoo只是對象,它們之間是兄弟關系,并不是父類和子類的關系。代碼中 Foo委托了 SubFoo,反向委托也完全沒問題,我們也不需要實例化類,因為它們根本就不是類,它們只是對象。此外,我們擺脫了一大堆的prototype和借調,我們使用了一種極其簡單的方式完成了封裝。

          當然往深處里講,上述幾種方式每個方式都有自己的優缺點。不能很武斷地說這個好那個不好,在不同場景里選擇最合適的實現方式是作為一名高級技術人員時刻該考慮的事情。

          向過程和面向對象編程概述

          面向過程編程就是分析出解決問題的步驟,然后使用函數把這些步驟一步步實現,重心放在完成的每個過程上。

          面向對象則是以封裝的思想,將問題分析得到的數據封裝成一個個的對象,然后通過對對象的操作來完成相應的功能。

          舉個栗子:廚師炒菜

          以面向過程的思想來分析應該分為下面幾個步驟:

          ? 1.檢查食材是否齊全 2.如果不不夠,去菜市場買菜 3.洗菜 4.開火 5.按炒菜(按順序放入相應的食材,調料等) 6.出鍋裝盤

          以面向對象的思想分析則是這樣的:

          ? 1.廚師,檢查食材,炒菜 2.采購員,去菜市場買菜 3.墩子,洗菜,切菜,備菜

          ? 通過調用上面對象中的行為方法即可完成炒菜的整個過程

          從上面的例子可以看出,面向對象和面向過程最大的不同在于,面向對象關心的是由哪些對象,每個對象應該有哪些功能,而面向過程關心的是實現過程中的每個步驟。

          那么這兩種思想到底孰優孰劣呢?從表面上看,貌似面向對象更好,為什么呢?因為它完全符合我們的正常思維方式,所以在接受度方面,面向對象的思想肯定是更好。但是面向過程也有他的優勢,就是靈活便捷,而面向對象相對來說會更耗資源,更慢一點。

          所以,至于以后使用哪一種,這就需要看我們的具體需求,根據不同的需求做不同的選擇。

          面向對象編程的相關概念

          通過上面的分析,我們知道面向對象的重點在于功能分析和對象的封裝上,那么最終我們得到的對象的結構是怎樣的,我們繼續往下學習。

          比如,我通過對人的分析得到,每個人都有姓名,年齡,性別等屬性,同時也有吃飯睡覺等行為,那么用JS可以做如下的封裝:

          var p = {
              name : "xiao song",
              age : 10,
              sex : 1,
              eat : function () {
                  console.log("吃飯");
              },
              sleep : function () {
                  console.log("睡覺");
              }
          }
          console.log(p.name);//訪問對象的屬性
          p.eat();//訪問對象的方法
          

          上面的p則表示一個對象,其中的name / age / sex稱之為對象的屬性,eat / sleep 稱之為對象的方法,我們通過訪問該對象的屬性或者方法達到相應的目的即可。

          DOM操作相關知識點復習

          在學習了html之后我們發現,html文檔中的內容其實就是由一堆的標簽組成,由于在后面的課程中需要使用到html,所以我們先大致的回顧一下它的結構。

          <div id="div1" class="clz1">
              <h3>H5-JS面向對象</h3>
          </div>
          

          div h3:元素節點

          id class:屬性節點

          H5-JS面向對象:文本節點

          一個html文檔主要由這三部分組成,DOM(文檔對象模型)是對操作這些元素的屬性或者方法進行了封裝,從而達到方便快捷的操作html的目的。

          獲取元素對象:document.getElementById(“div1”)

          訪問元素的屬性:div1.className

          訪問元素的文本內容:div1.innerText

          增刪改元素:div1.appendChild(newNode)

          下面,我們就通過這些API來講解說明面向對象相關的內容。

          創建并設置標簽(面向過程)

          需求1:創建三個div元素,并設置邊框,背景色,文本及字體顏色

          for (var i = 0; i < 3; i++) {
          	var div = document.createElement("div");
          	div.innerText="div"+i;
          	div.style.backgroundColor="green";
          	div.style.border="1px solid #000";
          	div.style.color="white";
          	document.body.appendChild(div);
          }
          

          需求2:為頁面中存在的三個P元素設置邊框,背景色,文本及字體顏色

          <p>我是P1</p>
          <p>我是P2</p>
          <p>我是P3</p>
          <script>
              var ps = document.getElementsByTagName("p");
              for (var i = 0; i < ps.length; i++) {
                  ps[i].style.backgroundColor="red";
                  ps[i].style.border="1px solid #000";
                  ps[i].style.color="white";
              }
          </script>
          

          需求3:獲取頁面上三個class=“test”的元素,設置邊框,背景色,文本及字體顏色

          <h3 class="test">我是標題1</h3>
          <h3 class="test">我是標題2</h3>
          <h3 class="test">我是標題3</h3>
          
          <script>
          	var tests = document.getElementsByClassName("test");
          	for (var i = 0; i < tests.length; i++) {
          	    tests[i].style.backgroundColor="yellow";
          	    tests[i].style.border="1px solid #000";
          	    tests[i].style.color="red";
          	}
          </script>
          

          上面的代碼是以面向過程的思想完成的,可以看到,兩個需求中的每個步驟都是我們一步一步完成的,問題很明顯,代碼大量的冗余,這種代碼后期不好維護。

          創建并設置標簽(函數封裝)

          對于上面重復的代碼,我們可以使用函數對其進行封裝

          <script>
              function setStype(eles,bgcolor) {
                  for (var i = 0; i < eles.length; i++) {
                      eles[i].style.backgroundColor=bgcolor;
                      eles[i].style.border="1px solid #000";
                      eles[i].style.color="white";
                  }
              }
              function getElementsByTagName(tagName) {
                  return document.getElementsByTagName(tagName);
              }
              function getElementsByClassName(className) {
                  return document.getElementsByClassName(className);
              }
              var ps = getElementsByTagName("p")
              setStype(ps,"green");
              var tests=getElementsByClassName("test");
              setStype(tests,"red");
          </script>
          

          封裝了三個函數:

          1. setStype(eles,bgcolor):為元素設置樣式? eles:哪些元素? bgcolor:背景色
          2. getElementsByTagName(tagName):根據元素名稱獲取指定的元素? tagName:元素名
          3. getElementsByClassName(className):根據class屬性名獲取指定的元素? className:class屬性名

          接下來就是調用三個方法完成了上面的需求,解決了第一種方式中大量的重復代碼的問題。

          但是,這種方式仍然存在問題。在前面JS基礎中說過,我們應該盡量避免大量使用全局變量,這會降低程序的執行效率,在上面的程序中,我們就出現了5個(包括函數)。所以需要繼續優化。

          創建并設置標簽(面向對象)

          使用面向對象的思想來解決上面的問題,我們可以將上面的三個函數都裝到一個對象中

          var $ = {
              setStype:function (eles,bgcolor) {
              	for (var i = 0; i < eles.length; i++) {
              	    eles[i].style.backgroundColor=bgcolor;
              	    eles[i].style.border="1px solid #000";
              	    eles[i].style.color="white";
              	}
              },
              getElementsByTagName: function (tagName) {
                  return document.getElementsByTagName(tagName);
              },
              getElementsByClassName:function (className) {
                  return document.getElementsByClassName(className);
              }
          }
          var ps = $.getElementsByTagName("p")
          $.setStype(ps,"green");
          var tests=$.getElementsByClassName("test");
          $.setStype(tests,"red");
          

          后面如果我們還都需要封裝其他功能,可以直接在$這個對象中添加即可

          如,根據元素的id屬性獲取元素,并為其設置樣式

          getElementById:function (eleId) {
          	return [document.getElementById(eleId)];
          }
          

          需要注意的是,在設置樣式方法中,我們默認是將傳遞進來的元素當做數組進行處理的,所以,在這里,我們在getElementById方法中,手動將獲取到的元素添加到數組中返回。

          通過觀察,在$對象中,存在三個獲取元素的方法,這里我們最好將其按照下面的方式來歸類

          var $ = {
              getElements:{
                  byTagName: function (tagName) {
                      return document.getElementsByTagName(tagName);
                  },
                  byClassName:function (className) {
                      return document.getElementsByClassName(className);
                  },
                  byId:function (eleId) {
                      return [document.getElementById(eleId)];
                  }
              },
              setStype:function (eles,bgcolor) {
                  for (var i = 0; i < eles.length; i++) {
                      eles[i].style.backgroundColor=bgcolor;
                      eles[i].style.border="1px solid #000";
                      eles[i].style.color="white";
                  }
              }
          }
          

          將獲取元素的方法封裝到$對象的getElements屬性中,今后如果還有其他獲取元素的方法,都應該是添加到getElements屬性中,其他類型的方法也應該按照這種思想進行封裝。

          面向對象編程的三大特性

          面向對象的特性:

          1. 封裝作用:復用和信息隱藏封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
          2. 繼承它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴展。通過繼承創建的新類稱為“子類”或“派生類”。被繼承的類稱為“基類”、“父類”或“超類”。繼承的過程,就是從一般到特殊的過程。
          3. 多態當存在繼承關系時,允許將父類對象看成為和它的一個或多個子類對象等同.這樣,可以根據當前賦給父類對象的子對象的具體特性以不同的方式進行運行.

          用字面量方式創建對象

          直接使用字面量方式創建對象比較方便,以鍵值對的格式來定義數據

          var book1 = {
              name:"JavaScript權威指南",
              price:100,
              author:"tim",
              showInfo:function () {
                  console.log(this.name,this.price,this.author);
              }
          }
          console.log(book1);
          

          上面定義了一個書對象,并為其添加了屬性和方法,我們也可以直接訪問其中的屬性和方法。

          這種方式的弊端是,如果需要創建多個類似的對象,就顯得不太方便了,會出現大量的重復代碼。

          也就是說,這種方式不適合創建大量的相同或相似的對象。

          內置構造函數和簡單工廠創建對象

          使用new關鍵字+內置的構造函數創建對象

          var book2 = new Object();
          book2.name="JS";
          book2.price=10;
          book2.author="作者";
          book2.showInfo=function () {
              console.log(this.name,this.price,this.author);
          }
          book2.showInfo();
          

          這種方式和字面量方式創建對象存在的問題差不多,在大量創建對象的時候都會存在大量重復的代碼。

          那么,利用前面的封裝的思想,我們應該可以想到,當有重復代碼的時候,我們可以將這些重復代碼抽取到函數中來解決。

          function createBook(name, price, author) {
              var book = new Object();
              book.name=name;
              book.price=price;
              book.author=author;
              book.showInfo=function () {
                  console.log(this.name,this.price,this.author);
              }
              return book;
          }
          var book3 = createBook("bookName1",10,"author1");
          var book4 = createBook("bookName2",10,"author2");
          console.log(book3);
          console.log(book4);
          

          我們將創建book對象的代碼封裝到createBook函數中,當需要創建一個book對象的時候,直接調用該函數,將函數需要的參數傳遞過去即可。

          那么,相同的思想,如果我們需要創建其他的對象,一樣可以使用封裝函數的方法來解決,這是沒問題的。

          function createPerson(name, age) {
              var p = new Object();
              p.name = name;
              p.age = age;
              return p;
          }
          console.log(createPerson("Neld", 10))
          

          利用上面的函數,我們可以創建一個Person對象出來,但是通過打印對比,我們無法通過創建出來的對象判斷該對象的類型,而在實際開發中,判斷對象的類型是我們經常需要執行的,所以我們繼續看下面的自定義構造函數創建對象。

          自定義構造函數創建對象

          構造函數和普通的函數的定義方式完全一樣,如下,我們定義一個創建Person的構造函數

          function createPerson(name, age, sex) {
              this.name=name;
              this.age=age;
              this.sex=sex;
          }
          var p = new createPerson("Neld", 10, 1);
          var p2 = new createPerson("Song", 12, 0);
          console.log(p);
          console.log(p2);
          

          自定義函數和工廠函數非常相似,但是還是存在很大的區別

          1. 構造函數名的首字母要求大寫
          2. 需要使用new關鍵字和構造函數一起創建對象
          3. 在函數中,不需要手動創建對象進行數據封裝,會自動創建并封裝數據
          4. 在函數最后,不需要手動返回創建好的對象,會自動返回

          到這里,大家肯定會有疑問,自定義構造函數到底是如何創建并封裝對象呢?

          1. 在函數內部默認會創建一個空對象 var obj = new Object();
          2. 默認把創建好的對象賦值給this this = obj;
          3. 默認設置this的原型對象為當前構造函數的原型對象
          4. 通過this添加屬性和方法
          5. 默認會把內部創建的對象返回 return this;

          通過上面的分析,相信大家已經能夠理解自定義構造函數的基本使用以及基本的原理了。

          構造函數創建對象的返回值

          默認情況下,構造函數內部會返回新創建好的對象(this)

          主動返回:

          1. 如果返回值類型的數據,仍然返回創建好的對象(this),不做任何修改
          2. 如果返回引用類型的數據,則返回指定的數據,不再返回this。

          函數作為構造函數參數使用

          在JS世界里,函數屬于一等公民,擁有最高特權,在使用過程中可以作為變量賦值,可以作為參數傳遞,也可以作為函數的返回值,下面我們具體來看看他的使用。

          函數作為參數使用

          function f1(name,age,fn) {
              console.log("name:",name,"age:",age);
              fn();
          }
          function fn(){
              console.log("Hello H5");
          }
          f1("Neld", 10, fn);
          

          輸出結果:

          ? name: Neld age: 10? Hello H5

          在上面的代碼中,我們將函數fn作為參數傳遞給了函數f1,并且在函數f1中調用,得到的相應的打印輸出。

          函數作為返回值使用

          function f1(name,age,fn) {
              console.log("name:",name,"age:",age);
              return fn;
          }
          function fn(){
              console.log("Hello H5");
          }
          var retFun = f1("Neld", 10, fn);
          retFun();
          

          在函數f1中將傳遞進來的fn作為返回值返回,接收到調用f1之后的返回值得到的是返回的函數,然后再調用retFun得到打印結果。

          此時的f1為高階函數,即參數中有一個或多個函數,并且把函數作為返回值。

          此時的fn為回調函數,fn作為參數傳遞給函數f1,在f1內部調用。

          函數作為構造函數的參數使用

          function createPerson(name, age, sex, say) {
              this.name=name;
              this.age=age;
              this.sex=sex;
              this.say=say;
          }
          var p = new createPerson("Neld", 10, 1, function () {
              console.log("say hello");
          });
          var p2 = new createPerson("Song", 12, 0,function () {
              console.log("say bye");
          });
          p.say();
          p2.say();
          

          在構造函數中也可以對方法進行封裝,如果方法的實現是由調用者決定的,那么可以在構造函數中接收一個函數對象,然后在構造函數中進行封裝。

          如上面的函數say,在創建p和p2對象的時候傳遞并賦值給形參say,然后在構造函數中賦值給當前對象。

          構造器屬性

          前面說到工廠函數創建對象是比較方便的,但是存在一個問題就是無法得知創建出來的對象的類型,所以我們選擇使用自定義的構造函數來創建,構造函數創建對象我們已經會使用了,那么如何通過他得知創建對象的類型呢?這里我們提供兩種方式。

          1. constructor屬性

          使用constructor屬性可以獲取到創建對象使用的構造器函數對象,所以我們可以通過判斷構造器的類型來得知創建的對象的類型

          2.instanceof關鍵字


          instanceof關鍵字可以直接用來判斷對象的類型,如果是指定的類型,返回true,反之返回false。

          構造函數的調用和命名

          在學習了構造函數之后,有的同學對于它和普通函數的區別還是不太清楚,這里我們就再對構造函數做一個說明。

          1. 構造函數和普通函數在定義語法上沒有任何區別function 函數名(參數列表){代碼塊;}
          2. 為了和普通函數區分開,我們約定將構造函數的名稱首字母大寫
          3. 構造函數一樣可以直接調用,此時內部的this執行window,這種方式不太安全,有可能會在函數內部修改當前的全局變量,不建議使用,而且這樣做也不能創建對象
          4. 想要創建對象,必須使用new和構造函數一起使用

          函數上下文和this指針

          在JS編程的過程中發現,我們大量使用到this關鍵字,用好了this,能讓我們的代碼更加優雅。

          this總是執行一個對象(引用類型),但是具體執行誰,需要根據我們在哪里使用this有關。這里主要分為下面幾種情況:

          1. 函數外部函數外部的作用域是全局作用域(window),所以,在全局作用域中使用的this指向window
          2. 普通函數內部函數內部的作用域是局部的,屬于調用當前函數的對象,所以this執向調用當前函數的對象
          3. 構造函數內部在構造函數中,this直接執行當前創建出來的新對象

          在開發中,我們也可以使用call或者apply函數修改this的執行,這一點我們在后面繼續說明。

          自定義構造函數存在的問題

          自定義構造函數可以解決工廠函數帶來的對象類型不確定的問題,在開發中用得非常多,那么目前我們的自定義構造函數又是否存在問題呢?先來看看下面的對象內存結構分析。

          function Person(name, age, say) {
              this.name = name;
              this.age = age;
              this.say = function(){
                  console.log("say hello");
              }
          }
          var p = new Person("zs", 10, say);
          console.log(p);
          

          上面創建的p對象的內存結構圖:

          可以看出,我們每創建一個Person對象,都會在內存中分配如0x22和0x33這樣的內存來存儲數據,但是通過觀察發現,0x33中存儲的是一個函數,而這個函數在每個對象中都是相同

          所以從內存資源分配考慮,我們無需為每個對象創建并分配一份新的函數對象(完全相同),這種函數大家最好共享同一份。


          主站蜘蛛池模板: 在线精品日韩一区二区三区| 狠狠爱无码一区二区三区| 国产日韩精品一区二区三区| 日本道免费精品一区二区| 国产精品无码亚洲一区二区三区| 久久国产精品最新一区| 亚洲高清偷拍一区二区三区| 日本精品无码一区二区三区久久久 | av无码一区二区三区| 国产激情一区二区三区四区| 精品国产一区AV天美传媒| 无码少妇一区二区性色AV| 精品国产日产一区二区三区| 任你躁国产自任一区二区三区| 一区二区三区视频观看| 日韩一区二区三区视频| 麻豆一区二区三区蜜桃免费| 国产精品视频一区国模私拍| 无码中文字幕乱码一区| 亚洲中文字幕乱码一区| 日本一区二三区好的精华液| 欧美日本精品一区二区三区 | 美女啪啪一区二区三区| 国产亚洲一区二区手机在线观看 | 日本精品少妇一区二区三区| 免费人妻精品一区二区三区| 国产成人综合亚洲一区| 日韩一区二区三区视频| 中文字幕在线观看一区| 日韩AV无码一区二区三区不卡毛片 | 国产乱码精品一区二区三区四川人 | 美女视频免费看一区二区| 一区二区三区杨幂在线观看| AV无码精品一区二区三区宅噜噜| 国产精品免费一区二区三区 | 久久一区二区三区精华液使用方法| 色综合视频一区中文字幕| 日本v片免费一区二区三区 | 无码人妻一区二区三区免费| 国产成人一区二区三区精品久久| 国产在线步兵一区二区三区|