1. 程式人生 > >JavaScript基礎解析

JavaScript基礎解析

JS封神篇

一:說說你對JavaScript 的最直接的瞭解

這個問題怎麼說呢,
		官方的講,
		通過這幾年對js接觸和使用,
		我感覺它時而強大,時而笨拙,有時候無所不能,有事時候
		也讓人抓耳撓腮,尤其近年來node的火熱,js的地位可謂更
		不可取代,作為 前端開發靈魂吧,js的重要性就
		不在強調了,總的概括 就是掌握js的精髓我感覺答題劃分幾個
		部分,首先 它的語法結構我們要深入瞭解,熟練用js語言玩耍
		我們的業務邏輯,其次就是對js高階部分 例如 面向物件開發,
		原型,閉包,各種回撥等等 有深入的理解,其次就是當你掌握
		js 到一定程度時 自己能夠有封裝類似jquery的功能函式。反正
		還是那句話吧, js乃前端的靈魂和核心 至少目前為止是這樣,
		我認為  呵呵呵!
	
	面試官:“小夥子,精闢!!!”

二:js 的資料型別有哪些,你認為目前這些資料型別完善嗎? 1.js的資料型別 包括兩大類: 分別是 基本資料型別,和引用資料型別 基本:string number boolean null undefined 引用:Object Array Function

    2.一般我用什麼 關鍵字 來判別資料型別
       那肯定是 typeof操作符啦

    3.你認為js的資料型別很嚴謹嗎?
       哦,這個...
       這麼說吧,js本身就是一門 鬆散型的 指令碼語言,它
       的資料型別的定義 肯定也是不夠嚴謹的,向我們平時
       使用的一些資料型別 即使定義錯了,也不報錯,不過
       這種狀況也在 慢慢改善,例如 ES6 裡面就新增了,let
       ,const 關鍵字 相對來更接近一些 嚴格的後臺語言那樣
	   
    4.你認為目前的資料型別足夠完善嗎?
       完善的話,我想肯定是否定的,因為隨著技術和業務現實
       的不斷結合,當下我們認識的 肯定日後有滿足不了業務需要
       的情況,就比如說 現在有一些火熱 視屏 影象 音訊 使用者行為
       等等的資源,都可能是促進 我們資料型別擴充套件的一些要求,反正
       個人認為,沒有足夠完善 是能越來越改善 	

三:變數聲明後 它在記憶體結構中是如何體現的? 概述: 說到這個問題 首先提幾個概念,什麼是資料,什麼是變數, 什麼是記憶體 1. 我認為資料簡單地說,就是在程式語言中它是儲存在記憶體或硬碟中代表特定資訊,其最大的特點就是 可儲存,可修改。我們常說一切皆物件,有它的道理但細究的話也不全面,物件實際上是資料的一種容器,或者說是一系列有關係的資料的集合。我覺的更準確的說法是一切皆資料。

       2. 變數 簡單解釋就是 可變化的量, 由變數名和變數值組成,在js中變數名是

字串型別的每個變數都對應的一塊小記憶體, 變數名用來查詢對應的記憶體, 變數值就是記憶體中儲存的資料。變數的值不會是一個物件,引用型別變數儲存的是物件資料的在堆記憶體中的地址

       3. 記憶體 就是我們計算機的儲存資料的空間,當然準確點講,是通電情況下的

儲存空間,臨時的。它分為 堆和棧 結構

    總結:當我們宣告一個 基本資料型別的變數 它直接就會 存到棧空間,用的時候

根據變數名獲取變數值。 當變數是引用型別時 它會將物件的變數名存到棧 空間,物件值儲存到堆空間,我們訪問的時候 會先到棧空間找到對應的指 針也就是變數名,然後再去獲取相對應的值。簡單地說就是 基本資料型別 直接儲存它的值,而引用資料型別 存的是指向堆記憶體的地址。

    經典的實驗:分別宣告一個基本資料型別的變數和一個引用資料型別的變數,當給基

本資料型別的變數重新賦值後 它之前賦給第二個變數的是還是存在的。 反之,引用資料型別由於是儲存的是值得指標也就是地址,當你改變之 前的值,第二個被賦值的變數也會改變。 /基本資料型別/ var num1 = 5; var num2 = num1; num1 = 6; alert(num2);//5 /引用資料型別/ var obj1=new Object(); var obj2=obj1; obj1.name=“Jim”; alert(obj2.name);//“Jim”

四:什麼是變數提升,或者預解析 以及我們需要注意的問題?

  概述:所謂變數提升 就是隻要我們進行變數或者函式宣告,當js載入的時候,js引

擎就會將變數宣告提升到它所在作用域的最開始的部分。 注意的的地方: –當變數名和函式名同名的時候,以函式名優先

五 你能區別一下 typeof 和 instanceof

1.先來說說typeof吧。首先需要注意的是,typeof方法返回一個字串,來表示資料的型別。		  
	  大體對照情況就是這樣:
		Undefined                             “undefined”
		Null	                                   “object”
		布林值	                              “boolean”
		數值	                              “number”
		字串	                              “string”
		Symbol (ECMAScript 6 新增)	             “symbol”
		宿主物件(JS環境提供的,比如瀏覽器)	  Implementation-dependent
		函式物件	                          “function”                
		任何其他物件	                      “object”	
	  存在的問題:
		我們會發現一個問題,就是typeof來判斷資料型別其實並不準確。比如陣列、

