正文

arguments-argument數(shù)學(xué)復(fù)數(shù)

xinfeng335
文章最后更新時(shí)間2025年02月05日,若文章內(nèi)容或圖片失效,請(qǐng)留言反饋!

  

arguments-argument數(shù)學(xué)復(fù)數(shù)
(圖片來源網(wǎng)絡(luò),侵刪)

  Java是按照ECMA標(biāo)準(zhǔn)設(shè)計(jì)和實(shí)現(xiàn)arguments的,后文說的Java語法其實(shí)是ES5的標(biāo)準(zhǔn)的實(shí)現(xiàn)。

  先說說有哪些基礎(chǔ)語法?

  01-

  最基礎(chǔ)語法有哪些?

  基礎(chǔ)語法幾乎所有的語言差異不大,無非數(shù)據(jù)類型、操作符、控制語句、函數(shù)等,簡(jiǎn)單列舉下。

  5種基本數(shù)據(jù)類型 & 1種復(fù)雜的數(shù)據(jù)類型

  Java包含5種基本數(shù)據(jù)類型,分別是undefined / null / boolean / number / string,基本數(shù)據(jù)類型就這五種,沒有其arguments他的!

  Java包含1種復(fù)雜的數(shù)據(jù)類型,就是Object類型,Object類型是所有其他對(duì)象的基類。(注意:Java并不區(qū)分浮點(diǎn)數(shù)和整數(shù),都是用number來表示。)

  前面提到的5種基本數(shù)據(jù)類型,以及這兒的1種復(fù)雜數(shù)據(jù)類型,這就是數(shù)據(jù)類型的全部arguments了!

  基本操作符

  這個(gè)是常識(shí),知道怎么回事就好。

  常用的操作符包括:算術(shù)操作符、關(guān)系操作符、布爾操作符、賦值操作符等。

  控制語句

  這就是我們常說的if-else之類的控制語句。

  常用的并不多:if語句、switch語句、for語句、while語句、for-in語句。

  函數(shù)

  函數(shù)就是一小段邏輯的封裝,理論上邏輯越獨(dú)立越好。

  Java函數(shù)相對(duì)其他語言來說有很大不同。Java函數(shù)既可以作為參數(shù),也可以作為返回值。

  此外Java函數(shù)可以接受任意數(shù)量的參數(shù),并且可以通過arguments對(duì)象來訪問這些參數(shù)。

  任何一門語言的基礎(chǔ)語法都是相通的,除開一些細(xì)節(jié)差異,大致就是上面這些了:數(shù)據(jù)類型、操作符、控制語句、函數(shù)、模塊等等。

  接下來介紹稍微復(fù)雜的一些概念。

  02-

  變量、作用域、內(nèi)存問題

  變量

  Java變量分為兩種:基本類型和引用類型。其中基本類型就是前面提到的5種基本數(shù)據(jù)類型,引用類型就是前面提到的Object以及基于它的其他復(fù)雜數(shù)據(jù)類型。

基本類型:在內(nèi)存中占據(jù)實(shí)際大小的空間,賦值的時(shí)候,會(huì)在內(nèi)存中創(chuàng)建一份新的副本。保存在棧內(nèi)存中。

引用類型:指向?qū)ο蟮闹羔樁皇菍?duì)象本身,賦值的時(shí)候,只是創(chuàng)建了一個(gè)新的指針指向?qū)ο?。保存在堆?nèi)存中。

  