正則、日期、物件的typeof返回值都是object,這就會造成一些誤差。 解決: 所以在typeof判斷型別的基礎上,我們還需要利用Object.prototype.toString方 法來進一步判斷資料型別。

2.接下來該說說instanceof方法了。instanceof運算子可以用來判斷某個建構函式
  的prototype屬性是否存在於另外一個要檢測物件的原型鏈上。它的返回值是Boolean

六:js中this指向 簡單的說說?? 概述: 這個問題簡單的可總結為三點 1. 全域性作用域或者普通函式中this指向全域性物件window。 2. 方法呼叫中誰呼叫this指向誰 3. 在建構函式或者建構函式原型物件中this指向建構函式的例項 當然,比較特殊的一個地方也要提一嘴,那就是ES6中 的箭頭函式 this 指向需要記住以下兩點: 1.箭頭函式的this繫結看的是this所在的函式定義在哪個物件下, 繫結到哪個物件則this就指向哪個物件 2.如果有物件巢狀的情況,則this繫結到最近的一層物件上

七:你是如何 理解js中的面向物件說法呢? 概述:說道面向物件,我認為 js中的面向物件可謂是 大道至簡,迴歸原始, 其他語言中說道面向物件,必然會有類的概念,還有明確的繼承等等。 而js中是沒有的,有的只是最經典的 object,從object出發,他的原型 繼承以及類式繼承 也體現了繼承的思想,同時js中的模組化思想也照應了 其他面向物件中的“類”,以至於我們js也是 另一種風格的面嚮物件語言, 做到了 高內聚,低耦合的表現。對於程式碼的擴充套件和未來的優化也是逐步升級。 當然,ES6 中出現了 “class 類”的概念 以及 extends 這也標誌js逐步也在往 純後端語言靠攏。這就是我大概的理解。

八:js中集中建立物件的方式你有什麼見解?? 概述: 分細了說,建立物件的方式五花八門,有很多,什麼字面量,工廠,建構函式,原 型等等。簡單的劃分大體我們最常用的 我感覺就有三種: 1. 直接建立 也就是字面量形式 將成員資訊寫到{}中,並賦值給一個變數,此時這個變數就是一個物件

		2. 建構函式建立方式
		   var obj = new 函式名();
		   
		3. 通過object方式建立。
		   先通過object構造器new一個物件,再往裡豐富成員資訊。
		   var obj = new Object(); 
	總結:這是最常用的,如果要說工廠模式也算一種,我感覺有點牽強,工廠模式只

不過是相當於一個 生產池 將建立物件的事情放到內部函式中 最後將其返 回,它內部的結構也是建構函式 我大體是這麼理解的。

九:new一個物件背後做了些什麼? 概述: 1.建立一個空物件 2.給物件設定__proto, 指向(等價於)建構函式物件的prototype屬性值 3.執行建構函式體(給物件新增屬性/方法)

十:你對 js中的 函式有什麼理解和看法? 概述:通過我的經驗和理解,咱們js中的函式 總的來講分為js內建函式和我們的自 定義函式,內建函式不多介紹了,自定義函式裡大體分為幾種型別 1. 字面量形式的函式 function 函式名(形參){ 函式體 }

	   2.命名函式
		 var 函式名 = function(形參){
		     函式體
		 }
	   
	   3.建構函式
	     var 函式名 = new Function(形參,函式體);
         注意:形參 和 函式體 必須用引號括起來
		 
	   4.回撥函式
	     注意:回撥函式本質也是自定義的普通函式,當它作為引數出現的時候
		       就是我們所謂的回撥函數了
			   
	   5.自調函式
	     (function(){
		   函式體;
		 })();
	     第一個小括號 - 定義函式
         第二個小括號 - 呼叫函式
         作用 - 節省全域性域名稱空間
		 
	總結看法:js中的函式可以稱之為 多樣 靈活。但是其實,函式的本質就是物件。

確切一點來說,其實是第一類物件(first-class object)。關於第一類物件,解釋如下: 第一類物件又稱第一類公民,在程式語言中指的是一個具有以下特性的實體: 1.能夠作為引數被傳遞 2.能夠從一個函式結果中返回 3.能夠被修改和賦值給變數 雖然看起來高大上,但是我們只要先記住,在js裡函式也是物件,可以擁 有自己的屬性和方法,而它和一般js物件的區別是:可以被呼叫,也就是 可執行。當然,函式還有一個明顯的特點就是,提供作用域:在函式作用 域內的變數都是區域性變數,對外部不可見。由於js中其他程式碼塊,比如 for和while迴圈等並不提供作用域,所以有很多地方會利用函式來控制 作用域。

十一:趁熱打鐵 那你能說一下對 建構函式 的認識嗎?

  1.概述:先解釋一下什麼是建構函式
          簡單地說建構函式就是你用new關鍵字建立物件時呼叫的函式。
		作用:
		     建立多個共享特定屬性和行為的物件,主要是用於生成物件的餅乾模具
        缺點:
		     當例項化多個物件時,會重複的建立物件,造成記憶體空間的浪費,
		     增大CPU的開銷,並沒有消除程式碼的冗餘,(如後面程式碼所示,原型正好解

決了此類問題) 存在問題: 同一個建構函式創建出來不同的例項化物件,公用的方法不等同,也就是 說,當你new一個構造器物件,上面的建構函式就執行一遍,每次都會新建一個function,會新開闢一個記憶體空間,每次都是指向一個新的堆的物件 ,這樣佔用記憶體消耗非常的大,怎麼解決這個問題 如何解決: 將建構函式裡面自定義的方法拿出來,獨立放在建構函式外

  2.普通函式和建構函式有什麼區別:
        有new與無new的差別
        寫法上,建構函式首字母大寫(目的只是用於區分普通函式與建構函式,
		提醒你在建立例項化物件前加new操作符)
        當函式沒有被new呼叫時,建構函式中的this就能與全域性this物件(即window)