變量內(nèi)存分配

  一句話就是,基本類型在內(nèi)存中是實(shí)際的值;而引用類型在內(nèi)存中就是一個(gè)指針,指向一個(gè)對(duì)象,多個(gè)引用類型可能同時(shí)指向同一個(gè)對(duì)象。

  那么,如何確定某個(gè)變量是哪種數(shù)據(jù)類型呢?

  確定一個(gè)變量是哪種基本類型用typeof操作符。

  確定一個(gè)變量是哪種引用類型用instanceof操作符。

  這個(gè)別忘了!

  作用域

  變量是在某個(gè)特定的作用域中聲明的,作用域決定了這些變量的生命周期,以及哪些代碼可以訪問其中的變量。

  Java作用域只包括全局作用域和函數(shù)作用域,并不包含塊級(jí)作用域!

  作用域是可以嵌套的,從而形成作用域鏈。由于作用域鏈的存在,可以讓變量的查找向上追溯,即子函數(shù)可以訪問父函數(shù)的作用域=>祖先函數(shù)的作用域=>直到全局作用域,這種函數(shù)我們也稱為閉包,后文會(huì)介紹。

  var color = "blue";

  function changeColor() {

  var anotherColor = "red";

  function swapColors() {

  var tempColor = anotherColor;

  anotherColor = color;

  color = tempColor;

  // 這里可以訪問color、anotherColor、tempColor

  }

  // 這里可以訪問color、anotherColor,但不能訪問tempColor

  swapColors();

  }

  // 這里只能訪問color、changeColor();

  如下圖所示,每個(gè)作用域能夠訪問到的變量以及嵌套的作用域可向上追溯。

  

作用域鏈

  作用域的概念看著簡(jiǎn)單,實(shí)際使用會(huì)有不少問題,遇到問題要細(xì)心分析。

  內(nèi)存問題

  Java引擎具有自動(dòng)垃圾回收機(jī)制,不需要太關(guān)注內(nèi)存分配和垃圾回收問題。這兒就不展開了!

  03-

  引用類型

  前面提過,Object是唯一的復(fù)雜數(shù)據(jù)類型,引用類型都是從Object類型上繼承而來。

Array:數(shù)組類型

Date:日期類型

RegExp:正則表達(dá)式類型,這個(gè)多學(xué)學(xué)有好處!

等等...

  那問題來了,我們用的最多的函數(shù)是什么數(shù)據(jù)類型呢?答案是Function類型!

  誒,好像發(fā)現(xiàn)了點(diǎn)什么東西?由于Function是引用類型,而Java又可以往引用類型上加屬性和方法。那么,函數(shù)也可以!這也是Java函數(shù)強(qiáng)大和復(fù)雜的地方。也就是說:函數(shù)也可以擁有自定義方法和屬性!

  此外,Java對(duì)前面提到的5種基本類型的其中3種也做了引用類型封裝,分別是Boolean、Number、String,但其實(shí)使用不多,了解就行。

  對(duì)了,在所有代碼執(zhí)行之前,作用域就內(nèi)置了兩個(gè)對(duì)象,分別是Global和Math,其中瀏覽器的Global就是window啦!

  到此為止,Java中基礎(chǔ)的概念都差不多介紹了,其中函數(shù)和作用域相對(duì)來說復(fù)雜一些,其他的都比較淺顯。

  接下來,我會(huì)介紹介紹Java中一些稍微復(fù)雜一些的概念:面向?qū)ο蟆?/p>

  03-

  面向?qū)ο缶幊?/p>

  Java本身并沒有類和接口的概念了,面向?qū)ο蠖际腔谠蛯?shí)現(xiàn)的。

  為了簡(jiǎn)單,我們只分析面向?qū)ο蟮膬蓚€(gè)問題:

如何定義一個(gè)類?

如何實(shí)現(xiàn)類的繼承

  定義一個(gè)類

  不扯其他的,直接告訴你。我們使用構(gòu)造函數(shù)+原型的方式來定義一個(gè)類。

  使用構(gòu)造函數(shù)創(chuàng)建自定義類型,然后使用new操作符來創(chuàng)建類的實(shí)例,但是構(gòu)造函數(shù)上的方法和屬性在每個(gè)示例上都存在,不能共享,于是我們引入原型來實(shí)現(xiàn)方法和屬性的共享。

  

原型

  最后,我們將需要共享的方法和屬性定義在原型上,把專屬于實(shí)例的方法和屬性放到構(gòu)造函數(shù)中。到這兒,我們就通過構(gòu)造函數(shù)+原型的方式定義了一個(gè)類。

  // 構(gòu)造函數(shù)

  function Person(name, age, job) {

  this.name = name;

  this.age = age;

  this.job = job;

  this.friends = ["Shelby", "Court"];

  }

  // 原型

  Person.prototype = {

  constructor: Person,

  sayName: function() {

  return this.name;

  }

  }

  // 實(shí)例化

  var person1 = new Person("Nicholas", 29, "Software Engineer");

  var person2 = new Person("Greg", 27, "Doctor");

  person1.friends.push("Van");

  alert(person1.friends); //輸出"Shelby,Count,Van"

  alert(person2.friends); //輸出"Shelby,Count"

  alert(person1.friends === person2.friends); //輸出false

  alert(person1.sayName === person2.sayName); //輸出true

  實(shí)現(xiàn)繼承

  前文講了如何定義一個(gè)類,那么我們定義一個(gè)父類,一個(gè)子類。

  如何讓子類繼承父類呢?不扯別的,直接告訴你。Java通過原型鏈來實(shí)現(xiàn)繼承!

  如何構(gòu)建原型鏈呢?將父類實(shí)例賦值給子類構(gòu)造函數(shù)的原型即可。好繞,但是千萬得記住了!

  

原型鏈繼承

  構(gòu)建原型鏈之后,子類就可以訪問父類的所有屬性和方法!

  // 父類

  function SuperType() {

  this.property = true;

  }

  SuperType.prototype.getSuperValue = function() {

  return this.property;

  };

  // 子類

  function SubType() {

  this.subproperty = false;

  }

  //子類繼承父類

  SubType.prototype = new SuperType();

  //給子類添加新方法

  SubType.prototype.getSubValue = function() {

  return this.subproperty;

  };

  //重寫父類的方法

  SubType.prototype.getSuperValue = function() {

  return false;

  };

  // 實(shí)例化

  var instance = new SubType();

  console.log(instance.getSuperValue()); //輸出false

  面向?qū)ο蟮闹R(shí)可以用一本書來寫,這兒只是簡(jiǎn)單的介紹下最基礎(chǔ)最常用的概念。

  03-

  函數(shù)表達(dá)式

  Java中有兩種定義函數(shù)的方式:函數(shù)聲明和函數(shù)表達(dá)式。使用函數(shù)表達(dá)式無須對(duì)函數(shù)命名,從而實(shí)現(xiàn)動(dòng)態(tài)編程,也即匿名函數(shù)。有了匿名函數(shù),Java函數(shù)有了更強(qiáng)大的用處。

  遞歸

  遞歸是一種很常見的算法,經(jīng)典例子就是階乘。也不扯其他的,直接說遞歸的最佳實(shí)踐,上代碼:

  // 最佳實(shí)踐,函數(shù)表達(dá)式

  var factorial = (function f(num) {

  if (num <= 1) {

  return 1;

  } else {

  return num * f(num - 1);

  }

  });

  // 缺點(diǎn):

  // factorial存在被修改的可能

  // 導(dǎo)致 return num * factorial(num - 1) 報(bào)錯(cuò)

  function factorial(num) {

  if (num <= 1) {

  return 1;

  } else {

  return num * factorial(num - 1);

  }

  }

  // 缺點(diǎn):

  // arguments.callee,規(guī)范已經(jīng)不推薦使用

  function factorial(num) {

  if (num <= 1) {

  return 1;

  } else {

  return num * arguments.callee(num - 1);

  }

  }

  遞歸就是這樣,好多人還在使用arguments.callee的方式,改回函數(shù)表達(dá)式的方式吧,這才是最佳實(shí)踐。

  啰嗦一句,好多人覺得遞歸難寫,其實(shí)你將其分為兩個(gè)步驟就會(huì)清晰很多了。

邊界條件,通常是if-else。