3.如果想普通函式也具有建構函式的功能,怎麼做? function Animal(name,age){ // 加一this條件判斷,用instanceof來檢查自己是否被new呼叫 if(this instanceof Animal){ this.name = name; this.age = age; this.fun = function(){ return this.name+" "+this.age+“歲了”; } }else{ // 以new遞迴呼叫自己來為物件建立正確的例項,這樣做的目的是在 不同的情況下表現出一致的行為,常常是為了保護那些忘記了使用 new的情況 return new Animal(name,age); } } // 無new的情況 var animal1 = new Animal(“cat”,2); var animal2 = Animal(“dog”,3);

  4. 為何內建建構函式無new也能工作??
      例如:
        var arr = Array; // 當沒有引數時,建構函式後面的圓括號可以省略
		var obj = Object({
			name:"隨筆川跡",
			sex:"boy",
			fun:function(){
			   return this.name+" "+this.sex+" "+Object.prototype.toString.call(this);
		}});
		console.log(obj.fun());
      原因:
	     因為那些內建系統建構函式(Array,Object,RegExp,Date,Error,String等)
		 都被設計為作用域安全的建構函式,也就是說在整個全域性範圍內都是可見的,
		 一個作用域安全的建構函式無new也可以工作,並返回同樣型別的物件

十二:小夥子你太棒啦!以上問題理解的這麼透徹 我決定給你漲5000工資 如果你再能很好的解釋一下 回撥函式 的深意?

  概述:何為回撥函式,官方解釋:當程式跑起來時,一般情況下,應用程式會時常通

過API呼叫庫裡所預先備好的函式。但是有些庫函式卻要求應用先傳給它一個 函式,好在合適的時候呼叫,以完成目標任務。這個被傳入的、後又被呼叫的 函式就稱為回撥函式(callback function)。 也可以這樣說: 通常將一個函式B傳入另一個函式A,並且在 需要的時候再呼叫函式A。 最簡單地說: 說白了,回撥就是回溯,先定義好將要使用的函式體,然後再 呼叫這個函式,我們通常把callback作為一個引數傳入先定義的那個函式。 總結: 我認為回撥函式就是 js 中無法替代的一種靈魂,包括我所瞭解的nodeJS 裡 面大量的回撥函式應用數不盡數,總歸一句話,js是單執行緒 事件驅動的,造就了他無阻塞的特點,那麼回撥就是完成無阻塞呼叫的最強助手。

十三: OK,你已經超神了,我們目前有個 開發組長的位置,不知你願不願意,如果可以的話請跟我再解釋一下我們js中 原型的 理解?

   概述:1.原型說起來比較抽象,我先舉個簡單的小例子:
		“原型”這個東西,就是萬惡的產品經理經常搞的那個嘛~假設產品經理是用

的Sketch(一款製作原型圖的軟體)做的產品原型,那麼UI設計會從產品 經理那兒拷貝一份產品經理做好的原型,然後在上面修改(當然不一定這麼 幹,不過可以這麼幹就是了),最後的苦逼的小前端就會基於設計給出的設 計稿生成頁面。我們也可以這麼說,設計做的事情是把原型改成設計稿, 而 小前端做的則是把設計稿作為參考生成html/css/js。而JavaScript中的原型, 也差不是這個理兒,也就是用來複制一個副本 & 在副本的基礎上修改。

		 2. 技術人員的眼中的原型:
		      原型,顧名思義,本身就擁有一定的功能,只需要改改就能用。
			  JavaScript中每個可構造的函式(非箭頭函式)都有個prototype欄位,
			  也就是所謂的原型。按照基於原型的OO語言的慣例,新建物件相當於

對原有物件的擴充套件或者複製。對應到JS,就是prototype,而JavaScript有 一個new關鍵字,語義是將建構函式F的prototype設定為新物件的 __proto, 並執行F。我們可以換一種等價的說法,new操作符建立了一 個新物件,並將F.prototype以某種形式複製(實際上是給了個引用,但是可以理解為寫時複製),而F本身則是對新物件的一些修飾。這樣的, 原型就真的成為了"原型"。既然new是對原型的拷貝,那麼很自然的就 有:obj instanceof F的判別方式是obj是否具有F.prototype的所有屬性; Function.prototype 是function(){}。這樣是很符合直覺的。

		 3. 順帶也說一下原型鏈: 
		      我們都知道每個物件都有一個prototype屬性,指向它的原型物件.這個

原型物件裡面同時還有自己的原型,原型一環扣一環,直到某個物件的原 型為null,這一級一級的鏈結構就是原型鏈。

			  其實原型和原型鏈不用那麼複雜的去理解,很簡單!!!
			       a = new A();
				   a的原型(模板)就是A這個建構函式,a是通過A刻出來的,a

的爸(或者媽)就是A,A的爸(或者媽)又是誰呢?A是一個構 造函式,那麼函式是一個物件,是一個具體的物件,那它的爸爸 媽媽就是一個抽象的物件 也就是ObjectObject的爸爸媽媽是誰 呢,沒了,他自己已經到頭了,所以它的原型就是null。

			   再舉個例子:
			       var A = function(){};
                   var a = new A();
			分析:
				   a._proto_ 就是 例項化之前的A ,模板就是function,也就是

A.prototype,A.prototype就是指的是function這個物件,而 constructor就是指向自己本身,那麼 function的模板呢,當然就是 Object啦,所以A.prototype._proto_就等價於 function.proto, 具體的function的模板就是Object而最高的物件的模板呢,沒了, 就是null,我想你們應該都明白了吧。看到好多人用圖,其實讓大 家思考的時候大家就按自己的想法走了

			 4. 原型鏈繼承也簡單說一下:
					 js屬性查詢:由於js原型鏈的存在,當查詢一個物件屬性時候,不

只是在物件上查詢,還會沿著該js物件的原型鏈往上查詢,直到找到一 個匹配的屬性或者查詢到原型鏈末尾。當然如果js物件上與其原型 的物件上都有同名的屬性,我們遵循該屬性的作用域就近原則 (術語 叫做"屬性遮蔽").而這種物件能共享原型中的屬性和方法的關係就叫 做繼承。當然ES6 中引出了 偉大的class 我們以後也可以清晰明瞭 的利用class 和關鍵字extends來建立繼承關係,更好的實現面向對 象的多型的思想。

			 5. 在實際開發當中 我們 什麼場景下用到了原型的相關知識
			    在實際開發中,會怎麼實現面向物件的原型繼承呢。正常在我們拿到

需求的時候,如果需求邏輯複雜,且在多個頁面中有相似邏輯的時候, 我們就會想到使用面向物件了,因為面向物件解決的就是邏輯的封裝 和複用。假設頁面A,頁面B,頁面C中存在相同邏輯,那麼我們可 以封裝父建構函式物件,首先將頁面A,頁面B,頁面C中相同邏輯 抽離,相同邏輯可以是同一個ajax請求返回資料,或者是資料格式化 等等的相同操作。將這些方法在Parent.prototype中定義,至於A, B,C頁面自己特有的方法,則在如A.prototype中定義。這樣很好地 瞭解決了我們的問題,邏輯清晰,程式碼複用性強。如果在Parent方法 引數中加入了回撥callback,並且在callback中想呼叫子函式方法或者 屬性

十四: 你的回答很完美,其實有個祕密想告訴你,我就是咱們公司 的產品經理, 我們產品是很可愛的,不要有偏見。放下偏 見 你能再回答一下閉包的相關知識嗎? 此時心裡有一萬頭“草泥馬”奔過… 這題一定要好好答 概述:談閉包首先 鋪墊一下 我們js中的作用域和執行上下文的內容 1. 理解作用域: 在javascript中,作用域是執行程式碼的上下文(方法呼叫中this所代表 的值),作用域有三種類型: a. 全域性作用域(Global scope) b. 區域性作用域(Local/Function scope,函式作用域) c. eval作用域 在函式內部使用var定義的程式碼,其作用域都是區域性的,且只對該函式 的其他表示式是可見的,包括巢狀子函式中的程式碼,區域性變數只能在它 被呼叫的作用域範圍內進行讀和寫的操作,在全域性作用域內定義的變 量從任何地方都是可以訪問的,因為它是作用域鏈中的最高層中的最 後一個,在整個範圍內都是可見的

             注意:在Es6之前是沒有塊級作用域的,而Es6後是有的,也就是說

if,while,switch, for語句是有了塊級作用域的,可以使用let關鍵字 宣告變數,修正了var關鍵字的缺點,注意let使用規則 2. 理解js執行環境: 所謂執行壞境,它定義了變數或函式有訪問的其他資料的能力,它決定了 各自的行為,它的側重點在於函式的作用域,而並不是所要糾結的上下文, 一旦函式一宣告定義,就會自動的分配產生了作用域,有著自己的執行壞 境,執行壞境可以分為建立與執行 兩個階段: a.第一階段階段,js解析器首先會建立一個變數物件(活動物件),它由 定義在執行壞境中的變數,函式宣告和引數組成,在這個階段,系統 會自動的產生一個this物件, 作用域鏈會被初始化,隨之,this的值也會被確定 b.第二階段,也就是程式碼執行,程式碼會被解釋執行,你會發現,每個執行 壞境都有一個與之關聯的變數物件,執行壞境中所有定義的變數和 函式都儲存在這個物件中, 注意: 我們是無法手動的訪問這個物件的,只有js解析器才能夠訪問它, 其實也就是this,儘管很抽象,但是理解它還是蠻重要的

		  3. 分析作用域鏈:
		        當javascript查詢與變數相關聯的值時,會遵循一定的規則,也就是沿

著作用域鏈從當前函式作用域內逐級的向上查詢,直到頂層全域性作用域結束,若找到則返回該值,若無則返回undefined,這個鏈條是基於作用域的層次結構的,一旦當代碼在壞境中執行時,會自動的建立一個變數物件的作用域鏈,其作用域鏈的用途也就是保證對執行壞境的全域性變數和具有訪問許可權函式內的區域性變數定製特殊的規則,由內到外有序的對變數或者函式進行訪問。這條抽象的鏈式結構我們就稱之為作用域鏈

		  4.閉包的形成(其原因就是作用域鏈造成的)
		        如果理解了上面的內容,那閉包也就好理解了,
				閉包在js高程中的解釋是:有權訪問另一個函式作用域中的變數的

函式。 簡答的說就是 假設函式a是定義在函式b中的函式,那麼函式a就是一個閉 包。正常情況下,在函式的外部訪問不到函式內部的變數,但有了閉包就可以 間接的實現訪問內部變數的需要。也就是說,閉包是連線函式內部和外部的橋 樑。這就是閉包的 第一個作用:訪問函式內部的變數。 還有另外一個作用就是:讓被引用的變數值始終保持在記憶體中。

		 5.閉包使用時需要注意的問題:
		        1、由於閉包會使得函式中被引用的變數一直儲存在記憶體中,消耗內

存,所以謹慎使用閉包,否則會造成網頁的效能問題。 2、閉包會改變父函式內部變數的值。如果父函式再次被執行的, 而在外部已經執行過閉包修改變數的值,那麼,這次執行的結 果就會和上次的不一樣。

	面試官:“這小子,難不住他,再問問...”
	       6. 哦哦 很不錯,你回答的很到位,那你能詳細解釋一下你剛才提到的eval作用域嗎?
		      我:“臥槽!死磕到底,放馬過來吧!!!”
              回答:首先這是一個js原生的內建方法
      eval函式是強大的數碼轉換引擎,字串經eval轉換後得到一個javascript物件一個物件經過eval轉換後資料型別不確定,在相加過程中自動與其他資料型別一致這裡為什麼會提到 eval作用域 呢?是因為當我們使用eval時,他可以被賦值給變數,例如   var evalg = eval;  evalg("alert(1)"); eval被賦值時,也會把當前eval所處的變數作用域也賦值過去;這樣就造成了 一個 作用域的混亂,所以這個我們平時也不用 太多坑...
					
	面試官:“OK啦!!,面試了三個月,終於找到一個合適的,恭喜您,正式成為我們公司產品部門的新成員!!”
	我:“我靠,玩勺子把去吧,哥去下家面試了!!!”			 

十五:你能解釋一下js中的繼承實現的原理和思路嗎? 概述: 說道繼承,js長得繼承可謂是五花八門,不止一種,這是因為 JavaScript(ES6之 前)中的繼承機制並不是明確規定的,而是通過模仿實現的。這意味著所有的繼承細節並非完全由解釋程式處理。可以根據需求決定適合的繼承方式。

	  1.物件冒充方式(類式繼承)
			  function ClassA(name) {
				this.name = name;
				this.sayName = function () {
					console.log(this.name);
				};
			}

			  function ClassB(name,age) {
				this.classA = ClassA;
				this.classA(name);
				delete this.classA;
				this.age = age;
				this.sayAge = function(){
					console.log(this.age);
				}
			}
建構函式使用this關鍵字給所有屬性和方法賦值(即採用類宣告的建構函式方式)。因為建構函式只是一個函式,所以可使ClassA建構函式成為ClassB的方法,然後呼叫它。ClassB就會收到ClassA的建構函式中定義的屬性和方法。
	注意:所有新屬性和新方法都必須在刪除了新方法的程式碼行後定義,因為可能會覆

蓋父類的相關屬性和方法

	  2.物件冒充也可以實現多重繼承
	     如果存在ClassA和ClassB,這時ClassC想繼承這兩個類,如下:
		    function ClassA(name){
				this.name = name;
				this.sayName = function (){
					console.log(this.name);
				}
			}

			function ClassB(age){
				this.age = age;
				this.sayAge = function(){
					console.log(this.age);
				}
			}

			function ClassC(name,age){
				this.method = ClassA;
				this.method(name);

				this.method = ClassB;
				this.method(age);
				delete this.method;
			}
        注意:這種實現方式的缺陷是:如果兩個類ClassA和ClassB具有同名的屬性或方法, ClassB具有高優先順序,因為它從後面的類繼承。由於這種繼承方法的流行, ECMAScript(ES3)的第三版為Function物件加入了兩個方法,即call()和apply()。
	    
	  3. 原型鏈繼承就很好理解了,例如:
	        function ClassA() {}
				ClassA.prototype.name = 'Tom';
				ClassA.prototype.sayName = function () {
					console.log(this.name);
				};

				function ClassB() {}
				ClassB.prototype = new ClassA();
				ClassB.prototype.age = 25;
				ClassB.prototype.sayAge = function () {
					console.log(this.age);
				};

				var tom = new ClassA();
				var jerry = new ClassB();
				tom.sayName();                         //'Tom'
				jerry.sayName();                       //'Tom'
				jerry.name = 'Jerry';
				tom.sayName();                         //'Tom'
				jerry.sayName();                       //'Jerry'
				jerry.sayAge();                        //25
				console.log(tom instanceof ClassA);    //true
				console.log(jerry instanceof ClassA);  //true
				console.log(jerry instanceof ClassB);  //true  
				
說道原型本質就是 複製貼上,在這裡:
這裡把ClassB的prototype屬性設定稱ClassA的例項,避免逐個賦值prototpye屬性。在呼叫ClassA時沒有設定引數,因為在原型鏈中要確保建構函式是無參的。在原型鏈中,instanceof的結果也有了變化,對於ClassA和ClassB都返回了true。因為prototype屬性的重指定,子類中的新屬性都必須出現在prototype被賦值後。
  1. 組合式繼承:由於原型鏈繼承沒辦法傳參,而冒充物件方式可以傳參,二者結 合就是組合繼承。

  2. 深入談談 call()和 apply() 唄! call概述: call方法是與經典的物件冒充方法最相似的方法。它的第一個引數用 作this的物件,其他引數都直接傳遞給函式自身 a.經典的用法: function sayName(a) { console.log(a + this.name); };

     				var tom = {};
     				tom.name = "Tom";
    
     				sayName.call(tom, 'This is ');  //'This is Tom'	
    
     			分析:函式sayName在物件外定義,但也可以引用this。
     			
     	      b. call() 方法改寫物件冒充
     		     function ClassA(name){
     				this.name = name;
     				this.sayName = function(){
     					console.log(this.name);
     				}
     			}
    
     			function ClassB(name,age){
     				//this.method = ClassA;
     				//this.method(name);
     				//delete this.method;
     				ClassA.call(this,name);
     				this.age = age;
     				this.sayAge = function (){
     					console.log(this.age);
     				}
     			}
    
     			var tom = new ClassB('Tom',25);
     			tom.sayName();                       //'Tom'
     			tom.sayAge();                        //25
     			console.log(tom instanceof ClassA);  //false
     			console.log(tom instanceof ClassB);  //true
                 分析:這是一個典型的 call 取代 物件冒充的方式
     			
             
         	apply概述:
                  apply方法有兩個引數,用作this的物件和要傳遞給函式的引數陣列
                  a. 例如:
     					 function sayName(prefex,mark) {
     							console.log(prefex+ this.name+ mark);
     						};
    
     						var tom = {};
     						tom.name = 'Tom';
    
     						sayName.apply(tom, ['This is ','!']);  //'This is Tom!'	
    
                  							
    
                   b. 同樣可以使用apply改寫物件冒充
     					 function ClassA(name){
     						this.name = name;
     						this.sayName = function(){
     							console.log(this.name);
     						}
     					}
    
     					function ClassB(name,age){
     						ClassA.apply(this,arguments);
     						this.age = age;
     						this.sayAge = function (){
     							console.log(this.age);
     						}
     					}
    
     					var tom = new ClassB('Tom',25);
     					tom.sayName();                       //'Tom'
     					tom.sayAge();                        //25  
     					console.log(tom instanceof ClassA);  //false
     					console.log(tom instanceof ClassB);  //true
     注意:只有父類中引數順序和子類中的引數完全一致時才可以傳遞引數陣列
    

十六:JavaScript 中 this 是如何工作的 ? 輔助用例: var x = 0; var foo = { x:1, bar:{ x:2, baz: function () { console.log(this.x) } } }

			var a = foo.bar.baz
			foo.bar.baz() // 2
			a() //0
   總結概述:
		1.this 永遠指向函式執行時所在的物件,而不是函式建立時所在的物件
        2.匿名函式和不處於任何物件中的函式,This指向window
        3.call, apply指的This是誰就是誰。
        4.普通函式呼叫,函式被誰呼叫,This就指向誰
		5.當然箭頭函式得先看他外層有沒有函式 有就是當前this 沒有就是window

十七:如何檢測物件型別?或者怎麼檢測一個數據是陣列型別? 檢測一個物件的型別,強烈推薦使用 Object.prototype.toString 方法; 因為這是唯一一個可依賴的方式。 我們使用Object.prototype.toString 方法: Object.prototype.toString.call([]) // “[object Array]” Object.prototype.toString.call({}) // “[object Object]” Object.prototype.toString.call(2) // “[object Number]” 注意: 為什麼不能用typeOf ? typeof只有在基本型別的檢測上面才好使,在引用型別(Function除外) 裡面他返回的都是object,另 typeof null === “object”

十八:你瞭解 事件流? 事件捕獲?事件冒泡?

  事件流:從頁面中接收事件的順序。也就是說當一個事件產生時,
          這個事件的傳播過程,就是事件流。

  IE中的事件流叫事件冒泡;
                事件冒泡:事件開始時由最具體的元素接收,然後逐級向上
				傳播到較為不具體的節點(文件)。對於html來說,
				就是當一個元素產生了一個事件,它會把這個事件傳遞給它的父元素,
				父元素接收到了之後,還要繼續傳遞給它的上一級元素,
				就這樣一直傳播到document物件
				(親測現在的瀏覽器到window物件,只有IE8及下不這樣

  事件捕獲是不太具體的元素應該更早接受到事件,而最具體的節點應該最後接收到事件。他們的用意是在事件到達目標之前就捕獲它;也就是跟冒泡的過程正好相反,以html的click事件為例,document物件(DOM級規範要求從document開始傳播,但是現在的瀏覽器是從window物件開始的)最先接收到click事件的然後事件沿著DOM樹依次向下傳播,一直傳播到事件的實際目標;

十九:如何清除一個定時器?

	window.clearInterval();
	window.clearTimeout();

二十:如何新增一個dom物件到body中?innerHTML和innerText區別? body.appendChild(dom元素); innerHTML:從物件的起始位置到終止位置的全部內容,包括Html標籤。 innerText:從起始位置到終止位置的內容, 但它去除Html標籤 分別簡述五個window物件、屬性

	成員物件 
	window.event  window.document  window.history 
	window.screen window.navigator window.external
	Window物件的屬性如下:
	window //窗戶自身
	window.self //引用本窗戶window=window.self 
	window.name //為窗戶命名 
	window.defaultStatus //設定窗戶狀態列資訊 
	window.location //URL地址,配備佈置這個屬性可以開啟新的頁面

二十一:你瞭解js的垃圾回收機制嗎?閉包會不會導致記憶體洩漏? 概述: JavaScript 具有自動垃圾收集機制,就是找出那些不再繼續使用的變數, 然後釋放其佔用的記憶體。為此,垃圾收集器會按照固定的時間間隔 (或程式碼執行中預定的收集時間)。常用的的方法有兩種,即標記清楚和引用數。

	   1.標記清除
	       JavaScript 中最常用的垃圾收集方式是標記清除(mark-and-sweep)。
		   垃圾收集器在執行的時候會給儲存在記憶體中的所有變數都加上標記
		   (可以使用任何標記方式)。然後,它會去掉環境中的變數以及被環
		   境中的變數引用的變數的標記。而在此之後再被加上標記的變數將被
		   視為準備刪除的變數,原因是環境中的變數已經無法訪問到這些變量了。
		   最後,垃圾收集器完成記憶體清除工作,銷燬那些帶標記的值並回收它們
		   所佔用的記憶體空間。
		   
	  

 2.引用計數

引用計數(reference counting)的含義是跟蹤記錄每個值被引用的次數。 當聲明瞭一個變數並將一個引用型別值賦給該變數時,則這個值的引用次數就是1。如果同一個值又被賦給另一個變數,則該值的引用次數加1。相反,如果包含對這個值引用的變數又取得了另外一個值,則這個值的引用次數減1。當這個值的引用次數變成0 時,則說明沒有辦法再訪問這個值了,因而就可以將其佔用的記憶體空間回收回來。這樣,當垃圾收集器下次再執行時,它就會釋放那些引用次數為零的值所佔用的記憶體。

  3. 閉包到底會不會 造成 記憶體洩漏呢?
	      由於IE9 之前的版本對JScript 物件和COM 物件使用不同的垃圾收集。
		  因此閉包在IE 的這些版本中會導致一些特殊的問題。具體來說,
		  如果閉包的作用域鏈中儲存著一個HTML 元素,那麼就意味著該元素將無

法被銷燬 請看例子: function assignHandler(){ var element = document.getElementById(“someElement”); element.onclick = function(){ alert(element.id); }; } 以上程式碼建立了一個作為element 元素事件處理程式的閉包,而這個閉包則又建立了一個迴圈引用。由於匿名函式儲存了一個對assignHandler()的活動物件的引用,因此就會導致無法減少element 的引用數。只要匿名函式存在, element 的引用數至少也是1,因此它所佔用的記憶體就永遠不會被回收 解決辦法: 已經提到過,把element.id 的一個副本儲存在一個變數中,從而消除閉 包中該變數的迴圈引用同時將element變數設為null。 function assignHandler(){ var element = document.getElementById(“someElement”); var id = element.id; element.onclick = function(){ alert(id); }; element = null; } 總結:閉包並不會引起記憶體洩漏,只是由於IE9之前的版本對JScript物件 和COM物件使用不同的垃圾收集,從而導致記憶體無法進行回收, 這是IE的問題,所以閉包和記憶體洩漏沒半毛錢關係。

二十二:你瞭解瀏覽器的執行緒機制嗎? 這個很難很難… but 我一說就不難了 1. 理解執行緒和程序 執行緒和程序區分不清,是很多新手都會犯的錯誤,沒有關係。這很正常。 先看看下面這個形象的比喻: - 程序是一個工廠,工廠有它的獨立資源

           - 工廠之間相互獨立

           - 執行緒是工廠中的工人,多個工人協作完成任務

           - 工廠內有一個或多個工人

           - 工人之間共享空間
	    
	    再完善完善概念:
		   - 工廠的資源 -> 系統分配的記憶體(獨立的一塊記憶體)

		   - 工廠之間的相互獨立 -> 程序之間相互獨立

		   - 多個工人協作完成任務 -> 多個執行緒在程序中協作完成任務

		   - 工廠內有一個或多個工人 -> 一個程序由一個或多個執行緒組成

	       - 工人之間共享空間 -> 同一程序下的各個執行緒之間共享程式的內
		     存空間(包括程式碼段、資料集、堆等)
			 
	    然後再鞏固下:
		    如果是windows電腦中,可以開啟工作管理員,可以看到有一個後臺程序列表。對,那裡就是檢視程序的地方,而且可以看到每個程序的記憶體資源資訊以及cpu佔有率。所以,應該更容易理解了:程序是cpu資源分配的最小單位(系統會給它分配記憶體)

最後,再用較為官方的術語描述一遍: 程序是cpu資源分配的最小單位(是能擁有資源和獨立執行的最小單位) 執行緒是cpu排程的最小單位(執行緒是建立在程序的基礎上的一次程式執行單位, 一個程序中可以有多個執行緒)

	2. 掌握瀏覽器是多程序的
       
	   理解了程序與執行緒了區別後,接下來對瀏覽器進行一定程度上的認識:(先看下

簡化理解)瀏覽器是多程序的瀏覽器之所以能夠執行,是因為系統給它的程序分配了資源(cpu、記憶體) 簡單點理解:每開啟一個Tab頁,就相當於建立了一個獨立的瀏覽器程序 注意:打開了Chrome瀏覽器的多個標籤頁,然後可以在Chrome的工作管理員中看到有多個程序(分別是每一個Tab頁面有一個獨立的程序,以及一個主程序)。 感興趣的可以自行嘗試下,如果再多開啟一個Tab頁,程序正常會+1以上在這裡瀏覽器應該也有自己的優化機制,有時候開啟多個tab頁後,可以在Chrome工作管理員中看到,有些程序被合併了(所以每一個Tab標籤對應一個程序並不一定是絕對的)

3.瀏覽器都包含哪些程序?
	    知道了瀏覽器是多程序後,再來看看它到底包含哪些程序:(為了簡化理解,僅列舉主要程序)
		a.Browser程序:瀏覽器的主程序(負責協調、主控),只有一個。作用有
          負責瀏覽器介面顯示,與使用者互動。如前進,後退等
          將Renderer程序得到的記憶體中的Bitmap,繪製到使用者介面上
          網路資源的管理,下載等

        b.第三方外掛程序:每種型別的外掛對應一個程序,僅當使用該外掛時才建立

        c.GPU程序:最多一個,用於3D繪製等

        d.瀏覽器渲染程序(瀏覽器核心)(Renderer程序,內部是多執行緒的):預設每

個Tab頁面一個程序,互不影響。主要作用為頁面渲染,腳 本執行,事件處理等

	4.瀏覽器多程序的優勢
	  a. 避免單個page crash影響整個瀏覽器

      b. 避免第三方外掛crash影響整個瀏覽器

      c. 多程序充分利用多核優勢

      d. 方便使用沙盒模型隔離外掛等程序,提高瀏覽器穩定性
	  
	  簡單一點說:
	      如果瀏覽器是單程序,那麼某個Tab頁崩潰了,就影響了整個瀏覽器,
		  體驗有多差;同理如果是單程序,外掛崩潰了也會影響整個瀏覽器;
		  而且多程序還有其它的諸多優勢。。。
		  
		  當然,記憶體等資源消耗也會更大,有利就有弊。
		  
    5.那麼重點中的重點來了:瀏覽器核心(渲染程序)
	      上面扯了那麼多,其實就是為了引出這個主題,對於普通的前端操作來說,
		  最終要的是什麼呢?答案是渲染程序
		  
		可以這樣理解,頁面的渲染,JS的執行,事件的迴圈,都在這個程序內進行。
		接下來重點分析這個程序:
		     請牢記,瀏覽器的渲染程序是多執行緒的
		   a.GUI渲染執行緒
			  負責渲染瀏覽器介面,解析HTML,CSS,構建DOM樹和RenderObject

樹,佈局和繪製等。當介面需要重繪(Repaint)或由於某種操作引發回 流(reflow)時,該執行緒就會執行 注意:GUI渲染執行緒與JS引擎執行緒是互斥的,當JS引擎執行時GUI執行緒 會被掛起(相當於被凍結了),GUI更新會被儲存在一個佇列中等 到JS引擎空閒時立即被執行。

           b.JS引擎執行緒
             也稱為JS核心,負責處理Javascript指令碼程式。(例如V8引擎)
             JS引擎執行緒負責解析Javascript指令碼,執行程式碼。
             JS引擎一直等待著任務佇列中任務的到來,然後加以處理,
			 瀏覽器無論什麼時候都只有一個JS執行緒在執行JS程式
             注意: GUI渲染執行緒與JS引擎執行緒是互斥的,所以如果JS執行的時間過

長,這樣就會造成頁面的渲染不連貫,導致頁面渲染載入阻塞。

           c.事件觸發執行緒
              歸屬於瀏覽器而不是JS引擎,用來控制事件迴圈(可以理解,
			  JS引擎自己都忙不過來,需要瀏覽器另開執行緒協助)
              當JS引擎執行程式碼塊如setTimeOut時(也可來自瀏覽器核心的其他線

程,如滑鼠點選、AJAX非同步請求等),會將對應任務新增到事件執行緒中當 對應的事件符合觸發條件被觸發時,該執行緒會把事件新增到待處理佇列 的隊尾,等待JS引擎的處理 注意: 由於JS的單執行緒關係,所以這些待處理佇列中的事件都得排隊 等待JS引擎處理(當JS引擎空閒時才會去執行)

           d.定時觸發器執行緒
              傳說中的setInterval與setTimeout所線上程
              瀏覽器定時計數器並不是由JavaScript引擎計數的,(因為JavaScript引擎是單執行緒的,如果處於阻塞執行緒狀態就會影響記計時的準確)

因此通過單獨執行緒來計時並觸發定時(計時完畢後,新增到事件佇列中, 等待JS引擎空閒後執行) 注意:W3C在HTML標準中規定,規定要求setTimeout中低於4ms的時 間間隔算為4ms。

           e.非同步http請求執行緒
              在XMLHttpRequest在連線後是通過瀏覽器新開一個執行緒請求
              將檢測到狀態變更時,如果設定有回撥函式,非同步執行緒就產生狀態變更

事件,將這個回撥再放入事件佇列中。再由JavaScript引擎執行。看到 這裡,如果覺得累了,可以先休息下,這些概念需要被消化理解,後面 還有什麼 特別難,那就只掌握這些就可以啦 以內後面的我也不會…