遞歸調(diào)用。

  按這個(gè)模式,找?guī)讉€(gè)經(jīng)典的遞歸練練手,就熟悉了。

  閉包

  很多人經(jīng)常覺得閉包很復(fù)雜,很容易掉到坑里,其實(shí)不然。

  那么閉包是什么呢?如果一個(gè)函數(shù)可以訪問另一個(gè)函數(shù)作用域中的變量,那么前者就是閉包。由于Java函數(shù)可以返回函數(shù),自然,創(chuàng)建閉包的常用方式就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)!

  這并沒有什么神奇的,在父函數(shù)中定義子函數(shù)就可以創(chuàng)建閉包,而子函數(shù)可以訪問父函數(shù)的作用域。

  我們通常是因?yàn)楸婚]包坑了,才會(huì)被閉包嚇到,尤其是面試題里一堆閉包。

  閉包的定義前面提了,如何創(chuàng)建閉包也說了,那么我們說說閉包的缺陷以及如何解決?

  /* 我們通過subFuncs返回函數(shù)數(shù)組,然后分別調(diào)用執(zhí)行 */

  // 返回函數(shù)的數(shù)組subFuncs,而這些函數(shù)對(duì)superFunc的變量有引用

  // 這就是一個(gè)典型的閉包

  // 那么有什么問題呢?

  // 當(dāng)我們回頭執(zhí)行subFuncs中的函數(shù)的時(shí)候,我們得到的i其實(shí)一直都是10,為什么?

  // 因?yàn)楫?dāng)我們返回subFuncs之后,superFunc中的i=10

  // 所以當(dāng)執(zhí)行subFuncs中的函數(shù)的時(shí)候,輸出i都為10。

  //

  // 以上,就是閉包最大的坑,一句話理解就是:

  // 子函數(shù)對(duì)父函數(shù)變量的引用,是父函數(shù)運(yùn)行結(jié)束之后的變量的狀態(tài)

  function superFunc() {

  var subFuncs = new Array();

  for (var i = 0; i < 10; i++) {

  subFuncs[i] = function() {

  return i;

  };

  }

  return subFuncs;

  }

  // 那么,如何解決上訴的閉包坑呢?

  // 其實(shí)原理很簡(jiǎn)單,既然閉包坑的本質(zhì)是:子函數(shù)對(duì)父函數(shù)變量的引用,是父函數(shù)運(yùn)行結(jié)束之后的變量的狀態(tài)

  // 那么我們解決這個(gè)問題的方式就是:子函數(shù)對(duì)父函數(shù)變量的引用,使用運(yùn)行時(shí)的狀態(tài)

  // 如何做呢?

  // 在函數(shù)表達(dá)式的基礎(chǔ)上,加上自執(zhí)行即可。

  function superFunc() {

  var subFuncs = new Array();

  for (var i = 0; i < 10; i++) {

  subFuncs[i] = function(num) {

  return function() {

  return num;

  };

  }(i);

  }

  return subFuncs;

  }

  綜上,閉包本身不是什么復(fù)雜的機(jī)制,就是子函數(shù)可以訪問父函數(shù)的作用域。

  而由于Java函數(shù)的特殊性,我們可以返回函數(shù),如果我們將作為閉包的函數(shù)返回,那么該函數(shù)引用的父函數(shù)變量是父函數(shù)運(yùn)行結(jié)束之后的狀態(tài),而不是運(yùn)行時(shí)的狀態(tài),這便是閉包最大的坑。而為了解決這個(gè)坑,我們常用的方式就是讓函數(shù)表達(dá)式自執(zhí)行。

  此外,由于閉包引用了祖先函數(shù)的作用域,所以濫用閉包會(huì)有內(nèi)存問題。

  好像把閉包說得一無是處,那么閉包有什么用處呢?

  主要是封裝吧...

  封裝

  閉包可以封裝私有變量或者封裝塊級(jí)作用域。

  ? 封裝塊級(jí)作用域

  Java并沒有塊級(jí)作用域的概念,只有全局作用域和函數(shù)作用域,那么如果想要?jiǎng)?chuàng)建塊級(jí)作用域的話,我們可以通過閉包來模擬。

  創(chuàng)建并立即調(diào)用一個(gè)函數(shù),就可以封裝一個(gè)塊級(jí)作用域。該函數(shù)可以立即執(zhí)行其中的代碼,內(nèi)部變量執(zhí)行結(jié)束就會(huì)被立即銷毀。

  function outputNumbers(count) {

  // 在函數(shù)作用域下,利用閉包封裝塊級(jí)作用域

  // 這樣的話,i在外部不可用,便有了類似塊級(jí)作用域

  (function() {

  for (var i = 0; i < count; i++) {

  alert(i);

  }

  })();

  alert(i); //導(dǎo)致一個(gè)錯(cuò)誤!

  }

  // 在全局作用域下,利用閉包封裝塊級(jí)作用域

  // 這樣的話,代碼塊不會(huì)對(duì)全局作用域造成污染

  (function() {

  var now = new Date();

  if (now.getMonth() == 0 && now.getDate() == 1) {

  alert("Happy new year!");

  }

  })();

  // 是的,封裝塊級(jí)作用域的核心就是這個(gè):函數(shù)表達(dá)式 + 自執(zhí)行!

  (function() {

  //這里是塊級(jí)作用域

  })();

  ? 封裝私有變量

  Java也沒有私有變量的概念,我們也可以使用閉包來實(shí)現(xiàn)公有方法,通過隱藏變量暴露方法的方式來實(shí)現(xiàn)封裝私有變量。

  (function() {

  //私有變量和私有函數(shù)

  var privateVariable = 10;

  function privateFunction() {

  return false;

  }

  //構(gòu)造函數(shù)

  MyObject = function() {};

  //公有/特權(quán)方法

  MyObject.prototype.publicMethod = function() {

  privateVariable++;

  return privateFunction();

  };

  })();

  03-

  總結(jié)說點(diǎn)啥?

  這差不多就是Java的一些基礎(chǔ)語法和稍微高級(jí)一些的用法,其實(shí)所謂的高級(jí),都是Java“不太成熟”的表現(xiàn),尤其是面向?qū)ο?,出于工程化的需要但是Java本身并不完美支持。好在ES6最新標(biāo)準(zhǔn)解決了很多問題,結(jié)合Babel用起來也不用太考慮兼容性問題,如果你是新手的話,建議你直接去擼ES6+Babel吧。

Java的基礎(chǔ)主要包括:5中基本數(shù)據(jù)類型、1種復(fù)雜的數(shù)據(jù)類型、操作符、控制語句、函數(shù)等。

了解基本的語法后,你還需要學(xué)習(xí)學(xué)習(xí)Java的變量、作用域、作用域鏈。

常見的引用類型可以邊查邊用。作為過來人,建議多學(xué)學(xué)正則,對(duì)你的代碼功底會(huì)有較大的提升。

面向?qū)ο缶幊痰牟糠滞饷嬗泻芏喾N方式,你只需要記住使用構(gòu)造函數(shù)+原型去定義一個(gè)類,使用原型鏈去實(shí)現(xiàn)繼承即可。更多的擴(kuò)展,去翻翻書吧。

函數(shù)表達(dá)式引出了幾個(gè)比較好玩的東西:遞歸、閉包、封裝。記住遞歸的最佳實(shí)踐、閉包的定義及缺陷、閉包的適用場(chǎng)景。

  Java作為一門動(dòng)態(tài)語言,和其他語言有較大的差異,這也造成很多人學(xué)習(xí)Java時(shí)會(huì)覺得難學(xué)。但你現(xiàn)在看看前文,雖然是一個(gè)簡(jiǎn)略的總結(jié),但Java主要的內(nèi)容就這些了,所以不要被自己嚇到了。

  再補(bǔ)一句,如果你是新手的話,建議你直接去擼ES6+Babel吧。

? ? ? ? ? ? ? ?

  作者:好奇心日?qǐng)?bào)前端主管 齊修 原文:

  http://www.jianshu.com/p/66f3aef3e131

  點(diǎn)擊“閱讀原文”,看更多

  精選文章

↓↓↓

-- 展開閱讀全文 --